├── .gitignore ├── README.md ├── aligndiff ├── .gitignore ├── Makefile ├── README.md ├── algorithms │ ├── editdistance_dp.cpp │ ├── editdistance_linearspace.cpp │ ├── editdistance_ondgreedy.cpp │ ├── lcslength_dp.cpp │ ├── lcslength_linearspace.cpp │ ├── lcslength_ondgreedy.cpp │ ├── ses_dp.cpp │ ├── ses_linearspace.cpp │ ├── ses_ondgreedy.cpp │ └── ses_weavinglinearspace.cpp ├── aligndiff.cpp ├── aligndiff.h ├── main.cpp ├── optional.h ├── utility.cpp └── utility.h ├── approximate-winter ├── .gitignore ├── Makefile ├── MisspelledWords.txt ├── README.md ├── SINGLE.TXT ├── custom.txt └── main.cpp ├── diff-summer ├── .gitignore ├── Makefile ├── README.md └── main.cpp ├── fourfolium ├── .gitignore ├── CommonlyUsedUnicodeCharacters.md ├── Makefile ├── README.md ├── UnicodeData.txt └── main.cpp ├── githubapis ├── .gitignore ├── GitHubClient.cpp ├── GitHubClient.h ├── Makefile ├── README.md └── main.cpp ├── glsl-include ├── .gitignore ├── Makefile ├── README.md ├── main.cpp ├── test1.glsl ├── test2.glsl ├── test3.glsl └── test4.glsl ├── low-level-graphics-api └── README.md ├── mami ├── .gitignore ├── ArrayView.h ├── ContainerAlgorithm.h ├── EndPoint.cpp ├── EndPoint.h ├── Error.cpp ├── Error.h ├── IOService.cpp ├── IOService.h ├── Makefile ├── README.md ├── Socket.cpp ├── Socket.h └── example │ └── EchoServer │ └── main.cpp ├── matoi ├── .gitignore ├── Makefile ├── README.md ├── main.cpp └── thirdparty │ ├── ConvertUTF.c │ └── ConvertUTF.h ├── mostcommonwords ├── .gitignore ├── Makefile ├── README.md └── main.cpp ├── nakayamasan ├── .gitignore ├── Makefile ├── README.md └── main.cpp ├── naoi ├── .gitignore ├── Makefile ├── README.md └── main.cpp ├── natsume ├── .gitignore ├── Makefile ├── README.md └── main.cpp ├── nazuna ├── .gitignore ├── Makefile ├── README.md └── main.cpp ├── nori ├── .gitignore ├── CompileOptions.h ├── MSBuild.cpp ├── MSBuild.h ├── Makefile ├── README.md ├── XcodeProject.cpp ├── XcodeProject.h └── main.cpp ├── pomdog-refactor ├── .gitignore ├── Makefile ├── README.md └── main.cpp ├── rhodanthe ├── .gitignore ├── Makefile ├── README.md ├── main.cpp └── temp.md ├── room203 └── README.md ├── slackbot ├── .gitignore ├── HttpService.cpp ├── HttpService.h ├── HttpUtility.cpp ├── HttpUtility.h ├── Makefile ├── README.md ├── SlackClient.cpp ├── SlackClient.h ├── TerminalHelper.cpp ├── TerminalHelper.h ├── iTunesNowPlaying.cpp ├── iTunesNowPlaying.h └── main.cpp ├── somera ├── Any.h ├── CommandLineParser.cpp ├── CommandLineParser.h ├── Defer.h ├── FileSystem.cpp ├── FileSystem.h ├── Optional.h ├── README.md ├── StringHelper.cpp ├── StringHelper.h ├── SubprocessHelper.cpp ├── SubprocessHelper.h ├── UnicodeHelper.cpp ├── UnicodeHelper.h ├── signals │ ├── Connection.cpp │ ├── Connection.h │ ├── EventQueue.cpp │ ├── EventQueue.h │ ├── Signal.h │ └── detail │ │ ├── ForwardDeclarations.h │ │ └── SignalBody.h └── tests │ ├── Any.cpp │ ├── CommandLineParser.cpp │ ├── FileSystem.cpp │ ├── Optional.cpp │ ├── StringHelper.cpp │ └── main.cpp ├── typo-poi ├── .clang-format ├── .gitignore ├── C++_Common.txt ├── C++_Keywords.txt ├── Cocos2d-x.txt ├── English_Grammar.txt ├── English_Words.txt ├── Makefile ├── README.md └── source │ ├── ConsoleColor.cpp │ ├── ConsoleColor.h │ ├── EditDistance.cpp │ ├── EditDistance.h │ ├── Replacement.h │ ├── SpellCheck.h │ ├── SpellCheckMac.mm │ ├── SpellCheckWin.cpp │ ├── SpellChecker.cpp │ ├── SpellChecker.h │ ├── Typo.cpp │ ├── Typo.h │ ├── UTF8.cpp │ ├── UTF8.h │ ├── WordDiff.cpp │ ├── WordDiff.h │ ├── WordSegmenter.cpp │ ├── WordSegmenter.h │ ├── main.cpp │ └── thirdparty │ ├── ConvertUTF.c │ └── ConvertUTF.h └── what-is-this-file ├── .gitignore ├── Error.cpp ├── Error.h ├── Makefile ├── README.md ├── WhatIsThisFile.cpp ├── WhatIsThisFile.h └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.swp 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # daily-snippets 2 | 3 | A set of tools, libraries or code snippets that I develop as a hobby. 4 | This repository is mostly for personal use but feel free to fork and modify anywhere you like. 5 | It is also open source and distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | -------------------------------------------------------------------------------- /aligndiff/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | aligndiff 10 | 11 | # Visual Studio 12 | *.filters 13 | *.opensdf 14 | *.sdf 15 | *.sln 16 | *.vcxproj 17 | -------------------------------------------------------------------------------- /aligndiff/Makefile: -------------------------------------------------------------------------------- 1 | CXX = clang++ 2 | NORI = ../nori/bin/nori 3 | PRODUCT_NAME = aligndiff 4 | 5 | CXXFLAGS = \ 6 | -std=c++14 \ 7 | -stdlib=libc++ \ 8 | -O2 \ 9 | -Wall \ 10 | -I. 11 | HEADERS = \ 12 | *.h 13 | SOURCES = \ 14 | algorithms/editdistance_dp.cpp \ 15 | algorithms/editdistance_linearspace.cpp \ 16 | algorithms/editdistance_ondgreedy.cpp \ 17 | algorithms/lcslength_dp.cpp \ 18 | algorithms/lcslength_linearspace.cpp \ 19 | algorithms/lcslength_ondgreedy.cpp \ 20 | algorithms/ses_dp.cpp \ 21 | algorithms/ses_linearspace.cpp \ 22 | algorithms/ses_ondgreedy.cpp \ 23 | algorithms/ses_weavinglinearspace.cpp \ 24 | aligndiff.cpp \ 25 | utility.cpp \ 26 | main.cpp 27 | 28 | build: 29 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $(PRODUCT_NAME) $(SOURCES) $(LDLIBS) 30 | 31 | xcode: 32 | $(NORI) \ 33 | -generator=xcode \ 34 | -o $(PRODUCT_NAME) \ 35 | $(CXXFLAGS) \ 36 | $(LDFLAGS) \ 37 | $(HEADERS) \ 38 | $(SOURCES) \ 39 | $(LDLIBS) 40 | @xcodebuild -project $(PRODUCT_NAME).xcodeproj -configuration Release 41 | @mkdir -p bin 42 | @cp build/Release/$(PRODUCT_NAME) bin/$(PRODUCT_NAME) 43 | -------------------------------------------------------------------------------- /aligndiff/README.md: -------------------------------------------------------------------------------- 1 | # aligndiff 2 | 3 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 4 | 5 | ## Build and run 6 | 7 | **Build:** 8 | 9 | ```sh 10 | git clone https://github.com/mogemimi/daily-snippets.git 11 | cd daily-snippets/aligndiff 12 | 13 | # Setup build tool 14 | make -C ../nori bootstrap 15 | 16 | # Build 17 | make xcode 18 | 19 | # Run 20 | ./bin/aligndiff -help 21 | ``` 22 | -------------------------------------------------------------------------------- /aligndiff/algorithms/editdistance_dp.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "aligndiff.h" 4 | #include 5 | 6 | namespace aligndiff { 7 | namespace { 8 | 9 | void fillTable(const std::string& text1, const std::string& text2, std::vector & matrix) 10 | { 11 | const auto rows = static_cast(text1.size()) + 1; 12 | const auto columns = static_cast(text2.size()) + 1; 13 | 14 | // NOTE: 15 | // text1 = "type" 16 | // text2 = "typo" 17 | 18 | // NOTE: 19 | // _ t y p e 20 | // _ 0 0 0 0 0 21 | // t 0 0 0 0 0 22 | // y 0 0 0 0 0 23 | // p 0 0 0 0 0 24 | // o 0 0 0 0 0 25 | 26 | matrix.resize(rows * columns, 0); 27 | 28 | const auto mat = [&matrix, rows, columns](int row, int column) -> auto& { 29 | const auto index = row + rows * column; 30 | assert(index < static_cast(matrix.size())); 31 | return matrix[index]; 32 | }; 33 | 34 | // NOTE: 35 | // _ t y p e 36 | // _ 0 1 2 3 4 37 | // t 1 0 0 0 0 38 | // y 2 0 0 0 0 39 | // p 3 0 0 0 0 40 | // o 4 0 0 0 0 41 | 42 | for (int row = 1; row < rows; row++) { 43 | mat(row, 0) = row; 44 | } 45 | for (int column = 1; column < columns; column++) { 46 | mat(0, column) = column; 47 | } 48 | 49 | // NOTE: 50 | // _ t y p e 51 | // _ 0 1 2 3 4 52 | // t 1 0 1 2 3 53 | // y 2 1 0 1 2 54 | // p 3 2 1 0 1 55 | // o 4 3 2 1 2 56 | 57 | for (int row = 1; row < rows; row++) { 58 | for (int column = 1; column < columns; column++) { 59 | auto minCost = std::min(mat(row - 1, column), mat(row, column - 1)) + 1; 60 | if (text1[row - 1] == text2[column - 1]) { 61 | minCost = std::min(mat(row - 1, column - 1), minCost); 62 | } 63 | mat(row, column) = minCost; 64 | } 65 | } 66 | } 67 | 68 | } // end of anonymous namespace 69 | 70 | void printEditGraphTableAsString(const std::string& text1, const std::string& text2) 71 | { 72 | const auto rows = static_cast(text1.size()) + 1; 73 | const auto columns = static_cast(text2.size()) + 1; 74 | 75 | std::vector matrix; 76 | fillTable(text1, text2, matrix); 77 | 78 | const auto mat = [&matrix, rows, columns](int row, int column) -> auto& { 79 | const auto index = row + rows * column; 80 | assert(index < static_cast(matrix.size())); 81 | return matrix[index]; 82 | }; 83 | 84 | std::printf("\n"); 85 | for (int row = 0; row < rows; row++) { 86 | for (int column = 0; column < columns; column++) { 87 | std::printf("%3d", mat(row, column)); 88 | } 89 | std::printf("\n"); 90 | } 91 | std::printf("\n"); 92 | } 93 | 94 | int computeLevenshteinDistance_DynamicProgramming(const std::string& text1, const std::string& text2) 95 | { 96 | std::vector matrix; 97 | fillTable(text1, text2, matrix); 98 | 99 | // NOTE: The following code is the same as "return mat(rows - 1, columns - 1);". 100 | return matrix.back(); 101 | } 102 | 103 | } // namespace aligndiff 104 | -------------------------------------------------------------------------------- /aligndiff/algorithms/editdistance_linearspace.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "aligndiff.h" 4 | #include 5 | 6 | namespace aligndiff { 7 | 8 | int computeLevenshteinDistance_LinearSpace( 9 | const std::string& text1, 10 | const std::string& text2) 11 | { 12 | // NOTE: 13 | // This algorithm is based on dynamic programming, using only linear space. 14 | // It is O(N^2) time and O(N) space algorithm. 15 | 16 | const auto rows = static_cast(text1.size()) + 1; 17 | const auto columns = static_cast(text2.size()) + 1; 18 | std::vector c1(columns); 19 | std::vector c2(columns); 20 | 21 | for (int i = 0; i < columns; ++i) { 22 | c1[i] = i; 23 | } 24 | 25 | for (int row = 1; row < rows; row++) { 26 | c2[0] = row; 27 | for (int column = 1; column < columns; column++) { 28 | auto minCost = std::min(c1[column], c2[column - 1]) + 1; 29 | if (text1[row - 1] == text2[column - 1]) { 30 | minCost = std::min(c1[column - 1], minCost); 31 | } 32 | c2[column] = minCost; 33 | } 34 | // NOTE: Use std::swap() function instead of "c1 = c2" to assign faster. 35 | std::swap(c1, c2); 36 | } 37 | return c1.back(); 38 | } 39 | 40 | } // namespace aligndiff 41 | -------------------------------------------------------------------------------- /aligndiff/algorithms/editdistance_ondgreedy.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "aligndiff.h" 4 | #include 5 | #include 6 | 7 | namespace aligndiff { 8 | 9 | int computeLevenshteinDistance_ONDGreedyAlgorithm( 10 | const std::string& text1, const std::string& text2) 11 | { 12 | // NOTE: 13 | // This algorithm is based on Myers's An O((M+N)D) Greedy Algorithm in 14 | // "An O(ND)Difference Algorithm and Its Variations", 15 | // Algorithmica (1986), pages 251-266. 16 | 17 | if (text1.empty() || text2.empty()) { 18 | return static_cast(std::max(text1.size(), text2.size())); 19 | } 20 | 21 | const auto M = static_cast(text1.size()); 22 | const auto N = static_cast(text2.size()); 23 | 24 | const auto maxD = M + N; 25 | const auto offset = N; 26 | 27 | std::vector vertices(M + N + 1); 28 | #if !defined(NDEBUG) 29 | // NOTE: 30 | // There is no need to initialize with the zero value for array elements, 31 | // but you have to assign the zero value to `vertices[1 + offset]`. 32 | std::fill(std::begin(vertices), std::end(vertices), -1); 33 | #endif 34 | vertices[1 + offset] = 0; 35 | 36 | for (int d = 0; d <= maxD; ++d) { 37 | const int startK = -std::min(d, (N * 2) - d); 38 | const int endK = std::min(d, (M * 2) - d); 39 | 40 | assert((-N <= startK) && (endK <= M)); 41 | assert(std::abs(startK % 2) == (d % 2)); 42 | assert(std::abs(endK % 2) == (d % 2)); 43 | assert((d > N) ? (startK == -(N * 2 - d)) : (startK == -d)); 44 | assert((d > M) ? (endK == (M * 2 - d)) : (endK == d)); 45 | 46 | for (int k = startK; k <= endK; k += 2) { 47 | assert((-N <= k) && (k <= M)); 48 | assert(std::abs(k % 2) == (d % 2)); 49 | 50 | const auto kOffset = k + offset; 51 | 52 | int x = 0; 53 | if (k == startK) { 54 | // NOTE: Move directly from vertex(x, y - 1) to vertex(x, y) 55 | x = vertices[kOffset + 1]; 56 | } 57 | else if (k == endK) { 58 | // NOTE: Move directly from vertex(x - 1, y) to vertex(x, y) 59 | x = vertices[kOffset - 1] + 1; 60 | } 61 | else if (vertices[kOffset - 1] < vertices[kOffset + 1]) { 62 | // NOTE: Move from vertex(k + 1) to vertex(k) 63 | // vertex(k + 1) is ahead of vertex(k - 1). 64 | assert(-N < k && k < M); 65 | assert((k != -d) && (k != -N)); 66 | assert((k != d) && (k != M)); 67 | x = vertices[kOffset + 1]; 68 | } 69 | else { 70 | // NOTE: Move from vertex(k - 1) to vertex(k) 71 | // vertex(k - 1) is ahead of vertex(k + 1). 72 | assert(-N < k && k < M); 73 | assert((k != -d) && (k != -N)); 74 | assert((k != d) && (k != M)); 75 | assert(vertices[kOffset - 1] >= vertices[kOffset + 1]); 76 | x = vertices[kOffset - 1] + 1; 77 | } 78 | 79 | // NOTE: `k` is defined from `x - y = k`. 80 | int y = x - k; 81 | assert(x >= 0 && y >= 0); 82 | 83 | #if !defined(NDEBUG) 84 | if (d == 0) { 85 | assert((x == 0) && (y == 0) && (k == 0)); 86 | } 87 | #endif 88 | 89 | while (x < M && y < N && text1[x] == text2[y]) { 90 | // NOTE: This loop finds a possibly empty sequence 91 | // of diagonal edges called a 'snake'. 92 | x += 1; 93 | y += 1; 94 | } 95 | 96 | vertices[kOffset] = x; 97 | if (x >= M && y >= N) { 98 | return d; 99 | } 100 | } 101 | } 102 | return M + N; 103 | } 104 | 105 | } // namespace aligndiff 106 | -------------------------------------------------------------------------------- /aligndiff/algorithms/lcslength_dp.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "aligndiff.h" 4 | #include 5 | 6 | namespace aligndiff { 7 | 8 | int computeLCSLength_DynamicProgramming( 9 | const std::string& text1, 10 | const std::string& text2) 11 | { 12 | if (text1.empty() || text2.empty()) { 13 | return 0; 14 | } 15 | 16 | const auto rows = static_cast(text1.size()) + 1; 17 | const auto columns = static_cast(text2.size()) + 1; 18 | 19 | // NOTE: 20 | // text1 = "type" 21 | // text2 = "typo" 22 | 23 | // NOTE: 24 | // _ t y p e 25 | // _ 0 0 0 0 0 26 | // t 0 0 0 0 0 27 | // y 0 0 0 0 0 28 | // p 0 0 0 0 0 29 | // o 0 0 0 0 0 30 | 31 | std::vector matrix(rows * columns, 0); 32 | 33 | const auto mat = [&matrix, rows, columns](int row, int column) -> auto& { 34 | const auto index = row + rows * column; 35 | assert(index < static_cast(matrix.size())); 36 | return matrix[index]; 37 | }; 38 | 39 | // NOTE: 40 | // _ t y p e 41 | // _ 0 0 0 0 0 42 | // t 0 0 0 0 0 43 | // y 0 0 0 0 0 44 | // p 0 0 0 0 0 45 | // o 0 0 0 0 0 46 | 47 | for (int row = 1; row < rows; row++) { 48 | mat(row, 0) = 0; 49 | } 50 | for (int column = 1; column < columns; column++) { 51 | mat(0, column) = 0; 52 | } 53 | 54 | // NOTE: 55 | // _ t y p e 56 | // _ 0 0 0 0 0 57 | // t 0 1 1 1 1 58 | // y 0 1 2 2 2 59 | // p 0 1 2 3 3 60 | // o 0 1 2 3 3 61 | 62 | for (int row = 1; row < rows; row++) { 63 | for (int column = 1; column < columns; column++) { 64 | if (text1[row - 1] == text2[column - 1]) { 65 | mat(row, column) = mat(row - 1, column - 1) + 1; 66 | } 67 | else { 68 | mat(row, column) = std::max(mat(row - 1, column), mat(row, column - 1)); 69 | } 70 | } 71 | } 72 | return mat(rows - 1, columns - 1); 73 | } 74 | 75 | } // namespace aligndiff 76 | -------------------------------------------------------------------------------- /aligndiff/algorithms/lcslength_linearspace.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "aligndiff.h" 4 | #include 5 | 6 | namespace aligndiff { 7 | namespace { 8 | 9 | std::vector LCS_Column( 10 | const std::string& x, 11 | const std::string& y, 12 | std::size_t m, 13 | std::size_t n) 14 | { 15 | #if 0 16 | // NOTE: Algorithms on Strings, p266, LCS-COLUMN(x,m,y,n) 17 | std::vector c1(m + 1); 18 | for (auto & c : c1) { 19 | c = 0; 20 | } 21 | std::vector c2(m + 1); 22 | for (std::size_t j = 0; j < n; ++j) { 23 | c2[0] = 0; 24 | for (std::size_t i = 0; i < m; ++i) { 25 | if (x[i] == y[j]) { 26 | c2[i + 1] = c1[i] + 1; 27 | } 28 | else { 29 | c2[i + 1] = std::max(c1[i + 1], c2[i]); 30 | } 31 | } 32 | c1 = c2; 33 | } 34 | return c1; 35 | #else 36 | const auto rows = static_cast(x.size()) + 1; 37 | const auto columns = static_cast(y.size()) + 1; 38 | 39 | std::vector c1(columns); 40 | for (int i = 0; i < columns; ++i) { 41 | c1[i] = i; 42 | } 43 | std::vector c2(columns); 44 | 45 | for (int row = 1; row < rows; row++) { 46 | c2[0] = row; 47 | for (int column = 1; column < columns; column++) { 48 | auto minCost = std::min(c1[column], c2[column - 1]) + 1; 49 | if (x[row - 1] == y[column - 1]) { 50 | minCost = std::min(c1[column - 1], minCost); 51 | } 52 | c2[column] = minCost; 53 | } 54 | c1 = c2; 55 | } 56 | 57 | return c1; 58 | #endif 59 | } 60 | 61 | } // end of anonymous namespace 62 | 63 | int computeLCSLength_LinearSpace(const std::string& text1, const std::string& text2) 64 | { 65 | auto lcsColumn = LCS_Column(text1, text2, static_cast(text1.size()), static_cast(text2.size())); 66 | auto lcsLength = lcsColumn.back(); 67 | 68 | #if 0 69 | std::printf("{"); 70 | for (int i = 0; i < (int)lcsColumn.size(); ++i) { 71 | std::printf("%2d, ", lcsColumn[i]); 72 | } 73 | std::printf("}\n"); 74 | #endif 75 | 76 | return lcsLength; 77 | } 78 | 79 | } // namespace aligndiff 80 | -------------------------------------------------------------------------------- /aligndiff/algorithms/lcslength_ondgreedy.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "aligndiff.h" 4 | #include 5 | #include 6 | 7 | namespace aligndiff { 8 | 9 | int computeLCSLength_ONDGreedyAlgorithm( 10 | const std::string& text1, 11 | const std::string& text2) 12 | { 13 | // NOTE: 14 | // This algorithm is based on Myers's An O((M+N)D) Greedy Algorithm in 15 | // "An O(ND)Difference Algorithm and Its Variations", 16 | // Algorithmica (1986), pages 251-266. 17 | 18 | if (text1.empty() || text2.empty()) { 19 | return 0; 20 | } 21 | 22 | const auto M = static_cast(text1.size()); 23 | const auto N = static_cast(text2.size()); 24 | 25 | const auto maxD = M + N; 26 | const auto offset = N; 27 | 28 | std::vector vertices(M + N + 1); 29 | #if !defined(NDEBUG) 30 | // NOTE: 31 | // There is no need to initialize with the zero value for array elements, 32 | // but you have to assign the zero value to `vertices[1 + offset]`. 33 | std::fill(std::begin(vertices), std::end(vertices), -1); 34 | #endif 35 | vertices[1 + offset] = 0; 36 | 37 | std::vector lcsLengths(M + N + 1); 38 | lcsLengths[1 + offset] = 0; 39 | 40 | for (int d = 0; d <= maxD; ++d) { 41 | const int startK = -std::min(d, (N * 2) - d); 42 | const int endK = std::min(d, (M * 2) - d); 43 | 44 | assert((-N <= startK) && (endK <= M)); 45 | assert(std::abs(startK % 2) == (d % 2)); 46 | assert(std::abs(endK % 2) == (d % 2)); 47 | assert((d > N) ? (startK == -(N * 2 - d)) : (startK == -d)); 48 | assert((d > M) ? (endK == (M * 2 - d)) : (endK == d)); 49 | 50 | for (int k = startK; k <= endK; k += 2) { 51 | assert((-N <= k) && (k <= M)); 52 | assert(std::abs(k % 2) == (d % 2)); 53 | 54 | const auto kOffset = k + offset; 55 | 56 | int x = 0; 57 | int lcsLength = 0; 58 | if (k == startK) { 59 | // NOTE: Move directly from vertex(x, y - 1) to vertex(x, y) 60 | x = vertices[kOffset + 1]; 61 | lcsLength = lcsLengths[kOffset + 1]; 62 | } 63 | else if (k == endK) { 64 | // NOTE: Move directly from vertex(x - 1, y) to vertex(x, y) 65 | x = vertices[kOffset - 1] + 1; 66 | lcsLength = lcsLengths[kOffset - 1]; 67 | } 68 | else if (vertices[kOffset - 1] < vertices[kOffset + 1]) { 69 | // NOTE: Move from vertex(k + 1) to vertex(k) 70 | // vertex(k + 1) is ahead of vertex(k - 1). 71 | assert(-N < k && k < M); 72 | assert((k != -d) && (k != -N)); 73 | assert((k != d) && (k != M)); 74 | x = vertices[kOffset + 1]; 75 | lcsLength = lcsLengths[kOffset + 1]; 76 | } 77 | else { 78 | // NOTE: Move from vertex(k - 1) to vertex(k) 79 | // vertex(k - 1) is ahead of vertex(k + 1). 80 | assert(-N < k && k < M); 81 | assert((k != -d) && (k != -N)); 82 | assert((k != d) && (k != M)); 83 | assert(vertices[kOffset - 1] >= vertices[kOffset + 1]); 84 | x = vertices[kOffset - 1] + 1; 85 | lcsLength = lcsLengths[kOffset - 1]; 86 | } 87 | 88 | // NOTE: `k` is defined from `x - y = k`. 89 | int y = x - k; 90 | assert(x >= 0 && y >= 0); 91 | 92 | #if !defined(NDEBUG) 93 | if (d == 0) { 94 | assert((x == 0) && (y == 0) && (k == 0)); 95 | } 96 | #endif 97 | 98 | while (x < M && y < N && text1[x] == text2[y]) { 99 | // NOTE: This loop finds a possibly empty sequence 100 | // of diagonal edges called a 'snake'. 101 | x += 1; 102 | y += 1; 103 | lcsLength += 1; 104 | } 105 | 106 | if (x >= M && y >= N) { 107 | return lcsLength; 108 | } 109 | 110 | vertices[kOffset] = x; 111 | lcsLengths[kOffset] = lcsLength; 112 | } 113 | } 114 | return 0; 115 | } 116 | 117 | } // namespace aligndiff 118 | -------------------------------------------------------------------------------- /aligndiff/algorithms/ses_dp.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "aligndiff.h" 4 | #include "optional.h" 5 | #include 6 | 7 | namespace aligndiff { 8 | 9 | std::vector computeShortestEditScript_DynamicProgramming( 10 | const std::string& text1, 11 | const std::string& text2) 12 | { 13 | if (text1.empty() && text2.empty()) { 14 | return {}; 15 | } 16 | 17 | const auto rows = static_cast(text1.size()) + 1; 18 | const auto columns = static_cast(text2.size()) + 1; 19 | 20 | std::vector matrix(rows * columns, 0); 21 | 22 | const auto mat = [&matrix, rows, columns](int row, int column) -> auto& { 23 | const auto index = row + rows * column; 24 | assert(index < static_cast(matrix.size())); 25 | return matrix[index]; 26 | }; 27 | 28 | for (int row = 1; row < rows; row++) { 29 | mat(row, 0) = row; 30 | } 31 | for (int column = 1; column < columns; column++) { 32 | mat(0, column) = column; 33 | } 34 | 35 | // levenshtein distance 36 | for (int row = 1; row < rows; row++) { 37 | for (int column = 1; column < columns; column++) { 38 | auto minCost = std::min(mat(row - 1, column), mat(row, column - 1)) + 1; 39 | if (text1[row - 1] == text2[column - 1]) { 40 | minCost = std::min(mat(row - 1, column - 1), minCost); 41 | } 42 | mat(row, column) = minCost; 43 | } 44 | } 45 | 46 | #if 0 47 | std::printf("\n"); 48 | for (int row = 0; row < rows; row++) { 49 | for (int column = 0; column < columns; column++) { 50 | std::printf("%3d", mat(row, column)); 51 | } 52 | std::printf("\n"); 53 | } 54 | std::printf("\n"); 55 | #endif 56 | 57 | std::vector edits; 58 | 59 | int row = rows - 1; 60 | int column = columns - 1; 61 | aligndiff::optional longestCommonSubsequence; 62 | 63 | while ((row > 0) && (column > 0)) { 64 | // edit costs 65 | const auto deletion = mat(row - 1, column); 66 | const auto insertion = mat(row, column - 1); 67 | const auto equality = mat(row - 1, column - 1); 68 | 69 | if (longestCommonSubsequence 70 | && (*longestCommonSubsequence == mat(row, column)) 71 | && (text1[row - 1] == text2[column - 1])) { 72 | edits.push_back(makeDiffEdit(text1[row - 1], DiffOperation::Equality)); 73 | --row; 74 | --column; 75 | continue; 76 | } 77 | 78 | longestCommonSubsequence = aligndiff::nullopt; 79 | 80 | if ((text1[row - 1] == text2[column - 1]) 81 | && (equality < deletion) 82 | && (equality < insertion)) { 83 | edits.push_back(makeDiffEdit(text1[row - 1], DiffOperation::Equality)); 84 | --row; 85 | --column; 86 | longestCommonSubsequence = mat(row, column); 87 | } 88 | else if (deletion < insertion) { 89 | edits.push_back(makeDiffEdit(text1[row - 1], DiffOperation::Deletion)); 90 | --row; 91 | } 92 | else { 93 | edits.push_back(makeDiffEdit(text2[column - 1], DiffOperation::Insertion)); 94 | --column; 95 | } 96 | } 97 | 98 | while (column > 0) { 99 | edits.push_back(makeDiffEdit(text2[column - 1], DiffOperation::Insertion)); 100 | --column; 101 | } 102 | while (row > 0) { 103 | edits.push_back(makeDiffEdit(text1[row - 1], DiffOperation::Deletion)); 104 | --row; 105 | } 106 | 107 | std::reverse(std::begin(edits), std::end(edits)); 108 | return edits; 109 | } 110 | 111 | } // namespace aligndiff 112 | -------------------------------------------------------------------------------- /aligndiff/aligndiff.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "aligndiff.h" 4 | #include 5 | 6 | namespace aligndiff { 7 | 8 | DiffEdit makeDiffEdit(char character, DiffOperation operation) 9 | { 10 | DiffEdit edit; 11 | edit.character = character; 12 | edit.operation = operation; 13 | return edit; 14 | } 15 | 16 | void sortDiffEdits(std::vector & edits) 17 | { 18 | for (size_t k = 0; k < edits.size(); ++k) { 19 | bool swapped = false; 20 | for (size_t i = 1 + (k % 2); i < edits.size(); i += 2) { 21 | auto & a = edits[i - 1]; 22 | auto & b = edits[i]; 23 | if ((a.operation == DiffOperation::Insertion) && (b.operation == DiffOperation::Deletion)) { 24 | std::swap(a, b); 25 | swapped = true; 26 | } 27 | else if ((a.character == b.character) && (a.operation < b.operation)) { 28 | static_assert(DiffOperation::Deletion < DiffOperation::Equality, ""); 29 | static_assert(DiffOperation::Equality < DiffOperation::Insertion, ""); 30 | std::swap(a, b); 31 | swapped = true; 32 | } 33 | } 34 | if (!swapped) { 35 | break; 36 | } 37 | } 38 | } 39 | 40 | std::vector convertToDiffHunk(const std::vector& edits) 41 | { 42 | std::vector hunks; 43 | for (const auto& edit : edits) { 44 | if (hunks.empty() || (hunks.back().operation != edit.operation)) { 45 | DiffHunk hunk; 46 | hunk.text = edit.character; 47 | hunk.operation = edit.operation; 48 | hunks.push_back(std::move(hunk)); 49 | } 50 | else { 51 | assert(hunks.back().operation == edit.operation); 52 | hunks.back().text += edit.character; 53 | } 54 | } 55 | return hunks; 56 | } 57 | 58 | std::string convertToLCS(const std::vector& edits) 59 | { 60 | std::string lcs; 61 | for (const auto& edit : edits) { 62 | if (edit.operation == DiffOperation::Equality) { 63 | lcs += edit.character; 64 | } 65 | } 66 | return lcs; 67 | } 68 | 69 | } // namespace aligndiff 70 | -------------------------------------------------------------------------------- /aligndiff/aligndiff.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include 4 | #include 5 | 6 | namespace aligndiff { 7 | 8 | enum class DiffOperation { 9 | Equality = 0, 10 | Insertion = 1, 11 | Deletion = -1, 12 | }; 13 | 14 | struct DiffEdit { 15 | char character; 16 | DiffOperation operation; 17 | }; 18 | 19 | struct DiffHunk { 20 | std::string text; 21 | DiffOperation operation; 22 | }; 23 | 24 | // ------------------------------- 25 | // Utility 26 | // ------------------------------- 27 | 28 | ///@brief Create a new operation of an edit-script. 29 | DiffEdit makeDiffEdit(char character, DiffOperation operation); 30 | 31 | ///@brief Sort sequence of edits (called an edit-script) for readability, using odd-even sort. 32 | void sortDiffEdits(std::vector & edits); 33 | 34 | ///@brief Convert edit-script to UNIX's 'diff'-like hunks. 35 | std::vector convertToDiffHunk(const std::vector& edits); 36 | 37 | ///@brief Convert edit-script to longest common subsequence (LCS). 38 | std::string convertToLCS(const std::vector& edits); 39 | 40 | ///@brief Print m x n DP table (called a edit-graph). 41 | void printEditGraphTableAsString( 42 | const std::string& text1, 43 | const std::string& text2); 44 | 45 | // ------------------------------- 46 | // Edit distance algorithms 47 | // ------------------------------- 48 | 49 | ///@brief Compute levenshtein distance using dynamic programming, in O(mn) time and O(mn) space. 50 | int computeLevenshteinDistance_DynamicProgramming( 51 | const std::string& text1, 52 | const std::string& text2); 53 | 54 | ///@brief Compute levenshtein distance using dynamic programming with linear-space refinement, in O(mn) time and O(m) space. 55 | int computeLevenshteinDistance_LinearSpace( 56 | const std::string& text1, 57 | const std::string& text2); 58 | 59 | ///@brief Compute levenshtein distance using O(ND) greedy algorithm, in O((m+n)D) time and O(m + n) space. 60 | int computeLevenshteinDistance_ONDGreedyAlgorithm( 61 | const std::string& text1, 62 | const std::string& text2); 63 | 64 | // ------------------------------- 65 | // LCS length algorithms 66 | // ------------------------------- 67 | 68 | ///@brief Compute LCS length using dynamic programming, in O(mn) time and O(mn) space. 69 | int computeLCSLength_DynamicProgramming( 70 | const std::string& text1, 71 | const std::string& text2); 72 | 73 | ///@brief Compute LCS length using dynamic programming with linear-space refinement, in O(mn) time and O(m) space. 74 | int computeLCSLength_LinearSpace( 75 | const std::string& text1, 76 | const std::string& text2); 77 | 78 | ///@brief Compute LCS length using O(ND) greedy algorithm, in O((m+n)D) time and O(m + n) space. 79 | int computeLCSLength_ONDGreedyAlgorithm( 80 | const std::string& text1, 81 | const std::string& text2); 82 | 83 | // ------------------------------- 84 | // Shortest edit script algorithms 85 | // ------------------------------- 86 | 87 | ///@brief Compute shortest edit script (SES) using dynamic programming, in O(mn) time and O(mn) space. 88 | std::vector computeShortestEditScript_DynamicProgramming( 89 | const std::string& text1, 90 | const std::string& text2); 91 | 92 | ///@brief Compute shortest edit script (SES) using divide and conquer, in O(mn) time and O(m + n) space. 93 | std::vector computeShortestEditScript_LinearSpace( 94 | const std::string& text1, 95 | const std::string& text2); 96 | 97 | ///@brief Compute shortest edit script (SES) using O(ND) greedy algorithm in O(mn) time and O(mn) space. 98 | ///@note This function doesn't use the 'divide and conquer' refinement (please see also Myers 1987 "Refinements 4b"). 99 | std::vector computeShortestEditScript_ONDGreedyAlgorithm( 100 | const std::string& text1, 101 | const std::string& text2); 102 | 103 | ///@brief Compute shortest edit script (SES) using divide and conquer and 'weaving' refinement, in O(mn) time and O(m + n) space. 104 | std::vector computeShortestEditScript_WeaveingLinearSpace( 105 | const std::string& text1, 106 | const std::string& text2); 107 | 108 | } // namespace aligndiff 109 | -------------------------------------------------------------------------------- /aligndiff/optional.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace aligndiff { 9 | 10 | // TODO: 11 | // In C++17, this 'Optional' class should be marked as deprecated 12 | // in favour of std::optional. 13 | 14 | struct NullOptionalType final { 15 | struct init { constexpr init() = default; }; 16 | constexpr explicit NullOptionalType(init) noexcept {} 17 | }; 18 | 19 | constexpr NullOptionalType nullopt{ NullOptionalType::init{} }; 20 | 21 | template 22 | struct optional { 23 | private: 24 | T data; 25 | bool valid = false; 26 | 27 | public: 28 | constexpr optional() 29 | : data() 30 | , valid(false) 31 | {} 32 | 33 | constexpr optional(NullOptionalType const&) 34 | : data() 35 | , valid(false) 36 | {} 37 | 38 | optional(optional const&) = default; 39 | optional(optional &&) = default; 40 | 41 | constexpr optional(T const& v) 42 | : data(v) 43 | , valid(true) 44 | {} 45 | 46 | constexpr optional(T && v) 47 | : data(std::move(v)) 48 | , valid(true) 49 | {} 50 | 51 | optional & operator=(NullOptionalType const&) 52 | { 53 | valid = false; 54 | data.~T(); 55 | return *this; 56 | } 57 | 58 | optional & operator=(optional const&) = default; 59 | optional & operator=(optional &&) = default; 60 | 61 | optional & operator=(T const& v) 62 | { 63 | this->valid = true; 64 | this->data = v; 65 | return *this; 66 | } 67 | 68 | optional & operator=(T && v) 69 | { 70 | this->valid = true; 71 | this->data = std::move(v); 72 | return *this; 73 | } 74 | 75 | T const* operator->() const noexcept 76 | { 77 | assert(valid); 78 | return &data; 79 | } 80 | 81 | T* operator->() noexcept 82 | { 83 | assert(valid); 84 | return &data; 85 | } 86 | 87 | T const& operator*() const 88 | { 89 | assert(valid); 90 | return data; 91 | } 92 | 93 | T & operator*() 94 | { 95 | assert(valid); 96 | return data; 97 | } 98 | 99 | explicit operator bool() const noexcept 100 | { 101 | return valid; 102 | } 103 | 104 | T const& value() const 105 | { 106 | assert(valid); 107 | return data; 108 | } 109 | 110 | T & value() 111 | { 112 | assert(valid); 113 | return data; 114 | } 115 | }; 116 | 117 | } // namespace aligndiff 118 | -------------------------------------------------------------------------------- /aligndiff/utility.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "utility.h" 4 | #include 5 | 6 | namespace aligndiff { 7 | 8 | ArgumentsParseResult parseArguments(int argc, const char *argv[]) 9 | { 10 | assert(argc > 0); 11 | assert(argv != nullptr); 12 | 13 | ArgumentsParseResult result; 14 | if (argc >= 1) { 15 | result.executablePath = argv[0]; 16 | } 17 | if (argc >= 2) { 18 | auto argument = argv[1]; 19 | if (argument[0] == '-') { 20 | result.operation = argument; 21 | } 22 | } 23 | for (int i = 2; i < argc; i++) { 24 | auto argument = argv[i]; 25 | result.parameters.push_back(argument); 26 | } 27 | return result; 28 | } 29 | 30 | } // namespace aligndiff 31 | -------------------------------------------------------------------------------- /aligndiff/utility.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include 4 | #include 5 | 6 | namespace aligndiff { 7 | 8 | struct ArgumentsParseResult final { 9 | std::string executablePath; 10 | std::string operation; 11 | std::vector parameters; 12 | }; 13 | 14 | ArgumentsParseResult parseArguments(int argc, const char *argv[]); 15 | 16 | } // namespace aligndiff 17 | -------------------------------------------------------------------------------- /approximate-winter/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | 10 | # Visual Studio 11 | *.filters 12 | *.opensdf 13 | *.sdf 14 | *.sln 15 | *.vcxproj 16 | -------------------------------------------------------------------------------- /approximate-winter/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = approximate-winter 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | ../typo-poi/source/thirdparty/*.h \ 7 | ../typo-poi/source/*.h \ 8 | *.h 9 | 10 | SOURCES = \ 11 | ../somera/*.cpp \ 12 | ../typo-poi/source/thirdparty/ConvertUTF.c \ 13 | ../typo-poi/source/ConsoleColor.cpp \ 14 | ../typo-poi/source/EditDistance.cpp \ 15 | ../typo-poi/source/SpellChecker.cpp \ 16 | ../typo-poi/source/UTF8.cpp \ 17 | ../typo-poi/source/WordDiff.cpp \ 18 | ../typo-poi/source/WordSegmenter.cpp \ 19 | main.cpp 20 | 21 | xcode: 22 | $(NORI) \ 23 | -generator=xcode \ 24 | -o $(PRODUCTNAME) \ 25 | -std=c++14 \ 26 | -stdlib=libc++ \ 27 | -I.. \ 28 | -I../typo-poi/source \ 29 | $(HEADERS) \ 30 | $(SOURCES) 31 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 32 | @mkdir -p bin 33 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 34 | 35 | rebuild-xcode: 36 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 37 | @mkdir -p bin 38 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 39 | -------------------------------------------------------------------------------- /approximate-winter/README.md: -------------------------------------------------------------------------------- 1 | # approximate-winter 2 | 3 | A experimental project for diff algorithms. 4 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 5 | 6 | ## Build and run 7 | 8 | **Build:** 9 | 10 | ```sh 11 | git clone https://github.com/mogemimi/daily-snippets.git 12 | cd daily-snippets/approximate-winter 13 | 14 | # Setup build tool 15 | make -C ../nori bootstrap 16 | 17 | # Build 18 | make xcode 19 | 20 | # Run 21 | ./bin/approximate-winter -help 22 | ``` 23 | -------------------------------------------------------------------------------- /approximate-winter/custom.txt: -------------------------------------------------------------------------------- 1 | apple 2 | defer 3 | deferred 4 | refer 5 | referred 6 | reference 7 | offer 8 | -------------------------------------------------------------------------------- /diff-summer/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | 10 | # Visual Studio 11 | *.filters 12 | *.opensdf 13 | *.sdf 14 | *.sln 15 | *.vcxproj 16 | -------------------------------------------------------------------------------- /diff-summer/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = diff-summer 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | ../typo-poi/source/thirdparty/*.h \ 7 | ../typo-poi/source/*.h \ 8 | *.h 9 | 10 | SOURCES = \ 11 | ../somera/*.cpp \ 12 | ../typo-poi/source/thirdparty/ConvertUTF.c \ 13 | ../typo-poi/source/ConsoleColor.cpp \ 14 | ../typo-poi/source/EditDistance.cpp \ 15 | ../typo-poi/source/UTF8.cpp \ 16 | ../typo-poi/source/WordDiff.cpp \ 17 | ../typo-poi/source/WordSegmenter.cpp \ 18 | main.cpp 19 | 20 | xcode: 21 | $(NORI) \ 22 | -generator=xcode \ 23 | -o $(PRODUCTNAME) \ 24 | -std=c++14 \ 25 | -stdlib=libc++ \ 26 | -I.. \ 27 | -I../typo-poi/source \ 28 | $(HEADERS) \ 29 | $(SOURCES) 30 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 31 | @mkdir -p bin 32 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 33 | 34 | rebuild-xcode: 35 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 36 | @mkdir -p bin 37 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 38 | -------------------------------------------------------------------------------- /diff-summer/README.md: -------------------------------------------------------------------------------- 1 | # diff-summer 2 | 3 | A experimental project for diff algorithms. 4 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 5 | 6 | ## Build and run 7 | 8 | **Build:** 9 | 10 | ```sh 11 | git clone https://github.com/mogemimi/daily-snippets.git 12 | cd daily-snippets/diff-summer 13 | 14 | # Setup build tool 15 | make -C ../nori bootstrap 16 | 17 | # Build 18 | make xcode 19 | 20 | # Run 21 | ./bin/diff-summer -help 22 | ``` 23 | -------------------------------------------------------------------------------- /fourfolium/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | 10 | # Visual Studio 11 | *.filters 12 | *.opensdf 13 | *.sdf 14 | *.sln 15 | *.vcxproj 16 | -------------------------------------------------------------------------------- /fourfolium/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = fourfolium 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | ../typo-poi/source/thirdparty/*.h \ 7 | ../typo-poi/source/*.h \ 8 | *.h 9 | 10 | SOURCES = \ 11 | ../somera/*.cpp \ 12 | ../typo-poi/source/thirdparty/ConvertUTF.c \ 13 | ../typo-poi/source/UTF8.cpp \ 14 | main.cpp 15 | 16 | xcode: 17 | $(NORI) \ 18 | -generator=xcode \ 19 | -o $(PRODUCTNAME) \ 20 | -std=c++14 \ 21 | -stdlib=libc++ \ 22 | -I.. \ 23 | -I../typo-poi/source \ 24 | $(HEADERS) \ 25 | $(SOURCES) 26 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 27 | @mkdir -p bin 28 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 29 | 30 | rebuild-xcode: 31 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 32 | @mkdir -p bin 33 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 34 | -------------------------------------------------------------------------------- /fourfolium/README.md: -------------------------------------------------------------------------------- 1 | # fourfolium 2 | 3 | Fast isupper/islower/tolower/toupper for UTF-32. 4 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 5 | 6 | ## Build and run 7 | 8 | **Build:** 9 | 10 | ```sh 11 | git clone https://github.com/mogemimi/daily-snippets.git 12 | cd daily-snippets/fourfolium 13 | 14 | # Setup build tool 15 | make -C ../nori bootstrap 16 | 17 | # Build 18 | make xcode 19 | 20 | # Run 21 | ./bin/fourfolium -help 22 | ``` 23 | -------------------------------------------------------------------------------- /githubapis/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | 10 | # Visual Studio 11 | *.filters 12 | *.opensdf 13 | *.sdf 14 | *.sln 15 | *.vcxproj 16 | 17 | # Third party 18 | rapidjson 19 | -------------------------------------------------------------------------------- /githubapis/GitHubClient.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/Optional.h" 4 | #include "HttpService.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace somera { 12 | 13 | // NOTE: https://developer.github.com/v3/pulls/#get-a-single-pull-request 14 | struct GitHubPullRequest { 15 | std::string url; 16 | int id; 17 | std::string html_url; 18 | std::string diff_url; 19 | std::string patch_url; 20 | std::string issue_url; 21 | int number; 22 | std::string state; 23 | bool locked; 24 | std::string title; 25 | 26 | std::string body; 27 | std::string created_at; 28 | std::string updated_at; 29 | std::string closed_at; 30 | std::string merged_at; 31 | std::string merge_commit_sha; 32 | 33 | std::string commits_url; 34 | std::string review_comments_url; 35 | std::string review_comment_url; 36 | std::string comments_url; 37 | std::string statuses_url; 38 | 39 | bool merged = false; 40 | 41 | int comments = 0; 42 | int review_comments = 0; 43 | bool maintainer_can_modify = false; 44 | int commits = 0; 45 | int additions = 0; 46 | int deletions = 0; 47 | int changed_files = 0; 48 | }; 49 | 50 | class GitHubClient final { 51 | private: 52 | somera::HttpService http; 53 | std::function errorCallback; 54 | 55 | public: 56 | GitHubClient(); 57 | 58 | void onError(std::function callback); 59 | 60 | void apiTest(std::function callback); 61 | 62 | // NOTE: https://developer.github.com/v3/pulls/#get-a-single-pull-request 63 | void getPullRequest( 64 | std::string repositoryOwner, 65 | std::string repositoryName, 66 | int pullRequestNumber, 67 | std::function callback); 68 | 69 | private: 70 | void apiCall( 71 | const std::string& method, 72 | std::map && params, 73 | std::function && callback, 74 | HttpRequestMethod httpVerbs); 75 | 76 | void emitError(const std::string& errorMessage); 77 | }; 78 | 79 | } // namespace somera 80 | -------------------------------------------------------------------------------- /githubapis/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = githubapis 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | ../slackbot/*.h \ 7 | *.h 8 | 9 | SOURCES = \ 10 | ../somera/*.cpp \ 11 | ../slackbot/HttpUtility.cpp \ 12 | ../slackbot/HttpService.cpp \ 13 | ../slackbot/TerminalHelper.cpp \ 14 | GitHubClient.cpp \ 15 | main.cpp 16 | 17 | xcode: 18 | $(NORI) \ 19 | -generator=xcode \ 20 | -o $(PRODUCTNAME) \ 21 | -std=c++14 \ 22 | -stdlib=libc++ \ 23 | -llibcurl.tbd \ 24 | -I.. \ 25 | -Irapidjson/include \ 26 | $(HEADERS) \ 27 | $(SOURCES) 28 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 29 | @mkdir -p bin 30 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 31 | 32 | rebuild-xcode: 33 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 34 | @mkdir -p bin 35 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 36 | -------------------------------------------------------------------------------- /githubapis/README.md: -------------------------------------------------------------------------------- 1 | # GitHub API Client 2 | 3 | My personal GitHub client for C++. :octocat: 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Build and run 8 | 9 | ```sh 10 | # Clone third-party libraries 11 | git clone --depth=1 https://github.com/miloyip/rapidjson rapidjson 12 | 13 | # Setup build tool 14 | make -C ../nori bootstrap 15 | 16 | # Build 17 | make xcode 18 | 19 | # Run 20 | ./bin/githubapis -help 21 | ``` 22 | 23 | ## Thanks 24 | 25 | - libcurl 26 | - RapidJSON 27 | -------------------------------------------------------------------------------- /githubapis/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "StringHelper.h" 4 | #include "GitHubClient.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | somera::GitHubClient client; 16 | 17 | client.onError([](std::string message) { 18 | std::cout << message << std::endl; 19 | }); 20 | 21 | const std::vector pullRequestNumbers = { 22 | 17334, 23 | 17333 24 | }; 25 | 26 | int additions = 0; 27 | int deletions = 0; 28 | 29 | for (auto pullRequestNumber : pullRequestNumbers) { 30 | client.getPullRequest("cocos2d", "cocos2d-x", pullRequestNumber, [&](somera::GitHubPullRequest pr) { 31 | std::cout << pr.url << std::endl; 32 | std::cout << pr.title << std::endl; 33 | std::cout 34 | << "state: " << pr.state 35 | << " " 36 | << "+" << pr.additions 37 | << " " 38 | << "-" << pr.deletions 39 | << " " 40 | << pr.commits << " commits" 41 | << std::endl; 42 | std::cout << "----" << std::endl; 43 | additions += pr.additions; 44 | deletions += pr.deletions; 45 | }); 46 | } 47 | 48 | std::cout << "Additions: " << additions << std::endl; 49 | std::cout << "Deletions: " << deletions << std::endl; 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /glsl-include/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /glsl-include/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = glsl-include 3 | HEADERS = ../somera/*.h 4 | SOURCES = ../somera/*.cpp main.cpp 5 | 6 | xcode: 7 | $(NORI) \ 8 | -generator=xcode \ 9 | -o $(PRODUCTNAME) \ 10 | -std=c++14 \ 11 | -stdlib=libc++ \ 12 | -I.. \ 13 | $(HEADERS) \ 14 | $(SOURCES) 15 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 16 | @mkdir -p bin 17 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 18 | -------------------------------------------------------------------------------- /glsl-include/README.md: -------------------------------------------------------------------------------- 1 | # glsl-include 2 | 3 | Simple glsl includer. 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Build and run 8 | 9 | ```sh 10 | # Setup build tool 11 | make -C ../glsl-include bootstrap 12 | 13 | # Build 14 | make xcode 15 | 16 | # Run 17 | ./bin/glsl-include -help 18 | ``` 19 | -------------------------------------------------------------------------------- /glsl-include/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/CommandLineParser.h" 4 | #include "somera/FileSystem.h" 5 | #include "somera/Optional.h" 6 | #include "somera/StringHelper.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using somera::CommandLineParser; 13 | using somera::Optional; 14 | using somera::NullOpt; 15 | namespace FileSystem = somera::FileSystem; 16 | namespace StringHelper = somera::StringHelper; 17 | 18 | namespace { 19 | 20 | void setupCommandLineParser(CommandLineParser & parser) 21 | { 22 | using somera::CommandLineArgumentType::Flag; 23 | using somera::CommandLineArgumentType::JoinedOrSeparate; 24 | parser.setUsageText("glsl-include [directory path]"); 25 | parser.addArgument("-h", Flag, "Display available options"); 26 | parser.addArgument("-help", Flag, "Display available options"); 27 | } 28 | 29 | std::string includeGLSLFilesRecursive(const std::string& path, std::set & includes) 30 | { 31 | if (FileSystem::isDirectory(path)) { 32 | std::cerr << "error: " << path << " is directory, not text file." << std::endl; 33 | return ""; 34 | } 35 | 36 | std::ifstream input(path); 37 | if (!input) { 38 | return ""; 39 | } 40 | std::istreambuf_iterator start(input); 41 | std::istreambuf_iterator end; 42 | std::string text(start, end); 43 | input.close(); 44 | 45 | auto currentDirectory = FileSystem::getDirectoryName(FileSystem::normalize(path)); 46 | 47 | auto lines = StringHelper::split(text, '\n'); 48 | text.clear(); 49 | for (const auto& line : lines) { 50 | std::regex includeRegex(R"(\s*#\s*include\s+\"([\w\.\/\\]+)\")"); 51 | std::smatch match; 52 | 53 | bool matched = std::regex_match(line, match, includeRegex); 54 | if (!matched || match.size() != 2) { 55 | text += line; 56 | text += '\n'; 57 | continue; 58 | } 59 | 60 | auto includePath = FileSystem::join(currentDirectory, match[1]); 61 | if (includes.find(includePath) == includes.end()) { 62 | includes.insert(includePath); 63 | text += includeGLSLFilesRecursive(includePath, includes); 64 | } 65 | text += '\n'; 66 | } 67 | 68 | return text; 69 | } 70 | 71 | } // unnamed namespace 72 | 73 | int main(int argc, char *argv[]) 74 | { 75 | CommandLineParser parser; 76 | setupCommandLineParser(parser); 77 | parser.parse(argc, argv); 78 | 79 | if (parser.hasParseError()) { 80 | std::cerr << parser.getErrorMessage() << std::endl; 81 | return 1; 82 | } 83 | if (parser.exists("-h") || parser.exists("-help")) { 84 | std::cout << parser.getHelpText() << std::endl; 85 | return 0; 86 | } 87 | if (parser.getPaths().empty()) { 88 | std::cerr << "error: no input file" << std::endl; 89 | return 1; 90 | } 91 | 92 | for (auto & path : parser.getPaths()) { 93 | std::set includes; 94 | includes.insert(FileSystem::normalize(path)); 95 | std::cout << includeGLSLFilesRecursive(path, includes); 96 | } 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /glsl-include/test1.glsl: -------------------------------------------------------------------------------- 1 | #include "test2.glsl" 2 | #include "test3.glsl" 3 | #include "test4.glsl" 4 | 5 | void test1() 6 | { 7 | // this is test1 8 | } 9 | -------------------------------------------------------------------------------- /glsl-include/test2.glsl: -------------------------------------------------------------------------------- 1 | void test2() 2 | { 3 | // this is test2 4 | } 5 | -------------------------------------------------------------------------------- /glsl-include/test3.glsl: -------------------------------------------------------------------------------- 1 | #include "test2.glsl" 2 | 3 | void test3() 4 | { 5 | // this is test3 6 | test2(); 7 | } 8 | -------------------------------------------------------------------------------- /glsl-include/test4.glsl: -------------------------------------------------------------------------------- 1 | #include "test3.glsl" 2 | 3 | void test4() 4 | { 5 | // this is test4 6 | test3(); 7 | } 8 | -------------------------------------------------------------------------------- /mami/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /mami/ArrayView.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace somera { 8 | 9 | template 10 | struct ArrayView { 11 | T* data = nullptr; 12 | size_t size = 0; 13 | }; 14 | 15 | template 16 | ArrayView MakeArrayView(T* data, size_t size) 17 | { 18 | ArrayView view; 19 | view.data = data; 20 | view.size = size; 21 | return std::move(view); 22 | } 23 | 24 | template 25 | ArrayView CastArrayView(const ArrayView& source) 26 | { 27 | ArrayView view; 28 | view.data = reinterpret_cast(source.data); 29 | view.size = source.size * (sizeof(T) / sizeof(R)); 30 | return std::move(view); 31 | } 32 | 33 | } // namespace somera 34 | -------------------------------------------------------------------------------- /mami/ContainerAlgorithm.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | 10 | template 11 | void EraseIf(Container & c, Func f) 12 | { 13 | c.erase(std::remove_if(std::begin(c), std::end(c), f), std::end(c)); 14 | } 15 | 16 | template 17 | void EraseMapIf(Container & c, Func f) 18 | { 19 | typename Container::iterator it = std::begin(c); 20 | while (it != std::end(c)) { 21 | if (f(*it)) { 22 | c.erase(it++); 23 | } else { 24 | ++it; 25 | } 26 | } 27 | } 28 | 29 | template 30 | auto Find(Container & c, const T& value) 31 | { 32 | return std::find(std::begin(c), std::end(c), value); 33 | } 34 | 35 | template 36 | auto FindIf(Container & c, Func f) 37 | { 38 | return std::find_if(std::begin(c), std::end(c), f); 39 | } 40 | 41 | } // namespace somera 42 | -------------------------------------------------------------------------------- /mami/EndPoint.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "EndPoint.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace somera { 11 | namespace { 12 | 13 | sa_family_t ToAddressFamilyPOSIX(AddressFamily family) 14 | { 15 | switch (family) { 16 | case AddressFamily::InterNetwork: return AF_INET; 17 | case AddressFamily::InterNetworkV6: return AF_INET6; 18 | } 19 | return AF_INET; 20 | } 21 | 22 | } // unnamed namespace 23 | 24 | AddressFamily EndPoint::GetFamily() const 25 | { 26 | return family; 27 | } 28 | 29 | EndPoint EndPoint::CreateFromV4(const std::string& internetAddress, uint16_t port) 30 | { 31 | EndPoint endPoint; 32 | endPoint.family = AddressFamily::InterNetwork; 33 | std::memset(&endPoint.address, 0, sizeof(endPoint.address)); 34 | 35 | auto & v4 = endPoint.address.asV4; 36 | v4.sin_family = ToAddressFamilyPOSIX(endPoint.family); 37 | v4.sin_port = htons(port); 38 | if (internetAddress.empty()) { 39 | v4.sin_addr.s_addr = htonl(INADDR_ANY); 40 | } 41 | else { 42 | v4.sin_addr.s_addr = ::inet_addr(internetAddress.c_str()); 43 | if (v4.sin_addr.s_addr == 0xffffffff) { 44 | auto host = ::gethostbyname(internetAddress.c_str()); 45 | if (host == nullptr) { 46 | throw std::runtime_error("Failed to call gethostbyname"); 47 | } 48 | v4.sin_addr.s_addr = 49 | *reinterpret_cast(host->h_addr_list[0]); 50 | } 51 | } 52 | return endPoint; 53 | } 54 | 55 | EndPoint EndPoint::CreateFromV6(uint16_t port, uint32_t scopeId) 56 | { 57 | EndPoint endPoint; 58 | endPoint.family = AddressFamily::InterNetworkV6; 59 | std::memset(&endPoint.address, 0, sizeof(address)); 60 | auto & v6 = endPoint.address.asV6; 61 | v6.sin6_family = ToAddressFamilyPOSIX(endPoint.family); 62 | v6.sin6_port = htons(port); 63 | v6.sin6_scope_id = scopeId; 64 | return endPoint; 65 | } 66 | 67 | EndPoint EndPoint::CreateFromAddressStorage(const ::sockaddr_storage& storage) 68 | { 69 | EndPoint endPoint; 70 | std::memset(&endPoint.address, 0, sizeof(address)); 71 | 72 | assert(storage.ss_family == AF_INET || storage.ss_family == AF_INET6); 73 | if (storage.ss_family == AF_INET) { 74 | endPoint.family = AddressFamily::InterNetwork; 75 | endPoint.address.asV4 = *reinterpret_cast(&storage); 76 | } 77 | else if (storage.ss_family == AF_INET6) { 78 | endPoint.family = AddressFamily::InterNetworkV6; 79 | endPoint.address.asV6 = *reinterpret_cast(&storage); 80 | } 81 | return endPoint; 82 | } 83 | 84 | AddressViewPOSIX EndPoint::GetAddressViewPOSIX() const 85 | { 86 | if (family == AddressFamily::InterNetwork) { 87 | AddressViewPOSIX view; 88 | view.data = reinterpret_cast(&address.asV4); 89 | view.size = sizeof(address.asV4); 90 | return view; 91 | } 92 | assert(family == AddressFamily::InterNetworkV6); 93 | AddressViewPOSIX view; 94 | view.data = reinterpret_cast(&address.asV6); 95 | view.size = sizeof(address.asV6); 96 | return view; 97 | } 98 | 99 | std::string EndPoint::GetAddressNumber() const 100 | { 101 | if (family == AddressFamily::InterNetwork) { 102 | return ::inet_ntoa(address.asV4.sin_addr); 103 | } 104 | assert(family == AddressFamily::InterNetworkV6); 105 | 106 | std::array numericName; 107 | std::memset(numericName.data(), 0, numericName.size()); 108 | 109 | return ::inet_ntop( 110 | ToAddressFamilyPOSIX(family), 111 | &address.asV6.sin6_addr, 112 | numericName.data(), 113 | numericName.size()); 114 | } 115 | 116 | int EndPoint::GetPort() const 117 | { 118 | if (family == AddressFamily::InterNetwork) { 119 | return ntohs(address.asV4.sin_port); 120 | } 121 | assert(family == AddressFamily::InterNetworkV6); 122 | return ntohs(address.asV6.sin6_port); 123 | } 124 | 125 | } // namespace somera 126 | -------------------------------------------------------------------------------- /mami/EndPoint.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | 11 | enum class AddressFamily { 12 | InterNetwork, 13 | InterNetworkV6, 14 | }; 15 | 16 | struct AddressViewPOSIX final { 17 | const ::sockaddr* data = nullptr; 18 | ::socklen_t size = 0; 19 | }; 20 | 21 | class EndPoint final { 22 | public: 23 | AddressFamily GetFamily() const; 24 | 25 | AddressViewPOSIX GetAddressViewPOSIX() const; 26 | 27 | std::string GetAddressNumber() const; 28 | 29 | int GetPort() const; 30 | 31 | static EndPoint CreateFromAddressStorage(const ::sockaddr_storage& storage); 32 | 33 | static EndPoint CreateFromV4(const std::string& internetAddress, uint16_t port); 34 | 35 | static EndPoint CreateFromV6(uint16_t port, uint32_t scopeId); 36 | 37 | private: 38 | AddressFamily family; 39 | union { 40 | ::sockaddr_in asV4; 41 | ::sockaddr_in6 asV6; 42 | } address; 43 | }; 44 | 45 | } // namespace somera 46 | -------------------------------------------------------------------------------- /mami/Error.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "Error.h" 4 | 5 | namespace somera { 6 | 7 | // MARK: Error 8 | 9 | Error::operator bool() const noexcept 10 | { 11 | return description.operator bool(); 12 | } 13 | 14 | std::string Error::What() const noexcept 15 | { 16 | std::string what; 17 | bool needToSeparate = false; 18 | if (description) { 19 | what += *description; 20 | needToSeparate = true; 21 | } 22 | if (file) { 23 | if (needToSeparate) { 24 | what += ", "; 25 | } 26 | what += *file; 27 | needToSeparate = true; 28 | } 29 | if (line) { 30 | if (needToSeparate) { 31 | what += ", Line="; 32 | } 33 | what += *line; 34 | } 35 | return what; 36 | } 37 | 38 | Error MakeError(const std::string& description) 39 | { 40 | Error error; 41 | error.description = description; 42 | return error; 43 | } 44 | 45 | } // namespace somera 46 | -------------------------------------------------------------------------------- /mami/Error.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "somera/Optional.h" 6 | #include 7 | 8 | namespace somera { 9 | 10 | struct Error { 11 | somera::Optional description; 12 | somera::Optional file; 13 | somera::Optional line; 14 | 15 | operator bool() const noexcept; 16 | 17 | std::string What() const noexcept; 18 | }; 19 | 20 | Error MakeError(const std::string& description); 21 | 22 | } // namespace somera 23 | -------------------------------------------------------------------------------- /mami/IOService.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "IOService.h" 4 | #include "ContainerAlgorithm.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | namespace { 11 | 12 | // MARK: ConnectionEventLoop 13 | 14 | class ConnectionEventLoop final : public detail::ConnectionBody { 15 | public: 16 | ConnectionEventLoop(IOService* serviceIn, int connectionIdIn) 17 | : service(serviceIn) 18 | , connectionId(connectionIdIn) 19 | { 20 | } 21 | 22 | void Disconnect() override 23 | { 24 | if (service == nullptr) { 25 | return; 26 | } 27 | service->Disconnect(connectionId); 28 | service = nullptr; 29 | connectionId = 0; 30 | } 31 | 32 | bool Valid() const noexcept override 33 | { 34 | return (service != nullptr) && service->IsConnected(connectionId); 35 | } 36 | 37 | std::unique_ptr DeepCopy() const override 38 | { 39 | auto copy = std::make_unique(service, connectionId); 40 | return std::move(copy); 41 | } 42 | 43 | private: 44 | IOService* service = nullptr; 45 | int connectionId = 0; 46 | }; 47 | 48 | } // unnamed namespace 49 | 50 | // MARK: IOService 51 | 52 | void IOService::Run() 53 | { 54 | while (!exitRequest) { 55 | Step(); 56 | 57 | // NOTE: I want to suppress energy impact if possible. 58 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 59 | } 60 | } 61 | 62 | void IOService::Step() 63 | { 64 | for (auto & listener : listeners) { 65 | if (Find(removedListeners, listener.id) != std::end(removedListeners)) { 66 | listener.needToRemove = true; 67 | continue; 68 | } 69 | 70 | // NOTE: Execute listener's callback 71 | if (listener.callback) { 72 | listener.callback(); 73 | } 74 | } 75 | // TODO: mutex for addedListeners 76 | if (!addedListeners.empty()) { 77 | // NOTE: Add listener to listeners container 78 | std::vector temp; 79 | std::swap(temp, addedListeners); 80 | for (auto & listener : temp) { 81 | listeners.push_back(std::move(listener)); 82 | } 83 | } 84 | // TODO: mutex for removedListeners 85 | if (!removedListeners.empty()) { 86 | // NOTE: Remove listener from listeners container 87 | std::vector temp; 88 | std::swap(temp, removedListeners); 89 | EraseIf(listeners, [&temp](const Listener& listener) { 90 | return listener.needToRemove 91 | || (Find(temp, listener.id) != std::end(temp)); 92 | }); 93 | } 94 | } 95 | 96 | void IOService::ExitLoop() 97 | { 98 | exitRequest = true; 99 | } 100 | 101 | Connection IOService::ScheduleTask(std::function func) 102 | { 103 | if (!func) { 104 | return {}; 105 | } 106 | Listener listener; 107 | listener.callback = std::move(func); 108 | listener.id = this->nextListenersId; 109 | nextListenersId++; 110 | 111 | addedListeners.push_back(std::move(listener)); 112 | 113 | auto body = std::make_unique(this, listener.id); 114 | Connection connection(std::move(body)); 115 | return connection; 116 | } 117 | 118 | bool IOService::IsConnected(int connectionId) const 119 | { 120 | if (Find(removedListeners, connectionId) != std::end(removedListeners)) { 121 | return false; 122 | } 123 | const auto equal = [&connectionId](const Listener& listener)-> bool { 124 | return listener.id == connectionId; 125 | }; 126 | if (FindIf(listeners, equal) != std::end(listeners)) { 127 | return true; 128 | } 129 | if (FindIf(addedListeners, equal) != std::end(addedListeners)) { 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | void IOService::Disconnect(int connectionId) 136 | { 137 | removedListeners.push_back(connectionId); 138 | } 139 | 140 | } // namespace somera 141 | -------------------------------------------------------------------------------- /mami/IOService.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "somera/signals/Connection.h" 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | 11 | class IOService { 12 | public: 13 | void Run(); 14 | 15 | void Step(); 16 | 17 | void ExitLoop(); 18 | 19 | Connection ScheduleTask(std::function func); 20 | 21 | bool IsConnected(int connectionId) const; 22 | 23 | void Disconnect(int connectionId); 24 | 25 | private: 26 | struct Listener { 27 | std::function callback; 28 | int id = 0; 29 | bool needToRemove = false; 30 | }; 31 | 32 | private: 33 | std::vector listeners; 34 | std::vector addedListeners; 35 | std::vector removedListeners; 36 | int nextListenersId = 42; 37 | bool exitRequest = false; 38 | }; 39 | 40 | } // namespace somera 41 | -------------------------------------------------------------------------------- /mami/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = mami 3 | HEADERS = \ 4 | ../somera/*.h \ 5 | ../somera/signals/*.h \ 6 | *.h 7 | SOURCES = \ 8 | ../somera/*.cpp \ 9 | ../somera/signals/*.cpp \ 10 | example/EchoServer/main.cpp \ 11 | *.cpp 12 | 13 | xcode: 14 | $(NORI) \ 15 | -generator=xcode \ 16 | -o $(PRODUCTNAME) \ 17 | -std=c++14 \ 18 | -stdlib=libc++ \ 19 | -I.. \ 20 | $(HEADERS) \ 21 | $(SOURCES) 22 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 23 | @mkdir -p bin 24 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 25 | -------------------------------------------------------------------------------- /mami/README.md: -------------------------------------------------------------------------------- 1 | # Mami 2 | 3 | Learning socket programming. 4 | 5 | ## Features 6 | 7 | * TCP protocol support. 8 | * Non-blocking, single thread, event driven I/O 9 | 10 | ## Build and run 11 | 12 | ```sh 13 | # Setup build tool 14 | make -C ../nori bootstrap 15 | 16 | # Build 17 | make xcode 18 | 19 | # Run echo server 20 | ./bin/mami -help 21 | ``` 22 | -------------------------------------------------------------------------------- /mami/Socket.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "ArrayView.h" 6 | #include "EndPoint.h" 7 | #include "Error.h" 8 | #include "IOService.h" 9 | #include "somera/signals/Connection.h" 10 | #include "somera/Optional.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace somera { 20 | namespace detail { 21 | 22 | enum class ProtocolType { 23 | Tcp, 24 | //Udp, 25 | }; 26 | 27 | class DescriptorPOSIX final { 28 | public: 29 | DescriptorPOSIX() = default; 30 | 31 | DescriptorPOSIX(const DescriptorPOSIX&) = delete; 32 | 33 | DescriptorPOSIX(DescriptorPOSIX && other); 34 | 35 | DescriptorPOSIX & operator=(const DescriptorPOSIX&) = delete; 36 | 37 | DescriptorPOSIX & operator=(DescriptorPOSIX && other); 38 | 39 | void Bind(const EndPoint& endPoint); 40 | 41 | std::tuple Connect(const EndPoint& endPoint); 42 | 43 | std::tuple Accept(ProtocolType protocolType); 44 | 45 | void Close(); 46 | 47 | int GetHandle() const; 48 | 49 | private: 50 | ///@brief The file descriptor for socket 51 | Optional descriptor_; 52 | }; 53 | 54 | } // namespace detail 55 | 56 | enum class SocketError { 57 | TimedOut, 58 | Shutdown, 59 | NotConnected, 60 | }; 61 | 62 | class Socket { 63 | public: 64 | Socket() = default; 65 | 66 | Socket(IOService & service); 67 | 68 | Socket( 69 | IOService & service, 70 | detail::DescriptorPOSIX && descriptor, 71 | EndPoint && endPoint); 72 | 73 | ~Socket(); 74 | 75 | Socket(const Socket&) = delete; 76 | Socket & operator=(const Socket&) = delete; 77 | 78 | Socket(Socket && other); 79 | Socket & operator=(Socket && other); 80 | 81 | void Connect(const EndPoint& endPoint, std::function onConnected); 82 | 83 | void Close(); 84 | 85 | void Read(const std::function&)>& onRead); 86 | 87 | // void ReadError(const std::function& onReadError); 88 | // 89 | // void Timeout(const std::function& onReadError); 90 | // 91 | // void Disconnect(const std::function& onReadError); 92 | 93 | void Write(const ArrayView& data); 94 | 95 | ///@brief Sets the interval to wait for socket activity. 96 | void SetTimeout(const std::chrono::milliseconds& timeout); 97 | 98 | ///@brief Sets the interval to wait for socket activity. 99 | void SetTimeout( 100 | const std::chrono::milliseconds& timeout, 101 | const std::function& callback); 102 | 103 | void SetErrorListener(const std::function& callback); 104 | 105 | void SetCloseListener(const std::function& callback); 106 | 107 | EndPoint GetEndPoint() const; 108 | 109 | int GetHandle() const; 110 | 111 | private: 112 | void ConnectEventLoop(const std::chrono::system_clock::time_point& startTime); 113 | 114 | void ReadEventLoop(); 115 | 116 | private: 117 | using DescriptorType = detail::DescriptorPOSIX; 118 | IOService* service_ = nullptr; 119 | DescriptorType descriptor_; 120 | EndPoint endPoint_; 121 | ScopedConnection connectionActive_; 122 | std::chrono::milliseconds timeout_; 123 | std::function onConnected_; 124 | std::function onTimeout_; 125 | std::function& view)> onRead_; 126 | std::function onError_; 127 | std::function onClose_; 128 | }; 129 | 130 | class Server { 131 | public: 132 | Server(IOService & service); 133 | 134 | ~Server(); 135 | 136 | void Listen( 137 | const EndPoint& endPoint, 138 | int backlog, 139 | const std::function&)>& onAccept); 140 | 141 | void Close(); 142 | 143 | void Read(const std::function&, const ArrayView&)>& onRead); 144 | 145 | // void ReadError(const std::function& onReadError); 146 | // 147 | // void Timeout(const std::function& onReadError); 148 | // 149 | // void Disconnect(const std::function& onReadError); 150 | 151 | void SetErrorListener(const std::function& callback); 152 | 153 | void SetCloseListener(const std::function&)>& callback); 154 | 155 | private: 156 | void ListenEventLoop(); 157 | 158 | void ReadEventLoop(); 159 | 160 | private: 161 | struct Session { 162 | std::shared_ptr socket; 163 | bool isClosed = false; 164 | }; 165 | 166 | private: 167 | using DescriptorType = detail::DescriptorPOSIX; 168 | IOService* service_ = nullptr; 169 | DescriptorType descriptor_; 170 | std::vector sessions_; 171 | ScopedConnection connectionListen_; 172 | ScopedConnection connectionRead_; 173 | int maxSessionCount_ = 5; 174 | std::function&)> onAccept_; 175 | std::function&, const ArrayView&)> onRead_; 176 | std::function&)> onClose_; 177 | std::function onError_; 178 | }; 179 | 180 | } // namespace somera 181 | -------------------------------------------------------------------------------- /mami/example/EchoServer/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "mami/IOService.h" 4 | #include "mami/Socket.h" 5 | #include "somera/signals/EventQueue.h" 6 | #include "somera/CommandLineParser.h" 7 | #include "somera/StringHelper.h" 8 | #include 9 | #include 10 | 11 | using namespace somera; 12 | 13 | namespace { 14 | 15 | void SetupCommandLineParser(CommandLineParser & parser) 16 | { 17 | using somera::CommandLineArgumentType::Flag; 18 | using somera::CommandLineArgumentType::JoinedOrSeparate; 19 | parser.setUsageText("mami [options ...]"); 20 | parser.addArgument("-h", Flag, "Display available options"); 21 | parser.addArgument("-help", Flag, "Display available options"); 22 | parser.addArgument("-s", Flag, "server"); 23 | parser.addArgument("-c", Flag, "client"); 24 | parser.addArgument("port", JoinedOrSeparate, "port number (default is 8000)"); 25 | } 26 | 27 | void Log(const std::string& text) 28 | { 29 | std::puts(text.c_str()); 30 | } 31 | 32 | void RunServer(uint16_t port) 33 | { 34 | IOService service; 35 | 36 | Server server(service); 37 | server.SetErrorListener([](const Error& error) { 38 | Log("Error: " + error.What()); 39 | }); 40 | server.SetCloseListener([](std::shared_ptr client) { 41 | Log(StringHelper::format("Close: client = { fd : %d }", client->GetHandle())); 42 | }); 43 | server.Listen(EndPoint::CreateFromV4("localhost", port), 5, [](std::shared_ptr client) { 44 | Log(StringHelper::format("Listen: client = { fd : %d }", client->GetHandle())); 45 | }); 46 | server.Read([](std::shared_ptr client, const ArrayView& view) { 47 | std::string text(reinterpret_cast(view.data), view.size); 48 | text = StringHelper::trimRight(text, '\n'); 49 | text = StringHelper::trimRight(text, '\r'); 50 | text = StringHelper::trimRight(text, '\n'); 51 | 52 | Log(StringHelper::format( 53 | "Read: client = { fd : %d, result = %s }", 54 | client->GetHandle(), 55 | text.c_str())); 56 | }); 57 | 58 | Log("Run"); 59 | service.Run(); 60 | } 61 | 62 | void RunClient(uint16_t port) 63 | { 64 | IOService service; 65 | EventQueue eventQueue; 66 | 67 | Socket socket(service); 68 | socket.SetTimeout(std::chrono::seconds(500), [](Socket &) { 69 | Log("Timeout."); 70 | }); 71 | socket.SetErrorListener([&socket](Socket &, const Error& error) { 72 | Log("Error: " + error.What()); 73 | socket.Close(); 74 | }); 75 | socket.SetCloseListener([](Socket&) { 76 | Log("Close."); 77 | }); 78 | socket.Connect(EndPoint::CreateFromV4("localhost", port), 79 | [&eventQueue, &service, &socket](Socket &) { 80 | Log("Connected."); 81 | eventQueue.Connect([&socket, &service](const Any& event) { 82 | if (!event.is()) { 83 | return; 84 | } 85 | auto text = event.as(); 86 | auto view = MakeArrayView(text.data(), text.size()); 87 | socket.Write(CastArrayView(view)); 88 | 89 | if (StringHelper::startWith(text, "exit")) { 90 | socket.Close(); 91 | service.ExitLoop(); 92 | } 93 | }); 94 | }); 95 | socket.Read([&socket](Socket &, const ArrayView& view) { 96 | std::string text(reinterpret_cast(view.data), view.size); 97 | text = StringHelper::trimRight(text, '\n'); 98 | text = StringHelper::trimRight(text, '\r'); 99 | text = StringHelper::trimRight(text, '\n'); 100 | 101 | Log(StringHelper::format( 102 | "Read: server = { fd : %d, result = %s }", 103 | socket.GetHandle(), 104 | text.c_str())); 105 | }); 106 | 107 | std::thread keyboardWorker([&] { 108 | std::this_thread::sleep_for(std::chrono::seconds(5)); 109 | bool exitLoop = false; 110 | while (!exitLoop) { 111 | std::string text; 112 | std::cin >> text; 113 | if (StringHelper::startWith(text, "exit")) { 114 | exitLoop = true; 115 | } 116 | eventQueue.Enqueue(std::move(text)); 117 | std::this_thread::sleep_for(std::chrono::seconds(1)); 118 | } 119 | }); 120 | 121 | ScopedConnection conn = service.ScheduleTask([&] { 122 | eventQueue.Emit(); 123 | }); 124 | 125 | Log("Run"); 126 | service.Run(); 127 | keyboardWorker.join(); 128 | } 129 | 130 | } // unnamed namespace 131 | 132 | int main(int argc, char *argv[]) 133 | { 134 | somera::CommandLineParser parser; 135 | SetupCommandLineParser(parser); 136 | parser.parse(argc, argv); 137 | 138 | if (parser.hasParseError()) { 139 | std::cerr << parser.getErrorMessage() << std::endl; 140 | return 1; 141 | } 142 | if (parser.exists("-h") || parser.exists("-help")) { 143 | std::cout << parser.getHelpText() << std::endl; 144 | return 0; 145 | } 146 | 147 | uint16_t port = 8000; 148 | if (auto p = parser.getValue("port")) { 149 | try { 150 | port = static_cast(std::stoi(*p)); 151 | } 152 | catch (const std::invalid_argument&) { 153 | return 1; 154 | } 155 | } 156 | if (parser.exists("-s")) { 157 | RunServer(port); 158 | } 159 | else if (parser.exists("-c")) { 160 | RunClient(port); 161 | } 162 | 163 | Log("Done"); 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /matoi/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /matoi/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = matoi 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | thirdparty/*.h 7 | 8 | SOURCES = \ 9 | ../somera/*.cpp \ 10 | thirdparty/*.c \ 11 | main.cpp 12 | 13 | COMPILER_OPTIONS = \ 14 | -o $(PRODUCTNAME) \ 15 | -std=c++14 \ 16 | -stdlib=libc++ \ 17 | -I.. \ 18 | $(SOURCES) 19 | 20 | xcode: 21 | $(NORI) \ 22 | -generator=xcode \ 23 | $(HEADERS) \ 24 | $(COMPILER_OPTIONS) 25 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 26 | @mkdir -p bin 27 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 28 | -------------------------------------------------------------------------------- /matoi/README.md: -------------------------------------------------------------------------------- 1 | # matoi 2 | 3 | A experimental/simple C++ lexer as a hobby. 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Build and run 8 | 9 | ```sh 10 | # Setup build tool 11 | make -C ../matoi bootstrap 12 | 13 | # Build 14 | make xcode 15 | 16 | # Run 17 | ./bin/matoi -help 18 | ``` 19 | -------------------------------------------------------------------------------- /mostcommonwords/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | 10 | # Visual Studio 11 | *.filters 12 | *.opensdf 13 | *.sdf 14 | *.sln 15 | *.vcxproj 16 | -------------------------------------------------------------------------------- /mostcommonwords/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = mostcommonwords 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | ../typo-poi/source/thirdparty/*.h \ 7 | ../typo-poi/source/*.h \ 8 | *.h 9 | 10 | SOURCES = \ 11 | ../somera/*.cpp \ 12 | ../typo-poi/source/EditDistance.cpp \ 13 | ../typo-poi/source/WordDiff.cpp \ 14 | ../typo-poi/source/WordSegmenter.cpp \ 15 | ../typo-poi/source/thirdparty/ConvertUTF.c \ 16 | ../typo-poi/source/UTF8.cpp \ 17 | main.cpp 18 | 19 | xcode: 20 | $(NORI) \ 21 | -generator=xcode \ 22 | -o $(PRODUCTNAME) \ 23 | -std=c++14 \ 24 | -stdlib=libc++ \ 25 | -I.. \ 26 | -I../typo-poi/source \ 27 | $(HEADERS) \ 28 | $(SOURCES) 29 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 30 | @mkdir -p bin 31 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 32 | 33 | rebuild-xcode: 34 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 35 | @mkdir -p bin 36 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 37 | -------------------------------------------------------------------------------- /mostcommonwords/README.md: -------------------------------------------------------------------------------- 1 | # mostcommonwords 2 | 3 | This program enumerates most commonly used words. 4 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 5 | 6 | ## Build and run 7 | 8 | **Build:** 9 | 10 | ```sh 11 | git clone https://github.com/mogemimi/daily-snippets.git 12 | cd daily-snippets/mostcommonwords 13 | 14 | # Setup build tool 15 | make -C ../nori bootstrap 16 | 17 | # Build 18 | make xcode 19 | 20 | # Run 21 | ./bin/mostcommonwords -help 22 | ``` 23 | -------------------------------------------------------------------------------- /mostcommonwords/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 mogemimi. Distributed under the MIT license. 2 | 3 | #include "EditDistance.h" 4 | #include "WordDiff.h" 5 | #include "Optional.h" 6 | #include "CommandLineParser.h" 7 | #include "WordSegmenter.h" 8 | #include "StringHelper.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace somera; 20 | using somera::CommandLineParser; 21 | using somera::Optional; 22 | using somera::NullOpt; 23 | 24 | namespace { 25 | 26 | void SetupCommandLineParser(CommandLineParser & parser) 27 | { 28 | using Type = somera::CommandLineArgumentType; 29 | parser.setUsageText("mostcommonwords [options ...] [C/C++ file ...]"); 30 | parser.addArgument("-h", Type::Flag, "Display available options"); 31 | parser.addArgument("-help", Type::Flag, "Display available options"); 32 | parser.addArgument("-v", Type::Flag, "Display version"); 33 | parser.addArgument("-dict", Type::JoinedOrSeparate, "Dictionary file"); 34 | } 35 | 36 | bool ReadTextFile( 37 | const std::string& path, 38 | const std::function& callback) 39 | { 40 | std::ifstream input(path, std::ios::binary); 41 | if (!input) { 42 | std::cerr << "error: Cannot open the file. " << path << std::endl; 43 | return false; 44 | } 45 | 46 | std::istreambuf_iterator start(input); 47 | std::istreambuf_iterator end; 48 | 49 | std::string word; 50 | for (; start != end; ++start) { 51 | auto c = *start; 52 | if (c == '\r' || c == '\n' || c == '\0') { 53 | if (!word.empty()) { 54 | callback(word); 55 | } 56 | word.clear(); 57 | continue; 58 | } 59 | word += c; 60 | } 61 | if (!word.empty()) { 62 | // TODO: The word must be a UTF-8 encoded string but this function 63 | // does not validate that the word is UTF-8 string. 64 | callback(word); 65 | } 66 | return true; 67 | } 68 | 69 | } // unnamed namespace 70 | 71 | int main(int argc, char *argv[]) 72 | { 73 | CommandLineParser parser; 74 | SetupCommandLineParser(parser); 75 | parser.parse(argc, argv); 76 | 77 | if (parser.hasParseError()) { 78 | std::cerr << parser.getErrorMessage() << std::endl; 79 | return 1; 80 | } 81 | if (parser.exists("-h") || parser.exists("-help")) { 82 | std::cout << parser.getHelpText() << std::endl; 83 | return 0; 84 | } 85 | if (parser.exists("-v")) { 86 | std::cout << "mostcommonwords version 0.1.0 (Jan 24, 2017)" << std::endl; 87 | return 0; 88 | } 89 | if (parser.getPaths().empty()) { 90 | std::cerr << "error: no input file" << std::endl; 91 | return 1; 92 | } 93 | 94 | std::vector dictionaryPaths = parser.getValues("-dict"); 95 | 96 | std::unordered_map wordFrequencies; 97 | 98 | WordSegmenter segmenter; 99 | auto onPartOfSpeech = [&](const PartOfSpeech& p) { 100 | if (p.tag != PartOfSpeechTag::Word) { 101 | return; 102 | } 103 | auto iter = wordFrequencies.find(p.text); 104 | if (iter == std::end(wordFrequencies)) { 105 | wordFrequencies.emplace(p.text, 1); 106 | } 107 | else { 108 | ++iter->second; 109 | } 110 | }; 111 | 112 | for (auto & path : parser.getPaths()) { 113 | ReadTextFile(path, [&](const std::string& text) { 114 | segmenter.Parse(text, onPartOfSpeech); 115 | }); 116 | } 117 | 118 | struct WordFreq { 119 | std::string word; 120 | int frequency = 0; 121 | }; 122 | std::vector words; 123 | for (const auto& pair : wordFrequencies) { 124 | WordFreq f; 125 | f.word = pair.first; 126 | f.frequency = pair.second; 127 | words.push_back(std::move(f)); 128 | } 129 | 130 | std::stable_sort(std::begin(words), std::end(words), [](auto & a, auto & b) { 131 | return a.frequency > b.frequency; 132 | }); 133 | 134 | for (auto & w : words) { 135 | std::cout << w.word << "\t" << w.frequency << std::endl; 136 | } 137 | 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /nakayamasan/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /nakayamasan/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = nakayamasan 3 | HEADERS = ../somera/*.h 4 | SOURCES = ../somera/*.cpp main.cpp 5 | 6 | xcode: 7 | $(NORI) \ 8 | -generator=xcode \ 9 | -o $(PRODUCTNAME) \ 10 | -std=c++14 \ 11 | -stdlib=libc++ \ 12 | -I.. \ 13 | $(HEADERS) \ 14 | $(SOURCES) 15 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 16 | @mkdir -p bin 17 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 18 | -------------------------------------------------------------------------------- /nakayamasan/README.md: -------------------------------------------------------------------------------- 1 | # nakayamasan 2 | 3 | A search utility which is able to easily find files managed by git. 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | >Nakayama-san is a classmate of Yuno and Miyako and also Mami's best friend. 8 | 9 | ## Build and run 10 | 11 | ```sh 12 | # Setup build tool 13 | make -C ../nakayamasan bootstrap 14 | 15 | # Build 16 | make xcode 17 | 18 | # Run 19 | ./bin/nakayamasan -help 20 | ``` 21 | -------------------------------------------------------------------------------- /nakayamasan/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/CommandLineParser.h" 4 | #include "somera/FileSystem.h" 5 | #include "somera/Optional.h" 6 | #include "somera/StringHelper.h" 7 | #include "somera/SubprocessHelper.h" 8 | #include 9 | #include 10 | #include 11 | 12 | using somera::CommandLineParser; 13 | using somera::Optional; 14 | using somera::NullOpt; 15 | namespace FileSystem = somera::FileSystem; 16 | namespace StringHelper = somera::StringHelper; 17 | namespace SubprocessHelper = somera::SubprocessHelper; 18 | 19 | namespace { 20 | 21 | void setupCommandLineParser(CommandLineParser & parser) 22 | { 23 | using somera::CommandLineArgumentType::Flag; 24 | using somera::CommandLineArgumentType::JoinedOrSeparate; 25 | parser.setUsageText("nakayamasan [options ...]"); 26 | parser.addArgument("-h", Flag, "Display available options"); 27 | parser.addArgument("-help", Flag, "Display available options"); 28 | parser.addArgument("-days", JoinedOrSeparate, "Days ago"); 29 | parser.addArgument("-month", JoinedOrSeparate, "Month ago"); 30 | } 31 | 32 | std::chrono::hours convertFromDaysToHours(int days) 33 | { 34 | return std::chrono::hours(24) * days; 35 | } 36 | 37 | std::chrono::hours convertFromMonthsToHours(int months) 38 | { 39 | return std::chrono::hours(24) * 30 * months; 40 | } 41 | 42 | std::string getTimeAgo(const std::chrono::hours& hours) 43 | { 44 | using std::chrono::system_clock; 45 | auto now = system_clock::now(); 46 | auto tt = system_clock::to_time_t(now - hours); 47 | auto utcTime = *gmtime(&tt); 48 | 49 | return StringHelper::format("%04d-%02d-%02d", utcTime.tm_year + 1900, utcTime.tm_mon + 1, utcTime.tm_mday); 50 | } 51 | 52 | bool compareYyyyMmDd(const std::string& a, const std::string& b) 53 | { 54 | return a < b; 55 | } 56 | 57 | void enumerateFileList(const std::chrono::hours& hours) 58 | { 59 | std::error_code err; 60 | std::string stringResult; 61 | std::tie(stringResult, err) = SubprocessHelper::call("git ls-files"); 62 | 63 | if (err) { 64 | std::cerr << "error: enumerateFileList(): " << stringResult; 65 | return; 66 | } 67 | 68 | std::vector olderFiles; 69 | 70 | const auto timeAgo = getTimeAgo(hours); 71 | 72 | auto files = StringHelper::split(stringResult, '\n'); 73 | for (auto & file : files) { 74 | std::tie(stringResult, err) = SubprocessHelper::call( 75 | "git log --date=short --pretty=format:'%ad' -n1 " + file); 76 | if (err) { 77 | std::cerr << "error: enumerateFileList()"; 78 | return; 79 | } 80 | 81 | const auto& date = stringResult; 82 | if (compareYyyyMmDd(date, timeAgo)) { 83 | std::cout << file << std::endl; 84 | olderFiles.push_back(file); 85 | } 86 | } 87 | } 88 | 89 | } // unnamed namespace 90 | 91 | int main(int argc, char *argv[]) 92 | { 93 | CommandLineParser parser; 94 | setupCommandLineParser(parser); 95 | parser.parse(argc, argv); 96 | 97 | if (parser.hasParseError()) { 98 | std::cerr << parser.getErrorMessage() << std::endl; 99 | return 1; 100 | } 101 | if (parser.exists("-h") || parser.exists("-help")) { 102 | std::cout << parser.getHelpText() << std::endl; 103 | return 0; 104 | } 105 | 106 | auto hours = convertFromMonthsToHours(2); 107 | if (auto months = parser.getValue("-month")) { 108 | if (months->empty()) { 109 | std::cerr << "error: invalid arguments" << std::endl; 110 | return 1; 111 | } 112 | hours = convertFromMonthsToHours(std::atoi(months->c_str())); 113 | } 114 | if (auto days = parser.getValue("-days")) { 115 | if (days->empty()) { 116 | std::cerr << "error: invalid arguments" << std::endl; 117 | return 1; 118 | } 119 | hours = convertFromDaysToHours(std::atoi(days->c_str())); 120 | } 121 | 122 | enumerateFileList(hours); 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /naoi/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /naoi/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = naoi 3 | HEADERS = ../somera/*.h 4 | SOURCES = ../somera/*.cpp main.cpp 5 | 6 | xcode: 7 | $(NORI) \ 8 | -generator=xcode \ 9 | -o $(PRODUCTNAME) \ 10 | -std=c++14 \ 11 | -stdlib=libc++ \ 12 | -I.. \ 13 | $(HEADERS) \ 14 | $(SOURCES) 15 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 16 | @mkdir -p bin 17 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 18 | -------------------------------------------------------------------------------- /naoi/README.md: -------------------------------------------------------------------------------- 1 | # naoi 2 | 3 | This tool will remove include duplicates in your C++ files. 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Build and run 8 | 9 | ```sh 10 | # Setup build tool 11 | make -C ../naoi bootstrap 12 | 13 | # Build 14 | make xcode 15 | 16 | # Run 17 | ./bin/naoi -help 18 | ``` 19 | -------------------------------------------------------------------------------- /naoi/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/CommandLineParser.h" 4 | #include "somera/FileSystem.h" 5 | #include "somera/Optional.h" 6 | #include "somera/StringHelper.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using somera::CommandLineParser; 13 | using somera::Optional; 14 | using somera::NullOpt; 15 | namespace FileSystem = somera::FileSystem; 16 | namespace StringHelper = somera::StringHelper; 17 | 18 | namespace { 19 | 20 | void setupCommandLineParser(CommandLineParser & parser) 21 | { 22 | using somera::CommandLineArgumentType::Flag; 23 | using somera::CommandLineArgumentType::JoinedOrSeparate; 24 | parser.setUsageText("naoi [directory path]"); 25 | parser.addArgument("-h", Flag, "Display available options"); 26 | parser.addArgument("-help", Flag, "Display available options"); 27 | } 28 | 29 | void refactorSourceCode(const std::string& path) 30 | { 31 | if (FileSystem::isDirectory(path)) { 32 | std::cerr << "error: " << path << " is directory, not text file." << std::endl; 33 | return; 34 | } 35 | 36 | std::ifstream input(path); 37 | if (!input) { 38 | return; 39 | } 40 | std::istreambuf_iterator start(input); 41 | std::istreambuf_iterator end; 42 | std::string text(start, end); 43 | input.close(); 44 | 45 | std::ofstream output(path, std::ios::out | std::ios::trunc); 46 | if (!output) { 47 | return; 48 | } 49 | 50 | std::vector> includeSetStack; 51 | includeSetStack.push_back(std::set{}); 52 | 53 | auto lines = StringHelper::split(text, '\n'); 54 | const auto lastLine = lines.back(); 55 | lines.pop_back(); 56 | text.clear(); 57 | for (const auto& line : lines) { 58 | using StringHelper::trimLeft; 59 | using StringHelper::trimRight; 60 | std::string chunk; 61 | chunk = trimRight(trimRight(line, ' '), '\t'); 62 | chunk = trimLeft(trimLeft(chunk, ' '), '\t'); 63 | 64 | if (!StringHelper::startWith(chunk, "#include")) { 65 | if (StringHelper::startWith(chunk, "#if")) { 66 | includeSetStack.push_back(std::set{}); 67 | } 68 | else if (StringHelper::startWith(chunk, "#elif") 69 | || StringHelper::startWith(chunk, "#else")) { 70 | if (includeSetStack.empty()) { 71 | std::cout << "Warning: preprocessor mismatch" << std::endl; 72 | } 73 | if (!includeSetStack.empty()) { 74 | includeSetStack.pop_back(); 75 | } 76 | includeSetStack.push_back(std::set{}); 77 | } 78 | else if (StringHelper::startWith(chunk, "#endif")) { 79 | if (includeSetStack.empty()) { 80 | std::cout << "Warning: preprocessor mismatch" << std::endl; 81 | } 82 | if (!includeSetStack.empty()) { 83 | includeSetStack.pop_back(); 84 | } 85 | } 86 | text += line; 87 | text += '\n'; 88 | continue; 89 | } 90 | 91 | auto findInclude = [&includeSetStack](const std::string& headerPath) -> bool { 92 | for (const auto& includes : includeSetStack) { 93 | auto iter = includes.find(headerPath); 94 | if (iter != std::end(includes)) { 95 | return true; 96 | } 97 | } 98 | return false; 99 | }; 100 | 101 | if (!findInclude(chunk)) { 102 | text += line; 103 | text += '\n'; 104 | assert(!includeSetStack.empty()); 105 | auto & includes = includeSetStack.back(); 106 | includes.emplace(std::move(chunk)); 107 | continue; 108 | } 109 | std::cout << "Found the dup: '" << chunk << "' at " << path << std::endl; 110 | } 111 | text += lastLine; 112 | 113 | output << text; 114 | } 115 | 116 | } // unnamed namespace 117 | 118 | int main(int argc, char *argv[]) 119 | { 120 | CommandLineParser parser; 121 | setupCommandLineParser(parser); 122 | parser.parse(argc, argv); 123 | 124 | if (parser.hasParseError()) { 125 | std::cerr << parser.getErrorMessage() << std::endl; 126 | return 1; 127 | } 128 | if (parser.exists("-h") || parser.exists("-help")) { 129 | std::cout << parser.getHelpText() << std::endl; 130 | return 0; 131 | } 132 | if (parser.getPaths().empty()) { 133 | std::cerr << "error: no input file" << std::endl; 134 | return 1; 135 | } 136 | 137 | for (auto & path : parser.getPaths()) { 138 | refactorSourceCode(path); 139 | } 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /natsume/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /natsume/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = natsume 3 | HEADERS = ../somera/*.h 4 | SOURCES = ../somera/*.cpp main.cpp 5 | 6 | xcode: 7 | $(NORI) \ 8 | -generator=xcode \ 9 | -o $(PRODUCTNAME) \ 10 | -std=c++14 \ 11 | -stdlib=libc++ \ 12 | -I.. \ 13 | $(HEADERS) \ 14 | $(SOURCES) 15 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 16 | @mkdir -p bin 17 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 18 | -------------------------------------------------------------------------------- /natsume/README.md: -------------------------------------------------------------------------------- 1 | # natsume 2 | 3 | A simple file rename tool. 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Build and run 8 | 9 | ```sh 10 | # Setup build tool 11 | make -C ../natsume bootstrap 12 | 13 | # Build 14 | make xcode 15 | 16 | # Run 17 | ./bin/natsume -help 18 | ``` 19 | -------------------------------------------------------------------------------- /natsume/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/CommandLineParser.h" 4 | #include "somera/FileSystem.h" 5 | #include "somera/Optional.h" 6 | #include "somera/StringHelper.h" 7 | #include 8 | #include 9 | #include 10 | 11 | using somera::CommandLineParser; 12 | using somera::Optional; 13 | using somera::NullOpt; 14 | namespace FileSystem = somera::FileSystem; 15 | namespace StringHelper = somera::StringHelper; 16 | 17 | namespace { 18 | 19 | void setupCommandLineParser(CommandLineParser & parser) 20 | { 21 | using somera::CommandLineArgumentType::Flag; 22 | using somera::CommandLineArgumentType::JoinedOrSeparate; 23 | parser.setUsageText("natsume [directory path]"); 24 | parser.addArgument("-h", Flag, "Display available options"); 25 | parser.addArgument("-help", Flag, "Display available options"); 26 | } 27 | 28 | void renameFiles(const std::string& directory, const std::regex& searchString, const std::string& replaceString) 29 | { 30 | if (!FileSystem::isDirectory(directory)) { 31 | // error 32 | std::cerr << "error: Invalid path, not directory." << std::endl; 33 | return; 34 | } 35 | 36 | auto result = FileSystem::readDirectory(directory); 37 | auto files = std::get<0>(result); 38 | auto error = std::get<1>(result); 39 | 40 | if (error) { 41 | // error 42 | std::cerr << "error: Failed to read directory." << std::endl; 43 | return; 44 | } 45 | 46 | std::sort(std::begin(files), std::end(files)); 47 | 48 | for (auto & file : files) { 49 | auto newname = std::regex_replace(file, searchString, replaceString); 50 | if (newname == file) { 51 | continue; 52 | } 53 | if (newname.empty()) { 54 | continue; 55 | } 56 | error = FileSystem::rename(FileSystem::join(directory, file), FileSystem::join(directory, newname)); 57 | if (error) { 58 | // error 59 | std::cerr << "error: Failed to rename: " << file << " => " << newname << std::endl; 60 | continue; 61 | } 62 | std::cout << file << " => " << newname << std::endl; 63 | } 64 | } 65 | 66 | } // unnamed namespace 67 | 68 | int main(int argc, char *argv[]) 69 | { 70 | CommandLineParser parser; 71 | setupCommandLineParser(parser); 72 | parser.parse(argc, argv); 73 | 74 | if (parser.hasParseError()) { 75 | std::cerr << parser.getErrorMessage() << std::endl; 76 | return 1; 77 | } 78 | if (parser.exists("-h") || parser.exists("-help")) { 79 | std::cout << parser.getHelpText() << std::endl; 80 | return 0; 81 | } 82 | if (parser.getPaths().empty()) { 83 | std::cerr << "error: no input file" << std::endl; 84 | return 1; 85 | } 86 | 87 | for (auto & path : parser.getPaths()) { 88 | renameFiles(path, std::regex(R"(image_(\d+).png)"), "thumbnail_$1.png"); 89 | } 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /nazuna/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /nazuna/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = nazuna 3 | HEADERS = ../somera/*.h 4 | SOURCES = ../somera/*.cpp main.cpp 5 | 6 | xcode: 7 | $(NORI) \ 8 | -generator=xcode \ 9 | -o $(PRODUCTNAME) \ 10 | -std=c++14 \ 11 | -stdlib=libc++ \ 12 | -I.. \ 13 | $(HEADERS) \ 14 | $(SOURCES) 15 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 16 | @mkdir -p bin 17 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 18 | -------------------------------------------------------------------------------- /nazuna/README.md: -------------------------------------------------------------------------------- 1 | # nazuna 2 | 3 | A simple C/C++ refactoring tool. 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Features 8 | 9 | * Add new line to end of file. 10 | * Replace tabs with white spaces 11 | * Remove trailing spaces, tabs and extra newlines 12 | 13 | ## Build and run 14 | 15 | ```sh 16 | # Setup build tool 17 | make -C ../nori bootstrap 18 | 19 | # Build 20 | make xcode 21 | 22 | # Run 23 | ./bin/nazuna -help 24 | ``` 25 | -------------------------------------------------------------------------------- /nazuna/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/CommandLineParser.h" 4 | #include "somera/FileSystem.h" 5 | #include "somera/Optional.h" 6 | #include "somera/StringHelper.h" 7 | #include 8 | #include 9 | 10 | using somera::CommandLineParser; 11 | using somera::Optional; 12 | using somera::NullOpt; 13 | namespace StringHelper = somera::StringHelper; 14 | 15 | namespace { 16 | 17 | void setupCommandLineParser(CommandLineParser & parser) 18 | { 19 | using somera::CommandLineArgumentType::Flag; 20 | using somera::CommandLineArgumentType::JoinedOrSeparate; 21 | parser.setUsageText("nazuna [options ...] [C/C++ file ...]"); 22 | parser.addArgument("-h", Flag, "Display available options"); 23 | parser.addArgument("-help", Flag, "Display available options"); 24 | } 25 | 26 | std::string removeUnnecessaryWhitespace(const std::string& text) 27 | { 28 | using StringHelper::trimRight; 29 | std::string result; 30 | for (auto & line : StringHelper::split(text, '\n')) { 31 | result += trimRight(trimRight(line, ' '), '\t'); 32 | result += '\n'; 33 | } 34 | return std::move(result); 35 | } 36 | 37 | std::string replaceHardTabsWithWhiteSpaces(const std::string& text) 38 | { 39 | constexpr auto spaces = " "; 40 | return StringHelper::replace(text, "\t", spaces); 41 | } 42 | 43 | std::string replaceCRLFWithLF(const std::string& text) 44 | { 45 | return StringHelper::replace(text, "\r\n", "\n"); 46 | } 47 | 48 | std::string trimLastLineBreaks(const std::string& text) 49 | { 50 | return StringHelper::trimRight(text, '\n') + '\n'; 51 | } 52 | 53 | void refactorSourceCode(const std::string& path) 54 | { 55 | std::ifstream input(path); 56 | if (!input) { 57 | return; 58 | } 59 | std::istreambuf_iterator start(input); 60 | std::istreambuf_iterator end; 61 | std::string text(start, end); 62 | input.close(); 63 | 64 | std::ofstream output(path, std::ios::out | std::ios::trunc); 65 | if (!output) { 66 | return; 67 | } 68 | text = replaceCRLFWithLF(text); 69 | text = replaceHardTabsWithWhiteSpaces(text); 70 | text = removeUnnecessaryWhitespace(text); 71 | text = trimLastLineBreaks(text); 72 | output << text; 73 | } 74 | 75 | } // unnamed namespace 76 | 77 | int main(int argc, char *argv[]) 78 | { 79 | CommandLineParser parser; 80 | setupCommandLineParser(parser); 81 | parser.parse(argc, argv); 82 | 83 | if (parser.hasParseError()) { 84 | std::cerr << parser.getErrorMessage() << std::endl; 85 | return 1; 86 | } 87 | if (parser.exists("-h") || parser.exists("-help")) { 88 | std::cout << parser.getHelpText() << std::endl; 89 | return 0; 90 | } 91 | if (parser.getPaths().empty()) { 92 | std::cerr << "error: no input file" << std::endl; 93 | return 1; 94 | } 95 | 96 | for (auto & path : parser.getPaths()) { 97 | refactorSourceCode(path); 98 | } 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /nori/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | nori.xcodeproj 9 | -------------------------------------------------------------------------------- /nori/CompileOptions.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | 11 | struct GeneratorError { 12 | GeneratorError() 13 | : hasError(false) 14 | { 15 | } 16 | 17 | explicit GeneratorError(const std::string& desc) 18 | : description(desc) 19 | , hasError(true) 20 | {} 21 | 22 | std::string description; 23 | bool hasError = false; 24 | }; 25 | 26 | struct CompileOptions { 27 | std::string generatorOutputDirectory; 28 | std::string productName; 29 | std::string targetName; 30 | std::vector sources; 31 | std::vector libraries; 32 | std::vector includeSearchPaths; 33 | std::vector librarySearchPaths; 34 | std::vector preprocessorDefinitions; 35 | std::vector otherLinkerFlags; 36 | std::map buildSettings; 37 | std::string author; 38 | bool enableCppExceptions = true; 39 | bool enableCppRtti = true; 40 | }; 41 | 42 | } // namespace somera 43 | -------------------------------------------------------------------------------- /nori/MSBuild.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "CompileOptions.h" 6 | 7 | namespace somera { 8 | namespace MSBuild { 9 | 10 | GeneratorError GenerateMSBuildProject(const CompileOptions& options); 11 | 12 | } // namespace MSBuild 13 | } // namespace somera 14 | -------------------------------------------------------------------------------- /nori/Makefile: -------------------------------------------------------------------------------- 1 | CXX = clang++ 2 | PRODUCTNAME = nori 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | *.h 7 | 8 | SOURCES = \ 9 | ../somera/*.cpp \ 10 | MSBuild.cpp \ 11 | XcodeProject.cpp \ 12 | main.cpp 13 | 14 | COMPILER_OPTIONS = \ 15 | -o $(PRODUCTNAME) \ 16 | -std=c++14 \ 17 | -stdlib=libc++ \ 18 | -I.. \ 19 | $(SOURCES) 20 | 21 | bootstrap: 22 | $(CXX) $(COMPILER_OPTIONS) 23 | @mkdir -p bin 24 | @mv nori bin/ 25 | 26 | xcode: 27 | ./bin/nori \ 28 | -generator=xcode \ 29 | $(HEADERS) \ 30 | $(COMPILER_OPTIONS) 31 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 32 | @mkdir -p bin 33 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 34 | -------------------------------------------------------------------------------- /nori/README.md: -------------------------------------------------------------------------------- 1 | # nori 2 | 3 | A Clang/GCC-compatible C/C++ project file generator for coding sketch. 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Features 8 | 9 | - Easily generate Xcode project (`.xcodeproj`) 10 | - Easily generate Visual Studio solution (`sln`) and project (`vcxproj`) files 11 | 12 | ## Build and run 13 | 14 | ```sh 15 | # Build 16 | make bootstrap 17 | make xcode 18 | 19 | # Run 20 | ./bin/nori -help 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```sh 26 | # Generate myapp.xcodeproj file 27 | nori -generator=xcode -o myapp myapp.cpp 28 | 29 | # Generate myapp.sln and myapp.vcxproj file 30 | nori -generator=msbuild -o myapp myapp.cpp 31 | ``` 32 | 33 | ### To build your application with Xcode 34 | 35 | ```sh 36 | # Generate myapp.xcodeproj file 37 | nori \ 38 | -generator=xcode \ 39 | -o myapp \ 40 | -std=c++14 \ 41 | -stdlib=libc++ \ 42 | -Ipath/to/include \ 43 | *.cpp 44 | 45 | # Build your project with Xcode 46 | xcodebuild -project myapp.xcodeproj -configuration Release 47 | ``` 48 | 49 | The command-line tool is Clang/GCC-compatible, so it is the same as running the following commands in terminal: 50 | 51 | ```sh 52 | clang \ 53 | -o myapp \ 54 | -std=c++14 \ 55 | -stdlib=libc++ \ 56 | -Ipath/to/include \ 57 | *.cpp 58 | ``` 59 | -------------------------------------------------------------------------------- /nori/XcodeProject.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "CompileOptions.h" 6 | 7 | namespace somera { 8 | namespace Xcode { 9 | 10 | GeneratorError GenerateXcodeProject(const CompileOptions& options); 11 | 12 | } // namespace Xcode 13 | } // namespace somera 14 | -------------------------------------------------------------------------------- /pomdog-refactor/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /pomdog-refactor/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = pomdog-refactor 3 | HEADERS = \ 4 | ../somera/*.h \ 5 | *.h 6 | SOURCES = \ 7 | ../somera/*.cpp \ 8 | main.cpp 9 | 10 | xcode: 11 | $(NORI) \ 12 | -generator=xcode \ 13 | -o $(PRODUCTNAME) \ 14 | -std=c++14 \ 15 | -stdlib=libc++ \ 16 | -I.. \ 17 | $(HEADERS) \ 18 | $(SOURCES) 19 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 20 | @mkdir -p bin 21 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 22 | -------------------------------------------------------------------------------- /pomdog-refactor/README.md: -------------------------------------------------------------------------------- 1 | # pomdog-refactor 2 | 3 | A refactoring tool for Pomdog. 4 | -------------------------------------------------------------------------------- /pomdog-refactor/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/CommandLineParser.h" 4 | #include "somera/StringHelper.h" 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace somera; 10 | 11 | namespace { 12 | 13 | void setupCommandLineParser(CommandLineParser & parser) 14 | { 15 | using somera::CommandLineArgumentType::Flag; 16 | using somera::CommandLineArgumentType::JoinedOrSeparate; 17 | parser.setUsageText("pomdog-refactor [options ...] [source file ...]"); 18 | parser.addArgument("-h", Flag, "Display available options"); 19 | parser.addArgument("-help", Flag, "Display available options"); 20 | } 21 | 22 | std::string trimLastLineBreaks(const std::string& text) 23 | { 24 | return StringHelper::trimRight(text, '\n') + '\n'; 25 | } 26 | 27 | std::string replaceHeaderWithPragmaOnce(const std::string& text) 28 | { 29 | std::regex re(R"(#ifndef POMDOG_[A-Z0-9_]*_HPP\n#define POMDOG_[A-Z0-9_]*_HPP\n((.*\n)*)\n#endif \/\/ POMDOG_[A-Z0-9_]*_HPP)"); 30 | return std::regex_replace(text, re, "#pragma once\n$1"); 31 | } 32 | 33 | std::string replaceLicenseYear(const std::string& text) 34 | { 35 | return StringHelper::replace( 36 | text, 37 | "2013-2015 mogemimi.", 38 | "2013-2016 mogemimi."); 39 | } 40 | 41 | std::string replaceLicense(const std::string& text) 42 | { 43 | return StringHelper::replace( 44 | text, 45 | "// Copyright (c) 2013-2016 mogemimi.\n" 46 | "// Distributed under the MIT license. See LICENSE.md file for details.", 47 | "// Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license."); 48 | } 49 | 50 | void refactorSourceCode(const std::string& path) 51 | { 52 | std::ifstream input(path); 53 | if (!input) { 54 | return; 55 | } 56 | std::istreambuf_iterator start(input); 57 | std::istreambuf_iterator end; 58 | std::string text(start, end); 59 | input.close(); 60 | 61 | std::ofstream output(path, std::ios::out | std::ios::trunc); 62 | if (!output) { 63 | return; 64 | } 65 | 66 | text = replaceHeaderWithPragmaOnce(text); 67 | text = trimLastLineBreaks(text); 68 | text = replaceLicenseYear(text); 69 | text = replaceLicense(text); 70 | 71 | output << text; 72 | } 73 | 74 | } // unnamed namespace 75 | 76 | int main(int argc, char *argv[]) 77 | { 78 | CommandLineParser parser; 79 | setupCommandLineParser(parser); 80 | parser.parse(argc, argv); 81 | 82 | if (parser.hasParseError()) { 83 | std::cerr << parser.getErrorMessage() << std::endl; 84 | return 1; 85 | } 86 | if (parser.exists("-h") || parser.exists("-help")) { 87 | std::cout << parser.getHelpText() << std::endl; 88 | return 0; 89 | } 90 | if (parser.getPaths().empty()) { 91 | std::cerr << "error: no input file" << std::endl; 92 | return 1; 93 | } 94 | 95 | for (auto & path : parser.getPaths()) { 96 | refactorSourceCode(path); 97 | } 98 | std::cout << "ok" << std::endl; 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /rhodanthe/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | 10 | # Visual Studio 11 | *.filters 12 | *.opensdf 13 | *.sdf 14 | *.sln 15 | *.vcxproj 16 | -------------------------------------------------------------------------------- /rhodanthe/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = rhodanthe 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | ../typo-poi/source/thirdparty/*.h \ 7 | ../typo-poi/source/*.h \ 8 | *.h 9 | 10 | SOURCES = \ 11 | ../somera/*.cpp \ 12 | ../typo-poi/source/EditDistance.cpp \ 13 | ../typo-poi/source/WordDiff.cpp \ 14 | ../typo-poi/source/thirdparty/ConvertUTF.c \ 15 | ../typo-poi/source/UTF8.cpp \ 16 | main.cpp 17 | 18 | xcode: 19 | $(NORI) \ 20 | -generator=xcode \ 21 | -o $(PRODUCTNAME) \ 22 | -std=c++14 \ 23 | -stdlib=libc++ \ 24 | -I.. \ 25 | -I../typo-poi/source \ 26 | $(HEADERS) \ 27 | $(SOURCES) 28 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 29 | @mkdir -p bin 30 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 31 | 32 | rebuild-xcode: 33 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 34 | @mkdir -p bin 35 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 36 | -------------------------------------------------------------------------------- /rhodanthe/README.md: -------------------------------------------------------------------------------- 1 | # rhodanthe 2 | 3 | Measuring performance about SES (Shortest Edit Script)/DIff algorithms 4 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 5 | 6 | ## Build and run 7 | 8 | **Build:** 9 | 10 | ```sh 11 | git clone https://github.com/mogemimi/daily-snippets.git 12 | cd daily-snippets/rhodanthe 13 | 14 | # Setup build tool 15 | make -C ../nori bootstrap 16 | 17 | # Build 18 | make xcode 19 | 20 | # Run 21 | ./bin/rhodanthe -help 22 | ``` 23 | -------------------------------------------------------------------------------- /rhodanthe/temp.md: -------------------------------------------------------------------------------- 1 | #### Edit graph 2 | 3 | ``` 4 | a b c d e f g h i j k 5 | 0 1 2 3 4 5 6 7 8 9 10 11 6 | A 1 2 3 4 5 6 7 8 9 10 11 12 7 | b 2 3 2 3 4 5 6 7 8 9 10 11 8 | c 3 4 3 2 3 4 5 6 7 8 9 10 9 | D 4 5 4 3 4 5 6 7 8 9 10 11 10 | e 5 6 5 4 5 4 5 6 7 8 9 10 11 | H 6 7 6 5 6 5 6 7 8 9 10 11 12 | i 7 8 7 6 7 6 7 8 9 8 9 10 13 | j 8 9 8 7 8 7 8 9 10 9 8 9 14 | k 9 10 9 8 9 8 9 10 11 10 9 8 15 | ``` 16 | 17 | ``` 18 | A b c D e H i j k 19 | 0 1 2 3 4 5 6 7 8 9 20 | a 1 2 3 4 5 6 7 8 9 10 21 | b 2 3 2 3 4 5 6 7 8 9 22 | c 3 4 3 2 3 4 5 6 7 8 23 | d 4 5 4 3 4 5 6 7 8 9 24 | e 5 6 5 4 5 4 5 6 7 8 25 | ``` 26 | 27 | ``` 28 | k j i H e D c b A 29 | 0 1 2 3 4 5 6 7 8 9 30 | k 1 0 1 2 3 4 5 6 7 8 31 | j 2 1 0 1 2 3 4 5 6 7 32 | i 3 2 1 0 1 2 3 4 5 6 33 | h 4 3 2 1 2 3 4 5 6 7 34 | g 5 4 3 2 3 4 5 6 7 8 35 | f 6 5 4 3 4 5 6 7 8 9 36 | ``` 37 | -------------------------------------------------------------------------------- /room203/README.md: -------------------------------------------------------------------------------- 1 | ## Concept 2 | 3 | - makes build settings more simple 4 | 5 | ## Draft 6 | 7 | ```yaml 8 | --- 9 | my_config_debug: 10 | type: "configuration" 11 | name: "Debug" 12 | defines: 13 | - "DEBUG=1" 14 | 15 | targets: 16 | mylib: 17 | product_name: "MyLib" 18 | type: "shared_library" 19 | configurations: 20 | Debug: 21 | defines: 22 | - "DEBUG=1" 23 | myapp: 24 | product_name: "MyLib" 25 | type: "shared_library" 26 | configurations: 27 | Debug: 28 | defines: 29 | - "DEBUG=1" 30 | Release: 31 | defines: 32 | - "NDEBUG=1" 33 | 34 | include_dirs: 35 | - "../dependencies/vendor/glew/include" 36 | 37 | headers: 38 | - "include/HttpService.h" 39 | 40 | sources: 41 | - "src/HttpService.cpp" 42 | - "src/main.cpp" 43 | 44 | msbuild_settings: 45 | ClCompile: 46 | TreatWarningAsError: true # /WX 47 | PreprocessorDefinitions: 48 | - "_WIN32_WINNT=0x0601" # Windows 7 or later 49 | - "WIN32_LEAN_AND_MEAN" 50 | - "NOMINMAX" 51 | Link: 52 | GenerateDebugInformation: true # /DEBUG 53 | 54 | xcode_settings: 55 | CLANG_CXX_LANGUAGE_STANDARD: "c++14" 56 | CLANG_CXX_LIBRARY: "libc++" 57 | GCC_WARN_64_TO_32_BIT_CONVERSION: "YES" 58 | GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS: "YES" 59 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS: "YES" 60 | GCC_WARN_ABOUT_MISSING_NEWLINE: "YES" 61 | GCC_WARN_ABOUT_RETURN_TYPE: "YES" 62 | GCC_WARN_CHECK_SWITCH_STATEMENTS: "YES" 63 | GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS: "YES" 64 | ``` 65 | 66 | ## Settings 67 | 68 | - Build Configuration(s) 69 | - Debug 70 | - Release 71 | 72 | - Targets 73 | - Executable (Mac/iOS/tvOS) 74 | - Library (Mac/iOS/tvOS) 75 | 76 | - Platform(s) 77 | - Windows 78 | - Mac 79 | - iOS 80 | - Android 81 | 82 | - Underlying build tool 83 | - Xcode (Xcodebuild) 84 | - Visual Studio (MSBuild) 85 | - CMake 86 | - Make 87 | -------------------------------------------------------------------------------- /slackbot/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | 10 | ## Third party 11 | rapidjson 12 | -------------------------------------------------------------------------------- /slackbot/HttpService.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "somera/Optional.h" 6 | extern "C" { 7 | #include 8 | } 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace somera { 19 | 20 | enum class HttpRequestMethod { 21 | HEAD, 22 | POST, 23 | GET, 24 | PUT, 25 | DELETE, 26 | }; 27 | 28 | struct HttpRequestOptions { 29 | Optional hostname; 30 | Optional path; 31 | Optional port; 32 | Optional method; 33 | Optional postFields; 34 | Optional agent; 35 | std::map headers; 36 | }; 37 | 38 | class HttpRequest { 39 | private: 40 | std::function& data)> callback; 41 | std::vector blob; 42 | CURL* curl; 43 | 44 | public: 45 | HttpRequest( 46 | const std::string& url, 47 | std::function& data)> callback); 48 | 49 | HttpRequest( 50 | const HttpRequestOptions& options, 51 | std::function& data)> callback); 52 | 53 | ~HttpRequest(); 54 | 55 | HttpRequest(HttpRequest const&) = delete; 56 | HttpRequest & operator=(HttpRequest const&) = delete; 57 | 58 | CURL* getCurl() const; 59 | void onCompleted(); 60 | void onError(); 61 | 62 | void setTimeout(const std::chrono::seconds& timeout); 63 | 64 | private: 65 | static size_t writeCallback( 66 | void const* contents, 67 | size_t size, 68 | size_t nmemb, 69 | void* userPointer); 70 | }; 71 | 72 | class HttpService { 73 | private: 74 | CURLM* multiHandle; 75 | std::map> sessions; 76 | std::chrono::seconds timeout; 77 | 78 | public: 79 | HttpService(); 80 | ~HttpService(); 81 | 82 | HttpService(const HttpService&) = delete; 83 | HttpService & operator=(const HttpService&) = delete; 84 | 85 | void setTimeout(const std::chrono::seconds& timeout); 86 | 87 | void poll(); 88 | 89 | void waitAll(); 90 | 91 | void request( 92 | const HttpRequestOptions& options, 93 | std::function&)> callback); 94 | 95 | void get( 96 | const std::string& uri, 97 | std::function&)> callback); 98 | 99 | bool empty() const; 100 | }; 101 | 102 | } // namespace somera 103 | -------------------------------------------------------------------------------- /slackbot/HttpUtility.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "HttpUtility.h" 4 | #include 5 | #include 6 | 7 | namespace somera { 8 | namespace { 9 | 10 | char toHex(char code) 11 | { 12 | constexpr auto hex = "0123456789ABCDEF"; 13 | return hex[code & 15]; 14 | } 15 | 16 | bool isSafeCharacter(char32_t c) 17 | { 18 | constexpr char32_t characters[] = 19 | U"-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"; 20 | return std::binary_search(std::begin(characters), std::end(characters), c); 21 | } 22 | 23 | } // unnamed namespace 24 | 25 | std::string HttpUtility::encodeURIComponent(const std::string& source) 26 | { 27 | std::stringstream stream; 28 | for (const auto& c : source) { 29 | if (isSafeCharacter(c)) { 30 | stream << c; 31 | } else { 32 | stream << '%' << toHex(c >> 4) << toHex(c & 15); 33 | } 34 | } 35 | return stream.str(); 36 | } 37 | 38 | std::string HttpUtility::stringify( 39 | const std::map& params) 40 | { 41 | std::stringstream stream; 42 | bool needAmpersand = false; 43 | for (auto & pair : params) { 44 | if (needAmpersand) { 45 | stream << '&'; 46 | } 47 | stream << pair.first << '=' << encodeURIComponent(pair.second); 48 | needAmpersand = true; 49 | } 50 | return stream.str(); 51 | } 52 | 53 | } // namespace somera 54 | -------------------------------------------------------------------------------- /slackbot/HttpUtility.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | namespace HttpUtility { 10 | 11 | std::string encodeURIComponent(const std::string& source); 12 | 13 | std::string stringify( 14 | const std::map& params); 15 | 16 | } // namespace HttpUtility 17 | } // namespace somera 18 | -------------------------------------------------------------------------------- /slackbot/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = slackbot 3 | HEADERS = \ 4 | ../somera/*.h \ 5 | *.h 6 | SOURCES = \ 7 | ../somera/*.cpp \ 8 | HttpUtility.cpp \ 9 | HttpService.cpp \ 10 | iTunesNowPlaying.cpp \ 11 | main.cpp \ 12 | SlackClient.cpp \ 13 | TerminalHelper.cpp 14 | 15 | xcode: 16 | $(NORI) \ 17 | -generator=xcode \ 18 | -o $(PRODUCTNAME) \ 19 | -std=c++14 \ 20 | -stdlib=libc++ \ 21 | -llibcurl.tbd \ 22 | -I.. \ 23 | -Irapidjson/include \ 24 | $(HEADERS) \ 25 | $(SOURCES) 26 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 27 | @mkdir -p bin 28 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 29 | -------------------------------------------------------------------------------- /slackbot/README.md: -------------------------------------------------------------------------------- 1 | # Slack Bot 2 | 3 | My personal slack bot. :boat: 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Build and run 8 | 9 | ```sh 10 | # Clone third-party libraries 11 | git clone --depth=1 https://github.com/miloyip/rapidjson rapidjson 12 | 13 | # Setup build tool 14 | make -C ../nori bootstrap 15 | 16 | # Build 17 | make xcode 18 | 19 | # Run 20 | ./bin/slackbot -help 21 | ``` 22 | 23 | ## Thanks 24 | 25 | - libcurl 26 | - RapidJSON 27 | -------------------------------------------------------------------------------- /slackbot/SlackClient.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/Optional.h" 4 | #include "HttpService.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace somera { 12 | 13 | //struct SlackTopic { 14 | // std::string value; 15 | // std::string creator; 16 | // int last_set; 17 | //}; 18 | // 19 | //struct SlackPurpose { 20 | // std::string value; 21 | // std::string creator; 22 | // int last_set; 23 | //}; 24 | 25 | struct SlackChannel { 26 | // std::vector members; 27 | // SlackTopic topic; 28 | // SlackPurpose purpose; 29 | std::string id; 30 | std::string name; 31 | int created; 32 | std::string creator; 33 | bool is_archived; 34 | bool is_general; 35 | bool is_member; 36 | }; 37 | 38 | struct SlackMessage { 39 | std::string type; 40 | std::string channel; 41 | std::string user; 42 | std::string text; 43 | std::string subtype; 44 | std::string ts; 45 | std::chrono::system_clock::time_point timestamp; 46 | }; 47 | 48 | struct SlackHistory { 49 | std::vector messages; 50 | std::string latest; 51 | bool has_more; 52 | }; 53 | 54 | struct SlackChatPostMessageOptions { 55 | std::string channel; 56 | std::string text; 57 | Optional username; 58 | Optional icon_url; 59 | Optional icon_emoji; 60 | Optional as_user; 61 | }; 62 | 63 | struct SlackChannelsHistoryOptions { 64 | std::string channel; 65 | Optional latest; 66 | Optional oldest; 67 | Optional count; 68 | }; 69 | 70 | class SlackClient final { 71 | private: 72 | somera::HttpService http; 73 | std::string token; 74 | std::function errorCallback; 75 | std::vector channels; 76 | 77 | public: 78 | explicit SlackClient(const std::string& token); 79 | 80 | void login(); 81 | 82 | Optional getChannelByID(const std::string& id); 83 | 84 | Optional getChannelByName(const std::string& name); 85 | 86 | ///@brief See https://api.slack.com/methods/api.test 87 | ///@example 88 | /// ``` 89 | /// slack.apiTest([](std::string json) { 90 | /// std::cout << json << std::endl; 91 | /// }); 92 | /// ``` 93 | void apiTest(std::function callback); 94 | 95 | ///@brief See https://api.slack.com/methods/auth.test 96 | ///@example 97 | /// ``` 98 | /// slack.authTest([](std::string json) { 99 | /// std::cout << json << std::endl; 100 | /// }); 101 | /// ``` 102 | void authTest(std::function callback); 103 | 104 | /// See https://api.slack.com/methods/channels.history 105 | void channelsHistory( 106 | const SlackChannelsHistoryOptions& options, 107 | std::function callback); 108 | 109 | /// See https://api.slack.com/methods/chat.postMessage 110 | void chatPostMessage( 111 | const SlackChatPostMessageOptions& options, 112 | std::function callback); 113 | 114 | void onError(std::function callback); 115 | 116 | private: 117 | void apiCall( 118 | const std::string& method, 119 | std::map && params, 120 | std::function && callback); 121 | 122 | void emitError(const std::string& errorMessage); 123 | }; 124 | 125 | } // namespace somera 126 | -------------------------------------------------------------------------------- /slackbot/TerminalHelper.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "TerminalHelper.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | 11 | std::string getHomeDirectory() 12 | { 13 | auto home = ::getenv("HOME"); 14 | if (home == nullptr) { 15 | auto pw = ::getpwuid(::getuid()); 16 | assert(pw != nullptr); 17 | home = pw->pw_dir; 18 | } 19 | return std::move(home); 20 | } 21 | 22 | } // namespace somera 23 | -------------------------------------------------------------------------------- /slackbot/TerminalHelper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace somera { 8 | 9 | std::string getHomeDirectory(); 10 | 11 | } // namespace somera 12 | -------------------------------------------------------------------------------- /slackbot/iTunesNowPlaying.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "iTunesNowplaying.h" 4 | #include "somera/SubprocessHelper.h" 5 | #include 6 | #include 7 | #ifdef __APPLE_CC__ 8 | #include 9 | #endif 10 | 11 | namespace somera { 12 | namespace iTunesNowPlaying { 13 | 14 | somera::Optional getCurrentTrack() 15 | { 16 | #if defined(__APPLE_CC__) \ 17 | && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8) 18 | // for Mac OS X 19 | #define SOMERA_TOSTRING(x) std::string(#x) 20 | std::error_code err; 21 | std::string result; 22 | std::tie(result, err) = SubprocessHelper::call("osascript -e" + SOMERA_TOSTRING( 23 | 'try' -e 24 | 'tell application "iTunes"' -e 25 | 'set trackName to name of current track' -e 26 | 'set artistName to artist of current track' -e 27 | 'set albumName to album of current track' -e 28 | 'return trackName & "\n" & artistName & "\n" & albumName' -e 29 | 'end tell' -e 30 | 'end try' 31 | )); 32 | if (err) { 33 | return somera::NullOpt; 34 | } 35 | if (result.empty()) { 36 | return somera::NullOpt; 37 | } 38 | 39 | Track track; 40 | std::stringstream stream; 41 | stream << result; 42 | std::getline(stream, track.trackName, '\n'); 43 | std::getline(stream, track.artistName, '\n'); 44 | std::getline(stream, track.albumName, '\n'); 45 | return std::move(track); 46 | #else 47 | return somera::NullOpt; 48 | #endif 49 | } 50 | 51 | } // namespace iTunesNowPlaying 52 | } // namespace somera 53 | -------------------------------------------------------------------------------- /slackbot/iTunesNowPlaying.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/Optional.h" 4 | #include 5 | 6 | namespace somera { 7 | namespace iTunesNowPlaying { 8 | 9 | struct Track { 10 | std::string trackName; 11 | std::string artistName; 12 | std::string albumName; 13 | }; 14 | 15 | somera::Optional getCurrentTrack(); 16 | 17 | } // namespace iTunesNowPlaying 18 | } // namespace somera 19 | -------------------------------------------------------------------------------- /somera/Any.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #ifdef _HAS_EXCEPTIONS 12 | #include 13 | #endif 14 | 15 | namespace somera { 16 | 17 | class Any final { 18 | private: 19 | struct HolderBase { 20 | virtual ~HolderBase() = default; 21 | }; 22 | 23 | template 24 | struct Holder final : public HolderBase { 25 | T Value; 26 | 27 | template 28 | explicit Holder(U && valueIn) 29 | : Value(std::forward(valueIn)) 30 | {} 31 | 32 | static_assert(std::is_object::value, ""); 33 | }; 34 | 35 | std::unique_ptr data; 36 | std::type_index typeIndex; 37 | 38 | public: 39 | Any() = delete; 40 | Any(Any const&) = delete; 41 | Any(Any &&) = default; 42 | 43 | Any& operator=(Any const&) = delete; 44 | Any& operator=(Any &&) = default; 45 | 46 | template 47 | Any(T && value) 48 | : data(std::make_unique>> 50 | >(std::forward(value))) 51 | , typeIndex(typeid(std::remove_const_t>)) 52 | {} 53 | 54 | template 55 | bool is() const 56 | { 57 | return typeIndex == typeid(T); 58 | } 59 | 60 | template 61 | T const& as() const 62 | { 63 | assert(typeIndex == typeid(T)); 64 | if (!is()) { 65 | #ifdef _HAS_EXCEPTIONS 66 | using BadAnyCast = std::runtime_error; 67 | throw BadAnyCast("BadAnyCast"); 68 | #endif 69 | } 70 | assert(is()); 71 | assert(data); 72 | auto derived = dynamic_cast*>(data.get()); 73 | assert(derived); 74 | return derived->Value; 75 | } 76 | 77 | template 78 | T & as() 79 | { 80 | assert(typeIndex == typeid(T)); 81 | if (!is()) { 82 | #ifdef _HAS_EXCEPTIONS 83 | using BadAnyCast = std::runtime_error; 84 | throw BadAnyCast("BadAnyCast"); 85 | #endif 86 | } 87 | assert(is()); 88 | assert(data); 89 | auto derived = dynamic_cast*>(data.get()); 90 | assert(derived); 91 | return derived->Value; 92 | } 93 | 94 | std::type_index type() const 95 | { 96 | return typeIndex; 97 | } 98 | }; 99 | 100 | } // namespace somera 101 | -------------------------------------------------------------------------------- /somera/CommandLineParser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "Optional.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace somera { 15 | 16 | enum class CommandLineArgumentType { 17 | Flag, 18 | JoinedOrSeparate, 19 | }; 20 | 21 | struct CommandLineArgumentHint { 22 | std::string name; 23 | std::string help; 24 | CommandLineArgumentType type; 25 | std::vector values; 26 | }; 27 | 28 | struct CommandLineParser { 29 | // void addArgument( 30 | // const std::string& help, 31 | // const std::string& type); 32 | 33 | void addArgument( 34 | const std::string& flag, 35 | CommandLineArgumentType type, 36 | const std::string& help); 37 | 38 | void parse(int argc, char* argv[]) 39 | { 40 | this->parse(argc, const_cast(argv)); 41 | } 42 | 43 | void parse(int argc, const char* argv[]); 44 | 45 | bool hasParseError() const; 46 | 47 | std::string getHelpText() const; 48 | 49 | std::string getErrorMessage() const; 50 | 51 | std::string getExecutablePath() const; 52 | 53 | bool exists(const std::string& flag) const; 54 | 55 | Optional getValue(const std::string& name) const; 56 | 57 | std::vector getValues(const std::string& name) const; 58 | 59 | std::vector getPaths() const; 60 | 61 | void setUsageText(const std::string& usage); 62 | 63 | private: 64 | std::string executablePath; 65 | std::vector hints; 66 | std::set flags; 67 | std::vector paths; 68 | std::stringstream errorMessage; 69 | std::string usageText; 70 | }; 71 | 72 | } // namespace somera 73 | -------------------------------------------------------------------------------- /somera/Defer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | 10 | class Defer final { 11 | std::function callback; 12 | public: 13 | Defer() = delete; 14 | Defer(const Defer&) = delete; 15 | Defer(Defer && other) = delete; 16 | Defer & operator=(const Defer&) = delete; 17 | Defer & operator=(Defer && other) = delete; 18 | 19 | explicit Defer(std::function && func) 20 | : callback(std::move(func)) 21 | {} 22 | 23 | explicit Defer(const std::function& func) 24 | : callback(func) 25 | {} 26 | 27 | ~Defer() 28 | { 29 | if (callback) { 30 | callback(); 31 | } 32 | } 33 | }; 34 | 35 | } // namespace somera 36 | -------------------------------------------------------------------------------- /somera/FileSystem.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace somera { 11 | namespace FileSystem { 12 | 13 | std::string join(const std::string& path1, const std::string& path2); 14 | 15 | std::string getBaseName(const std::string& path); 16 | 17 | std::string getDirectoryName(const std::string& path); 18 | 19 | std::size_t getFileSize(const std::string& path); 20 | 21 | std::size_t getFileSize(const std::string& path, std::error_code& errorCode); 22 | 23 | std::tuple split(const std::string& path); 24 | 25 | std::tuple splitExtension(const std::string& path); 26 | 27 | bool isAbsolute(const std::string& path); 28 | 29 | std::string normalize(const std::string& path); 30 | 31 | std::string relative(const std::string& path, const std::string& start); 32 | 33 | std::string getCurrentDirectory(); 34 | 35 | bool createDirectory(const std::string& path); 36 | 37 | bool createDirectories(const std::string& path); 38 | 39 | bool exists(const std::string& path); 40 | 41 | bool isDirectory(const std::string& path); 42 | 43 | // NOTE: Return a list the names of files or folders in the directory. 44 | std::tuple, std::error_code> 45 | readDirectory(const std::string& directory) noexcept; 46 | 47 | std::error_code rename(const std::string& oldname, const std::string& newname) noexcept; 48 | 49 | } // namespace FileSystem 50 | } // namespace somera 51 | -------------------------------------------------------------------------------- /somera/Optional.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | 10 | struct NullOptionalType final { 11 | struct init { constexpr init() = default; }; 12 | constexpr explicit NullOptionalType(init) noexcept {} 13 | }; 14 | 15 | constexpr NullOptionalType NullOpt{ NullOptionalType::init{} }; 16 | 17 | template 18 | struct Optional { 19 | private: 20 | T data; 21 | bool valid = false; 22 | 23 | public: 24 | constexpr Optional() 25 | : data() 26 | , valid(false) 27 | {} 28 | 29 | constexpr Optional(NullOptionalType const&) 30 | : data() 31 | , valid(false) 32 | {} 33 | 34 | Optional(Optional const&) = default; 35 | Optional(Optional &&) = default; 36 | 37 | constexpr Optional(T const& v) 38 | : data(v) 39 | , valid(true) 40 | {} 41 | 42 | constexpr Optional(T && v) 43 | : data(std::move(v)) 44 | , valid(true) 45 | {} 46 | 47 | Optional & operator=(NullOptionalType const&) 48 | { 49 | valid = false; 50 | data.~T(); 51 | return *this; 52 | } 53 | 54 | Optional & operator=(Optional const&) = default; 55 | Optional & operator=(Optional &&) = default; 56 | 57 | Optional & operator=(T const& v) 58 | { 59 | this->valid = true; 60 | this->data = v; 61 | return *this; 62 | } 63 | 64 | Optional & operator=(T && v) 65 | { 66 | this->valid = true; 67 | this->data = std::move(v); 68 | return *this; 69 | } 70 | 71 | T const* operator->() const noexcept 72 | { 73 | assert(valid); 74 | return &data; 75 | } 76 | 77 | T* operator->() noexcept 78 | { 79 | assert(valid); 80 | return &data; 81 | } 82 | 83 | T const& operator*() const 84 | { 85 | assert(valid); 86 | return data; 87 | } 88 | 89 | T & operator*() 90 | { 91 | assert(valid); 92 | return data; 93 | } 94 | 95 | explicit operator bool() const noexcept 96 | { 97 | return valid; 98 | } 99 | 100 | T const& value() const 101 | { 102 | assert(valid); 103 | return data; 104 | } 105 | 106 | T & value() 107 | { 108 | assert(valid); 109 | return data; 110 | } 111 | }; 112 | 113 | } // namespace somera 114 | -------------------------------------------------------------------------------- /somera/README.md: -------------------------------------------------------------------------------- 1 | # somera 2 | 3 | Small utilities and experimental codes for C++14. 4 | 5 | ## Libraries and Classes 6 | 7 | ### Utility 8 | 9 | - **Any** - any implementation 10 | - **CommandLineParser** - A command line parser 11 | - **FileSystem** - filesystem utility 12 | - **Optional** - optional implementation 13 | - **StringHelper** - string utility 14 | - **SubprocessHelper** - subprocess utility 15 | 16 | ### Signals 17 | 18 | A set of signals and slots implementation for game dev. 19 | 20 | - **Connection** - signal connection implementation 21 | - **ScopedConnection** - scoped connection implementation 22 | - **EventQueue** - thread-safe event queue for game programming 23 | - **Signal** - thread-safe signal implementation for game programming 24 | -------------------------------------------------------------------------------- /somera/StringHelper.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "StringHelper.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace somera { 12 | namespace { 13 | 14 | std::string UnsafeToFormatString(char const* format, std::va_list arg) 15 | { 16 | std::va_list copiedArguments; 17 | va_copy(copiedArguments, arg); 18 | #ifdef _MSC_VER 19 | char buffer[2048]; 20 | std::memset(buffer, 0, sizeof(buffer)); 21 | auto const length = vsnprintf_s(buffer, _countof(buffer), format, copiedArguments); 22 | static_assert(std::is_signed::value, ""); 23 | va_end(copiedArguments); 24 | assert(length > 0); 25 | std::string result(buffer, length); 26 | assert(result.size() == static_cast(length)); 27 | #else 28 | #if __cplusplus >= 201103L 29 | using std::vsnprintf; 30 | #endif 31 | auto const length = vsnprintf(nullptr, 0, format, copiedArguments); 32 | static_assert(std::is_signed::value, ""); 33 | va_end(copiedArguments); 34 | assert(length > 0); 35 | std::string result(length + 1, '\0'); 36 | vsnprintf(&result.front(), result.size(), format, arg); 37 | assert(result.back() == '\0'); 38 | result.resize(length); 39 | #endif 40 | return result; 41 | } 42 | 43 | } // unnamed namespace 44 | 45 | bool StringHelper::startWith(const std::string& text, const std::string& prefix) 46 | { 47 | return (text.size() >= prefix.size()) 48 | && (text.compare(0, prefix.size(), prefix) == 0); 49 | } 50 | 51 | bool StringHelper::endWith(const std::string& text, const std::string& suffix) 52 | { 53 | if (suffix.empty()) { 54 | return true; 55 | } 56 | if (text.size() < suffix.size()) { 57 | return false; 58 | } 59 | return (text.compare(text.size() - suffix.size(), suffix.size(), suffix) == 0); 60 | } 61 | 62 | std::string StringHelper::toLower(const std::string& source) 63 | { 64 | std::string output = source; 65 | std::transform(output.begin(), output.end(), output.begin(), ::tolower); 66 | return output; 67 | } 68 | 69 | std::string StringHelper::toUpper(const std::string& source) 70 | { 71 | std::string output = source; 72 | std::transform(output.begin(), output.end(), output.begin(), ::toupper); 73 | return output; 74 | } 75 | 76 | std::string StringHelper::replace( 77 | const std::string& source, 78 | const std::string& from, 79 | const std::string& to) 80 | { 81 | if (from.empty()) { 82 | return source; 83 | } 84 | auto result = source; 85 | std::string::size_type start = 0; 86 | while ((start = result.find(from, start)) != std::string::npos) { 87 | result.replace(start, from.length(), to); 88 | start += to.length(); 89 | } 90 | return result; 91 | } 92 | 93 | std::vector 94 | StringHelper::split(const std::string& source, char separator) 95 | { 96 | std::vector tokens; 97 | std::string::size_type start = 0; 98 | std::string::size_type end = 0; 99 | while ((end = source.find(separator, start)) != std::string::npos) { 100 | tokens.push_back(source.substr(start, end - start)); 101 | start = end + 1; 102 | } 103 | tokens.push_back(source.substr(start)); 104 | return tokens; 105 | } 106 | 107 | std::vector 108 | StringHelper::split(const std::string& source, const std::string& separator) 109 | { 110 | std::vector tokens; 111 | std::string::size_type start = 0; 112 | std::string::size_type end = 0; 113 | while ((end = source.find(separator, start)) != std::string::npos) { 114 | tokens.push_back(source.substr(start, end - start)); 115 | start = end + separator.size(); 116 | } 117 | tokens.push_back(source.substr(start)); 118 | return tokens; 119 | } 120 | 121 | std::string StringHelper::trimRight(const std::string& source, char separator) 122 | { 123 | const auto func = [&](char c){ return c != separator; }; 124 | std::string result( 125 | std::begin(source), 126 | std::find_if(std::rbegin(source), std::rend(source), func).base()); 127 | return result; 128 | } 129 | 130 | std::string StringHelper::trimLeft(const std::string& source, char separator) 131 | { 132 | const auto func = [&](char c){ return c != separator; }; 133 | std::string result( 134 | std::find_if(std::begin(source), std::end(source), func), 135 | std::end(source)); 136 | return result; 137 | } 138 | 139 | std::string StringHelper::trimRight( 140 | const std::string& source, 141 | std::function isSeparator) 142 | { 143 | auto func = std::not1(std::move(isSeparator)); 144 | std::string result( 145 | std::begin(source), 146 | std::find_if(std::rbegin(source), std::rend(source), func).base()); 147 | return result; 148 | } 149 | 150 | std::string StringHelper::trimLeft( 151 | const std::string& source, 152 | std::function isSeparator) 153 | { 154 | auto func = std::not1(std::move(isSeparator)); 155 | std::string result( 156 | std::find_if(std::begin(source), std::end(source), func), 157 | std::end(source)); 158 | return result; 159 | } 160 | 161 | std::string StringHelper::format(char const* formatText, ...) 162 | { 163 | std::va_list arg; 164 | va_start(arg, formatText); 165 | auto result = UnsafeToFormatString(formatText, arg); 166 | va_end(arg); 167 | return result; 168 | } 169 | 170 | } // namespace somera 171 | -------------------------------------------------------------------------------- /somera/StringHelper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | namespace StringHelper { 11 | 12 | // NOTE: prefix match 13 | bool startWith(const std::string& source, const std::string& prefix); 14 | 15 | // NOTE: suffix match 16 | bool endWith(const std::string& source, const std::string& suffix); 17 | 18 | std::string toLower(const std::string& source); 19 | 20 | std::string toUpper(const std::string& source); 21 | 22 | std::string replace( 23 | const std::string& source, 24 | const std::string& from, 25 | const std::string& to); 26 | 27 | std::vector split(const std::string& source, char separator); 28 | 29 | std::vector split( 30 | const std::string& source, 31 | const std::string& separator); 32 | 33 | std::string trimRight(const std::string& source, char separator); 34 | 35 | std::string trimLeft(const std::string& source, char separator); 36 | 37 | std::string trimRight( 38 | const std::string& source, 39 | std::function isSeparator); 40 | 41 | std::string trimLeft( 42 | const std::string& source, 43 | std::function isSeparator); 44 | 45 | std::string format(char const* formatText, ...) 46 | #if defined(__has_attribute) 47 | #if __has_attribute(format) 48 | __attribute__((__format__(printf, 1, 2))); 49 | #endif 50 | #elif __GNUC__ >= 4 51 | __attribute__((__format__(printf, 1, 2))); 52 | #else 53 | ; 54 | #endif 55 | 56 | } // namespace StringHelper 57 | } // namespace somera 58 | -------------------------------------------------------------------------------- /somera/SubprocessHelper.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "SubprocessHelper.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | namespace { 10 | 11 | FILE* openProcess(const char* command, const char* mode) 12 | { 13 | #if defined(_MSC_VER) 14 | return ::_popen(command, mode); 15 | #else 16 | return ::popen(command, mode); 17 | #endif 18 | } 19 | 20 | int closeProcess(FILE* stream) 21 | { 22 | #if defined(_MSC_VER) 23 | return ::_pclose(stream); 24 | #else 25 | return ::pclose(stream); 26 | #endif 27 | } 28 | 29 | } // unnamed namespace 30 | 31 | std::tuple 32 | SubprocessHelper::call(const std::string& command) 33 | { 34 | assert(!command.empty()); 35 | constexpr int maxBufferSize = 255; 36 | char buffer[maxBufferSize]; 37 | ::FILE* stream = openProcess(command.c_str(), "r"); 38 | if (stream == nullptr) { 39 | closeProcess(stream); 40 | // error: Failed to call popen() 41 | std::error_code err {errno, std::generic_category()}; 42 | return std::make_tuple(std::string{}, std::move(err)); 43 | } 44 | std::string output; 45 | while (::fgets(buffer, maxBufferSize, stream) != nullptr) { 46 | output.append(buffer); 47 | } 48 | if (closeProcess(stream) != 0) { 49 | // error: Failed to call popen() 50 | std::error_code err {errno, std::generic_category()}; 51 | return std::make_tuple(std::move(output), std::move(err)); 52 | } 53 | return std::make_tuple(std::move(output), std::error_code{}); 54 | } 55 | 56 | } // namespace somera 57 | -------------------------------------------------------------------------------- /somera/SubprocessHelper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | namespace SubprocessHelper { 11 | 12 | std::tuple call(const std::string& command); 13 | 14 | } // namespace SubprocessHelper 15 | } // namespace somera 16 | -------------------------------------------------------------------------------- /somera/UnicodeHelper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace somera { 8 | namespace UnicodeHelper { 9 | 10 | bool isGreekAndCoptic(char32_t utf32Character); 11 | 12 | bool isLetterlikeSymbols(char32_t utf32Character); 13 | 14 | bool isEnclosedAlphanumerics(char32_t utf32Character); 15 | 16 | bool isBoxDrawing(char32_t utf32Character); 17 | 18 | bool isBlockElements(char32_t utf32Character); 19 | 20 | bool isMiscellaneousSymbols(char32_t utf32Character); 21 | 22 | bool isCJKSymbolsAndPunctuation(char32_t utf32Character); 23 | 24 | bool isHiragana(char32_t utf32Character); 25 | 26 | bool isKatakana(char32_t utf32Character); 27 | 28 | bool isBopomofo(char32_t utf32Character); 29 | 30 | bool isHangulCompatibilityJamo(char32_t utf32Character); 31 | 32 | bool isKanbun(char32_t utf32Character); 33 | 34 | bool isBopomofoExtended(char32_t utf32Character); 35 | 36 | bool isCJKStrokes(char32_t utf32Character); 37 | 38 | bool isKatakanaPhoneticExtensions(char32_t utf32Character); 39 | 40 | bool isEnclosedCJKLettersAndMonths(char32_t utf32Character); 41 | 42 | bool isCJKCompatibility(char32_t utf32Character); 43 | 44 | bool isHalfwidthAndFullwidthForms(char32_t utf32Character); 45 | 46 | bool isHalfWidthKatakana(char32_t utf32Character); 47 | 48 | bool isCJKUnifiedIdeographs(char32_t utf32Character); 49 | 50 | bool isCJKUnifiedIdeographsExtensionA(char32_t utf32Character); 51 | 52 | bool isMathematicalAlphanumericSymbols(char32_t utf32Character); 53 | 54 | bool isMiscellaneousSymbolsAndPictographs(char32_t utf32Character); 55 | 56 | bool isEmoticons(char32_t utf32Character); 57 | 58 | } // namespace UnicodeHelper 59 | } // namespace somera 60 | -------------------------------------------------------------------------------- /somera/signals/Connection.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "Connection.h" 4 | #include 5 | 6 | namespace somera { 7 | 8 | Connection::Connection(std::unique_ptr && bodyIn) 9 | : body(std::forward>(bodyIn)) 10 | {} 11 | 12 | Connection::Connection(Connection const& connection) 13 | { 14 | if (connection.body) { 15 | body = connection.body->DeepCopy(); 16 | } 17 | } 18 | 19 | Connection & Connection::operator=(Connection const& connection) 20 | { 21 | if (connection.body) { 22 | body = connection.body->DeepCopy(); 23 | } 24 | return *this; 25 | } 26 | 27 | Connection::operator bool() const noexcept 28 | { 29 | return body && body->Valid(); 30 | } 31 | 32 | void Connection::Disconnect() 33 | { 34 | if (body) { 35 | body->Disconnect(); 36 | body.reset(); 37 | } 38 | } 39 | 40 | ScopedConnection::ScopedConnection(Connection const& c) 41 | : connection(c) 42 | {} 43 | 44 | ScopedConnection::ScopedConnection(Connection && c) 45 | : connection(std::move(c)) 46 | {} 47 | 48 | ScopedConnection::~ScopedConnection() 49 | { 50 | connection.Disconnect(); 51 | } 52 | 53 | ScopedConnection & ScopedConnection::operator=(Connection const& c) 54 | { 55 | connection.Disconnect(); 56 | connection = c; 57 | return *this; 58 | } 59 | 60 | ScopedConnection & ScopedConnection::operator=(Connection && c) 61 | { 62 | connection.Disconnect(); 63 | connection = std::move(c); 64 | return *this; 65 | } 66 | 67 | ScopedConnection::operator bool() const noexcept 68 | { 69 | return this->connection.operator bool(); 70 | } 71 | 72 | void ScopedConnection::Disconnect() 73 | { 74 | connection.Disconnect(); 75 | } 76 | 77 | } // namespace somera 78 | -------------------------------------------------------------------------------- /somera/signals/Connection.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace somera { 8 | namespace detail { 9 | 10 | class ConnectionBody { 11 | public: 12 | virtual ~ConnectionBody() = default; 13 | virtual void Disconnect() = 0; 14 | virtual bool Valid() const noexcept = 0; 15 | virtual std::unique_ptr DeepCopy() const = 0; 16 | }; 17 | 18 | } // namespace detail 19 | 20 | class Connection final { 21 | private: 22 | using ConnectionBody = detail::ConnectionBody; 23 | std::unique_ptr body; 24 | 25 | public: 26 | Connection() = default; 27 | 28 | explicit Connection(std::unique_ptr && body); 29 | 30 | Connection(Connection const& connection); 31 | Connection(Connection && connection) = default; 32 | 33 | Connection & operator=(Connection const& connection); 34 | Connection & operator=(Connection && connection) = default; 35 | 36 | operator bool() const noexcept; 37 | 38 | void Disconnect(); 39 | }; 40 | 41 | class ScopedConnection final { 42 | private: 43 | Connection connection; 44 | 45 | public: 46 | ScopedConnection() = default; 47 | ScopedConnection(ScopedConnection const&) = delete; 48 | ScopedConnection(ScopedConnection &&) = default; 49 | 50 | ScopedConnection(Connection const& connection); 51 | ScopedConnection(Connection && connection); 52 | 53 | ~ScopedConnection(); 54 | 55 | ScopedConnection & operator=(ScopedConnection const&) = delete; 56 | ScopedConnection & operator=(ScopedConnection &&) = default; 57 | 58 | ScopedConnection & operator=(Connection const& c); 59 | ScopedConnection & operator=(Connection && c); 60 | 61 | operator bool() const noexcept; 62 | 63 | void Disconnect(); 64 | }; 65 | 66 | } // namespace somera 67 | -------------------------------------------------------------------------------- /somera/signals/EventQueue.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "EventQueue.h" 4 | #include "detail/SignalBody.h" 5 | #include "Connection.h" 6 | 7 | namespace somera { 8 | 9 | EventQueue::EventQueue() 10 | : signalBody(std::make_shared()) 11 | {} 12 | 13 | Connection EventQueue::Connect(std::function const& slot) 14 | { 15 | assert(slot); 16 | assert(this->signalBody); 17 | return Connection{signalBody->Connect(slot)}; 18 | } 19 | 20 | Connection EventQueue::Connect(std::function && slot) 21 | { 22 | assert(slot); 23 | assert(this->signalBody); 24 | return Connection{signalBody->Connect(slot)}; 25 | } 26 | 27 | void EventQueue::Enqueue(Any && event) 28 | { 29 | std::lock_guard lock(notificationMutex); 30 | events.emplace_back(std::move(event)); 31 | } 32 | 33 | void EventQueue::Emit() 34 | { 35 | assert(signalBody); 36 | std::vector notifications; 37 | { 38 | std::lock_guard lock(notificationMutex); 39 | std::swap(notifications, events); 40 | } 41 | for (auto & event : notifications) { 42 | signalBody->operator()(event); 43 | } 44 | } 45 | 46 | } // namespace somera 47 | -------------------------------------------------------------------------------- /somera/signals/EventQueue.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "detail/ForwardDeclarations.h" 6 | #include "somera/Any.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace somera { 14 | 15 | class EventQueue final { 16 | public: 17 | EventQueue(); 18 | 19 | EventQueue(EventQueue const&) = delete; 20 | EventQueue(EventQueue &&) = delete; 21 | EventQueue & operator=(EventQueue const&) = delete; 22 | EventQueue & operator=(EventQueue &&) = delete; 23 | 24 | Connection Connect(std::function const& slot); 25 | 26 | Connection Connect(std::function && slot); 27 | 28 | void Enqueue(Any && event); 29 | 30 | template 31 | void Enqueue(T && argument) 32 | { 33 | Any event{std::forward>(argument)}; 34 | Enqueue(std::move(event)); 35 | } 36 | 37 | template 38 | void Enqueue(const T & argument) 39 | { 40 | Any event{std::remove_reference_t(argument)}; 41 | Enqueue(std::move(event)); 42 | } 43 | 44 | void Emit(); 45 | 46 | private: 47 | using SignalBody = detail::signals::SignalBody; 48 | std::vector events; 49 | std::recursive_mutex notificationMutex; 50 | std::shared_ptr signalBody; 51 | }; 52 | 53 | } // namespace somera 54 | -------------------------------------------------------------------------------- /somera/signals/Signal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "detail/ForwardDeclarations.h" 6 | #include "detail/SignalBody.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace somera { 13 | 14 | template 15 | class Signal; 16 | 17 | template 18 | class Signal final { 19 | public: 20 | Signal(); 21 | Signal(const Signal&) = delete; 22 | Signal(Signal &&) = default; 23 | Signal & operator=(const Signal&) = delete; 24 | Signal & operator=(Signal &&) = default; 25 | 26 | Connection Connect(const std::function& slot); 27 | 28 | Connection Connect(std::function && slot); 29 | 30 | void operator()(Args &&... args); 31 | 32 | std::size_t InvocationCount() const; 33 | 34 | private: 35 | using SignalBody = detail::signals::SignalBody; 36 | std::shared_ptr body; 37 | }; 38 | 39 | template 40 | Signal::Signal() 41 | : body(std::make_shared()) 42 | {} 43 | 44 | template 45 | Connection 46 | Signal::Connect(const std::function& slot) 47 | { 48 | assert(slot); 49 | assert(body); 50 | return Connection{body->Connect(slot)}; 51 | } 52 | 53 | template 54 | Connection 55 | Signal::Connect(std::function && slot) 56 | { 57 | assert(slot); 58 | assert(body); 59 | return Connection{body->Connect(std::move(slot))}; 60 | } 61 | 62 | template 63 | void Signal::operator()(Args &&... args) 64 | { 65 | assert(body); 66 | body->operator()(std::forward(args)...); 67 | } 68 | 69 | template 70 | std::size_t Signal::InvocationCount() const 71 | { 72 | return body->InvocationCount(); 73 | } 74 | 75 | } // namespace somera 76 | -------------------------------------------------------------------------------- /somera/signals/detail/ForwardDeclarations.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | namespace somera { 6 | namespace detail { 7 | namespace signals { 8 | 9 | template 10 | class SignalBody; 11 | 12 | } // namespace signals 13 | } // namespace detail 14 | 15 | class Connection; 16 | class EventQueue; 17 | class ScopedConnection; 18 | 19 | template 20 | class Signal; 21 | 22 | } // namespace somera 23 | -------------------------------------------------------------------------------- /somera/tests/Any.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "Any.h" 4 | #include 5 | 6 | using namespace somera; 7 | using somera::Any; 8 | 9 | TEST(Any, FirstCase) 10 | { 11 | Any x = std::string("Chuck Norris"); 12 | 13 | ASSERT_EQ(typeid(std::string), x.type()); 14 | ASSERT_TRUE(x.is()); 15 | EXPECT_EQ(std::string("Chuck Norris"), x.as()); 16 | EXPECT_EQ("Chuck Norris", x.as()); 17 | } 18 | 19 | TEST(Any, SharedPointer) 20 | { 21 | { 22 | Any x = std::make_shared("Chuck Norris"); 23 | ASSERT_EQ(typeid(std::shared_ptr), x.type()); 24 | ASSERT_TRUE(x.is>()); 25 | EXPECT_EQ(std::string("Chuck Norris"), *x.as>()); 26 | EXPECT_EQ("Chuck Norris", *x.as>()); 27 | } 28 | { 29 | auto original = std::make_shared("Chuck Norris"); 30 | auto copied = original; 31 | Any x = copied; 32 | 33 | ASSERT_EQ(typeid(std::shared_ptr), x.type()); 34 | ASSERT_TRUE(x.is>()); 35 | EXPECT_EQ(std::string("Chuck Norris"), *x.as>()); 36 | EXPECT_EQ("Chuck Norris", *x.as>()); 37 | EXPECT_EQ(original, x.as>()); 38 | } 39 | { 40 | auto original = std::make_shared("Chuck Norris"); 41 | auto copied = original; 42 | Any x = std::move(copied); 43 | 44 | ASSERT_EQ(typeid(std::shared_ptr), x.type()); 45 | ASSERT_TRUE(x.is>()); 46 | EXPECT_EQ(std::string("Chuck Norris"), *x.as>()); 47 | EXPECT_EQ("Chuck Norris", *x.as>()); 48 | EXPECT_EQ(original, x.as>()); 49 | } 50 | { 51 | Any x = std::make_shared("Chuck Norris"); 52 | ASSERT_EQ(typeid(std::shared_ptr), x.type()); 53 | 54 | auto copied = x.as>(); 55 | 56 | EXPECT_EQ("Chuck Norris", *copied); 57 | EXPECT_EQ(copied, x.as>()); 58 | 59 | auto moved = std::move(x.as>()); 60 | 61 | EXPECT_EQ(copied, moved); 62 | EXPECT_EQ("Chuck Norris", *moved); 63 | EXPECT_NE(moved, x.as>()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /somera/tests/CommandLineParser.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "CommandLineParser.h" 4 | #include 5 | 6 | using namespace somera; 7 | 8 | TEST(CommandLineParser, TrivialCase) 9 | { 10 | constexpr int argc = 4; 11 | const char* argv[argc] = { 12 | "/usr/local/bin/gcc", 13 | "-g", 14 | "hidamari.cpp", 15 | "sketch.cpp", 16 | }; 17 | 18 | CommandLineParser parser; 19 | parser.addArgument("-g", CommandLineArgumentType::Flag, 20 | "Debug flag"); 21 | parser.addArgument("-L", CommandLineArgumentType::JoinedOrSeparate, 22 | "Library search path"); 23 | parser.parse(argc, argv); 24 | 25 | EXPECT_FALSE(parser.hasParseError()); 26 | EXPECT_TRUE(parser.exists("-g")); 27 | EXPECT_TRUE(parser.getErrorMessage().empty()); 28 | EXPECT_EQ("/usr/local/bin/gcc", parser.getExecutablePath()); 29 | 30 | auto paths = parser.getPaths(); 31 | ASSERT_EQ(2, paths.size()); 32 | EXPECT_EQ("hidamari.cpp", paths[0]); 33 | EXPECT_EQ("sketch.cpp", paths[1]); 34 | } 35 | 36 | TEST(CommandLineParser, Spaces) 37 | { 38 | constexpr int argc = 8; 39 | const char* argv[argc] = { 40 | "/usr/local/bin/gcc", 41 | "", 42 | " ", 43 | " \n ", 44 | "yuno\n", 45 | " miyako ", 46 | "hiro\n", 47 | " sae\n ", 48 | }; 49 | 50 | CommandLineParser parser; 51 | parser.parse(argc, argv); 52 | EXPECT_FALSE(parser.hasParseError()); 53 | 54 | auto paths = parser.getPaths(); 55 | ASSERT_EQ(4, paths.size()); 56 | EXPECT_EQ("yuno", paths[0]); 57 | EXPECT_EQ("miyako", paths[1]); 58 | EXPECT_EQ("hiro", paths[2]); 59 | EXPECT_EQ("sae", paths[3]); 60 | } 61 | -------------------------------------------------------------------------------- /somera/tests/FileSystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "FileSystem.h" 4 | #include 5 | 6 | using namespace somera; 7 | 8 | TEST(FileSystem, normalize) 9 | { 10 | const auto normalize = FileSystem::normalize; 11 | EXPECT_EQ(FileSystem::getCurrentDirectory(), normalize("")); 12 | EXPECT_EQ(FileSystem::getCurrentDirectory(), normalize(".")); 13 | EXPECT_EQ(FileSystem::getCurrentDirectory(), normalize("./")); 14 | 15 | EXPECT_EQ("/usr/local/bin", normalize("/usr/local/bin")); 16 | EXPECT_EQ("/usr/local/bin", normalize("/usr/local/bin/")); 17 | EXPECT_EQ("/usr/local/bin", normalize("/usr/local/bin/.")); 18 | EXPECT_EQ("/usr/local/bin", normalize("/usr/local/bin/./")); 19 | EXPECT_EQ("/usr/local/bin", normalize("/usr/local/./bin")); 20 | EXPECT_EQ("/usr/local/bin", normalize("/usr/./local/bin")); 21 | EXPECT_EQ("/usr/local", normalize("/usr/local/bin/..")); 22 | EXPECT_EQ("/usr/local", normalize("/usr/local/bin/../")); 23 | EXPECT_EQ("/usr", normalize("/usr/local/bin/../..")); 24 | EXPECT_EQ("/usr", normalize("/usr/local/bin/../../")); 25 | EXPECT_EQ("/usr/local/bin", normalize("/usr/local/../local/bin")); 26 | EXPECT_EQ("/usr/local", normalize("/usr/local/../local/bin/..")); 27 | } 28 | 29 | TEST(FileSystem, relative) 30 | { 31 | const auto relative = FileSystem::relative; 32 | EXPECT_EQ(".", relative("/usr/local/bin", "/usr/local/bin")); 33 | EXPECT_EQ(".", relative("/usr/local/bin/.", "/usr/local/bin")); 34 | EXPECT_EQ(".", relative("/usr/local/bin/./", "/usr/local/bin")); 35 | EXPECT_EQ(".", relative("/usr/local/bin/././", "/usr/local/bin")); 36 | EXPECT_EQ(".", relative("/usr/local/bin/./././", "/usr/local/bin")); 37 | EXPECT_EQ("..", relative("/usr/local/bin/..", "/usr/local/bin")); 38 | EXPECT_EQ("..", relative("/usr/local/bin/../", "/usr/local/bin")); 39 | EXPECT_EQ("..", relative("/usr/local/bin/.././", "/usr/local/bin")); 40 | EXPECT_EQ("..", relative("/usr/local/bin/./../", "/usr/local/bin")); 41 | EXPECT_EQ("../..", relative("/usr/local/bin/../../", "/usr/local/bin")); 42 | EXPECT_EQ("../..", relative("/usr/local/bin/../..", "/usr/local/bin")); 43 | EXPECT_EQ("../..", relative("/usr/local/bin/../../.", "/usr/local/bin")); 44 | EXPECT_EQ("../..", relative("/usr/local/bin/.././../", "/usr/local/bin")); 45 | EXPECT_EQ("../..", relative("/usr/local/bin/./../../", "/usr/local/bin")); 46 | 47 | EXPECT_EQ(".", relative("/usr/local/../local/bin", "/usr/local/bin")); 48 | EXPECT_EQ("..", relative("/usr/local", "/usr/local/bin")); 49 | EXPECT_EQ("../..", relative("/usr", "/usr/local/bin")); 50 | EXPECT_EQ("../..", relative("/usr/local/..", "/usr/local/bin")); 51 | EXPECT_EQ("../..", relative("/usr/local/../local/..", "/usr/local/bin")); 52 | 53 | EXPECT_EQ("gcc", relative("/usr/local/bin/gcc", "/usr/local/bin")); 54 | EXPECT_EQ("../opt", relative("/usr/local/opt", "/usr/local/bin")); 55 | EXPECT_EQ("../../share", relative("/usr/share", "/usr/local/bin")); 56 | EXPECT_EQ("../../share/dict", relative("/usr/share/dict", "/usr/local/bin")); 57 | } 58 | -------------------------------------------------------------------------------- /somera/tests/Optional.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "Optional.h" 4 | #include 5 | 6 | using namespace somera; 7 | -------------------------------------------------------------------------------- /somera/tests/StringHelper.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "StringHelper.h" 4 | #include 5 | 6 | using namespace somera; 7 | 8 | TEST(StringHelper, startWith) 9 | { 10 | const auto startWith = StringHelper::startWith; 11 | EXPECT_TRUE(startWith("", "")); 12 | EXPECT_TRUE(startWith("-", "-")); 13 | EXPECT_TRUE(startWith("baka and test", "baka and test")); 14 | EXPECT_TRUE(startWith("baka and test", "baka")); 15 | EXPECT_TRUE(startWith("baka", "")); 16 | EXPECT_FALSE(startWith("baka", "baka and test")); 17 | EXPECT_FALSE(startWith("", "baka")); 18 | EXPECT_FALSE(startWith("-baka", "baka")); 19 | EXPECT_FALSE(startWith("baka", "-baka")); 20 | } 21 | 22 | TEST(StringHelper, split) 23 | { 24 | using StringHelper::split; 25 | { 26 | auto result = split("", '.'); 27 | ASSERT_EQ(1, result.size()); 28 | EXPECT_EQ("", result[0]); 29 | } 30 | { 31 | auto result = split("source.example.com", '.'); 32 | ASSERT_EQ(3, result.size()); 33 | EXPECT_EQ("source", result[0]); 34 | EXPECT_EQ("example", result[1]); 35 | EXPECT_EQ("com", result[2]); 36 | } 37 | { 38 | auto result = split("example.com.", '.'); 39 | ASSERT_EQ(3, result.size()); 40 | EXPECT_EQ("example", result[0]); 41 | EXPECT_EQ("com", result[1]); 42 | EXPECT_EQ("", result[2]); 43 | } 44 | { 45 | auto result = split("...", '.'); 46 | ASSERT_EQ(4, result.size()); 47 | EXPECT_EQ("", result[0]); 48 | EXPECT_EQ("", result[1]); 49 | EXPECT_EQ("", result[2]); 50 | EXPECT_EQ("", result[3]); 51 | } 52 | { 53 | auto result = split(".a..b", '.'); 54 | ASSERT_EQ(4, result.size()); 55 | EXPECT_EQ("", result[0]); 56 | EXPECT_EQ("a", result[1]); 57 | EXPECT_EQ("", result[2]); 58 | EXPECT_EQ("b", result[3]); 59 | } 60 | { 61 | auto result = split("", "//"); 62 | ASSERT_EQ(1, result.size()); 63 | EXPECT_EQ("", result[0]); 64 | } 65 | { 66 | auto result = split("source//example//com", "//"); 67 | ASSERT_EQ(3, result.size()); 68 | EXPECT_EQ("source", result[0]); 69 | EXPECT_EQ("example", result[1]); 70 | EXPECT_EQ("com", result[2]); 71 | } 72 | { 73 | auto result = split("example//com//", "//"); 74 | ASSERT_EQ(3, result.size()); 75 | EXPECT_EQ("example", result[0]); 76 | EXPECT_EQ("com", result[1]); 77 | EXPECT_EQ("", result[2]); 78 | } 79 | { 80 | auto result = split("//////", "//"); 81 | ASSERT_EQ(4, result.size()); 82 | EXPECT_EQ("", result[0]); 83 | EXPECT_EQ("", result[1]); 84 | EXPECT_EQ("", result[2]); 85 | EXPECT_EQ("", result[3]); 86 | } 87 | { 88 | auto result = split("//a////b", "//"); 89 | ASSERT_EQ(4, result.size()); 90 | EXPECT_EQ("", result[0]); 91 | EXPECT_EQ("a", result[1]); 92 | EXPECT_EQ("", result[2]); 93 | EXPECT_EQ("b", result[3]); 94 | } 95 | } 96 | 97 | TEST(StringHelper, trimRight) 98 | { 99 | using StringHelper::trimRight; 100 | EXPECT_EQ("", trimRight(" ", ' ')); 101 | EXPECT_EQ("hoge", trimRight("hoge ", ' ')); 102 | EXPECT_EQ(" hoge", trimRight(" hoge ", ' ')); 103 | EXPECT_EQ(" hoge", trimRight(" hoge", ' ')); 104 | } 105 | 106 | TEST(StringHelper, trimLeft) 107 | { 108 | using StringHelper::trimLeft; 109 | EXPECT_EQ("", trimLeft(" ", ' ')); 110 | EXPECT_EQ("hoge ", trimLeft("hoge ", ' ')); 111 | EXPECT_EQ("hoge ", trimLeft(" hoge ", ' ')); 112 | EXPECT_EQ("hoge", trimLeft(" hoge", ' ')); 113 | } 114 | -------------------------------------------------------------------------------- /somera/tests/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | IUTEST_INIT(&argc, argv); 8 | return IUTEST_RUN_ALL_TESTS(); 9 | } 10 | -------------------------------------------------------------------------------- /typo-poi/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # Requires clang-format version 3.7 or later 3 | Language: Cpp 4 | BasedOnStyle: LLVM 5 | AccessModifierOffset: -4 6 | AllowShortFunctionsOnASingleLine: Inline 7 | AllowShortIfStatementsOnASingleLine: false 8 | AllowShortLoopsOnASingleLine: false 9 | AlwaysBreakTemplateDeclarations: true 10 | BreakBeforeBraces: Stroustrup 11 | BreakConstructorInitializersBeforeComma: true 12 | DerivePointerAlignment: true 13 | IndentWidth: 4 14 | MaxEmptyLinesToKeep: 1 15 | NamespaceIndentation: None 16 | PointerAlignment: Left 17 | Standard: Cpp11 18 | UseTab: Never 19 | ... 20 | -------------------------------------------------------------------------------- /typo-poi/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | 10 | # Visual Studio 11 | *.filters 12 | *.opensdf 13 | *.sdf 14 | *.sln 15 | *.vcxproj 16 | -------------------------------------------------------------------------------- /typo-poi/C++_Common.txt: -------------------------------------------------------------------------------- 1 | size_t 2 | printf 3 | std 4 | cout 5 | endl 6 | int8_t 7 | int16_t 8 | int32_t 9 | int64_t 10 | uint8_t 11 | uint16_t 12 | uint32_t 13 | uint64_t 14 | ssize_t 15 | intptr_t 16 | nothrow 17 | pragma 18 | ifdef 19 | ifndef 20 | elif 21 | else 22 | endif 23 | undef 24 | define 25 | defined 26 | fixme 27 | todo 28 | dll 29 | cpp 30 | IME 31 | http 32 | https 33 | ftp 34 | www 35 | OpenGL 36 | noninfringement 37 | json 38 | png 39 | jpeg 40 | tiff 41 | mp3 42 | xml 43 | stdio 44 | strlen 45 | sstream 46 | stdlib 47 | emoticon 48 | stringify 49 | mingw 50 | dllimport 51 | plist 52 | memcpy 53 | -------------------------------------------------------------------------------- /typo-poi/C++_Keywords.txt: -------------------------------------------------------------------------------- 1 | alignas 2 | alignof 3 | and 4 | and_eq 5 | asm 6 | auto 7 | bitand 8 | bitor 9 | bool 10 | break 11 | case 12 | catch 13 | char 14 | char16_t 15 | char32_t 16 | class 17 | compl 18 | concept 19 | const 20 | constexpr 21 | const_cast 22 | continue 23 | decltype 24 | default 25 | delete 26 | do 27 | double 28 | dynamic_cast 29 | else 30 | enum 31 | explicit 32 | export 33 | extern 34 | false 35 | float 36 | for 37 | friend 38 | goto 39 | if 40 | inline 41 | int 42 | long 43 | mutable 44 | namespace 45 | new 46 | noexcept 47 | not 48 | not_eq 49 | nullptr 50 | operator 51 | or 52 | or_eq 53 | private 54 | protected 55 | public 56 | register 57 | reinterpret_cast 58 | requires 59 | return 60 | short 61 | signed 62 | sizeof 63 | static 64 | static_assert 65 | static_cast 66 | struct 67 | switch 68 | template 69 | this 70 | thread_local 71 | throw 72 | true 73 | try 74 | typedef 75 | typeid 76 | typename 77 | union 78 | unsigned 79 | using 80 | virtual 81 | void 82 | volatile 83 | wchar_t 84 | while 85 | xor 86 | xor_eq 87 | -------------------------------------------------------------------------------- /typo-poi/Cocos2d-x.txt: -------------------------------------------------------------------------------- 1 | CCLayer 2 | CCValue 3 | CCVector 4 | GLProgram 5 | CCMap 6 | CCSpriteFrame 7 | CCRef 8 | Zynga 9 | Ricardo 10 | Quesada 11 | bezier 12 | CCTweenFunction 13 | CCGeometry 14 | CCActionInterval 15 | cocos2d-iphone 16 | CCPlatformMacros 17 | CCActionEase 18 | CCPlatformDefine 19 | ccConfig 20 | noncopyable 21 | tizen 22 | iPhone 23 | CCApplication 24 | _MSC_VER 25 | Vec2 26 | Vec3 27 | Vec4 28 | CCComponent 29 | CCTouch 30 | CCComponentContainer 31 | mozilla 32 | CCMath 33 | CCAffineTransform 34 | cmath 35 | CCScriptSupport 36 | func 37 | TextHAlignment 38 | TextVAlignment 39 | CCNode 40 | CCFontAtlas 41 | CCQuadCommand 42 | CCCustomCommand 43 | fnt 44 | tga 45 | tmx 46 | pvr 47 | jpg 48 | jpeg 49 | UV 50 | API 51 | tileset 52 | CCTransition 53 | CCAtlasNode 54 | RGB 55 | NSDictionary 56 | ZOrder 57 | CCProtocols 58 | CCAtlasNode 59 | CCDrawNode 60 | xaml 61 | yaml 62 | len 63 | utils 64 | img 65 | href 66 | but 67 | dic 68 | html 69 | htm 70 | java 71 | js 72 | ui 73 | dpi 74 | CCLabel 75 | TMXTileMap 76 | CCGroupCommand 77 | CCFrustum 78 | CCImage 79 | CCScene 80 | CCTrianglesCommand 81 | CCSprite 82 | GLfloat 83 | CCBatchCommand 84 | WinRT 85 | lua 86 | GNU 87 | GNUC 88 | RGBA 89 | RGBA8888 90 | alloc 91 | CCDirector 92 | allocate 93 | ttf 94 | ctor 95 | CCAutoPolygon 96 | __ARGS__ 97 | GUI 98 | gcc 99 | CCAction 100 | CCAnimation 101 | Grid3DAction 102 | CCActionInstant 103 | zlib 104 | gzip 105 | GLuint 106 | Google 107 | UTF 108 | UTF-8 109 | UTF-16 110 | UTF-32 111 | utf8 112 | utf16 113 | utf32 114 | JNI 115 | jstring 116 | vbo 117 | vao 118 | AABB 119 | CCAssert 120 | CCASSERT 121 | iOS 122 | iPhone 123 | iPod 124 | iTunes 125 | CCFileUtils 126 | CCPlatformConfig 127 | CCEventTouch 128 | CCCommon 129 | NSMutableArray 130 | CCController 131 | CCEventController 132 | JniHelper 133 | StringUtils 134 | CCEventListenerController 135 | url 136 | linux 137 | GLint 138 | CCNotifications 139 | CCLog 140 | malloc 141 | calloc 142 | chrono 143 | ATC 144 | endian 145 | unicode 146 | GLboolean 147 | CCAnimate3D 148 | CCScheduler 149 | FLT_EPSILON 150 | HRESULT 151 | NSError 152 | impl 153 | unique_ptr 154 | shared_ptr 155 | ComPtr 156 | socketio 157 | SocketIO 158 | ssl 159 | NSInteger 160 | pthread 161 | NSObject 162 | NSString 163 | CCConsole 164 | NSRunLoop 165 | NSMutableData 166 | wrl 167 | mipmap 168 | texel 169 | CCRenderCommand 170 | CCPrimitive 171 | CCGLProgramState 172 | storageof 173 | CCTexture2D 174 | CCGL 175 | texcoord 176 | ptr 177 | params 178 | errno 179 | NDEBUG 180 | GLESContext 181 | coord 182 | CCVertexIndexData 183 | CCSkeleton3D 184 | GLPipeline 185 | WCHAR 186 | WAVEFORMATEX 187 | aabb 188 | CCLOG 189 | uri 190 | mutex 191 | lws 192 | args 193 | GPU 194 | CCStdC 195 | fbo 196 | CCMaterial 197 | CCRenderState 198 | CCRay 199 | CCEventDispatcher 200 | CCEventListenerCustom 201 | glsl 202 | CCCamera 203 | LOD 204 | VBO 205 | src 206 | iOS 207 | dest 208 | BGRA 209 | BGRA8888 -------------------------------------------------------------------------------- /typo-poi/English_Grammar.txt: -------------------------------------------------------------------------------- 1 | [cC]an 2 | [cC]an't 3 | [dD]oes 4 | [dD]oesn't 5 | [dD]on't 6 | [hH]ave 7 | [hH]aven't 8 | [mM]ustn't 9 | [sS]hould 10 | [sS]houldn't 11 | -------------------------------------------------------------------------------- /typo-poi/English_Words.txt: -------------------------------------------------------------------------------- 1 | benefit 2 | example 3 | function 4 | -------------------------------------------------------------------------------- /typo-poi/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = typo-poi 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | source/thirdparty/*.h \ 7 | source/*.h 8 | 9 | SOURCES = \ 10 | ../somera/*.cpp \ 11 | source/thirdparty/ConvertUTF.c \ 12 | source/ConsoleColor.cpp \ 13 | source/EditDistance.cpp \ 14 | source/SpellChecker.cpp \ 15 | source/Typo.cpp \ 16 | source/UTF8.cpp \ 17 | source/WordDiff.cpp \ 18 | source/WordSegmenter.cpp \ 19 | source/main.cpp 20 | 21 | ifeq ($(OS),Windows_NT) 22 | SOURCES += source/SpellCheckWin.cpp 23 | endif 24 | ifeq ($(shell uname -s),Darwin) 25 | SOURCES += source/SpellCheckMac.mm 26 | endif 27 | 28 | xcode: 29 | $(NORI) \ 30 | -generator=xcode \ 31 | -o $(PRODUCTNAME) \ 32 | -std=c++14 \ 33 | -stdlib=libc++ \ 34 | -lAppKit.framework \ 35 | -I.. \ 36 | $(HEADERS) \ 37 | $(SOURCES) 38 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 39 | @mkdir -p bin 40 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 41 | 42 | developer-xcode: 43 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 44 | @mkdir -p bin 45 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 46 | -------------------------------------------------------------------------------- /typo-poi/README.md: -------------------------------------------------------------------------------- 1 | # typo-poi 2 | 3 | Typo/misspell checking library for C/C++ and command-line tool. 4 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 5 | 6 | ## Build and run 7 | 8 | **Build:** 9 | 10 | ```sh 11 | git clone https://github.com/mogemimi/daily-snippets.git 12 | cd daily-snippets/typo-poi 13 | 14 | # Setup build tool 15 | make -C ../nori bootstrap 16 | 17 | # Build 18 | make xcode 19 | 20 | # Run 21 | ./bin/typo-poi -help 22 | ``` 23 | 24 | **Run:** 25 | 26 | Run: 27 | 28 | ```sh 29 | ./bin/typo-poi YourSourceCode.cpp 30 | ``` 31 | 32 | ## Thanks 33 | 34 | The following libraries and/or open source projects were used in typo-poi: 35 | 36 | * [LLVM Clang](http://clang.llvm.org/) 37 | -------------------------------------------------------------------------------- /typo-poi/source/ConsoleColor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "consolecolor.h" 4 | #include 5 | #if defined(__APPLE_CC__) || defined(linux) || defined(__linux) || defined(__linux__) 6 | #include 7 | #include 8 | #include 9 | #endif 10 | 11 | namespace somera { 12 | namespace { 13 | 14 | int getColorIndex(TerminalColor color) 15 | { 16 | switch (color) { 17 | case TerminalColor::Black: return 0; 18 | case TerminalColor::Red: return 1; 19 | case TerminalColor::Green: return 2; 20 | case TerminalColor::Yellow: return 3; 21 | case TerminalColor::Blue: return 4; 22 | case TerminalColor::Magenta: return 5; 23 | case TerminalColor::Cyan: return 6; 24 | case TerminalColor::White: return 7; 25 | } 26 | return 0; 27 | } 28 | 29 | bool hasTerminalColors() 30 | { 31 | #if defined(__APPLE_CC__) || defined(linux) || defined(__linux) || defined(__linux__) 32 | if (::isatty(::fileno(stdout)) == 1) { 33 | const auto term = ::getenv("TERM"); 34 | return (term != nullptr && ::strcmp(term, "dumb")); 35 | } 36 | #endif 37 | return false; 38 | } 39 | 40 | std::string getColor( 41 | TerminalColor textColor, 42 | somera::Optional backgroundColor = somera::NullOpt) 43 | { 44 | std::stringstream ss; 45 | ss << "\033["; 46 | ss << (30 + getColorIndex(textColor)); 47 | if (backgroundColor) { 48 | ss << ";" << (40 + getColorIndex(*backgroundColor)); 49 | } 50 | ss << "m"; 51 | return ss.str(); 52 | } 53 | 54 | } // unnamed namespace 55 | 56 | std::string changeTerminalTextColor( 57 | const std::string& text, 58 | TerminalColor textColor, 59 | somera::Optional backgroundColor) 60 | { 61 | static const bool enabled = hasTerminalColors(); 62 | if (!enabled) { 63 | return text; 64 | } 65 | 66 | constexpr auto resetColor = "\x1b[0m"; 67 | return getColor(textColor, backgroundColor) + text + resetColor; 68 | } 69 | 70 | } // namespace somera 71 | -------------------------------------------------------------------------------- /typo-poi/source/ConsoleColor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "somera/Optional.h" 6 | #include 7 | 8 | namespace somera { 9 | 10 | enum class TerminalColor { 11 | Black, 12 | Red, 13 | Green, 14 | Yellow, 15 | Blue, 16 | Magenta, 17 | Cyan, 18 | White 19 | }; 20 | 21 | std::string changeTerminalTextColor( 22 | const std::string& text, 23 | TerminalColor textColor, 24 | somera::Optional backgroundColor = somera::NullOpt); 25 | 26 | } // namespace somera 27 | -------------------------------------------------------------------------------- /typo-poi/source/EditDistance.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | 10 | ///@param left UTF-8 string 11 | ///@param right UTF-8 string 12 | double closestMatchFuzzySimilarity( 13 | const std::string& left, 14 | const std::string& right); 15 | 16 | ///@param left UTF-8 string 17 | ///@param right UTF-8 string 18 | double closestMatchFuzzySimilarity( 19 | const std::string& left, 20 | const std::string& right, 21 | int distanceThreshold); 22 | 23 | ///@param left UTF-8 string 24 | ///@param right UTF-8 string 25 | double closestMatchFuzzySimilarity_Boer( 26 | const std::string& left, 27 | const std::string& right); 28 | 29 | ///@param left UTF-8 string 30 | ///@param right UTF-8 string 31 | double jaroWinklerDistance( 32 | const std::string& left, 33 | const std::string& right); 34 | 35 | ///@param left UTF-8 string 36 | ///@param right UTF-8 string 37 | int levenshteinDistance( 38 | const std::string& left, 39 | const std::string& right); 40 | 41 | ///@param left UTF-8 string 42 | ///@param right UTF-8 string 43 | int levenshteinDistance_DynamicProgramming( 44 | const std::string& left, 45 | const std::string& right); 46 | 47 | ///@param left UTF-8 string 48 | ///@param right UTF-8 string 49 | int levenshteinDistance_DynamicProgramming_LinearSpace( 50 | const std::string& left, 51 | const std::string& right); 52 | 53 | ///@param left UTF-8 string 54 | ///@param right UTF-8 string 55 | int levenshteinDistance_DynamicProgramming_ReplacementCost1( 56 | const std::string& left, 57 | const std::string& right); 58 | 59 | ///@param left UTF-8 string 60 | ///@param right UTF-8 string 61 | int levenshteinDistance_ONDGreedyAlgorithm( 62 | const std::string& left, 63 | const std::string& right); 64 | 65 | ///@param left UTF-8 string 66 | ///@param right UTF-8 string 67 | int levenshteinDistance_ONDGreedyAlgorithm_Threshold( 68 | const std::string& left, 69 | const std::string& right, 70 | int threshold); 71 | 72 | ///@param left UTF-8 string 73 | ///@param right UTF-8 string 74 | int computeLCSLength_DynamicProgramming( 75 | const std::string& left, 76 | const std::string& right); 77 | 78 | ///@param left UTF-8 string 79 | ///@param right UTF-8 string 80 | int computeLCSLength_ONDGreedyAlgorithm( 81 | const std::string& left, 82 | const std::string& right); 83 | 84 | ///@param left UTF-8 string 85 | ///@param right UTF-8 string 86 | int computeLCSLengthInLinearSpace( 87 | const std::string& left, 88 | const std::string& right); 89 | 90 | ///@param left UTF-8 string 91 | ///@param right UTF-8 string 92 | std::vector LCS_Column( 93 | const std::string& left, 94 | const std::string& right, 95 | std::size_t m, 96 | std::size_t n); 97 | 98 | } // namespace somera 99 | -------------------------------------------------------------------------------- /typo-poi/source/Replacement.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | 10 | class SourceRange final { 11 | public: 12 | SourceRange() 13 | : offset(0) 14 | , length(0) 15 | { 16 | } 17 | 18 | SourceRange(SourceRange && other) 19 | { 20 | offset = std::move(other.offset); 21 | length = std::move(other.length); 22 | other.offset = 0; 23 | other.length = 0; 24 | } 25 | 26 | SourceRange(std::size_t offsetIn, std::size_t lengthIn) 27 | : offset(offsetIn) 28 | , length(lengthIn) 29 | { 30 | } 31 | 32 | SourceRange& operator=(SourceRange && other) 33 | { 34 | offset = std::move(other.offset); 35 | length = std::move(other.length); 36 | other.offset = 0; 37 | other.length = 0; 38 | return *this; 39 | } 40 | 41 | std::size_t GetLength() const noexcept 42 | { 43 | return length; 44 | } 45 | 46 | std::size_t GetOffset() const noexcept 47 | { 48 | return offset; 49 | } 50 | 51 | bool Contains(const SourceRange& range) const noexcept 52 | { 53 | return offset <= range.offset 54 | && (range.offset + range.length <= offset + length); 55 | } 56 | 57 | bool operator==(const SourceRange& range) const noexcept 58 | { 59 | return offset == range.offset && length == range.length; 60 | } 61 | 62 | private: 63 | std::size_t offset; 64 | std::size_t length; 65 | }; 66 | 67 | } // namespace somera 68 | -------------------------------------------------------------------------------- /typo-poi/source/SpellCheck.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "SpellChecker.h" 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | 11 | struct NativeSpellCheckOptions { 12 | std::string language = "en_US"; 13 | }; 14 | 15 | struct NativeSpellChecker { 16 | static std::string correctSpelling(const std::string& word); 17 | 18 | static std::string correctSpelling( 19 | const std::string& word, 20 | const NativeSpellCheckOptions& options); 21 | 22 | static std::vector findClosestWords(const std::string& word); 23 | 24 | static std::vector findClosestWords( 25 | const std::string& word, 26 | const NativeSpellCheckOptions& options); 27 | }; 28 | 29 | std::shared_ptr CreateSpellCheckerMac(); 30 | 31 | } // namespace somera 32 | -------------------------------------------------------------------------------- /typo-poi/source/SpellCheckMac.mm: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "spellcheck.h" 4 | #import 5 | #include 6 | 7 | namespace somera { 8 | 9 | std::string NativeSpellChecker::correctSpelling(const std::string& word) 10 | { 11 | NativeSpellCheckOptions options; 12 | options.language = "en_US"; 13 | return correctSpelling(word, options); 14 | } 15 | 16 | std::string NativeSpellChecker::correctSpelling( 17 | const std::string& word, 18 | const NativeSpellCheckOptions& options) 19 | { 20 | if (word.empty()) { 21 | return ""; 22 | } 23 | 24 | NSSpellChecker* checker = [NSSpellChecker sharedSpellChecker]; 25 | NSString* string = [NSString stringWithUTF8String:word.c_str()]; 26 | NSString* language = [NSString stringWithUTF8String:options.language.c_str()]; 27 | 28 | NSString* correction = [checker correctionForWordRange:NSMakeRange(0, [string length]) 29 | inString:string 30 | language:language 31 | inSpellDocumentWithTag:0]; 32 | 33 | if (correction != nil) { 34 | return [correction UTF8String]; 35 | } 36 | return ""; 37 | } 38 | 39 | std::vector NativeSpellChecker::findClosestWords(const std::string& word) 40 | { 41 | NativeSpellCheckOptions options; 42 | options.language = "en_US"; 43 | return findClosestWords(word, options); 44 | } 45 | 46 | std::vector NativeSpellChecker::findClosestWords( 47 | const std::string& word, 48 | const NativeSpellCheckOptions& options) 49 | { 50 | if (word.empty()) { 51 | return {}; 52 | } 53 | 54 | NSSpellChecker* checker = [NSSpellChecker sharedSpellChecker]; 55 | NSString* string = [NSString stringWithUTF8String:word.c_str()]; 56 | NSString* language = [NSString stringWithUTF8String:options.language.c_str()]; 57 | 58 | NSArray* corrections = [checker 59 | guessesForWordRange:NSMakeRange(0, [string length]) 60 | inString:string 61 | language:language 62 | inSpellDocumentWithTag:0]; 63 | 64 | std::vector results; 65 | if (corrections.count > 0) { 66 | results.reserve(corrections.count); 67 | } 68 | for (NSString* correction in corrections) { 69 | results.push_back([correction UTF8String]); 70 | } 71 | return results; 72 | } 73 | 74 | class SpellCheckerMac final : public SpellChecker { 75 | public: 76 | SpellCheckResult Suggest(const std::string& word) 77 | { 78 | SpellCheckResult result; 79 | result.correctlySpelled = false; 80 | result.suggestions = NativeSpellChecker::findClosestWords(word); 81 | if (result.suggestions.empty()) { 82 | result.correctlySpelled = true; 83 | } 84 | return result; 85 | } 86 | 87 | void AddWord(const std::string& word) 88 | { 89 | } 90 | 91 | void RemoveWord(const std::string& word) 92 | { 93 | } 94 | }; 95 | 96 | std::shared_ptr CreateSpellCheckerMac() 97 | { 98 | return std::make_shared(); 99 | } 100 | 101 | } // namespace somera 102 | -------------------------------------------------------------------------------- /typo-poi/source/SpellCheckWin.cpp: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /typo-poi/source/SpellChecker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | 11 | struct SpellCheckResult { 12 | std::vector suggestions; 13 | 14 | ///@brief `true` if the word is correctly spelled; `false` otherwise. 15 | bool correctlySpelled; 16 | }; 17 | 18 | class SpellChecker { 19 | public: 20 | virtual ~SpellChecker() = default; 21 | 22 | virtual SpellCheckResult Suggest(const std::string& word) = 0; 23 | 24 | virtual void AddWord(const std::string& word) = 0; 25 | 26 | virtual void RemoveWord(const std::string& word) = 0; 27 | }; 28 | 29 | class SpellCheckerFactory final { 30 | public: 31 | static std::shared_ptr Create(); 32 | }; 33 | 34 | } // namespace somera 35 | -------------------------------------------------------------------------------- /typo-poi/source/Typo.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "wordsegmenter.h" 6 | #include "somera/Optional.h" 7 | #include "SpellChecker.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace somera { 14 | 15 | struct SourceLocation { 16 | std::string filePath; 17 | }; 18 | 19 | struct Typo { 20 | SourceLocation location; 21 | //SourceRange range; 22 | std::string misspelledWord; 23 | std::vector corrections; 24 | }; 25 | 26 | struct TypoSource { 27 | SourceLocation location; 28 | //SourceRange range; 29 | }; 30 | 31 | class TypoCache final { 32 | public: 33 | TypoCache(); 34 | 35 | bool exists(const std::string& typo) const; 36 | 37 | void setCapacity(size_t capacity); 38 | 39 | void insert(Typo && typo); 40 | 41 | private: 42 | struct TypoCacheInfo { 43 | std::string misspelledWord; 44 | int count; 45 | }; 46 | 47 | std::vector typos; 48 | std::size_t maxCacheSize; 49 | }; 50 | 51 | class TypoMan final { 52 | private: 53 | std::shared_ptr spellChecker; 54 | somera::WordSegmenter segmenter; 55 | TypoCache cache; 56 | std::function onFoundTypo; 57 | int minimumWordSize; 58 | int maxCorrectWordCount; 59 | bool isStrictWhiteSpace; 60 | bool isStrictHyphen; 61 | bool isStrictLetterCase; 62 | bool isCacheEnabled; 63 | 64 | public: 65 | TypoMan() noexcept; 66 | 67 | void computeFromSentence( 68 | const std::string& sentence, const TypoSource& source); 69 | 70 | void computeFromWord(const std::string& word); 71 | 72 | void setSpellChecker(const std::shared_ptr& spellChecker); 73 | 74 | void setMinimumWordSize(int wordSize); 75 | 76 | void setMaxCorrectWordCount(int maxCount); 77 | 78 | void setStrictWhiteSpace(bool strictWhiteSpace); 79 | 80 | void setStrictHyphen(bool strictHyphen); 81 | 82 | void setCacheEnabled(bool cacheEnabled); 83 | 84 | void setCacheSize(std::size_t cacheSize); 85 | 86 | void setFoundCallback(std::function callback); 87 | }; 88 | 89 | } // namespace somera 90 | -------------------------------------------------------------------------------- /typo-poi/source/UTF8.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "utf8.h" 4 | #include 5 | #include 6 | 7 | #define USE_LLVM_CONVERTUTF 1 8 | #if defined(USE_LLVM_CONVERTUTF) 9 | #include "thirdparty/ConvertUTF.h" 10 | #include 11 | #else 12 | #include 13 | #include 14 | #endif 15 | 16 | namespace somera { 17 | 18 | std::u32string toUtf32(const std::string& utf8) 19 | { 20 | if (utf8.empty()) { 21 | return {}; 22 | } 23 | 24 | #if defined(USE_LLVM_CONVERTUTF) 25 | auto src = reinterpret_cast(utf8.data()); 26 | auto srcEnd = reinterpret_cast(utf8.data() + utf8.size()); 27 | 28 | std::u32string result; 29 | result.resize(utf8.length() + 1); 30 | auto dest = reinterpret_cast(&result[0]); 31 | auto destEnd = dest + result.size(); 32 | 33 | ConversionResult CR = 34 | ::ConvertUTF8toUTF32(&src, srcEnd, &dest, destEnd, strictConversion); 35 | assert(CR != targetExhausted); 36 | 37 | if (CR != conversionOK) { 38 | // error 39 | result.clear(); 40 | return result; 41 | } 42 | 43 | result.resize(reinterpret_cast(dest) - result.data()); 44 | result.shrink_to_fit(); 45 | return result; 46 | #else 47 | std::wstring_convert, char32_t> convert; 48 | return convert.from_bytes(utf8); 49 | #endif 50 | } 51 | 52 | std::string toUtf8(const std::u32string& utf32) 53 | { 54 | if (utf32.empty()) { 55 | return {}; 56 | } 57 | 58 | #if defined(USE_LLVM_CONVERTUTF) 59 | auto src = reinterpret_cast(utf32.data()); 60 | auto srcEnd = reinterpret_cast(utf32.data() + utf32.size()); 61 | 62 | std::string result; 63 | result.resize(utf32.length() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1); 64 | auto dest = reinterpret_cast(&result[0]); 65 | auto destEnd = dest + result.size(); 66 | 67 | ConversionResult CR = 68 | ::ConvertUTF32toUTF8(&src, srcEnd, &dest, destEnd, strictConversion); 69 | assert(CR != targetExhausted); 70 | 71 | if (CR != conversionOK) { 72 | // error 73 | result.clear(); 74 | return result; 75 | } 76 | 77 | result.resize(reinterpret_cast(dest) - result.data()); 78 | result.shrink_to_fit(); 79 | return result; 80 | #else 81 | std::wstring_convert, char32_t> convert; 82 | return convert.to_bytes(utf32); 83 | #endif 84 | } 85 | 86 | } // namespace somera 87 | -------------------------------------------------------------------------------- /typo-poi/source/UTF8.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace somera { 8 | 9 | std::u32string toUtf32(const std::string& s); 10 | 11 | std::string toUtf8(const std::u32string& s); 12 | 13 | } // namespace somera 14 | -------------------------------------------------------------------------------- /typo-poi/source/WordDiff.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | 10 | enum class DiffOperation { 11 | Equality = 0, 12 | Insertion = 1, 13 | Deletion = -1, 14 | }; 15 | 16 | template 17 | struct DiffEdit { 18 | T character; 19 | DiffOperation operation; 20 | }; 21 | 22 | template 23 | struct DiffHunk { 24 | std::basic_string text; 25 | DiffOperation operation; 26 | }; 27 | 28 | std::vector> computeDiff( 29 | const std::string& text1, 30 | const std::string& text2); 31 | 32 | std::vector> computeDiff_DynamicProgramming( 33 | const std::string& text1, 34 | const std::string& text2); 35 | 36 | std::vector> computeDiff_ONDGreedyAlgorithm( 37 | const std::string& text1, 38 | const std::string& text2); 39 | 40 | std::vector> computeDiff_LinearSpace( 41 | const std::string& text1, 42 | const std::string& text2); 43 | 44 | std::vector> computeDiff_WeavingLinearSpace( 45 | const std::string& text1, 46 | const std::string& text2); 47 | 48 | std::vector> computeShortestEditScript_DynamicProgramming( 49 | const std::string& text1, 50 | const std::string& text2); 51 | 52 | std::vector> computeShortestEditScript_ONDGreedyAlgorithm( 53 | const std::string& text1, 54 | const std::string& text2); 55 | 56 | std::vector> computeShortestEditScript_LinearSpace( 57 | const std::string& text1, 58 | const std::string& text2); 59 | 60 | std::vector> computeShortestEditScript_WeaveingLinearSpace( 61 | const std::string& text1, 62 | const std::string& text2); 63 | 64 | } // namespace somera 65 | -------------------------------------------------------------------------------- /typo-poi/source/WordSegmenter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #include "WordSegmenter.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace somera { 11 | namespace { 12 | 13 | bool IsSeparator(char32_t c) 14 | { 15 | // NOTE: Ascii characters without alphabet, numbers and [-']. 16 | std::string separators = "!\"#$%&()*+,./:;<=>?@[\\]^`{|}~"; 17 | assert(std::is_sorted(separators.begin(), separators.end())); 18 | return std::binary_search(std::begin(separators), std::end(separators), c); 19 | } 20 | 21 | bool IsConcatenator(char32_t c) 22 | { 23 | std::string separators = "'-"; 24 | assert(std::is_sorted(separators.begin(), separators.end())); 25 | return std::binary_search(std::begin(separators), std::end(separators), c); 26 | } 27 | 28 | std::vector> SplitBySpace(const std::string& str) 29 | { 30 | std::vector> tokens; 31 | std::string buffer; 32 | std::string separator; 33 | auto flushBuffer = [&] { 34 | if (!buffer.empty()) { 35 | tokens.emplace_back(buffer, PartOfSpeechTag::Word); 36 | buffer.clear(); 37 | } 38 | }; 39 | auto flushSeparator = [&] { 40 | if (!separator.empty()) { 41 | tokens.emplace_back(separator, PartOfSpeechTag::Spaces); 42 | separator.clear(); 43 | } 44 | }; 45 | 46 | auto iter = str.begin(); 47 | auto end = str.end(); 48 | for (; iter != end; ++iter) { 49 | auto character = *iter; 50 | if (::isspace(character)) { 51 | flushBuffer(); 52 | separator += character; 53 | } 54 | else { 55 | flushSeparator(); 56 | buffer += character; 57 | } 58 | } 59 | assert(buffer.empty() || separator.empty()); 60 | flushBuffer(); 61 | flushSeparator(); 62 | return tokens; 63 | } 64 | 65 | void TokenizeByAsciiSymbols( 66 | const std::string& str, 67 | std::function callback) 68 | { 69 | std::string buffer; 70 | std::string separator; 71 | 72 | auto flushBuffer = [&] { 73 | if (buffer.empty()) { 74 | return; 75 | } 76 | 77 | auto trimRight = std::find_if(std::rbegin(buffer), std::rend(buffer), 78 | [&](char32_t c){ return !IsConcatenator(c); }).base(); 79 | 80 | std::string prefix(std::begin(buffer), trimRight); 81 | std::string suffix(trimRight, std::end(buffer)); 82 | buffer.clear(); 83 | 84 | if (std::regex_match(prefix, std::regex(R"(\d+)"))) { 85 | // integer 86 | callback({prefix, PartOfSpeechTag::Integer}); 87 | } 88 | else if (std::regex_match(prefix, std::regex(R"(0x[\dA-Fa-f]+)"))) { 89 | // hex integer 90 | callback({prefix, PartOfSpeechTag::IntegerHex}); 91 | } 92 | else if (std::regex_match(prefix, std::regex(R"(0b[01]+)"))) { 93 | // binary integer 94 | callback({prefix, PartOfSpeechTag::IntegerBinary}); 95 | } 96 | else if (std::regex_match(prefix, std::regex(R"((\d*\.\d+|\d+\.\d*)f?)"))) { 97 | // float number 98 | callback({prefix, PartOfSpeechTag::FloatNumber}); 99 | } 100 | else { 101 | callback({prefix, PartOfSpeechTag::Word}); 102 | } 103 | 104 | if (!suffix.empty()) { 105 | callback({suffix, PartOfSpeechTag::Symbol}); 106 | } 107 | }; 108 | auto flushSeparator = [&] { 109 | if (!separator.empty()) { 110 | callback({separator, PartOfSpeechTag::Spaces}); 111 | separator.clear(); 112 | } 113 | }; 114 | 115 | auto iter = str.begin(); 116 | auto end = str.end(); 117 | for (; iter != end; ++iter) { 118 | auto character = *iter; 119 | if ((IsSeparator(character)) || (buffer.empty() && IsConcatenator(character))) { 120 | flushBuffer(); 121 | separator += character; 122 | } 123 | else { 124 | flushSeparator(); 125 | buffer += character; 126 | } 127 | } 128 | assert(buffer.empty() || separator.empty()); 129 | flushBuffer(); 130 | flushSeparator(); 131 | } 132 | 133 | } // end anonymous namespace 134 | 135 | void WordSegmenter::Parse( 136 | const std::string& str, 137 | std::function callback) 138 | { 139 | auto splitStrings = SplitBySpace(str); 140 | 141 | for (auto & tuple : splitStrings) { 142 | auto & text = std::get<0>(tuple); 143 | auto & tag = std::get<1>(tuple); 144 | if (tag == PartOfSpeechTag::Spaces) { 145 | callback(PartOfSpeech{text, PartOfSpeechTag::Spaces}); 146 | continue; 147 | } 148 | if (std::regex_match(text, std::regex(R"(git@\w+\..+)"))) { 149 | callback(PartOfSpeech{text, PartOfSpeechTag::GitUrl}); 150 | continue; 151 | } 152 | if (std::regex_match(text, std::regex(R"((http|https|ftp)://.+)"))) { 153 | callback(PartOfSpeech{text, PartOfSpeechTag::Url}); 154 | continue; 155 | } 156 | TokenizeByAsciiSymbols(text, callback); 157 | } 158 | } 159 | 160 | } // namespace somera 161 | -------------------------------------------------------------------------------- /typo-poi/source/WordSegmenter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace somera { 9 | 10 | enum class PartOfSpeechTag { 11 | Word, 12 | Spaces, 13 | Symbol, 14 | GitUrl, 15 | Url, 16 | Integer, 17 | IntegerBinary, 18 | IntegerHex, 19 | FloatNumber, 20 | }; 21 | 22 | struct PartOfSpeech { 23 | std::string text; 24 | PartOfSpeechTag tag; 25 | }; 26 | 27 | class WordSegmenter { 28 | public: 29 | void Parse( 30 | const std::string& text, 31 | std::function callback); 32 | }; 33 | 34 | } // namespace somera 35 | -------------------------------------------------------------------------------- /what-is-this-file/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | .DS_Store 4 | 5 | ## Output 6 | bin/ 7 | build/ 8 | *.xcodeproj 9 | -------------------------------------------------------------------------------- /what-is-this-file/Error.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "Error.h" 4 | #include 5 | 6 | namespace somera { 7 | 8 | Error::operator bool() const noexcept 9 | { 10 | return description.operator bool(); 11 | } 12 | 13 | std::string Error::What() const noexcept 14 | { 15 | std::stringstream what; 16 | bool needToSeparate = false; 17 | if (description) { 18 | what << "error: "; 19 | what << *description; 20 | needToSeparate = true; 21 | } 22 | if (file) { 23 | if (needToSeparate) { 24 | what << ", "; 25 | } 26 | what << *file; 27 | needToSeparate = true; 28 | } 29 | if (line) { 30 | if (needToSeparate) { 31 | what << ", Line="; 32 | } 33 | what << *line; 34 | } 35 | return what.str(); 36 | } 37 | 38 | Error MakeError(const std::string& description) 39 | { 40 | Error error; 41 | error.description = description; 42 | return error; 43 | } 44 | 45 | Error MakeError(const std::error_code& errorCode) 46 | { 47 | Error error; 48 | error.description = errorCode.message(); 49 | return error; 50 | } 51 | 52 | Error MakeError(const std::string& description, const char* file, int line) 53 | { 54 | Error error; 55 | error.description = description; 56 | error.file = file; 57 | error.line = line; 58 | return error; 59 | } 60 | 61 | } // namespace somera 62 | -------------------------------------------------------------------------------- /what-is-this-file/Error.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "somera/Optional.h" 6 | #include 7 | #include 8 | 9 | namespace somera { 10 | 11 | struct Error { 12 | somera::Optional description; 13 | somera::Optional file; 14 | somera::Optional line; 15 | 16 | operator bool() const noexcept; 17 | 18 | std::string What() const noexcept; 19 | }; 20 | 21 | Error MakeError(const std::string& description); 22 | Error MakeError(const std::string& description, const char* file, int line); 23 | Error MakeError(const std::error_code& errorCode); 24 | 25 | } // namespace somera 26 | -------------------------------------------------------------------------------- /what-is-this-file/Makefile: -------------------------------------------------------------------------------- 1 | NORI = ../nori/bin/nori 2 | PRODUCTNAME = what-is-this-file 3 | 4 | HEADERS = \ 5 | ../somera/*.h \ 6 | *.h 7 | 8 | SOURCES = \ 9 | ../somera/*.cpp \ 10 | Error.cpp \ 11 | WhatIsThisFile.cpp \ 12 | main.cpp 13 | 14 | COMPILER_OPTIONS = \ 15 | -o $(PRODUCTNAME) \ 16 | -std=c++14 \ 17 | -stdlib=libc++ \ 18 | -I.. \ 19 | $(SOURCES) 20 | 21 | xcode: 22 | $(NORI) \ 23 | -generator=xcode \ 24 | $(HEADERS) \ 25 | $(COMPILER_OPTIONS) 26 | @xcodebuild -project $(PRODUCTNAME).xcodeproj -configuration Release 27 | @mkdir -p bin 28 | @cp build/Release/$(PRODUCTNAME) bin/$(PRODUCTNAME) 29 | -------------------------------------------------------------------------------- /what-is-this-file/README.md: -------------------------------------------------------------------------------- 1 | # what-is-this-file 2 | 3 | A file type detector. 4 | 5 | It is distributed under the [MIT License](https://opensource.org/licenses/MIT). 6 | 7 | ## Build and run 8 | 9 | ```sh 10 | # Setup build tool 11 | make -C ../what-is-this-file bootstrap 12 | 13 | # Build 14 | make xcode 15 | 16 | # Run 17 | ./bin/what-is-this-file -help 18 | ``` 19 | -------------------------------------------------------------------------------- /what-is-this-file/WhatIsThisFile.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #pragma once 4 | 5 | #include "Error.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace somera { 12 | 13 | struct FileDetection { 14 | std::string type; 15 | double accuracy = 0; 16 | }; 17 | 18 | struct FileFormatDescription final { 19 | std::vector extensions; 20 | std::vector signature; 21 | std::string name; 22 | }; 23 | 24 | class FileDetector final { 25 | public: 26 | explicit FileDetector(FileFormatDescription && descriptionIn); 27 | 28 | bool IsSignatureEnabled() const noexcept; 29 | 30 | bool IsExtensionEnabled() const noexcept; 31 | 32 | bool MatchExtension(const std::string& extension) const noexcept; 33 | 34 | bool MatchSignature(const std::vector& signature) const noexcept; 35 | 36 | std::string GetName() const noexcept; 37 | 38 | private: 39 | FileFormatDescription description; 40 | }; 41 | 42 | class WhatIsThisFile final { 43 | public: 44 | WhatIsThisFile(); 45 | 46 | std::tuple Detect(const std::string& path); 47 | 48 | private: 49 | std::vector> detectors; 50 | }; 51 | 52 | } // namespace somera 53 | -------------------------------------------------------------------------------- /what-is-this-file/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 mogemimi. Distributed under the MIT license. 2 | 3 | #include "somera/CommandLineParser.h" 4 | #include "somera/FileSystem.h" 5 | #include "somera/Optional.h" 6 | #include "somera/StringHelper.h" 7 | #include "Error.h" 8 | #include "WhatIsThisFile.h" 9 | #include 10 | #include 11 | #include 12 | 13 | using somera::CommandLineParser; 14 | using somera::Optional; 15 | using somera::NullOpt; 16 | using somera::Error; 17 | using somera::MakeError; 18 | using somera::WhatIsThisFile; 19 | using somera::FileDetection; 20 | namespace FileSystem = somera::FileSystem; 21 | namespace StringHelper = somera::StringHelper; 22 | 23 | namespace { 24 | 25 | void setupCommandLineParser(CommandLineParser & parser) 26 | { 27 | using somera::CommandLineArgumentType::Flag; 28 | using somera::CommandLineArgumentType::JoinedOrSeparate; 29 | parser.setUsageText("what-is-this-file [directory path]"); 30 | parser.addArgument("-h", Flag, "Display available options"); 31 | parser.addArgument("-help", Flag, "Display available options"); 32 | } 33 | 34 | void readFile(const std::string& path) 35 | { 36 | WhatIsThisFile witf; 37 | 38 | FileDetection detection; 39 | Error error; 40 | std::tie(detection, error) = witf.Detect(path); 41 | 42 | if (error) { 43 | std::cerr << error.What() << std::endl; 44 | return; 45 | } 46 | 47 | if (detection.type.empty()) { 48 | std::cerr << "error: Cannot detect the file type. " << path << std::endl; 49 | return; 50 | } 51 | 52 | std::cout << detection.type << std::endl; 53 | } 54 | 55 | } // unnamed namespace 56 | 57 | int main(int argc, char *argv[]) 58 | { 59 | CommandLineParser parser; 60 | setupCommandLineParser(parser); 61 | parser.parse(argc, argv); 62 | 63 | if (parser.hasParseError()) { 64 | std::cerr << parser.getErrorMessage() << std::endl; 65 | return 1; 66 | } 67 | if (parser.exists("-h") || parser.exists("-help")) { 68 | std::cout << parser.getHelpText() << std::endl; 69 | return 0; 70 | } 71 | if (parser.getPaths().empty()) { 72 | std::cerr << "error: no input file" << std::endl; 73 | return 1; 74 | } 75 | 76 | for (auto & path : parser.getPaths()) { 77 | readFile(path); 78 | } 79 | 80 | return 0; 81 | } 82 | --------------------------------------------------------------------------------