Lukhnos: Taiyan // Refactor for better thread safety
- See UPR269. - A vChewing-Specific change, allowing the HYPY input mode to support the intonation key "1", is unavailable in this update. It will be brought back later if possible.
This commit is contained in:
parent
40e28d8657
commit
d3032053ab
|
@ -5,245 +5,197 @@
|
||||||
* All rights reserved. See "LICENSE.TXT" for details.
|
* All rights reserved. See "LICENSE.TXT" for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef Mandarin_h
|
#ifndef MANDARIN_H_
|
||||||
#define Mandarin_h
|
#define MANDARIN_H_
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace Taiyan {
|
namespace Taiyan {
|
||||||
namespace Mandarin {
|
namespace Mandarin {
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
class BopomofoSyllable {
|
class BopomofoSyllable {
|
||||||
public:
|
public:
|
||||||
typedef unsigned int Component;
|
typedef uint16_t Component;
|
||||||
BopomofoSyllable(Component syllable = 0)
|
|
||||||
: m_syllable(syllable)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BopomofoSyllable(const BopomofoSyllable& another)
|
explicit BopomofoSyllable(Component syllable = 0) : syllable_(syllable) {}
|
||||||
: m_syllable(another.m_syllable)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~BopomofoSyllable()
|
BopomofoSyllable(const BopomofoSyllable&) = default;
|
||||||
{
|
BopomofoSyllable(BopomofoSyllable&& another) = default;
|
||||||
}
|
BopomofoSyllable& operator=(const BopomofoSyllable&) = default;
|
||||||
|
BopomofoSyllable& operator=(BopomofoSyllable&&) = default;
|
||||||
|
|
||||||
BopomofoSyllable& operator=(const BopomofoSyllable& another)
|
// takes the ASCII-form, "v"-tolerant, Non-Continental-style Hanyu Pinyin (fong, pong, bong
|
||||||
{
|
// acceptable)
|
||||||
m_syllable = another.m_syllable;
|
static const BopomofoSyllable FromHanyuPinyin(const std::string& str);
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// takes the ASCII-form, "v"-tolerant, TW-style Hanyu Pinyin (fong, pong, bong acceptable)
|
|
||||||
static const BopomofoSyllable FromHanyuPinyin(const string& str);
|
|
||||||
|
|
||||||
// TO DO: Support accented vowels
|
// TO DO: Support accented vowels
|
||||||
const string HanyuPinyinString(bool includesTone, bool useVForUUmlaut) const;
|
const std::string HanyuPinyinString(bool includesTone,
|
||||||
// const string HanyuPinyinString(bool includesTone, bool useVForUUmlaut, bool composeAccentedVowel) const;
|
bool useVForUUmlaut) const;
|
||||||
|
// const std::string HanyuPinyinString(bool includesTone, bool useVForUUmlaut,
|
||||||
|
// bool composeAccentedVowel) const;
|
||||||
|
|
||||||
// PHT = Pai-hua-tsi
|
// PHT = Pai-hua-tsi
|
||||||
static const BopomofoSyllable FromPHT(const string& str);
|
static const BopomofoSyllable FromPHT(const std::string& str);
|
||||||
const string PHTString(bool includesTone) const;
|
const std::string PHTString(bool includesTone) const;
|
||||||
|
|
||||||
static const BopomofoSyllable FromComposedString(const string& str);
|
static const BopomofoSyllable FromComposedString(const std::string& str);
|
||||||
const string composedString() const;
|
const std::string composedString() const;
|
||||||
|
|
||||||
void clear()
|
void clear() { syllable_ = 0; }
|
||||||
{
|
|
||||||
m_syllable = 0;
|
bool isEmpty() const { return !syllable_; }
|
||||||
|
|
||||||
|
bool hasConsonant() const { return !!(syllable_ & ConsonantMask); }
|
||||||
|
|
||||||
|
bool hasMiddleVowel() const { return !!(syllable_ & MiddleVowelMask); }
|
||||||
|
bool hasVowel() const { return !!(syllable_ & VowelMask); }
|
||||||
|
|
||||||
|
bool hasToneMarker() const { return !!(syllable_ & ToneMarkerMask); }
|
||||||
|
|
||||||
|
Component consonantComponent() const { return syllable_ & ConsonantMask; }
|
||||||
|
|
||||||
|
Component middleVowelComponent() const {
|
||||||
|
return syllable_ & MiddleVowelMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEmpty() const
|
Component vowelComponent() const { return syllable_ & VowelMask; }
|
||||||
{
|
|
||||||
return !m_syllable;
|
Component toneMarkerComponent() const { return syllable_ & ToneMarkerMask; }
|
||||||
|
|
||||||
|
bool operator==(const BopomofoSyllable& another) const {
|
||||||
|
return syllable_ == another.syllable_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasConsonant() const
|
bool operator!=(const BopomofoSyllable& another) const {
|
||||||
{
|
return syllable_ != another.syllable_;
|
||||||
return !!(m_syllable & ConsonantMask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasMiddleVowel() const
|
bool isOverlappingWith(const BopomofoSyllable& another) const {
|
||||||
{
|
#define IOW_SAND(mask) ((syllable_ & mask) && (another.syllable_ & mask))
|
||||||
return !!(m_syllable & MiddleVowelMask);
|
return IOW_SAND(ConsonantMask) || IOW_SAND(MiddleVowelMask) ||
|
||||||
}
|
IOW_SAND(VowelMask) || IOW_SAND(ToneMarkerMask);
|
||||||
bool hasVowel() const
|
#undef IOW_SAND
|
||||||
{
|
|
||||||
return !!(m_syllable & VowelMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasToneMarker() const
|
|
||||||
{
|
|
||||||
return !!(m_syllable & ToneMarkerMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
Component consonantComponent() const
|
|
||||||
{
|
|
||||||
return m_syllable & ConsonantMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component middleVowelComponent() const
|
|
||||||
{
|
|
||||||
return m_syllable & MiddleVowelMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component vowelComponent() const
|
|
||||||
{
|
|
||||||
return m_syllable & VowelMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component toneMarkerComponent() const
|
|
||||||
{
|
|
||||||
return m_syllable & ToneMarkerMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const BopomofoSyllable& another) const
|
|
||||||
{
|
|
||||||
return m_syllable == another.m_syllable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const BopomofoSyllable& another) const
|
|
||||||
{
|
|
||||||
return m_syllable != another.m_syllable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOverlappingWith(const BopomofoSyllable& another) const
|
|
||||||
{
|
|
||||||
#define IOW_SAND(mask) ((m_syllable & mask) && (another.m_syllable & mask))
|
|
||||||
return IOW_SAND(ConsonantMask) || IOW_SAND(MiddleVowelMask) || IOW_SAND(VowelMask) || IOW_SAND(ToneMarkerMask);
|
|
||||||
#undef IOW_SAND
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// consonants J, Q, X all require the existence of vowel I or UE
|
// consonants J, Q, X all require the existence of vowel I or UE
|
||||||
bool belongsToJQXClass() const
|
bool belongsToJQXClass() const {
|
||||||
{
|
Component consonant = syllable_ & ConsonantMask;
|
||||||
Component consonant = m_syllable & ConsonantMask;
|
|
||||||
return (consonant == J || consonant == Q || consonant == X);
|
return (consonant == J || consonant == Q || consonant == X);
|
||||||
}
|
}
|
||||||
|
|
||||||
// zi, ci, si, chi, chi, shi, ri
|
// zi, ci, si, chi, chi, shi, ri
|
||||||
bool belongsToZCSRClass() const
|
bool belongsToZCSRClass() const {
|
||||||
{
|
Component consonant = syllable_ & ConsonantMask;
|
||||||
Component consonant = m_syllable & ConsonantMask;
|
|
||||||
return (consonant >= ZH && consonant <= S);
|
return (consonant >= ZH && consonant <= S);
|
||||||
}
|
}
|
||||||
|
|
||||||
Component maskType() const
|
Component maskType() const {
|
||||||
{
|
|
||||||
Component mask = 0;
|
Component mask = 0;
|
||||||
mask |= (m_syllable & ConsonantMask) ? ConsonantMask : 0;
|
mask |= (syllable_ & ConsonantMask) ? ConsonantMask : 0;
|
||||||
mask |= (m_syllable & MiddleVowelMask) ? MiddleVowelMask : 0;
|
mask |= (syllable_ & MiddleVowelMask) ? MiddleVowelMask : 0;
|
||||||
mask |= (m_syllable & VowelMask) ? VowelMask : 0;
|
mask |= (syllable_ & VowelMask) ? VowelMask : 0;
|
||||||
mask |= (m_syllable & ToneMarkerMask) ? ToneMarkerMask : 0;
|
mask |= (syllable_ & ToneMarkerMask) ? ToneMarkerMask : 0;
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BopomofoSyllable operator+(const BopomofoSyllable& another) const
|
const BopomofoSyllable operator+(const BopomofoSyllable& another) const {
|
||||||
{
|
Component newSyllable = syllable_;
|
||||||
Component newSyllable = m_syllable;
|
#define OP_SOVER(mask) \
|
||||||
#define OP_SOVER(mask) if (another.m_syllable & mask) newSyllable = (newSyllable & ~mask) | (another.m_syllable & mask)
|
if (another.syllable_ & mask) { \
|
||||||
|
newSyllable = (newSyllable & ~mask) | (another.syllable_ & mask); \
|
||||||
|
}
|
||||||
OP_SOVER(ConsonantMask);
|
OP_SOVER(ConsonantMask);
|
||||||
OP_SOVER(MiddleVowelMask);
|
OP_SOVER(MiddleVowelMask);
|
||||||
OP_SOVER(VowelMask);
|
OP_SOVER(VowelMask);
|
||||||
OP_SOVER(ToneMarkerMask);
|
OP_SOVER(ToneMarkerMask);
|
||||||
#undef OP_SOVER
|
#undef OP_SOVER
|
||||||
return BopomofoSyllable(newSyllable);
|
return BopomofoSyllable(newSyllable);
|
||||||
}
|
}
|
||||||
|
|
||||||
BopomofoSyllable& operator+=(const BopomofoSyllable& another)
|
BopomofoSyllable& operator+=(const BopomofoSyllable& another) {
|
||||||
{
|
#define OPE_SOVER(mask) \
|
||||||
#define OPE_SOVER(mask) if (another.m_syllable & mask) m_syllable = (m_syllable & ~mask) | (another.m_syllable & mask)
|
if (another.syllable_ & mask) { \
|
||||||
|
syllable_ = (syllable_ & ~mask) | (another.syllable_ & mask); \
|
||||||
|
}
|
||||||
OPE_SOVER(ConsonantMask);
|
OPE_SOVER(ConsonantMask);
|
||||||
OPE_SOVER(MiddleVowelMask);
|
OPE_SOVER(MiddleVowelMask);
|
||||||
OPE_SOVER(VowelMask);
|
OPE_SOVER(VowelMask);
|
||||||
OPE_SOVER(ToneMarkerMask);
|
OPE_SOVER(ToneMarkerMask);
|
||||||
#undef OPE_SOVER
|
#undef OPE_SOVER
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
short absoluteOrder() const
|
uint16_t absoluteOrder() const {
|
||||||
{
|
|
||||||
// turn BPMF syllable into a 4*14*4*22 number
|
// turn BPMF syllable into a 4*14*4*22 number
|
||||||
return (short)(m_syllable & ConsonantMask) +
|
return (uint16_t)(syllable_ & ConsonantMask) +
|
||||||
(short)((m_syllable & MiddleVowelMask) >> 5) * 22 +
|
(uint16_t)((syllable_ & MiddleVowelMask) >> 5) * 22 +
|
||||||
(short)((m_syllable & VowelMask) >> 7) * 22 * 4 +
|
(uint16_t)((syllable_ & VowelMask) >> 7) * 22 * 4 +
|
||||||
(short)((m_syllable & ToneMarkerMask) >> 11) * 22 * 4 * 14;
|
(uint16_t)((syllable_ & ToneMarkerMask) >> 11) * 22 * 4 * 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string absoluteOrderString() const
|
const std::string absoluteOrderString() const {
|
||||||
{
|
|
||||||
// 5*14*4*22 = 6160, we use a 79*79 encoding to represent that
|
// 5*14*4*22 = 6160, we use a 79*79 encoding to represent that
|
||||||
short order = absoluteOrder();
|
uint16_t order = absoluteOrder();
|
||||||
char low = 48 + (char)(order % 79);
|
char low = 48 + static_cast<char>(order % 79);
|
||||||
char high = 48 + (char)(order / 79);
|
char high = 48 + static_cast<char>(order / 79);
|
||||||
string result(2, ' ');
|
std::string result(2, ' ');
|
||||||
result[0] = low;
|
result[0] = low;
|
||||||
result[1] = high;
|
result[1] = high;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BopomofoSyllable FromAbsoluteOrder(short order)
|
static BopomofoSyllable FromAbsoluteOrder(uint16_t order) {
|
||||||
{
|
return BopomofoSyllable((order % 22) | ((order / 22) % 4) << 5 |
|
||||||
return BopomofoSyllable(
|
|
||||||
(order % 22) |
|
|
||||||
((order / 22) % 4) << 5 |
|
|
||||||
((order / (22 * 4)) % 14) << 7 |
|
((order / (22 * 4)) % 14) << 7 |
|
||||||
((order / (22 * 4 * 14)) % 5) << 11
|
((order / (22 * 4 * 14)) % 5) << 11);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BopomofoSyllable FromAbsoluteOrderString(const string& str)
|
static BopomofoSyllable FromAbsoluteOrderString(const std::string& str) {
|
||||||
{
|
if (str.length() != 2) return BopomofoSyllable();
|
||||||
if (str.length() != 2)
|
|
||||||
return BopomofoSyllable();
|
|
||||||
|
|
||||||
return FromAbsoluteOrder((short)(str[1] - 48) * 79 + (short)(str[0] - 48));
|
return FromAbsoluteOrder((uint16_t)(str[1] - 48) * 79 +
|
||||||
|
(uint16_t)(str[0] - 48));
|
||||||
}
|
}
|
||||||
|
|
||||||
friend ostream& operator<<(ostream& stream, const BopomofoSyllable& syllable);
|
friend std::ostream& operator<<(std::ostream& stream,
|
||||||
|
const BopomofoSyllable& syllable);
|
||||||
|
|
||||||
static const Component
|
static constexpr Component
|
||||||
ConsonantMask = 0x001f, // 0000 0000 0001 1111, 21 consonants
|
ConsonantMask = 0x001f, // 0000 0000 0001 1111, 21 consonants
|
||||||
MiddleVowelMask = 0x0060, // 0000 0000 0110 0000, 3 middle vowels
|
MiddleVowelMask = 0x0060, // 0000 0000 0110 0000, 3 middle vowels
|
||||||
VowelMask = 0x0780, // 0000 0111 1000 0000, 13 vowels
|
VowelMask = 0x0780, // 0000 0111 1000 0000, 13 vowels
|
||||||
ToneMarkerMask = 0x3800, // 0011 1000 0000 0000, 5 tones (tone1 = 0x00)
|
ToneMarkerMask = 0x3800, // 0011 1000 0000 0000, 5 tones (tone1 = 0x00)
|
||||||
B = 0x0001, P = 0x0002, M = 0x0003, F = 0x0004,
|
B = 0x0001, P = 0x0002, M = 0x0003, F = 0x0004, D = 0x0005, T = 0x0006,
|
||||||
D = 0x0005, T = 0x0006, N = 0x0007, L = 0x0008,
|
N = 0x0007, L = 0x0008, G = 0x0009, K = 0x000a, H = 0x000b, J = 0x000c,
|
||||||
G = 0x0009, K = 0x000a, H = 0x000b,
|
Q = 0x000d, X = 0x000e, ZH = 0x000f, CH = 0x0010, SH = 0x0011, R = 0x0012,
|
||||||
J = 0x000c, Q = 0x000d, X = 0x000e,
|
Z = 0x0013, C = 0x0014, S = 0x0015, I = 0x0020, U = 0x0040,
|
||||||
ZH = 0x000f, CH = 0x0010, SH = 0x0011, R = 0x0012,
|
UE = 0x0060, // ue = u umlaut (we use the German convention here as an
|
||||||
Z = 0x0013, C = 0x0014, S = 0x0015,
|
// ersatz to the /ju:/ sound)
|
||||||
I = 0x0020, U = 0x0040, UE = 0x0060, // ue = u umlaut (we use the German convention here as an ersatz to the /ju:/ sound)
|
A = 0x0080, O = 0x0100, ER = 0x0180, E = 0x0200, AI = 0x0280, EI = 0x0300,
|
||||||
A = 0x0080, O = 0x0100, ER = 0x0180, E = 0x0200,
|
AO = 0x0380, OU = 0x0400, AN = 0x0480, EN = 0x0500, ANG = 0x0580,
|
||||||
AI = 0x0280, EI = 0x0300, AO = 0x0380, OU = 0x0400,
|
ENG = 0x0600, ERR = 0x0680, Tone1 = 0x0000, Tone2 = 0x0800,
|
||||||
AN = 0x0480, EN = 0x0500, ANG = 0x0580, ENG = 0x0600,
|
Tone3 = 0x1000, Tone4 = 0x1800, Tone5 = 0x2000;
|
||||||
ERR = 0x0680,
|
|
||||||
Tone1 = 0x0000, Tone2 = 0x0800, Tone3 = 0x1000, Tone4 = 0x1800, Tone5 = 0x2000;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Component m_syllable;
|
Component syllable_;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline ostream& operator<<(ostream& stream, const BopomofoSyllable& syllable)
|
inline std::ostream& operator<<(std::ostream& stream,
|
||||||
{
|
const BopomofoSyllable& syllable) {
|
||||||
stream << syllable.composedString();
|
stream << syllable.composedString();
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef BopomofoSyllable BPMF;
|
typedef BopomofoSyllable BPMF;
|
||||||
|
|
||||||
typedef map<char, vector<BPMF::Component> > BopomofoKeyToComponentMap;
|
typedef std::map<char, std::vector<BPMF::Component> > BopomofoKeyToComponentMap;
|
||||||
typedef map<BPMF::Component, char> BopomofoComponentToKeyMap;
|
typedef std::map<BPMF::Component, char> BopomofoComponentToKeyMap;
|
||||||
|
|
||||||
class BopomofoKeyboardLayout {
|
class BopomofoKeyboardLayout {
|
||||||
public:
|
public:
|
||||||
static void FinalizeLayouts();
|
|
||||||
static const BopomofoKeyboardLayout* StandardLayout();
|
static const BopomofoKeyboardLayout* StandardLayout();
|
||||||
static const BopomofoKeyboardLayout* ETenLayout();
|
static const BopomofoKeyboardLayout* ETenLayout();
|
||||||
static const BopomofoKeyboardLayout* HsuLayout();
|
static const BopomofoKeyboardLayout* HsuLayout();
|
||||||
|
@ -251,63 +203,60 @@ namespace Taiyan {
|
||||||
static const BopomofoKeyboardLayout* IBMLayout();
|
static const BopomofoKeyboardLayout* IBMLayout();
|
||||||
static const BopomofoKeyboardLayout* HanyuPinyinLayout();
|
static const BopomofoKeyboardLayout* HanyuPinyinLayout();
|
||||||
|
|
||||||
// recognizes (case-insensitive): standard, eten, hsu, eten26, ibm
|
BopomofoKeyboardLayout(const BopomofoKeyToComponentMap& ktcm,
|
||||||
static const BopomofoKeyboardLayout* LayoutForName(const string& name);
|
const std::string& name)
|
||||||
|
: m_keyToComponent(ktcm), m_name(name) {
|
||||||
BopomofoKeyboardLayout(const BopomofoKeyToComponentMap& ktcm, const string& name)
|
for (BopomofoKeyToComponentMap::const_iterator miter =
|
||||||
: m_keyToComponent(ktcm)
|
m_keyToComponent.begin();
|
||||||
, m_name(name)
|
miter != m_keyToComponent.end(); ++miter)
|
||||||
{
|
for (std::vector<BPMF::Component>::const_iterator viter =
|
||||||
for (BopomofoKeyToComponentMap::const_iterator miter = m_keyToComponent.begin() ; miter != m_keyToComponent.end() ; ++miter)
|
(*miter).second.begin();
|
||||||
for (vector<BPMF::Component>::const_iterator viter = (*miter).second.begin() ; viter != (*miter).second.end() ; ++viter)
|
viter != (*miter).second.end(); ++viter)
|
||||||
m_componentToKey[*viter] = (*miter).first;
|
m_componentToKey[*viter] = (*miter).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string name() const
|
const std::string name() const { return m_name; }
|
||||||
{
|
|
||||||
return m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
char componentToKey(BPMF::Component component) const
|
char componentToKey(BPMF::Component component) const {
|
||||||
{
|
BopomofoComponentToKeyMap::const_iterator iter =
|
||||||
BopomofoComponentToKeyMap::const_iterator iter = m_componentToKey.find(component);
|
m_componentToKey.find(component);
|
||||||
return (iter == m_componentToKey.end()) ? 0 : (*iter).second;
|
return (iter == m_componentToKey.end()) ? 0 : (*iter).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vector<BPMF::Component> keyToComponents(char key) const
|
const std::vector<BPMF::Component> keyToComponents(char key) const {
|
||||||
{
|
|
||||||
BopomofoKeyToComponentMap::const_iterator iter = m_keyToComponent.find(key);
|
BopomofoKeyToComponentMap::const_iterator iter = m_keyToComponent.find(key);
|
||||||
return (iter == m_keyToComponent.end()) ? vector<BPMF::Component>() : (*iter).second;
|
return (iter == m_keyToComponent.end()) ? std::vector<BPMF::Component>()
|
||||||
|
: (*iter).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string keySequenceFromSyllable(BPMF syllable) const
|
const std::string keySequenceFromSyllable(BPMF syllable) const {
|
||||||
{
|
std::string sequence;
|
||||||
string sequence;
|
|
||||||
|
|
||||||
BPMF::Component c;
|
BPMF::Component c;
|
||||||
char k;
|
char k;
|
||||||
#define STKS_COMBINE(component) if ((c = component)) { if ((k = componentToKey(c))) sequence += string(1, k); }
|
#define STKS_COMBINE(component) \
|
||||||
|
if ((c = component)) { \
|
||||||
|
if ((k = componentToKey(c))) sequence += std::string(1, k); \
|
||||||
|
}
|
||||||
STKS_COMBINE(syllable.consonantComponent());
|
STKS_COMBINE(syllable.consonantComponent());
|
||||||
STKS_COMBINE(syllable.middleVowelComponent());
|
STKS_COMBINE(syllable.middleVowelComponent());
|
||||||
STKS_COMBINE(syllable.vowelComponent());
|
STKS_COMBINE(syllable.vowelComponent());
|
||||||
STKS_COMBINE(syllable.toneMarkerComponent());
|
STKS_COMBINE(syllable.toneMarkerComponent());
|
||||||
#undef STKS_COMBINE
|
#undef STKS_COMBINE
|
||||||
return sequence;
|
return sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BPMF syllableFromKeySequence(const string& sequence) const
|
const BPMF syllableFromKeySequence(const std::string& sequence) const {
|
||||||
{
|
|
||||||
BPMF syllable;
|
BPMF syllable;
|
||||||
|
|
||||||
for (string::const_iterator iter = sequence.begin() ; iter != sequence.end() ; ++iter)
|
for (std::string::const_iterator iter = sequence.begin();
|
||||||
{
|
iter != sequence.end(); ++iter) {
|
||||||
bool beforeSeqHasIorUE = sequenceContainsIorUE(sequence.begin(), iter);
|
bool beforeSeqHasIorUE = sequenceContainsIorUE(sequence.begin(), iter);
|
||||||
bool aheadSeqHasIorUE = sequenceContainsIorUE(iter + 1, sequence.end());
|
bool aheadSeqHasIorUE = sequenceContainsIorUE(iter + 1, sequence.end());
|
||||||
|
|
||||||
vector<BPMF::Component> components = keyToComponents(*iter);
|
std::vector<BPMF::Component> components = keyToComponents(*iter);
|
||||||
|
|
||||||
if (!components.size())
|
if (!components.size()) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (components.size() == 1) {
|
if (components.size() == 1) {
|
||||||
syllable += BPMF(components[0]);
|
syllable += BPMF(components[0]);
|
||||||
|
@ -319,25 +268,24 @@ namespace Taiyan {
|
||||||
BPMF ending = components.size() > 2 ? BPMF(components[2]) : follow;
|
BPMF ending = components.size() > 2 ? BPMF(components[2]) : follow;
|
||||||
|
|
||||||
// apply the I/UE + E rule
|
// apply the I/UE + E rule
|
||||||
if (head.vowelComponent() == BPMF::E && follow.vowelComponent() != BPMF::E)
|
if (head.vowelComponent() == BPMF::E &&
|
||||||
{
|
follow.vowelComponent() != BPMF::E) {
|
||||||
syllable += beforeSeqHasIorUE ? head : follow;
|
syllable += beforeSeqHasIorUE ? head : follow;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (head.vowelComponent() != BPMF::E && follow.vowelComponent() == BPMF::E)
|
if (head.vowelComponent() != BPMF::E &&
|
||||||
{
|
follow.vowelComponent() == BPMF::E) {
|
||||||
syllable += beforeSeqHasIorUE ? follow : head;
|
syllable += beforeSeqHasIorUE ? follow : head;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply the J/Q/X + I/UE rule, only two components are allowed in the components vector here
|
// apply the J/Q/X + I/UE rule, only two components are allowed in the
|
||||||
|
// components vector here
|
||||||
if (head.belongsToJQXClass() && !follow.belongsToJQXClass()) {
|
if (head.belongsToJQXClass() && !follow.belongsToJQXClass()) {
|
||||||
if (!syllable.isEmpty()) {
|
if (!syllable.isEmpty()) {
|
||||||
if (ending != follow)
|
if (ending != follow) syllable += ending;
|
||||||
syllable += ending;
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
syllable += aheadSeqHasIorUE ? head : follow;
|
syllable += aheadSeqHasIorUE ? head : follow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,10 +294,8 @@ namespace Taiyan {
|
||||||
|
|
||||||
if (!head.belongsToJQXClass() && follow.belongsToJQXClass()) {
|
if (!head.belongsToJQXClass() && follow.belongsToJQXClass()) {
|
||||||
if (!syllable.isEmpty()) {
|
if (!syllable.isEmpty()) {
|
||||||
if (ending != follow)
|
if (ending != follow) syllable += ending;
|
||||||
syllable += ending;
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
syllable += aheadSeqHasIorUE ? follow : head;
|
syllable += aheadSeqHasIorUE ? follow : head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,30 +304,30 @@ namespace Taiyan {
|
||||||
|
|
||||||
// the nasty issue of only one char in the buffer
|
// the nasty issue of only one char in the buffer
|
||||||
if (iter == sequence.begin() && iter + 1 == sequence.end()) {
|
if (iter == sequence.begin() && iter + 1 == sequence.end()) {
|
||||||
if (head.hasVowel() || follow.hasToneMarker() || head.belongsToZCSRClass())
|
if (head.hasVowel() || follow.hasToneMarker() ||
|
||||||
|
head.belongsToZCSRClass()) {
|
||||||
syllable += head;
|
syllable += head;
|
||||||
else {
|
} else {
|
||||||
if (follow.hasVowel() || ending.hasToneMarker())
|
if (follow.hasVowel() || ending.hasToneMarker()) {
|
||||||
syllable += follow;
|
syllable += follow;
|
||||||
else
|
} else {
|
||||||
syllable += ending;
|
syllable += ending;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(syllable.maskType() & head.maskType()) && !endAheadOrAheadHasToneMarkKey(iter + 1, sequence.end())) {
|
if (!(syllable.maskType() & head.maskType()) &&
|
||||||
|
!endAheadOrAheadHasToneMarkKey(iter + 1, sequence.end())) {
|
||||||
syllable += head;
|
syllable += head;
|
||||||
}
|
} else {
|
||||||
else {
|
if (endAheadOrAheadHasToneMarkKey(iter + 1, sequence.end()) &&
|
||||||
if (endAheadOrAheadHasToneMarkKey(iter + 1, sequence.end()) && head.belongsToZCSRClass() && syllable.isEmpty()) {
|
head.belongsToZCSRClass() && syllable.isEmpty()) {
|
||||||
syllable += head;
|
syllable += head;
|
||||||
}
|
} else if (syllable.maskType() < follow.maskType()) {
|
||||||
else if (syllable.maskType() < follow.maskType()) {
|
|
||||||
syllable += follow;
|
syllable += follow;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
syllable += ending;
|
syllable += ending;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -390,24 +336,23 @@ namespace Taiyan {
|
||||||
// heuristics for Hsu keyboard layout
|
// heuristics for Hsu keyboard layout
|
||||||
if (this == HsuLayout()) {
|
if (this == HsuLayout()) {
|
||||||
// fix the left out L to ERR when it has sound, and GI, GUE -> JI, JUE
|
// fix the left out L to ERR when it has sound, and GI, GUE -> JI, JUE
|
||||||
if (syllable.vowelComponent() == BPMF::ENG && !syllable.hasConsonant() && !syllable.hasMiddleVowel()) {
|
if (syllable.vowelComponent() == BPMF::ENG && !syllable.hasConsonant() &&
|
||||||
|
!syllable.hasMiddleVowel()) {
|
||||||
syllable += BPMF(BPMF::ERR);
|
syllable += BPMF(BPMF::ERR);
|
||||||
}
|
} else if (syllable.consonantComponent() == BPMF::G &&
|
||||||
else if (syllable.consonantComponent() == BPMF::G && (syllable.middleVowelComponent() == BPMF::I || syllable.middleVowelComponent() == BPMF::UE)) {
|
(syllable.middleVowelComponent() == BPMF::I ||
|
||||||
|
syllable.middleVowelComponent() == BPMF::UE)) {
|
||||||
syllable += BPMF(BPMF::J);
|
syllable += BPMF(BPMF::J);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return syllable;
|
return syllable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
protected:
|
bool endAheadOrAheadHasToneMarkKey(std::string::const_iterator ahead,
|
||||||
bool endAheadOrAheadHasToneMarkKey(string::const_iterator ahead, string::const_iterator end) const
|
std::string::const_iterator end) const {
|
||||||
{
|
if (ahead == end) return true;
|
||||||
if (ahead == end)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
char tone1 = componentToKey(BPMF::Tone1);
|
char tone1 = componentToKey(BPMF::Tone1);
|
||||||
char tone2 = componentToKey(BPMF::Tone2);
|
char tone2 = componentToKey(BPMF::Tone2);
|
||||||
|
@ -418,70 +363,57 @@ namespace Taiyan {
|
||||||
if (tone1)
|
if (tone1)
|
||||||
if (*ahead == tone1) return true;
|
if (*ahead == tone1) return true;
|
||||||
|
|
||||||
if (*ahead == tone2 || *ahead == tone3 || *ahead == tone4 || *ahead == tone5)
|
if (*ahead == tone2 || *ahead == tone3 || *ahead == tone4 ||
|
||||||
|
*ahead == tone5)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sequenceContainsIorUE(string::const_iterator start, string::const_iterator end) const
|
bool sequenceContainsIorUE(std::string::const_iterator start,
|
||||||
{
|
std::string::const_iterator end) const {
|
||||||
char iChar = componentToKey(BPMF::I);
|
char iChar = componentToKey(BPMF::I);
|
||||||
char ueChar = componentToKey(BPMF::UE);
|
char ueChar = componentToKey(BPMF::UE);
|
||||||
|
|
||||||
for (; start != end; ++start)
|
for (; start != end; ++start)
|
||||||
if (*start == iChar || *start == ueChar)
|
if (*start == iChar || *start == ueChar) return true;
|
||||||
return true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string m_name;
|
std::string m_name;
|
||||||
BopomofoKeyToComponentMap m_keyToComponent;
|
BopomofoKeyToComponentMap m_keyToComponent;
|
||||||
BopomofoComponentToKeyMap m_componentToKey;
|
BopomofoComponentToKeyMap m_componentToKey;
|
||||||
|
};
|
||||||
|
|
||||||
static const BopomofoKeyboardLayout* c_StandardLayout;
|
class BopomofoReadingBuffer {
|
||||||
static const BopomofoKeyboardLayout* c_ETenLayout;
|
public:
|
||||||
static const BopomofoKeyboardLayout* c_HsuLayout;
|
explicit BopomofoReadingBuffer(const BopomofoKeyboardLayout* layout)
|
||||||
static const BopomofoKeyboardLayout* c_ETen26Layout;
|
: layout_(layout), pinyin_mode_(false) {
|
||||||
static const BopomofoKeyboardLayout* c_IBMLayout;
|
|
||||||
|
|
||||||
// this is essentially an empty layout, but we use pointer semantic to tell the differences--and pass on the responsibility to BopomofoReadingBuffer
|
|
||||||
static const BopomofoKeyboardLayout* c_HanyuPinyinLayout;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BopomofoReadingBuffer {
|
|
||||||
public:
|
|
||||||
BopomofoReadingBuffer(const BopomofoKeyboardLayout* layout)
|
|
||||||
: m_layout(layout)
|
|
||||||
, m_pinyinMode(false)
|
|
||||||
{
|
|
||||||
if (layout == BopomofoKeyboardLayout::HanyuPinyinLayout()) {
|
if (layout == BopomofoKeyboardLayout::HanyuPinyinLayout()) {
|
||||||
m_pinyinMode = true;
|
pinyin_mode_ = true;
|
||||||
m_pinyinSequence = "";
|
pinyin_sequence_ = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setKeyboardLayout(const BopomofoKeyboardLayout* layout)
|
void setKeyboardLayout(const BopomofoKeyboardLayout* layout) {
|
||||||
{
|
layout_ = layout;
|
||||||
m_layout = layout;
|
|
||||||
|
|
||||||
if (layout == BopomofoKeyboardLayout::HanyuPinyinLayout()) {
|
if (layout == BopomofoKeyboardLayout::HanyuPinyinLayout()) {
|
||||||
m_pinyinMode = true;
|
pinyin_mode_ = true;
|
||||||
m_pinyinSequence = "";
|
pinyin_sequence_ = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValidKey(char k) const
|
bool isValidKey(char k) const {
|
||||||
{
|
if (!pinyin_mode_) {
|
||||||
if (!m_pinyinMode) {
|
return layout_ ? (layout_->keyToComponents(k)).size() > 0 : false;
|
||||||
return m_layout ? (m_layout->keyToComponents(k)).size() > 0 : false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char lk = tolower(k);
|
char lk = tolower(k);
|
||||||
if (lk >= 'a' && lk <= 'z') {
|
if (lk >= 'a' && lk <= 'z') {
|
||||||
// if a tone marker is already in place
|
// if a tone marker is already in place
|
||||||
if (m_pinyinSequence.length()) {
|
if (pinyin_sequence_.length()) {
|
||||||
char lastc = m_pinyinSequence[m_pinyinSequence.length() - 1];
|
char lastc = pinyin_sequence_[pinyin_sequence_.length() - 1];
|
||||||
if (lastc >= '2' && lastc <= '5') {
|
if (lastc >= '2' && lastc <= '5') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -490,98 +422,83 @@ namespace Taiyan {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pinyinSequence.length() && (lk >= '2' && lk <= '5')) {
|
if (pinyin_sequence_.length() && (lk >= '2' && lk <= '5')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool combineKey(char k)
|
bool combineKey(char k) {
|
||||||
{
|
if (!isValidKey(k)) return false;
|
||||||
if (!isValidKey(k))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_pinyinMode) {
|
if (pinyin_mode_) {
|
||||||
m_pinyinSequence += string(1, tolower(k));
|
pinyin_sequence_ += std::string(1, tolower(k));
|
||||||
m_syllable = BPMF::FromHanyuPinyin(m_pinyinSequence);
|
syllable_ = BPMF::FromHanyuPinyin(pinyin_sequence_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string sequence = m_layout->keySequenceFromSyllable(m_syllable) + string(1, k);
|
std::string sequence =
|
||||||
m_syllable = m_layout->syllableFromKeySequence(sequence);
|
layout_->keySequenceFromSyllable(syllable_) + std::string(1, k);
|
||||||
|
syllable_ = layout_->syllableFromKeySequence(sequence);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear()
|
void clear() {
|
||||||
{
|
pinyin_sequence_.clear();
|
||||||
m_pinyinSequence.clear();
|
syllable_.clear();
|
||||||
m_syllable.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void backspace()
|
void backspace() {
|
||||||
{
|
if (!layout_) return;
|
||||||
if (!m_layout)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_pinyinMode) {
|
if (pinyin_mode_) {
|
||||||
if (m_pinyinSequence.length()) {
|
if (pinyin_sequence_.length()) {
|
||||||
m_pinyinSequence = m_pinyinSequence.substr(0, m_pinyinSequence.length() - 1);
|
pinyin_sequence_ =
|
||||||
|
pinyin_sequence_.substr(0, pinyin_sequence_.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_syllable = BPMF::FromHanyuPinyin(m_pinyinSequence);
|
syllable_ = BPMF::FromHanyuPinyin(pinyin_sequence_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string sequence = m_layout->keySequenceFromSyllable(m_syllable);
|
std::string sequence = layout_->keySequenceFromSyllable(syllable_);
|
||||||
if (sequence.length()) {
|
if (sequence.length()) {
|
||||||
sequence = sequence.substr(0, sequence.length() - 1);
|
sequence = sequence.substr(0, sequence.length() - 1);
|
||||||
m_syllable = m_layout->syllableFromKeySequence(sequence);
|
syllable_ = layout_->syllableFromKeySequence(sequence);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEmpty() const
|
bool isEmpty() const { return syllable_.isEmpty(); }
|
||||||
{
|
|
||||||
return m_syllable.isEmpty();
|
const std::string composedString() const {
|
||||||
|
if (pinyin_mode_) {
|
||||||
|
return pinyin_sequence_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string composedString() const
|
return syllable_.composedString();
|
||||||
{
|
|
||||||
if (m_pinyinMode) {
|
|
||||||
return m_pinyinSequence;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_syllable.composedString();
|
const BPMF syllable() const { return syllable_; }
|
||||||
|
|
||||||
|
const std::string standardLayoutQueryString() const {
|
||||||
|
return BopomofoKeyboardLayout::StandardLayout()->keySequenceFromSyllable(syllable_);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BPMF syllable() const
|
const std::string absoluteOrderQueryString() const {
|
||||||
{
|
return syllable_.absoluteOrderString();
|
||||||
return m_syllable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const string standardLayoutQueryString() const
|
bool hasToneMarker() const { return syllable_.hasToneMarker(); }
|
||||||
{
|
|
||||||
return BopomofoKeyboardLayout::StandardLayout()->keySequenceFromSyllable(m_syllable);
|
|
||||||
}
|
|
||||||
|
|
||||||
const string absoluteOrderQueryString() const
|
protected:
|
||||||
{
|
const BopomofoKeyboardLayout* layout_;
|
||||||
return m_syllable.absoluteOrderString();
|
BPMF syllable_;
|
||||||
}
|
|
||||||
|
|
||||||
bool hasToneMarker() const
|
bool pinyin_mode_;
|
||||||
{
|
std::string pinyin_sequence_;
|
||||||
return m_syllable.hasToneMarker();
|
};
|
||||||
}
|
} // namespace Mandarin
|
||||||
|
} // namespace Taiyan
|
||||||
|
|
||||||
protected:
|
#endif // MANDARIN_H_
|
||||||
const BopomofoKeyboardLayout* m_layout;
|
|
||||||
BPMF m_syllable;
|
|
||||||
|
|
||||||
bool m_pinyinMode;
|
|
||||||
string m_pinyinSequence;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue