diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c00bbcec..6860dcdd 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -9,22 +9,16 @@ jobs: strategy: matrix: include: - - name: "macOS 11 + Xcode 11.7" - os: macos-11 + - name: "macOS 13 + Xcode 15.0" + os: macos-13 + arch: x86_64 compiler: xcode - version: "11.7" - - name: "macOS 11 + Xcode 12.2" - os: macos-11 + version: "15.0" + - name: "macOS 14 Arm64 + Xcode 15.0" + os: macos-14 + arch: arm64 compiler: xcode - version: "12.4" - - name: "macOS 11 + gcc-10" - os: macos-11 - compiler: gcc - version: "10" - - name: "macOS 11 + gcc-11" - os: macos-11 - compiler: gcc - version: "11" + version: "15.0" runs-on: ${{ matrix.os }} name: ${{ matrix.name }} @@ -42,8 +36,8 @@ jobs: else ls -ls /Applications/ sudo xcode-select -switch /Applications/Xcode_${{ matrix.version }}.app - echo "CC=clang" >> $GITHUB_ENV - echo "CXX=clang++" >> $GITHUB_ENV + echo "CC=$(brew --prefix llvm@15)/bin/clang" >> $GITHUB_ENV + echo "CXX=$(brew --prefix llvm@15)/bin/clang++" >> $GITHUB_ENV fi - name: Configure Build run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DKIWI_JAVA_BINDING=1 .. @@ -74,11 +68,17 @@ jobs: ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt KIWI_ARCH_TYPE=none ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt KIWI_ARCH_TYPE=balanced ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt - KIWI_ARCH_TYPE=sse2 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt - KIWI_ARCH_TYPE=sse4_1 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt - KIWI_ARCH_TYPE=avx2 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt - KIWI_ARCH_TYPE=avx2 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out --sbg kowiki1000.txt - KIWI_ARCH_TYPE=avx2 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out --typos 6 kowiki1000.txt + if [ "${{ matrix.arch }}" = "x86_64" ]; then + KIWI_ARCH_TYPE=sse2 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt + KIWI_ARCH_TYPE=sse4_1 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt + KIWI_ARCH_TYPE=avx2 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt + KIWI_ARCH_TYPE=avx2 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out --sbg kowiki1000.txt + KIWI_ARCH_TYPE=avx2 ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out --typos 6 kowiki1000.txt + else + KIWI_ARCH_TYPE=neon ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out kowiki1000.txt + KIWI_ARCH_TYPE=neon ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out --sbg kowiki1000.txt + KIWI_ARCH_TYPE=neon ./build/kiwi-cli-* -m ./ModelGenerator -e -o test.out --typos 6 kowiki1000.txt + fi - name: Archive binaries uses: actions/upload-artifact@v2 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8783b9af..641426e8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -144,7 +144,7 @@ jobs: build-macos: strategy: matrix: - os: [macos-11] + os: [macos-13] arch: [x86_64, arm64] runs-on: ${{ matrix.os }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 51c6dd02..93bb0382 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.12) -project(kiwi VERSION 0.17.1 DESCRIPTION "Kiwi, Korean Intelligent Word Identifier") +project(kiwi VERSION 0.18.0 DESCRIPTION "Kiwi, Korean Intelligent Word Identifier") set ( CMAKE_CXX_STANDARD 14 ) set ( CMAKE_VERBOSE_MAKEFILE true ) @@ -48,7 +48,7 @@ set ( CORE_SRCS src/Form.cpp src/FeatureTestor.cpp src/FileUtils.cpp - src/HSDataset.cpp + src/Dataset.cpp src/Joiner.cpp src/Kiwi.cpp src/KiwiBuilder.cpp @@ -56,6 +56,7 @@ set ( CORE_SRCS src/PatternMatcher.cpp src/search.cpp src/ScriptType.cpp + src/SubstringExtractor.cpp src/SwTokenizer.cpp src/TagUtils.cpp src/TypoTransformer.cpp diff --git a/bindings/java/JniUtils.hpp b/bindings/java/JniUtils.hpp index 10fd70be..f0cb47d9 100644 --- a/bindings/java/JniUtils.hpp +++ b/bindings/java/JniUtils.hpp @@ -924,7 +924,8 @@ namespace jni { return CppType{ env, v }; } - if (!env->IsInstanceOf(v, JIteratorBase::jClass)) throw std::runtime_error{ StringConcat_v.data()}; + // The following line crashes clang compiler. I don't know why, but it's not necessary. So I commented it out. + if (!env->IsInstanceOf(v, JIteratorBase::jClass)) throw std::runtime_error{ ""/*StringConcat_v.data()*/}; return CppType{ env, v }; } }; diff --git a/bindings/java/kr/pe/bab2min/Kiwi.java b/bindings/java/kr/pe/bab2min/Kiwi.java index 0f343779..e8a607de 100644 --- a/bindings/java/kr/pe/bab2min/Kiwi.java +++ b/bindings/java/kr/pe/bab2min/Kiwi.java @@ -12,7 +12,7 @@ public class Kiwi implements AutoCloseable { private long _inst; - final private static String _version = "0.17.1"; + final private static String _version = "0.18.0"; public static class Match { final static public int none = 0, diff --git a/include/kiwi/HSDataset.h b/include/kiwi/Dataset.h similarity index 100% rename from include/kiwi/HSDataset.h rename to include/kiwi/Dataset.h diff --git a/include/kiwi/Form.h b/include/kiwi/Form.h index 3b8f294e..38eda09a 100644 --- a/include/kiwi/Form.h +++ b/include/kiwi/Form.h @@ -2,8 +2,8 @@ * @file Form.h * @author bab2min (bab2min@gmail.com) * @brief 형태 및 형태소에 관한 정보를 담는 구조체들이 선언된 헤더 - * @version 0.17.0 - * @date 2022-09-01 + * @version 0.18.0 + * @date 2024-07-01 * * */ diff --git a/include/kiwi/Kiwi.h b/include/kiwi/Kiwi.h index e052cbaf..f69c4eb7 100644 --- a/include/kiwi/Kiwi.h +++ b/include/kiwi/Kiwi.h @@ -2,8 +2,8 @@ * @file Kiwi.h * @author bab2min (bab2min@gmail.com) * @brief Kiwi C++ API를 담고 있는 헤더 파일 - * @version 0.17.0 - * @date 2022-09-01 + * @version 0.18.0 + * @date 2024-07-01 * * */ @@ -650,7 +650,7 @@ namespace kiwi * @param numThreads 모델 및 형태소 분석에 사용할 스레드 개수 * @param options 생성 옵션. `kiwi::BuildOption`을 참조 */ - KiwiBuilder(const std::string& modelPath, size_t numThreads = 0, BuildOption options = BuildOption::integrateAllomorph | BuildOption::loadDefaultDict, bool useSBG = false); + KiwiBuilder(const std::string& modelPath, size_t numThreads = 0, BuildOption options = BuildOption::default_, bool useSBG = false); /** * @brief 현재 KiwiBuilder 객체가 유효한 분석 모델을 로딩한 상태인지 알려준다. diff --git a/include/kiwi/Macro.h b/include/kiwi/Macro.h index 25b0c393..bda5ef8b 100644 --- a/include/kiwi/Macro.h +++ b/include/kiwi/Macro.h @@ -4,7 +4,7 @@ #define KIWI_STR(x) KIWI_STR_HELPER(x) #define KIWI_VERSION_MAJOR 0 -#define KIWI_VERSION_MINOR 17 -#define KIWI_VERSION_PATCH 1 +#define KIWI_VERSION_MINOR 18 +#define KIWI_VERSION_PATCH 0 #define KIWI_VERSION_STRING KIWI_STR(KIWI_VERSION_MAJOR) "." KIWI_STR(KIWI_VERSION_MINOR) "." KIWI_STR(KIWI_VERSION_PATCH) diff --git a/include/kiwi/SubstringExtractor.h b/include/kiwi/SubstringExtractor.h new file mode 100644 index 00000000..c4ba7c90 --- /dev/null +++ b/include/kiwi/SubstringExtractor.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace kiwi +{ + std::vector> extractSubstrings( + const char16_t* first, + const char16_t* last, + size_t minCnt, + size_t minLength = 2, + size_t maxLength = 32, + bool longestOnly = true, + char16_t stopChr = 0); +} diff --git a/include/kiwi/SwTokenizer.h b/include/kiwi/SwTokenizer.h index 64d61053..4019aa8b 100644 --- a/include/kiwi/SwTokenizer.h +++ b/include/kiwi/SwTokenizer.h @@ -2,8 +2,8 @@ * @file SwTokenizer.h * @author bab2min (bab2min@gmail.com) * @brief Subword Tokenizer - * @version 0.16.1 - * @date 2022-07-28 + * @version 0.18.0 + * @date 2024-07-01 * * */ diff --git a/include/kiwi/Types.h b/include/kiwi/Types.h index ea1fa317..6af80524 100644 --- a/include/kiwi/Types.h +++ b/include/kiwi/Types.h @@ -2,8 +2,8 @@ * @file Types.h * @author bab2min (bab2min@gmail.com) * @brief Kiwi C++ API에 쓰이는 주요 타입들을 모아놓은 헤더 파일 - * @version 0.17.0 - * @date 2022-09-01 + * @version 0.18.0 + * @date 2024-07-01 * * */ diff --git a/include/kiwi/capi.h b/include/kiwi/capi.h index d70a36b5..24591421 100644 --- a/include/kiwi/capi.h +++ b/include/kiwi/capi.h @@ -2,8 +2,8 @@ * @file capi.h * @author bab2min (bab2min@gmail.com) * @brief Kiwi C API를 담고 있는 헤더 파일 - * @version 0.17.0 - * @date 2022-09-01 + * @version 0.18.0 + * @date 2024-07-01 * * */ @@ -45,7 +45,11 @@ typedef struct { uint32_t line_number; /**< 줄 번호*/ uint16_t length; /**< 길이(UTF16 문자 기준) */ uint8_t tag; /**< 품사 태그 */ - uint8_t sense_id; /**< 의미 번호 */ + union + { + uint8_t sense_id; /**< 의미 번호 */ + uint8_t script; /**< 유니코드 영역에 기반한 문자 타입 */ + }; float score; /**< 해당 형태소의 언어모델 점수 */ float typo_cost; /**< 오타가 교정된 경우 오타 비용. 그렇지 않은 경우 0 */ uint32_t typo_form_id; /**< 교정 전 오타의 형태에 대한 정보 (typoCost가 0인 경우 의미 없음) */ @@ -1008,6 +1012,16 @@ DECL_DLL int kiwi_pt_add_token_to_span_w(kiwi_pretokenized_h handle, int span_id */ DECL_DLL int kiwi_pt_close(kiwi_pretokenized_h handle); +/** + * @brief `kiwi_token_info_t`의 `script`가 가리키는 문자 영역의 유니코드 상 이름을 반환합니다. + * + * @param script `kiwi_token_info_t`의 `script` 필드 값 + * @return 유니코드 영역의 이름을 반환합니다. 알 수 없을 경우 "Unknown"을 반환합니다. + * + * @note 이 함수가 반환하는 값은 string literal이므로 별도로 해제할 필요가 없습니다. + */ +DECL_DLL const char* kiwi_get_script_name(uint8_t script); + #ifdef __cplusplus } #endif diff --git a/src/HSDataset.cpp b/src/Dataset.cpp similarity index 99% rename from src/HSDataset.cpp rename to src/Dataset.cpp index f5f2e741..4a266ec4 100644 --- a/src/HSDataset.cpp +++ b/src/Dataset.cpp @@ -1,4 +1,4 @@ -#include +#include #include "RaggedVector.hpp" using namespace kiwi; diff --git a/src/KiwiBuilder.cpp b/src/KiwiBuilder.cpp index e5b2662d..06225241 100644 --- a/src/KiwiBuilder.cpp +++ b/src/KiwiBuilder.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "ArchAvailable.h" #include "KTrie.h" #include "StrUtils.h" diff --git a/src/PatternMatcher.cpp b/src/PatternMatcher.cpp index ac1e5b39..2eb054c8 100644 --- a/src/PatternMatcher.cpp +++ b/src/PatternMatcher.cpp @@ -272,7 +272,7 @@ size_t PatternMatcherImpl::testAbbr(const char16_t* first, const char16_t* last) if (b != last && *b == ' ') { if (l > (isUpperAlpha(*first) ? 5 : 3)) return 0; // reject too long patterns for abbreviation - ++b; + return b - first; } else { @@ -287,7 +287,6 @@ size_t PatternMatcherImpl::testAbbr(const char16_t* first, const char16_t* last) if (b != last && *b == '.') ++b; else return b - first; - if (b != last && *b == ' ') ++b; } if (b[-1] == ' ') --b; return b - first; diff --git a/src/ScriptType.cpp b/src/ScriptType.cpp index 7c23ca8a..7328038e 100644 --- a/src/ScriptType.cpp +++ b/src/ScriptType.cpp @@ -563,7 +563,7 @@ namespace kiwi if (type == ScriptType::chess_symbols) return "Chess Symbols"; if (type == ScriptType::symbols_for_legacy_computing) return "Symbols for Legacy Computing"; if (type == ScriptType::tags) return "Tags"; - return "unknown"; + return "Unknown"; } int isEmoji(char32_t c0, char32_t c1) diff --git a/src/SubstringExtractor.cpp b/src/SubstringExtractor.cpp new file mode 100644 index 00000000..bdcaffda --- /dev/null +++ b/src/SubstringExtractor.cpp @@ -0,0 +1,141 @@ +#include + +#include +#include + +#include "StrUtils.h" + +#include "sais/fm_index.hpp" + +using namespace std; + +namespace kiwi +{ + inline bool testRepetition(const char16_t* s, size_t l) + { + if (l < 5) return false; + for (size_t i = 1; i <= l / 3; ++i) + { + bool all = true; + for (size_t j = 1; j < l / i; ++j) + { + if (!equal(s, s + i, s + i * j)) + { + all = false; + break; + } + } + if (all) return true; + } + return false; + } + + vector> extractSubstrings( + const char16_t* first, + const char16_t* last, + size_t minCnt, + size_t minLength, + size_t maxLength, + bool longestOnly, + char16_t stopChr + ) + { + Vector buf(last - first + 1); + copy(first, last, buf.begin() + 1); + sais::FmIndex fi{ buf.data(), buf.size() }; + Vector> candCnts; + vector> ret; + fi.enumSuffices(minCnt, [&](const sais::FmIndex::SuffixTy& s, const sais::FmIndex::TraceTy& t) + { + if (s.size() > maxLength) return false; + + if (find(s.begin(), s.end(), stopChr) != s.end()) + { + return false; + } + + if (isLowSurrogate(s.back()) || isHighSurrogate(s.front())) return false; + + if (testRepetition(s.data(), s.size())) + { + return false; + } + + const auto ssLength = s.size(); + if (ssLength < minLength) + { + return true; + } + + const auto ssCnt = t.back().second - t.back().first; + if (ssCnt < minCnt) + { + return true; + } + + if (longestOnly) + { + if (candCnts.size() <= ssLength - minLength) candCnts.resize(ssLength - minLength + 1); + candCnts[ssLength - minLength].emplace(u16string{ s.rbegin(), s.rend() }, ssCnt); + } + else + { + ret.emplace_back(u16string{ s.rbegin(), s.rend() }, ssCnt); + } + return true; + }); + + if (longestOnly) + { + for (size_t i = 1; i < candCnts.size(); ++i) + { + auto& cands = candCnts[i]; + auto& subCands = candCnts[i - 1]; + for (auto& p : cands) + { + auto it = subCands.find(p.first.substr(1)); + if (it != subCands.end() && it->second == p.second) + { + subCands.erase(it); + } + + it = subCands.find(p.first.substr(0, p.first.size() - 1)); + if (it != subCands.end() && it->second == p.second) + { + subCands.erase(it); + } + } + } + + for (auto it = candCnts.rbegin(); it != candCnts.rend(); ++it) + { + size_t offset = ret.size(); + for (auto& p : *it) + { + ret.emplace_back(p.first, p.second); + } + sort(ret.begin() + offset, ret.end(), [&](const pair& a, const pair& b) + { + return a.second > b.second; + }); + } + } + else + { + sort(ret.begin(), ret.end(), [&](const pair& a, const pair& b) + { + if (a.first.size() > b.first.size()) + { + return true; + } + else if (a.first.size() < b.first.size()) + { + return false; + } + return a.second > b.second; + }); + } + + return ret; + } +} diff --git a/src/capi/kiwi_c.cpp b/src/capi/kiwi_c.cpp index 74e760e3..88d0fec8 100644 --- a/src/capi/kiwi_c.cpp +++ b/src/capi/kiwi_c.cpp @@ -1434,3 +1434,7 @@ int kiwi_pt_close(kiwi_pretokenized_h handle) } } +const char* kiwi_get_script_name(uint8_t script) +{ + return getScriptName((ScriptType)script); +} diff --git a/test/test_c.cpp b/test/test_c.cpp index f91c73da..257c750a 100644 --- a/test/test_c.cpp +++ b/test/test_c.cpp @@ -389,4 +389,11 @@ TEST(KiwiC, Pretokenized) EXPECT_EQ(kiwi_res_close(o), 0); EXPECT_EQ(kiwi_pt_close(pretokenized), 0); } -} \ No newline at end of file +} + +TEST(KiwiC, ScriptName) +{ + EXPECT_STREQ(kiwi_get_script_name(0), "Unknown"); + EXPECT_STREQ(kiwi_get_script_name(1), "Latin"); + EXPECT_STREQ(kiwi_get_script_name(30), "Hangul"); +} diff --git a/test/test_cpp.cpp b/test/test_cpp.cpp index 299f4fe6..50bda190 100644 --- a/test/test_cpp.cpp +++ b/test/test_cpp.cpp @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include -#include +#include +#include #include "common.h" class TestInitializer @@ -53,6 +54,36 @@ Kiwi& reuseKiwiInstance() return kiwi; } +TEST(KiwiCpp, ExtractSubstrings) +{ + const std::u16string s = u"자, 너 오늘 하루 뭐 했니? " + "난 오늘 하루가 좀 단순했지. 음 뭐 했는데? " + "아침에 수업 받다가 오구 그~ 학생이, 응. 미찌 학생인데, " + "음. 되게 귀엽게 생겼다, 음. 되게 웃으면 곰돌이 인형같이 생겼어. " + "곰돌이? 응 아니 곰돌이도 아니구, 어쨌든 무슨 인형같이 생겼어, " + "펜더곰 그런 거, 왜 이렇게 닮은 사람을 대 봐. " + "내가 아는 사람 중에서, 한무 한무? 금붕어잖아? " + "맞어 눈도 이렇게 톡 튀어나오구, 어. 한무? 조금 잘 생긴 한무. " + "잘 생긴 게 아니라 귀여운 한무. 귀여운 한무? " + "어 학원에서 별명도 귀여운 한무였어. " + "응. 눈이 똥그래 가지고 그래? 어. 좀 특이한 사람이구나."; + auto substrings = extractSubstrings(s.data(), s.data() + s.size(), 2, 2, 32, true, u' '); + EXPECT_EQ(substrings.size(), 23); + + const std::u16string t = u"어. 그러다가 갈아타야 되니까 비몽사몽간에 갈아타는 데로 사람 따라서 쓸려가. " + "음. 그러다가 요행히 자리를 차지하고 앉어. 음. 그러면은, 어. 예의상 처음부터는 이렇게 신문을 좀 " + "보는 척을 해. 그러다가 까무룩 잠이 드는 거야, 까무룩? 근데 내릴 때는 그래두 기가 막히게 내리지? " + "내릴 때 딱 한 번. 어, 들켰어? 넘어 간 넘어 간 적이 있었어, 너 어떻게 했어? 돌아왔지 뭐~, 다시 " + "타고 얼마나 갔는데? 한 정거장 더 갔을 거야. 아마 선릉인가 삼성인가에, 어. 내려 가지고 삼성인가 " + "보다 두 정거장 더 가서, 어. 다시 왔지. 음. 나는 버스 타고 다니잖아? 음. 근데 학원에 갈 때도 버스 " + "타고 아니면 여기 교보빌딩 갈 때도 버스 타잖아? 음 짜증나겠구나, 아니야 되게 가깝잖아, 버스 타고 십 " + "분, 막히잖아 광화문터널, 안 막혀 별루 그 시간에. 그래 좋겠다. 일곱시 반부터 여덟시부터는 막히는지 " + "모르겠는데 어쨌든 일곱시 반부터 내가 빨리 타면 일곱시 사십분 차를 타고 늦게 타면 사십오분 차를 " + "타거던, 음. 그때는 그까 앉아 가는 일은 별루 없어. 음. 별루 애들이 거기도 뭐~ 애들이 아현 거기쯤에 " + "내리더라, 학생들이 그쪽 학교 있지? 너두 거기 나왔잖아? 음. 이름이 뭐지? 한성! 어이구 아네,"; + substrings = extractSubstrings(t.data(), t.data() + t.size(), 3, 3, 32, true, u' '); + EXPECT_EQ(substrings.size(), 3); +} TEST(KiwiCpp, InitClose) { diff --git a/vsproj/kiwi_shared_library.vcxproj b/vsproj/kiwi_shared_library.vcxproj index c70f01cc..35a21b68 100644 --- a/vsproj/kiwi_shared_library.vcxproj +++ b/vsproj/kiwi_shared_library.vcxproj @@ -32,7 +32,7 @@ - + @@ -42,6 +42,7 @@ + @@ -115,9 +116,11 @@ + +