{********************************************************************} { } { Developer Express Visual Component Library } { ExpressSpellChecker } { } { Copyright (c) 1998-2009 Developer Express Inc. } { ALL RIGHTS RESERVED } { } { The entire contents of this file is protected by U.S. and } { International Copyright Laws. Unauthorized reproduction, } { reverse-engineering, and distribution of all or any portion of } { the code contained in this file is strictly prohibited and may } { result in severe civil and criminal penalties and will be } { prosecuted to the maximum extent possible under the law. } { } { RESTRICTIONS } { } { THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES } { (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE } { SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS } { LICENSED TO DISTRIBUTE THE EXPRESSSPELLCHECKER AND ALL } { ACCOMPANYING VCL CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY. } { } { THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED } { FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE } { COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE } { AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT } { AND PERMISSION FROM DEVELOPER EXPRESS INC. } { } { CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON } { ADDITIONAL RESTRICTIONS. } { } {********************************************************************} unit dxHunspellAffixes; {$I cxVer.inc} interface uses StrUtils, Math, SysUtils, Contnrs, dxHunspellUtils, dxHunspellWords, dxHunspellTypes; const AffixTableSize = 256; MaxLineLength = 8192; DefaultCompoundPartLength = 3; ExistentSuffixFlag = 1 shl 0; ExistentPrefixFlag = 1 shl 1; aoPrefixSuffixUnion = 1 shl 0; aeUTF8 = 1 shl 1; aoAffixFlagAliasTable = 1 shl 2; aoAffixMorphologyTable = 1 shl 3; aeLONGCOND = 1 shl 4; HashSize = 256; MaxFlagCount = 65536; MaxConditionLength = 20; type TdxExistentAffixFlags = array[0..MaxFlagCount - 1] of Byte; TdxStringData = record Data: PAnsiChar; Length: Integer; end; TdxReplaceTableItem = packed record Initial: PAnsiChar; Replacement: PAnsiChar; end; PdxReplaceArray = ^TdxReplaceArray; TdxReplaceArray = array[0..0] of TdxReplaceTableItem; TdxMapTableItem = packed record CharacterSet: PAnsiChar; Length: Integer; end; PdxMapArray = ^TdxMapArray; TdxMapArray = array[0..0] of TdxMapTableItem; TdxCompoundForbidPattern = record Pattern: PAnsiChar; Pattern2: PAnsiChar; Pattern3: PAnsiChar; Condition: Word; Condition2: Word; end; PdxCompoundForbidPatternArray = ^TdxCompoundForbidPatternArray; TdxCompoundForbidPatternArray = array[0..0] of TdxCompoundForbidPattern; TdxHunspellAffixManager = class; TdxAffix = class; TdxAffixType = (atNone, atPrefix, atSuffix); TdxCompoundWordPart = (cwpNone, cwpFirst, cwpLast, cwpOther); TAffixTable = array [0..AffixTableSize - 1] of TdxAffix; TWordFormArray = array [0..MAXWORDUTF8LEN + 4 - 1] of AnsiChar; TAffixManagerFlagArray = array[0..MaxFlagCount - 1] of Boolean; PdxAffixItem = ^TdxAffixItem; TdxAffixItem = packed record FStripString: PAnsiChar; FAppendString: PAnsiChar; FStripStringLength: Byte; FAppendStringLength: Byte; FConditionLength: Shortint; FCompatibleFlags: TdxHunspellFlags; FMorphologicalDescription: PAnsiChar; LongCondition: Boolean; case Integer of 1: (Condition: array [0..MaxConditionLength - 1] of AnsiChar); 2: (ConditionFirstPart: array [0..MaxConditionLength - 1] of AnsiChar; ConditionSecondPart: PAnsiChar;) end; PdxAffixItemArray = ^TdxAffixItemArray; TdxAffixItemArray = array[0..0] of TdxAffixItem; PdxCompoundFlag = ^TdxCompoundFlag; TdxCompoundFlag = record Pattern: PdxAffixFlagsData; Count: Integer; Length: Integer; end; PdxCompoundFlagArray = ^TdxCompoundFlagArray; TdxCompoundFlagArray = array[0..0] of TdxCompoundFlag; PdxAffixCondition = ^TdxAffixCondition; TdxAffixCondition = record case Integer of 1: (Condition: array [0..MaxConditionLength - 1] of AnsiChar); 2: (ConditionFirstPart: array [0..MaxConditionLength - 1] of AnsiChar; ConditionSecondPart: PAnsiChar); end; TdxSpellCheckerDataTable = class private FCurrentDataIndex: Integer; FDataIdentifier: PAnsiChar; FSize: Integer; procedure FreeData; protected FData: Pointer; function AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; virtual; function IsAllPiecesFound(APieceIndex: Integer): Boolean; virtual; function CheckDataType(const APiece: PAnsiChar): Boolean; procedure FreeDataItems; virtual; abstract; function GetDataIdentifier: PAnsiChar; virtual; abstract; function GetDataItemSize: Integer; virtual; abstract; function GetPieceQuantity: Integer; virtual; abstract; procedure InitializeItem(Index: Integer); virtual; function ReadDataHeader(ALine: PAnsiChar): Boolean; function ParseHeader(ALine: PAnsiChar; out ATableSize: Integer): Boolean; virtual; function ParseData(AAffixFileManager: TdxHunspellReader): Boolean; public constructor Create; overload; constructor Create(ADataIdentifier: PAnsiChar); overload; destructor Destroy; override; function AllocateData(ASize: Integer): Boolean; function ReadData(ALine: PAnsiChar; AAffixFileManager: TdxHunspellReader): Boolean; property Count: Integer read FSize; end; TdxSpellCheckerDataLinkedTable = class(TdxSpellCheckerDataTable) private FWordBaseManager: TdxHunspellWordBaseManager; FAffixManager: TdxHunspellAffixManager; public constructor Create(AWordBaseManager: TdxHunspellWordBaseManager; AAffixManager: TdxHunspellAffixManager); property AffixManager: TdxHunspellAffixManager read FAffixManager; property WordBaseManager: TdxHunspellWordBaseManager read FWordBaseManager; end; TdxAffixItemTable = class(TdxSpellCheckerDataLinkedTable) private FAffixesIsCreated: Boolean; FDecodedFlag: Word; FExistentAffixFlags: TdxExistentAffixFlags; FFlag: PAnsiChar; FPrefixSuffixUnion: Boolean; procedure AddLineRemainderToMorphologicDescription(ALine: PAnsiChar; ADataIndex: Integer); function CheckFlag(ALine: PAnsiChar): Boolean; function ConditionLength(ALine: PAnsiChar): Integer; function EncodeAffixCondition(ALine: PAnsiChar; ADataIndex: Integer): Boolean; function HasAffixFlagAliases: Boolean; function HasAffixMorphologicAliases: Boolean; function FlagIsNil: Boolean; function FlagIsUnique(AFlag: Word): Boolean; procedure FreeIfZero(var AString: TdxStringData); function GetData: PdxAffixItemArray; function GetItem(Index: Integer): TdxAffixItem; function GetOptions(AItem: TdxAffixItem): ShortInt; function IsReverseWritingDirection: Boolean; procedure ParseAppendString(const ALine: PAnsiChar; ADataIndex: Integer); function ParseAppendStringWithAffixes(const ALine: PAnsiChar; ADataIndex: Integer): Boolean; function ParseCompatibleFlags(const ALine: PAnsiChar; ADataIndex: Integer): Boolean; function ParseCondition(ALine: PAnsiChar; ADataIndex: Integer): Boolean; function ParseFlag(ALine: PAnsiChar): Boolean; function ParseMorphologicDescription(ALine: PAnsiChar; ADataIndex: Integer): Boolean; procedure ParsePrefixSuffixUnion(ALine: PAnsiChar); procedure ParseStripString(const ALine: PAnsiChar; ADataIndex: Integer); function ParseTableSize(ALine: PAnsiChar; out ATableSize: Integer): Boolean; procedure ProcessIgnoreChars(ALine: PAnsiChar); procedure ProcessReverseString(ALine: PAnsiChar); procedure RegisterCompatibleFlags(ADataIndex: Integer); procedure ReverseCondition(ACondition: PAnsiChar); procedure SetExisting; protected function AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; override; function IsAllPiecesFound(APieceIndex: Integer): Boolean; override; function IsComplexPrefixes: Boolean; procedure BuildAffixes; virtual; function ConditionContainsDuplicateInformation(strip: PAnsiChar; stripl: Integer; const ACondition: PAnsiChar; out Contains: Boolean): Boolean; virtual; abstract; function GetDataIdentifier: PAnsiChar; override; function GetDataItemSize: Integer; override; function GetPieceQuantity: Integer; override; procedure FreeDataItems; override; function IsPrefix: Boolean; virtual; abstract; function ParseHeader(ALine: PAnsiChar; out ATableSize: Integer): Boolean; override; property Data: PdxAffixItemArray read GetData; property Items[Index: Integer]: TdxAffixItem read GetItem; default; public constructor Create(AWordBaseManager: TdxHunspellWordBaseManager; AAffixManager: TdxHunspellAffixManager; AExistentAffixFlags: TdxExistentAffixFlags); destructor Destroy; override; property Flag: PAnsiChar read FFlag; end; TdxPrefixItemTable = class(TdxAffixItemTable) protected function ConditionContainsDuplicateInformation(strip: PAnsiChar; stripl: Integer; const ACondition: PAnsiChar; out Contains: Boolean): Boolean; override; function IsPrefix: Boolean; override; public procedure BuildAffixes; override; end; TdxSuffixItemTable = class(TdxAffixItemTable) protected function ConditionContainsDuplicateInformation(strip: PAnsiChar; stripl: Integer; const ACondition: PAnsiChar; out Contains: Boolean): Boolean; override; function IsPrefix: Boolean; override; public procedure BuildAffixes; override; end; TdxBreakTable = class(TdxSpellCheckerDataTable) private procedure FillDefaultData; function GetData: PdxPAnsiCharArray; function GetItem(Index: Integer): PAnsiChar; protected function AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; override; procedure FreeDataItems; override; function GetDataIdentifier: PAnsiChar; override; function GetDataItemSize: Integer; override; function GetPieceQuantity: Integer; override; public constructor Create; property Data: PdxPAnsiCharArray read GetData; property Items[Index: Integer]: PAnsiChar read GetItem; default; end; TdxCheckCompoundPatternTable = class(TdxSpellCheckerDataLinkedTable) private FIsSimplified: Boolean; procedure ExtractCondition(APattern: PAnsiChar; var ACondition: Word); function GetData: PdxCompoundForbidPatternArray; function GetItem(Index: Integer): TdxCompoundForbidPattern; protected function AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; override; procedure FreeDataItems; override; function GetDataIdentifier: PAnsiChar; override; function GetDataItemSize: Integer; override; function GetPieceQuantity: Integer; override; procedure InitializeItem(Index: Integer); override; public property Data: PdxCompoundForbidPatternArray read GetData; property IsSimplified: Boolean read FIsSimplified; property Items[Index: Integer]: TdxCompoundForbidPattern read GetItem; default; end; TdxCompoundRuleTable = class(TdxSpellCheckerDataLinkedTable) private function GetData: PdxCompoundFlagArray; function GetItem(Index: Integer): TdxCompoundFlag; function ParseFlags(APiece: PAnsiChar; AIndex: Integer): Boolean; protected function AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; override; procedure FreeDataItems; override; function GetDataIdentifier: PAnsiChar; override; function GetDataItemSize: Integer; override; function GetPieceQuantity: Integer; override; procedure InitializeItem(Index: Integer); override; public property Data: PdxCompoundFlagArray read GetData; property Items[Index: Integer]: TdxCompoundFlag read GetItem; default; end; TdxMapTable = class(TdxSpellCheckerDataTable) private function GetData: PdxMapArray; function GetItem(Index: Integer): TdxMapTableItem; protected function AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; override; procedure FreeDataItems; override; function GetDataIdentifier: PAnsiChar; override; function GetDataItemSize: Integer; override; function GetPieceQuantity: Integer; override; procedure InitializeItem(Index: Integer); override; public property Data: PdxMapArray read GetData; property Items[Index: Integer]: TdxMapTableItem read GetItem; default; end; TdxReplaceTable = class(TdxSpellCheckerDataTable) private function GetData: PdxReplaceArray; function GetItem(Index: Integer): TdxReplaceTableItem; protected function AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; override; procedure FreeDataItems; override; function GetDataIdentifier: PAnsiChar; override; function GetDataItemSize: Integer; override; function GetPieceQuantity: Integer; override; procedure InitializeItem(Index: Integer); override; public property Data: PdxReplaceArray read GetData; property Items[Index: Integer]: TdxReplaceTableItem read GetItem; default; end; TdxSortedReplaceTable = class(TdxReplaceTable) private function near1(const Aword: PAnsiChar): Integer; function match(const Aword: PAnsiChar; n: Integer): Integer; procedure Sort(AIndex: Integer); protected function AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; override; public function conv(const Aword: PAnsiChar; dest: PAnsiChar): Integer; end; TdxAffix = class protected FAffixManager: TdxHunspellAffixManager; FAppendString: PAnsiChar; FStripString: PAnsiChar; FAppendStringLength: Byte; FStripStringLength: Byte; FConditionLength: ShortInt; FOptions: ShortInt; FFlag: Word; FMorphologicalDescription: PAnsiChar; FCompatibleFlags: TdxHunspellFlags; FConditions: PdxAffixCondition; FNext: TdxAffix; FNextSimilar: TdxAffix; FNextDifferent: TdxAffix; FNextByFlag: TdxAffix; function GetAppendString: PAnsiChar; virtual; function GetPrefixSuffixUnion: Boolean; protected function CanUseInCompoundWordPart(ACompoundWordPart: TdxCompoundWordPart): Boolean; virtual; abstract; function GetNextConditionChar(AConditionCursor: PAnsiChar): PAnsiChar; function IsCompatibleWithFlag(AFlag: Word; CompatibleIfNull: Boolean = False): Boolean; function IsPrefixSuffixUnion(AOptions: ShortInt): Boolean; function IsWithoutAffixWordLengthCorrect(ALengthWithoutAffix: Integer): Boolean; procedure SearchConditionGroupEnd(var AConditionCursor: PAnsiChar); public constructor Create(AAffixManager: TdxHunspellAffixManager; AAffixItem: PdxAffixItem; AFlag: Word; AOptions: ShortInt); destructor Destroy; override; function CanUseInSimpleWordsOrIsFogemorpheme(ACompoundWordPart: TdxCompoundWordPart): Boolean; function CanUseInTheMiddleOfCompoundWords(ACompoundWordPart: TdxCompoundWordPart): Boolean; function IsCircumfix: Boolean; property AffixManager: TdxHunspellAffixManager read FAffixManager; property AppendString: PAnsiChar read GetAppendString; property AppendStringLength: Byte read FAppendStringLength write FAppendStringLength; property CompatibleFlags: TdxHunspellFlags read FCompatibleFlags write FCompatibleFlags; property Flag: Word read FFlag; property MorphologicalDescription: PAnsiChar read FMorphologicalDescription; property Options: ShortInt read FOptions; property PrefixSuffixUnion: Boolean read GetPrefixSuffixUnion; property Next: TdxAffix read FNext write FNext; property NextSimilar: TdxAffix read FNextSimilar write FNextSimilar; property NextDifferent: TdxAffix read FNextDifferent write FNextDifferent; property NextByFlag: TdxAffix read FNextByFlag write FNextByFlag; end; TdxSuffix = class; TdxPrefix = class(TdxAffix) protected function CanUseInCompoundWordPart(ACompoundWordPart: TdxCompoundWordPart): Boolean; override; public function CheckWord(const AWord: PAnsiChar; AWordLength: Integer; var ATakenSuffix: TdxSuffix; ACompoundWordPart: TdxCompoundWordPart; const ACompoundPartFlag: Word = NullFlag): TdxHunspellWordItem; function MakeInitialWordFormAndProcessTwoSuffixCheck(const AWord: PAnsiChar; AWordLength: Integer; var ATakenSuffix: TdxSuffix; ACompoundWordPart: TdxCompoundWordPart; const ACompoundPartFlag: Word = NullFlag): TdxHunspellWordItem; function IsWordSuitableToCondition(AWord: PAnsiChar): Boolean; end; TdxSuffix = class(TdxAffix) private FReverseAppendString: PAnsiChar; l_morph: TdxSuffix; //TODO: Remove r_morph: TdxSuffix; eq_morph: TdxSuffix; protected function CanUseInCompoundWordPart(ACompoundWordPart: TdxCompoundWordPart): Boolean; override; function GetAppendString: PAnsiChar; override; public constructor Create(AAffixManager: TdxHunspellAffixManager; AAffixItem: PdxAffixItem; AFlag: Word; AOptions: ShortInt); destructor Destroy; override; function CheckWord(const AWord: PAnsiChar; AWordLength, AOptions: Integer; APrefix: TdxPrefix; wlst: PPAnsiChar; maxSug: Integer; ns: PInteger; const ACompatibleFlag: Word = NullFlag; const ACompoundPartFlag: Word = NullFlag; const AIncompatibleFlag: Word = NullFlag): TdxHunspellWordItem; function MakeInitialWordFormAndProcessSuffixCheck(const AWord: PAnsiChar; AWordLength, AOptions: Integer; APrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; const ACompoundPartFlag: Word = 0): TdxHunspellWordItem; function GetWordWithAffix(const AWord: PAnsiChar; AWordLength: Integer): PAnsiChar; function getLM: TdxSuffix; function getRM: TdxSuffix; function getEQM: TdxSuffix; function IsWordSuitableToCondition(AWordEnd, AWordBegin: PAnsiChar): Boolean; end; TdxSuffixInfo = record Flag: Word; AppendString: PAnsiChar; end; TdxHunspellAffixManager = class private cpdvowels_utf16: PWideChar; cpdvowels_utf16_len: Integer; FAffixFilePath: string; FBreakTable: TdxBreakTable; FCheckCompoundCase: Boolean; FCheckCompoundDup: Boolean; FCheckCompoundPatternTable: TdxCheckCompoundPatternTable; FCheckCompoundRep: Boolean; FCheckCompoundTriple: Boolean; FCheckSharps: Integer; FCircumfix: Word; FCodePage: Cardinal; FCompatibleFlags: TAffixManagerFlagArray; FCompatibleFlagsExist: Boolean; FComplexPrefixes: Boolean; FCompoundBegin: Word; FCompoundEnd: Word; FCompoundFlag: Word; FCompoundForbidFlag: Word; FCompoundMiddle: Word; FCompoundPartMin: Integer; FCompoundPermitFlag: Word; FCompoundRoot: Word; FCompoundRuleTable: TdxCompoundRuleTable; FCompoundVowels: PAnsiChar; FCompoundWordMax: Integer; FExistentAffixFlags: TdxExistentAffixFlags; FForbiddenWord: Word; FFullStrip: Boolean; FIgnore: PAnsiChar; FInputReplaceTable: TdxSortedReplaceTable; FIsExistentAffixFlagsInitialized: Boolean; FIsUTF8: Boolean; FKeepCase: Word; FLanguage: Integer; FLemmaPresent: Word; FMapTable: TdxMapTable; FMaxCompoundSyllable: Integer; FMaxngramsugs: Integer; FNeedAffix: Word; FNoSplitSugs: Boolean; FNoSuggest: Word; FOnlyInCompound: Word; FOutputReplaceTable: TdxSortedReplaceTable; FPrefixTableArrangedByAppendString: TAffixTable; FPrefixTableIndexedByFlag: TAffixTable; FReplaceTable: TdxReplaceTable; FSavedSuffixInfo: TdxSuffixInfo; FSimplifiedTriple: Boolean; FSubstandard: Word; FSuffixTableArrangedByAppendString: TAffixTable; FSuffixTableIndexedByFlag: TAffixTable; FSugsWithDots: Boolean; FSyllableNum: PAnsiChar; FTryChars: PAnsiChar; FVersion: PAnsiChar; FWordBaseManager: TdxHunspellWordBaseManager; ignorechars_utf16: PWord; ignorechars_utf16_len: Integer; FIsHungarian: Boolean; wordchars_utf16: PWord; wordchars_utf16_len: Integer; function DoAffixParsing(ALine: PAnsiChar; AAffixFileManager: TdxHunspellReader; AAffixType: TdxAffixType): Boolean; function ParseFile(const AFileName: string): Integer; function ParseCompoundSyllable(ALine: PAnsiChar; AAffixFileManager: TdxHunspellReader): Boolean; function ParseRep(ALine: PAnsiChar; AAffixFileManager: TdxHunspellReader): Boolean; function ParseAffix(ALine: PAnsiChar; const IsPrefix: Boolean; AAffixFileManager: TdxHunspellReader; AExistentAffixFlags: TdxExistentAffixFlags): Boolean; function AreAffixesBothCircumfixesOrNot(APrefix, ASuffix: TdxAffix): Boolean; function CanBeFirstPartInCompoundWord(AWordBaseItem: TdxHunspellWordItem; AWordPartIndex: Cardinal): Boolean; procedure CompoundRootCheck(AWordBaseItem: TdxHunspellWordItem; var AWordPartIndex: SmallInt); function IsForbiddenOrNoSuggestWord(AWordBaseItem: TdxHunspellWordItem; ASuggest: Boolean): Boolean; function IsWordPartSuitsCheckCompoundPatternTable(AWordBaseItem: TdxHunspellWordItem; ATableIndex: Integer; AIsFirstWordPart: Boolean): Boolean; function IsWordPartSuitsCompoundFlags(AWordBaseItem: TdxHunspellWordItem; AWordPartIndex: SmallInt; AIsFirstWordPart: Boolean; words: PdxHunspellWordBaseTable): Boolean; function IsTripleLettersExistAndForbid(AWord: PAnsiChar; AJoint: Cardinal): Boolean; function UseWithAffixOnly(AWordBaseItem: TdxHunspellWordItem): Boolean; function HungarianCheck(AWordBaseItem: TdxHunspellWordItem; hu_mov_rule: Boolean): Boolean; function HungarianCheck2(var AWordBaseItem: TdxHunspellWordItem; AInitialWordForm: TWordFormArray; ACompoundWordJoint: Integer; hu_mov_rule: Boolean; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix): Boolean; procedure BuildAffixTree(AAffix: TdxAffix; var AAffixTableArrangedByAppendString: TAffixTable; var AAffixTableIndexedByFlag: TAffixTable); function ProcessSuffixOrPrefixCheck(const AWord: PAnsiChar; AWordLength: Integer; ACompoundPartFlag: Word; ACompoundWordPart: TdxCompoundWordPart; var AWordBaseTableItem: TdxHunspellWordItem; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix): Boolean; procedure LinkAffixesIntoSubsets(AAffixTableArrangedByAppendString: TAffixTable); function OrderAffixTree(AAffix, ANextAffix: TdxAffix): TdxAffix; procedure AffixTreeToList(var ATable: TAffixTable); function GetIsCompoundWordsAvailable: Boolean; function GetParseClasses: TObjectList; procedure SetLanguage(Value: Integer); protected property FullStrip: Boolean read FFullStrip; public constructor Create(const AAffixFilePath: string; AWordBaseManager: TdxHunspellWordBaseManager); destructor Destroy; override; function AffixCheck(const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; const ACompoundPartFlag: Word = 0; ACompoundWordPart: TdxCompoundWordPart = cwpNone): TdxHunspellWordItem; function ProcessPrefixCheck(const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; ACompoundWordPart: TdxCompoundWordPart; const ACompoundPartFlag: Word = NullFlag): TdxHunspellWordItem; function IsLeadingSubset(ASubset: PAnsiChar; ASet: PAnsiChar): Boolean; {$IFDEF DELPHI9} inline; {$ENDIF} function IsTrailingReversedSubset(AReversedSubset: PAnsiChar; AEndOfSet: PAnsiChar; ASetLength: Integer): Boolean; function ProcessPrefixWithSuffixesCheck(const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; ACompoundWordPart: TdxCompoundWordPart; const ACompoundPartFlag: Word = NullFlag): TdxHunspellWordItem; function ProcessSuffixCheck(const AWord: PAnsiChar; AWordLength, AOptions: Integer; APrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; wlst: PPAnsiChar; maxSug: Integer; ns: PInteger; const ACompatibleFlag: Word = NullFlag; const ACompoundPartFlag: Word = NullFlag; ACompoundWordPart: TdxCompoundWordPart = cwpNone): TdxHunspellWordItem; function ProcessTwoSuffixCheck(const AWord: PAnsiChar; AWordLength, AOptions: Integer; APrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; const ACompoundPartFlag: Word = NullFlag): TdxHunspellWordItem; function GetSyllableCount(const AWord: PAnsiChar; AWordLength: Integer): SmallInt; function IsSimpleWordWithReplacedCharacters( const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix): Boolean; function IsForbiddenByCheckCompoundPattern(const AWord: PAnsiChar; AJoint: Integer; AFirstPartWordBase, ASecondPartWordBase: TdxHunspellWordItem): Boolean; function IsWordPartsSuitCompoundRule(var AWords: PdxHunspellWordBaseTable; AWordCount: SmallInt; AWordBaseItem: TdxHunspellWordItem; ADefaultWords: PdxHunspellWordBaseTable; all: Boolean): Boolean; function IsJointUpperCaseExistAndForbid(const AWord: PAnsiChar; AJoint: Integer): Boolean; function CompoundWordWithReplacedCharactersExists(const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix): Boolean; procedure GetCompoundWordJointRange(var ACompoundWordJointMin, ACompoundWordJointMax: Integer; const AWord: PAnsiChar; AWordLength: Integer); function CompoundCheck(const AWord: PAnsiChar; AWordPartIndex, ASyllableCount, AWordPartMaxCount, wnum: SmallInt; words: PdxHunspellWordBaseTable = nil; hu_mov_rule: Boolean = False; ASuggest: Boolean = False): TdxHunspellWordItem; function Lookup(const AWord: PAnsiChar): TdxHunspellWordItem; function EncodeFlag(AFlag: Word): PAnsiChar; procedure SetCompatibleFlags(AIndex: Integer; AValue: Boolean); property BreakTable: TdxBreakTable read FBreakTable; property CheckSharps: Integer read FCheckSharps; property CodePage: Cardinal read FCodePage write FCodePage; property CompatibleFlagsExist: Boolean read FCompatibleFlagsExist write FCompatibleFlagsExist; property ComplexPrefixes: Boolean read FComplexPrefixes; property CompoundFlag: Word read FCompoundFlag; property ForbiddenWord: Word read FForbiddenWord; property Ignore: PAnsiChar read FIgnore; property InputReplaceTable: TdxSortedReplaceTable read FInputReplaceTable; property IsCompoundWordsAvailable: Boolean read GetIsCompoundWordsAvailable; property IsHungarian: Boolean read FIsHungarian; property IsUTF8: Boolean read FIsUTF8; property KeepCase: Word read FKeepCase; property Language: Integer read FLanguage write SetLanguage; property Maxngramsugs: Integer read FMaxngramsugs; property NeedAffix: Word read FNeedAffix; property OutputReplaceTable: TdxSortedReplaceTable read FOutputReplaceTable; property OnlyInCompound: Word read FOnlyInCompound; property TryChars: PAnsiChar read FTryChars; end; TAffixFileReader = class protected FAffixManager: TdxHunspellAffixManager; function GetDataIdentifier: PAnsiChar; virtual; abstract; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; virtual; abstract; function ParseFlag(ALine: PAnsiChar; var ADecodedFlag: Word; AReader: TdxHunspellReader): Boolean; function ParseNumber(ALine: PAnsiChar; out ANumber: Integer): Boolean; public constructor Create(AAffixManager: TdxHunspellAffixManager); end; TTryReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TSetReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TComplexPrefixesReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundFlagReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundBeginReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundEndReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundMiddleReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundWordMaxReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundRootReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundPermitFlagReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundForbidFlagReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCheckCompoundDupReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCheckCompoundRepReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCheckCompoundTripleReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TSimplifiedTripleReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCheckCompoundCaseReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TNoSuggestReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TForbiddenWordReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TLemmaPresentReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCircumfixReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TOnlyInCompoundReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TPseudoRootReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TNeedAffixReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundMinReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundSyllableReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TSyllableNumReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TWordCharsReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TIgnoreReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TRepReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TIConvReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TOConvReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCheckCompoundPatternReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCompoundRuleReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TMapReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TBreakReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TLangReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TVersionReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TNoSplitSugsReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TFullStripReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TSugsWithDotsReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TKeepCaseReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TSubStandardReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TCheckSharpsReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TPrefixReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; TSuffixReader = class(TAffixFileReader) protected function GetDataIdentifier: PAnsiChar; override; function Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; override; end; implementation uses dxSpellCheckerUtils; constructor TdxHunspellAffixManager.Create(const AAffixFilePath: string; AWordBaseManager: TdxHunspellWordBaseManager); var I: Integer; begin FWordBaseManager := AWordBaseManager; FForbiddenWord := ForbiddenWordFlag; FCompoundWordMax := -1; FCompoundPartMin := -1; FMaxngramsugs := -1; FBreakTable := TdxBreakTable.Create; FCheckCompoundPatternTable := TdxCheckCompoundPatternTable.Create(FWordBaseManager, Self); FCompoundRuleTable := TdxCompoundRuleTable.Create(FWordBaseManager, Self); FInputReplaceTable := TdxSortedReplaceTable.Create('ICONV'); FOutputReplaceTable := TdxSortedReplaceTable.Create('OCONV'); FMapTable := TdxMapTable.Create; FReplaceTable := TdxReplaceTable.Create; for I := 0 to AffixTableSize - 1 do begin FPrefixTableArrangedByAppendString[I] := nil; FSuffixTableArrangedByAppendString[I] := nil; FPrefixTableIndexedByFlag[I] := nil; FSuffixTableIndexedByFlag[I] := nil; end; for I := 0 to MaxFlagCount - 1 do FCompatibleFlags[I] := False; FAffixFilePath := AAffixFilePath; ParseFile(AAffixFilePath); if FCompoundPartMin = -1 then FCompoundPartMin := DefaultCompoundPartLength; end; destructor TdxHunspellAffixManager.Destroy; var I: Integer; AAffix, ANextAffix: TdxAffix; begin for I := 0 to AffixTableSize - 1 do begin FPrefixTableIndexedByFlag[I] := nil; FSuffixTableIndexedByFlag[I] := nil; AAffix := TdxPrefix(FPrefixTableArrangedByAppendString[I]); while AAffix <> nil do begin ANextAffix := AAffix.Next; AAffix.Free; AAffix := ANextAffix; end; AAffix := TdxSuffix(FSuffixTableArrangedByAppendString[I]); while AAffix <> nil do begin ANextAffix := AAffix.Next; AAffix.Free; AAffix := ANextAffix; end; FSuffixTableArrangedByAppendString[I] := nil; end; if FTryChars <> nil then StrDispose(FTryChars); FTryChars := nil; FreeAndNil(FMapTable); FreeAndNil(FBreakTable); FreeAndNil(FReplaceTable); FreeAndNil(FInputReplaceTable); FreeAndNil(FOutputReplaceTable); FreeAndNil(FCompoundRuleTable); FreeAndNil(FCheckCompoundPatternTable); FWordBaseManager := nil; if FCompoundVowels <> nil then StrDispose(FCompoundVowels); if cpdvowels_utf16 <> nil then FreeMem(cpdvowels_utf16, cpdvowels_utf16_len * SizeOf(WideChar)); StrDispose(FSyllableNum); if wordchars_utf16 <> nil then FreeMem(wordchars_utf16, wordchars_utf16_len * SizeOf(Word)); StrDispose(FIgnore); if ignorechars_utf16 <> nil then FreeMem(ignorechars_utf16, ignorechars_utf16_len * SizeOf(Word)); StrDispose(FVersion); inherited Destroy; end; function TdxHunspellAffixManager.DoAffixParsing(ALine: PAnsiChar; AAffixFileManager: TdxHunspellReader; AAffixType: TdxAffixType): Boolean; var I: Integer; begin Result := True; if AAffixType <> atNone then begin if not FIsExistentAffixFlagsInitialized then begin for I := 0 to MaxFlagCount - 1 do FExistentAffixFlags[I] := 0; FIsExistentAffixFlagsInitialized := True; end; if ParseAffix(ALine, AAffixType = atPrefix, AAffixFileManager, FExistentAffixFlags) then begin AffixTreeToList(FPrefixTableArrangedByAppendString); AffixTreeToList(FSuffixTableArrangedByAppendString); Result := False; end; end; end; function TdxHunspellAffixManager.ParseFile(const AFileName: string): Integer; var ALineCursor, ADataIdentifier: PAnsiChar; AIsFirstLine: Boolean; AAffixReader: TdxHunspellReader; AParseClasses: TObjectList; I: Integer; begin Result := 0; FIsExistentAffixFlagsInitialized := False; AIsFirstLine := True; AAffixReader := nil; ALineCursor := nil; AParseClasses := GetParseClasses; try AAffixReader := TdxHunspellReader.Create(AFileName); if AAffixReader = nil then begin Result := 1; Exit; end; ALineCursor := AAffixReader.getline; while ALineCursor <> nil do begin RemoveCRLF(ALineCursor); if AIsFirstLine then begin AIsFirstLine := False; RemoveMark(ALineCursor); end; for I := 0 to AParseClasses.Count - 1 do begin ADataIdentifier := TAffixFileReader(AParseClasses.Items[I]).GetDataIdentifier; if StrLComp(ALineCursor, ADataIdentifier, StrLen(ADataIdentifier)) = 0 then TAffixFileReader(AParseClasses.Items[I]).Process(ALineCursor, AAffixReader); end; StrDispose(ALineCursor); ALineCursor := AAffixReader.getline; end; finally StrDispose(ALineCursor); AAffixReader.Free; AParseClasses.Free; end; AffixTreeToList(FPrefixTableArrangedByAppendString); AffixTreeToList(FSuffixTableArrangedByAppendString); LinkAffixesIntoSubsets(FPrefixTableArrangedByAppendString); LinkAffixesIntoSubsets(FSuffixTableArrangedByAppendString); end; procedure TdxHunspellAffixManager.BuildAffixTree(AAffix: TdxAffix; var AAffixTableArrangedByAppendString: TAffixTable; var AAffixTableIndexedByFlag: TAffixTable); var ACurrentItem, APreviousAffix: TdxAffix; AAppendString: PAnsiChar; AFlag, AFirstChar: Byte; begin AAppendString := AAffix.AppendString; AFlag := AAffix.Flag; ACurrentItem := AAffixTableIndexedByFlag[AFlag]; AAffix.NextByFlag := ACurrentItem; AAffixTableIndexedByFlag[AFlag] := AAffix; if StrLen(AAppendString) = 0 then begin ACurrentItem := AAffixTableArrangedByAppendString[0]; AAffix.Next := ACurrentItem; AAffixTableArrangedByAppendString[0] := AAffix; Exit; end; AAffix.NextSimilar := nil; AAffix.NextDifferent := nil; AFirstChar := Ord(AAppendString^); ACurrentItem := AAffixTableArrangedByAppendString[AFirstChar]; if ACurrentItem = nil then begin AAffixTableArrangedByAppendString[AFirstChar] := AAffix; Exit; end; while True do begin APreviousAffix := ACurrentItem; if StrComp(AAffix.AppendString, ACurrentItem.AppendString) <= 0 then begin ACurrentItem := ACurrentItem.NextSimilar; if ACurrentItem = nil then begin APreviousAffix.NextSimilar := AAffix; Break; end; end else begin ACurrentItem := ACurrentItem.NextDifferent; if ACurrentItem = nil then begin APreviousAffix.NextDifferent := AAffix; Break; end; end; end; end; function TdxHunspellAffixManager.ProcessSuffixOrPrefixCheck(const AWord: PAnsiChar; AWordLength: Integer; ACompoundPartFlag: Word; ACompoundWordPart: TdxCompoundWordPart; var AWordBaseTableItem: TdxHunspellWordItem; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix): Boolean; begin AWordBaseTableItem := ProcessSuffixCheck(AWord, AWordLength, 0, nil, ATakenSuffix, nil, 0, nil, NullFlag, ACompoundPartFlag, ACompoundWordPart); Result := AWordBaseTableItem <> nil; if not Result then begin AWordBaseTableItem := ProcessPrefixCheck(AWord, AWordLength, ATakenPrefix, ATakenSuffix, ACompoundWordPart, ACompoundPartFlag); Result := AWordBaseTableItem <> nil; end; end; procedure TdxHunspellAffixManager.AffixTreeToList(var ATable: TAffixTable); var I: Integer; begin for I := 1 to AffixTableSize - 1 do ATable[I] := OrderAffixTree(ATable[I], nil); end; function TdxHunspellAffixManager.OrderAffixTree(AAffix, ANextAffix: TdxAffix): TdxAffix; begin if AAffix <> nil then begin ANextAffix := OrderAffixTree(AAffix.NextDifferent, ANextAffix); AAffix.Next := ANextAffix; ANextAffix := OrderAffixTree(AAffix.NextSimilar, AAffix); end; Result := ANextAffix; end; procedure TdxHunspellAffixManager.LinkAffixesIntoSubsets(AAffixTableArrangedByAppendString: TAffixTable); var AAffix, ANextAffix, ALastAffixInChain: TdxAffix; I: Integer; begin for I := 1 to AffixTableSize - 1 do begin AAffix := AAffixTableArrangedByAppendString[I]; while AAffix <> nil do begin ANextAffix := AAffix.Next; while ANextAffix <> nil do begin if not IsLeadingSubset(AAffix.AppendString, ANextAffix.AppendString) then Break; ANextAffix := ANextAffix.Next; end; AAffix.NextDifferent := ANextAffix; AAffix.NextSimilar := nil; if (AAffix.Next <> nil) and IsLeadingSubset(AAffix.AppendString, AAffix.Next.AppendString) then AAffix.NextSimilar := AAffix.Next; AAffix := AAffix.Next; end; AAffix := AAffixTableArrangedByAppendString[I]; while AAffix <> nil do begin ANextAffix := AAffix.Next; ALastAffixInChain := nil; while ANextAffix <> nil do begin if not IsLeadingSubset(AAffix.AppendString, ANextAffix.AppendString) then Break; ALastAffixInChain := ANextAffix; ANextAffix := ANextAffix.Next; end; if ALastAffixInChain <> nil then ALastAffixInChain.NextDifferent := nil; AAffix := AAffix.Next; end; end; end; function TdxHunspellAffixManager.IsLeadingSubset(ASubset: PAnsiChar; ASet: PAnsiChar): Boolean; begin while (ASubset^ in [ASet^, '.']) and (ASubset^ <> #0) do begin Inc(ASubset); Inc(ASet); end; Result := ASubset^ = #0; end; function TdxHunspellAffixManager.IsTrailingReversedSubset(AReversedSubset: PAnsiChar; AEndOfSet: PAnsiChar; ASetLength: Integer): Boolean; begin while (ASetLength > 0) and (AReversedSubset^ <> #0) and ((AReversedSubset^ = AEndOfSet^) or (AReversedSubset^ = '.')) do begin Inc(AReversedSubset); Dec(AEndOfSet); Dec(ASetLength); end; Result := AReversedSubset^ = #0; end; function TdxHunspellAffixManager.ProcessPrefixCheck(const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; ACompoundWordPart: TdxCompoundWordPart; const ACompoundPartFlag: Word): TdxHunspellWordItem; var APrefix: TdxPrefix; begin Result := nil; ATakenPrefix := nil; FSavedSuffixInfo.AppendString := nil; APrefix := TdxPrefix(FPrefixTableArrangedByAppendString[0]); while (APrefix <> nil) and (Result = nil) do begin if APrefix.CanUseInSimpleWordsOrIsFogemorpheme(ACompoundWordPart) and APrefix.CanUseInTheMiddleOfCompoundWords(ACompoundWordPart) then begin Result := APrefix.CheckWord(AWord, AWordLength, ATakenSuffix, ACompoundWordPart, ACompoundPartFlag); //TODO: Extract block if Result <> nil then ATakenPrefix := APrefix; end; APrefix := TdxPrefix(APrefix.Next); end; if Result = nil then begin APrefix := TdxPrefix(FPrefixTableArrangedByAppendString[PByte(AWord)^]); while (APrefix <> nil) and (Result = nil) do begin if IsLeadingSubset(APrefix.AppendString, AWord) then begin if APrefix.CanUseInSimpleWordsOrIsFogemorpheme(ACompoundWordPart) and APrefix.CanUseInTheMiddleOfCompoundWords(ACompoundWordPart) then begin Result := APrefix.CheckWord(AWord, AWordLength, ATakenSuffix, ACompoundWordPart, ACompoundPartFlag); //TODO: Extract block if Result <> nil then ATakenPrefix := APrefix; end; APrefix := TdxPrefix(APrefix.NextSimilar); end else APrefix := TdxPrefix(APrefix.NextDifferent); end; end; end; function TdxHunspellAffixManager.ProcessPrefixWithSuffixesCheck(const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; ACompoundWordPart: TdxCompoundWordPart; const ACompoundPartFlag: Word): TdxHunspellWordItem; var APrefix: TdxPrefix; begin Result := nil; ATakenPrefix := nil; FSavedSuffixInfo.AppendString := nil; APrefix := TdxPrefix(FPrefixTableArrangedByAppendString[0]); while (APrefix <> nil) and (Result = nil) do begin Result := APrefix.MakeInitialWordFormAndProcessTwoSuffixCheck(AWord, AWordLength, ATakenSuffix, ACompoundWordPart, ACompoundPartFlag); APrefix := TdxPrefix(APrefix.Next); end; if Result = nil then begin APrefix := TdxPrefix(FPrefixTableArrangedByAppendString[PByte(AWord)^]); while (APrefix <> nil) and (Result = nil) do begin if IsLeadingSubset(APrefix.AppendString, AWord) then begin Result := APrefix.MakeInitialWordFormAndProcessTwoSuffixCheck(AWord, AWordLength, ATakenSuffix, ACompoundWordPart, ACompoundPartFlag); if Result <> nil then ATakenPrefix := APrefix; APrefix := TdxPrefix(APrefix.NextSimilar); end else APrefix := TdxPrefix(APrefix.NextDifferent); end; end; end; function TdxHunspellAffixManager.IsSimpleWordWithReplacedCharacters( const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix): Boolean; var AChangedWord: array [0..MaxLineLength - 1] of AnsiChar; AWordCursor: PAnsiChar; AReplaceLength, AInitialLength, I: Integer; begin Result := False; if (AWordLength < 2) or (FReplaceTable.Count = 0) or not FCheckCompoundRep then Exit; for I := 0 to FReplaceTable.Count - 1 do begin AWordCursor := AWord; AReplaceLength := StrLen(FReplaceTable[I].Replacement); AInitialLength := StrLen(FReplaceTable[I].Initial); AWordCursor := StrPos(AWordCursor, FReplaceTable[I].Initial); while AWordCursor <> nil do begin StrCopy(AChangedWord, AWord); if AWordCursor - AWord + AReplaceLength + Integer(StrLen(AWordCursor + AInitialLength)) > MaxLineLength then Break; StrCopy(AChangedWord + (AWordCursor - AWord), FReplaceTable[I].Replacement); StrCopy(AChangedWord + (AWordCursor - AWord) + AReplaceLength, AWordCursor + AInitialLength); if CompoundWordWithReplacedCharactersExists(AChangedWord, strlen(AChangedWord), ATakenPrefix, ATakenSuffix) then begin Result := True; Exit; end; Inc(AWordCursor); AWordCursor := StrPos(AWordCursor, FReplaceTable[I].Initial); end; end; end; function TdxHunspellAffixManager.IsForbiddenByCheckCompoundPattern(const AWord: PAnsiChar; AJoint: Integer; AFirstPartWordBase, ASecondPartWordBase: TdxHunspellWordItem): Boolean; var ALength, I: Integer; begin Result := False; for I := 0 to FCheckCompoundPatternTable.Count - 1 do begin ALength := StrLen(FCheckCompoundPatternTable[I].Pattern); if IsLeadingSubset(FCheckCompoundPatternTable[I].Pattern2, AWord + AJoint) and ((AFirstPartWordBase = nil) or (FCheckCompoundPatternTable[I].Condition = 0) or AFirstPartWordBase.IsCompatibleWithFlag(FCheckCompoundPatternTable[I].Condition)) and ((ASecondPartWordBase = nil) or (FCheckCompoundPatternTable[I].Condition2 = 0) or ASecondPartWordBase.IsCompatibleWithFlag(FCheckCompoundPatternTable[I].Condition2)) and (ALength <> 0) and (AJoint > ALength) and (StrLComp(AWord + AJoint - ALength, FCheckCompoundPatternTable[I].Pattern, ALength) = 0) then begin Result := True; Break; end; end; end; function TdxHunspellAffixManager.IsJointUpperCaseExistAndForbid(const AWord: PAnsiChar; AJoint: Integer): Boolean; var A, B: AnsiChar; begin Result := False; begin A := (AWord + AJoint - 1)^; B := (AWord + AJoint)^; if (IsUpCase(A) or IsUpCase(B)) and (A <> '-') and (B <> '-') then Result := FCheckCompoundCase; end; end; function TdxHunspellAffixManager.IsWordPartsSuitCompoundRule(var AWords: PdxHunspellWordBaseTable; AWordCount: SmallInt; AWordBaseItem: TdxHunspellWordItem; ADefaultWords: PdxHunspellWordBaseTable; all: Boolean): Boolean; type TBackTrackingItem = record MetacharacterPosition: SmallInt; WordsIndex: SmallInt; MatchedCharactersCount: Integer; end; var ABackTrackingTable: array [0..MaxWordLength - 1] of TBackTrackingItem; ABackTrackingIndex, AOldBackTrackingIndex, AFlagCursor, AFlagCursor2, AMetacharacterPointer, AWordIndex: SmallInt; I, J, AWordIndexMax: Integer; AIsWordBaseItemCompatibleWithCompoundRule, AIsAllWordsCompatibleWithCompoundRule, AIsWordsInitialized: Boolean; AFlag: Word; begin Result := False; if (AWordBaseItem.AffixFlags.Length = 0) or (AWords = nil) and (ADefaultWords = nil) then Exit; AIsWordBaseItemCompatibleWithCompoundRule := False; for I := 0 to FCompoundRuleTable.Count - 1 do for J := 0 to FCompoundRuleTable[I].Length - 1 do begin AFlag := FCompoundRuleTable[I].Pattern[J]; if (AFlag <> Ord('*')) and (AFlag <> Ord('?')) and AWordBaseItem.IsCompatibleWithFlag(AFlag) then AIsWordBaseItemCompatibleWithCompoundRule := True; end; if AIsWordBaseItemCompatibleWithCompoundRule then begin AIsWordsInitialized := False; if AWords = nil then begin AIsWordsInitialized := True; AWords := ADefaultWords; end; AWords[AWordCount] := AWordBaseItem; ABackTrackingIndex := 0; I := 0; while (I < FCompoundRuleTable.Count) and not Result do begin AFlagCursor := 0; AWordIndex := 0; AIsWordBaseItemCompatibleWithCompoundRule := True; AIsAllWordsCompatibleWithCompoundRule := True; repeat while (AFlagCursor < FCompoundRuleTable[I].Length) and (AWordIndex <= AWordCount) do begin AMetacharacterPointer := AFlagCursor + 1; AFlag := 0; if AMetacharacterPointer < FCompoundRuleTable[I].Length then AFlag := FCompoundRuleTable[I].Pattern[AMetacharacterPointer]; if (AMetacharacterPointer < FCompoundRuleTable[I].Length) and ((AFlag = Ord('*')) or (AFlag = Ord('?'))) then begin AWordIndexMax := IfThen(AFlag = Ord('?'), AWordIndex, AWordCount); AIsAllWordsCompatibleWithCompoundRule := True; Inc(AFlagCursor, 2); ABackTrackingTable[ABackTrackingIndex].MetacharacterPosition := AFlagCursor; ABackTrackingTable[ABackTrackingIndex].WordsIndex := AWordIndex; while AWordIndex <= AWordIndexMax do begin if not AWords[AWordIndex].IsCompatibleWithFlag(FCompoundRuleTable[I].Pattern[AFlagCursor - 2]) then begin AIsAllWordsCompatibleWithCompoundRule := False; Break; end; Inc(AWordIndex); end; if AWordIndex <= AWordCount then AIsAllWordsCompatibleWithCompoundRule := False; ABackTrackingTable[ABackTrackingIndex].MatchedCharactersCount := AWordIndex - ABackTrackingTable[ABackTrackingIndex].WordsIndex; if ABackTrackingTable[ABackTrackingIndex].MatchedCharactersCount > 0 then Inc(ABackTrackingIndex); if AIsAllWordsCompatibleWithCompoundRule then Break; end else begin AIsAllWordsCompatibleWithCompoundRule := True; if (AWords[AWordIndex] = nil) or not AWords[AWordIndex].IsCompatibleWithFlag(FCompoundRuleTable[I].Pattern[AFlagCursor]) then begin AIsWordBaseItemCompatibleWithCompoundRule := False; Break; end; Inc(AFlagCursor); Inc(AWordIndex); if (FCompoundRuleTable[I].Length = AFlagCursor) and not(AWordIndex > AWordCount) then AIsWordBaseItemCompatibleWithCompoundRule := False; end; end; if AIsWordBaseItemCompatibleWithCompoundRule and AIsAllWordsCompatibleWithCompoundRule then begin AFlagCursor2 := AFlagCursor; while (AFlagCursor2 + 1 < FCompoundRuleTable[I].Length) and ((FCompoundRuleTable[I].Pattern[AFlagCursor2 + 1] = Ord('*')) or (FCompoundRuleTable[I].Pattern[AFlagCursor2 + 1] = Ord('?'))) do Inc(AFlagCursor2, 2); if FCompoundRuleTable[I].Length <= AFlagCursor2 then begin Result := True; Break; end; end; if ABackTrackingIndex > 0 then begin repeat AIsWordBaseItemCompatibleWithCompoundRule := True; Dec(ABackTrackingTable[ABackTrackingIndex - 1].MatchedCharactersCount); AFlagCursor := ABackTrackingTable[ABackTrackingIndex - 1].MetacharacterPosition; AWordIndex := ABackTrackingTable[ABackTrackingIndex - 1].WordsIndex + ABackTrackingTable[ABackTrackingIndex - 1].MatchedCharactersCount; AOldBackTrackingIndex := ABackTrackingIndex; Dec(ABackTrackingIndex); until not((ABackTrackingTable[AOldBackTrackingIndex - 1].MatchedCharactersCount < 0) and (ABackTrackingIndex > 0)); if not(ABackTrackingTable[AOldBackTrackingIndex - 1].MatchedCharactersCount < 0) then ABackTrackingIndex := AOldBackTrackingIndex; end; until ABackTrackingIndex = 0; if AIsWordBaseItemCompatibleWithCompoundRule and AIsAllWordsCompatibleWithCompoundRule and (not all or (FCompoundRuleTable[I].Length <= AFlagCursor)) then Result := True; while AIsWordBaseItemCompatibleWithCompoundRule and AIsAllWordsCompatibleWithCompoundRule and (AFlagCursor + 1 < FCompoundRuleTable[I].Length) and ((FCompoundRuleTable[I].Pattern[AFlagCursor + 1] = Ord('*')) or (FCompoundRuleTable[I].Pattern[AFlagCursor + 1] = Ord('?'))) do Inc(AFlagCursor, 2); if AIsWordBaseItemCompatibleWithCompoundRule and AIsAllWordsCompatibleWithCompoundRule and (FCompoundRuleTable[I].Length <= AFlagCursor) then Result := True; Inc(I); end; if not Result then begin AWords[AWordCount] := nil; if AIsWordsInitialized then AWords := nil; end; end; end; function TdxHunspellAffixManager.CompoundWordWithReplacedCharactersExists( const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix): Boolean; begin Result := True; if Lookup(AWord) = nil then if AffixCheck(AWord, AWordLength, ATakenPrefix, ATakenSuffix) = nil then Result := False; end; function TdxHunspellAffixManager.GetSyllableCount(const AWord: PAnsiChar; AWordLength: Integer): SmallInt; var I: Integer; begin Result := 0; if FMaxCompoundSyllable = 0 then Exit; if not FIsUTF8 then begin for I := 0 to AWordLength - 1 do if StrScan(FCompoundVowels, (AWord + I)^) <> nil then Inc(Result); end else //TODO: ; end; procedure TdxHunspellAffixManager.GetCompoundWordJointRange(var ACompoundWordJointMin, ACompoundWordJointMax: Integer; const AWord: PAnsiChar; AWordLength: Integer); var I: Integer; begin if FIsUTF8 then begin ACompoundWordJointMin := 0; I := 0; while (I < FCompoundPartMin) and ((AWord + ACompoundWordJointMin) <> nil) do begin Inc(ACompoundWordJointMin); while Ord((AWord + ACompoundWordJointMin)^) and $c0 = $80 do Inc(ACompoundWordJointMin); Inc(I); end; ACompoundWordJointMax := AWordLength; I := 0; while (I < FCompoundPartMin - 1) and (ACompoundWordJointMax <> 0) do begin Dec(ACompoundWordJointMax); while Ord((AWord + ACompoundWordJointMax)^) and $c0 = $80 do Dec(ACompoundWordJointMax); Inc(I); end; end else begin ACompoundWordJointMin := FCompoundPartMin; ACompoundWordJointMax := AWordLength - FCompoundPartMin + 1; end; end; function TdxHunspellAffixManager.CompoundCheck(const AWord: PAnsiChar; AWordPartIndex, ASyllableCount, AWordPartMaxCount, wnum: SmallInt; words: PdxHunspellWordBaseTable = nil; hu_mov_rule: Boolean = False; ASuggest: Boolean = False): TdxHunspellWordItem; var ACompoundWordJoint, AWordLength, ACompoundWordJointMin, ACompoundWordJointMax, ATableIndex, AInitialWordJoint: Integer; ACheckedPrefix: Boolean; ADoubleCharacter, ACheckWordWithoutTripleRemoving: Boolean; ASyllableCountStore, ASyllableCountStore2, AWordPartIndexStore, AWordPartIndexStore2: SmallInt; AWordBaseItem, AFirstPartWordBaseItem: TdxHunspellWordItem; ACompoundRuleWords: array[0..MaxWordLength] of TdxHunspellWordItem; AInitialWordForm: TWordFormArray; AJointCharacter: AnsiChar; ACompoundWordPart: TdxCompoundWordPart; ATakenPrefix: TdxPrefix; ATakenSuffix: TdxSuffix; function CheckSimpleWordWithReplacedCharacters: TdxHunspellWordItem; begin if IsSimpleWordWithReplacedCharacters(AWord, AWordLength, ATakenPrefix, ATakenSuffix) then Result := nil else Result := AFirstPartWordBaseItem; end; procedure HungarianSyllableProcess; begin if IsHungarian then begin Inc(ASyllableCount, GetSyllableCount(AInitialWordForm, AInitialWordJoint)); if (ATakenPrefix <> nil) and (GetSyllableCount(ATakenPrefix.AppendString, StrLen(ATakenPrefix.AppendString)) > 1) then Inc(AWordPartIndex); end; end; procedure HungarianDecreaseSyllableCount; begin if (AWordBaseItem <> nil) and IsHungarian and AWordBaseItem.IsCompatibleWithFlag(Ord('I')) and not AWordBaseItem.IsCompatibleWithFlag(Ord('J')) then Dec(ASyllableCount); end; begin Result := nil; AWordLength := StrLen(AWord); if IsSimpleWordWithReplacedCharacters(AWord, AWordLength, ATakenPrefix, ATakenSuffix) then Exit; AWordBaseItem := nil; ADoubleCharacter := False; ACheckWordWithoutTripleRemoving := False; ATableIndex := 0; GetCompoundWordJointRange(ACompoundWordJointMin, ACompoundWordJointMax, AWord, AWordLength); StrCopy(AInitialWordForm, AWord); ACompoundWordJoint := ACompoundWordJointMin; while ACompoundWordJoint < ACompoundWordJointMax do begin AWordLength := StrLen(AWord); GetCompoundWordJointRange(ACompoundWordJointMin, ACompoundWordJointMax, AWord, AWordLength); StrCopy(AInitialWordForm, AWord); ASyllableCountStore := ASyllableCount; AWordPartIndexStore := AWordPartIndex; ACheckedPrefix := False; if IsUTF8 then begin //TODO: end; repeat AInitialWordJoint := ACompoundWordJoint; if ATableIndex > 0 then begin while (ATableIndex <= FCheckCompoundPatternTable.Count) and ((FCheckCompoundPatternTable[ATableIndex - 1].Pattern3 = nil) or (StrLComp(AWord + ACompoundWordJoint, FCheckCompoundPatternTable[ATableIndex - 1].Pattern3, StrLen(FCheckCompoundPatternTable[ATableIndex - 1].Pattern3)) <> 0)) do Inc(ATableIndex); if ATableIndex > FCheckCompoundPatternTable.Count then Break; StrCopy(AInitialWordForm + ACompoundWordJoint, FCheckCompoundPatternTable[ATableIndex - 1].Pattern); AInitialWordJoint := ACompoundWordJoint; Inc(AInitialWordJoint, StrLen(FCheckCompoundPatternTable[ATableIndex - 1].Pattern)); StrCopy(AInitialWordForm + AInitialWordJoint, FCheckCompoundPatternTable[ATableIndex - 1].Pattern2); StrCopy(AInitialWordForm + AInitialWordJoint + StrLen(FCheckCompoundPatternTable[ATableIndex - 1].Pattern2), AWord + ACompoundWordJoint + StrLen(FCheckCompoundPatternTable[ATableIndex - 1].Pattern3)); Inc(AWordLength, StrLen(FCheckCompoundPatternTable[ATableIndex - 1].Pattern) + StrLen(FCheckCompoundPatternTable[ATableIndex - 1].Pattern2) - StrLen(FCheckCompoundPatternTable[ATableIndex - 1].Pattern3)); GetCompoundWordJointRange(ACompoundWordJointMin, ACompoundWordJointMax, AInitialWordForm, AWordLength); ACompoundWordJointMax := AWordLength - FCompoundPartMin + 1; //TODO: Remove after UTF8 removing, because GetCompoundWordJointRange hes this line end; AJointCharacter := AInitialWordForm[AInitialWordJoint]; AInitialWordForm[AInitialWordJoint] := #0; ATakenSuffix := nil; ATakenPrefix := nil; AWordBaseItem := Lookup(AInitialWordForm); while (AWordBaseItem <> nil) and not hu_mov_rule and (UseWithAffixOnly(AWordBaseItem) or not ((IsWordPartSuitsCompoundFlags(AWordBaseItem, AWordPartIndex, True, words) or ((words <> nil) or (AWordPartIndex = 0)) and IsWordPartsSuitCompoundRule(words, wnum, AWordBaseItem, @ACompoundRuleWords, False)) and IsWordPartSuitsCheckCompoundPatternTable(AWordBaseItem, ATableIndex, True))) do AWordBaseItem := AWordBaseItem.NextHomonym; if AWordBaseItem = nil then begin ACompoundWordPart := cwpFirst; if hu_mov_rule then ACompoundWordPart := cwpOther; if (FCompoundFlag <> 0) then begin AWordBaseItem := ProcessPrefixCheck(AInitialWordForm, AInitialWordJoint, ATakenPrefix, ATakenSuffix, ACompoundWordPart, FCompoundFlag); if AWordBaseItem = nil then begin AWordBaseItem := ProcessSuffixCheck(AInitialWordForm, AInitialWordJoint, 0, nil, ATakenSuffix, nil, 0, nil, NullFlag, FCompoundFlag, ACompoundWordPart); if (AWordBaseItem <> nil) and not hu_mov_rule and (((FCompoundForbidFlag <> 0) and ATakenSuffix.IsCompatibleWithFlag(FCompoundForbidFlag)) or ((FCompoundEnd <> 0) and ATakenSuffix.IsCompatibleWithFlag(FCompoundEnd))) then AWordBaseItem := nil; end; end; if (AWordBaseItem <> nil) or (((AWordPartIndex = 0) and (FCompoundBegin <> 0) and ProcessSuffixOrPrefixCheck(AInitialWordForm, AInitialWordJoint, FCompoundBegin, ACompoundWordPart, AWordBaseItem, ATakenPrefix, ATakenSuffix)) or ((AWordPartIndex > 0) and (FCompoundMiddle <> 0) and ProcessSuffixOrPrefixCheck(AInitialWordForm, AInitialWordJoint, FCompoundMiddle, ACompoundWordPart, AWordBaseItem, ATakenPrefix, ATakenSuffix))) then ACheckedPrefix := True; end else if UseWithAffixOnly(AWordBaseItem) or IsForbiddenOrNoSuggestWord(AWordBaseItem, ASuggest) then begin AInitialWordForm[AInitialWordJoint] := AJointCharacter; Continue; end; if (AWordBaseItem <> nil) and not hu_mov_rule and (((ATakenPrefix <> nil) and ATakenPrefix.IsCompatibleWithFlag(FCompoundForbidFlag)) or ((ATakenSuffix <> nil) and ATakenSuffix.IsCompatibleWithFlag(FCompoundForbidFlag))) then AWordBaseItem := nil; if (AWordBaseItem <> nil) and not ACheckedPrefix and (FCompoundEnd <> 0) and not hu_mov_rule and (((ATakenPrefix <> nil) and ATakenPrefix.IsCompatibleWithFlag(FCompoundEnd)) or ((ATakenSuffix <> nil) and ATakenSuffix.IsCompatibleWithFlag(FCompoundEnd))) then AWordBaseItem := nil; if (AWordBaseItem <> nil) and not ACheckedPrefix and (AWordPartIndex = 0) and (FCompoundMiddle <> 0) and not hu_mov_rule and (((ATakenPrefix <> nil) and ATakenPrefix.IsCompatibleWithFlag(FCompoundMiddle)) or ((ATakenSuffix <> nil) and ATakenSuffix.IsCompatibleWithFlag(FCompoundMiddle))) then AWordBaseItem := nil; if IsForbiddenOrNoSuggestWord(AWordBaseItem, ASuggest) then begin Result := nil; Exit; end; CompoundRootCheck(AWordBaseItem, AWordPartIndex); if ((AWordBaseItem <> nil) and (ACheckedPrefix or ((words <> nil) and (words[wnum] <> nil)) or CanBeFirstPartInCompoundWord(AWordBaseItem, AWordPartIndexStore) or HungarianCheck(AWordBaseItem, hu_mov_rule)) and IsWordPartSuitsCheckCompoundPatternTable(AWordBaseItem, ATableIndex, True) and not ((ATableIndex = 0) and (words = nil) and (IsTripleLettersExistAndForbid(AWord, AInitialWordJoint) or IsJointUpperCaseExistAndForbid(AWord, AInitialWordJoint)))) or HungarianCheck2(AWordBaseItem, AInitialWordForm, ACompoundWordJoint, hu_mov_rule, ATakenPrefix, ATakenSuffix) then begin HungarianSyllableProcess; // Second part of word AFirstPartWordBaseItem := AWordBaseItem; AInitialWordForm[AInitialWordJoint] := AJointCharacter; repeat if FSimplifiedTriple then begin if ADoubleCharacter then begin ACheckWordWithoutTripleRemoving := True; Dec(AInitialWordJoint); end else if (AInitialWordJoint > 2) and ((AWord + AInitialWordJoint - 1)^ = (AWord + AInitialWordJoint - 2)^) then ADoubleCharacter := True; end; AWordBaseItem := Lookup(AInitialWordForm + AInitialWordJoint); while (AWordBaseItem <> nil) and (UseWithAffixOnly(AWordBaseItem) or not ((IsWordPartSuitsCompoundFlags(AWordBaseItem, 0, False, words) or IsWordPartsSuitCompoundRule(words, wnum + 1, AWordBaseItem, nil, True)) and IsWordPartSuitsCheckCompoundPatternTable(AWordBaseItem, ATableIndex, False))) do AWordBaseItem := AWordBaseItem.NextHomonym; if (AWordBaseItem <> nil) and (words <> nil) and (words[wnum + 1] <> nil) then begin Result := AFirstPartWordBaseItem; Exit; end; if IsForbiddenOrNoSuggestWord(AWordBaseItem, ASuggest) then begin Result := nil; Exit; end; ASyllableCountStore2 := ASyllableCount; AWordPartIndexStore2 := AWordPartIndex; HungarianDecreaseSyllableCount; CompoundRootCheck(AWordBaseItem, AWordPartIndex); if (AWordBaseItem <> nil) and (AWordBaseItem.IsCompatibleWithFlag(FCompoundFlag) or AWordBaseItem.IsCompatibleWithFlag(FCompoundEnd)) and (((FCompoundWordMax = -1) or (AWordPartIndex + 1 < FCompoundWordMax)) or ((FMaxCompoundSyllable <> 0) and (ASyllableCount + GetSyllableCount(AWordBaseItem.WordBase, AWordBaseItem.clen) <= FMaxCompoundSyllable))) and ((ATableIndex <> 0) or not IsForbiddenByCheckCompoundPattern(AWord, ACompoundWordJoint, AFirstPartWordBaseItem, AWordBaseItem)) and (not FCheckCompoundDup or (AWordBaseItem <> AFirstPartWordBaseItem)) and IsWordPartSuitsCheckCompoundPatternTable(AWordBaseItem, ATableIndex, False) then begin Result := AFirstPartWordBaseItem; Exit; end; ASyllableCount := ASyllableCountStore2; AWordPartIndex := AWordPartIndexStore2; ATakenSuffix := nil; FSavedSuffixInfo.Flag := NullFlag; if FCompoundFlag <> 0 then AWordBaseItem := AffixCheck(AWord + AInitialWordJoint, StrLen(AWord + AInitialWordJoint), ATakenPrefix, ATakenSuffix, FCompoundFlag, cwpLast) else AWordBaseItem := nil; if (AWordBaseItem = nil) and (FCompoundEnd <> 0) then begin ATakenSuffix := nil; ATakenPrefix := nil; AWordBaseItem := AffixCheck(AWord + AInitialWordJoint, StrLen(AWord + AInitialWordJoint), ATakenPrefix, ATakenSuffix, FCompoundEnd, cwpLast); end; if (AWordBaseItem = nil) and (FCompoundRuleTable.Count <> 0) and (words <> nil) then begin AWordBaseItem := AffixCheck(AWord + AInitialWordJoint, StrLen(AWord + AInitialWordJoint), ATakenPrefix, ATakenSuffix, 0, cwpLast); if (AWordBaseItem <> nil) and IsWordPartsSuitCompoundRule(words, wnum + 1, AWordBaseItem, nil, True) then begin Result := AFirstPartWordBaseItem; Exit; end; AWordBaseItem := nil; end; if (AWordBaseItem <> nil) and not ((ATableIndex = 0) or (FCheckCompoundPatternTable[ATableIndex - 1].Condition2 = NullFlag) or AWordBaseItem.IsCompatibleWithFlag(FCheckCompoundPatternTable[ATableIndex - 1].Condition2)) then AWordBaseItem := nil; if (AWordBaseItem <> nil) and (ATableIndex = 0) and IsForbiddenByCheckCompoundPattern(AWord, AInitialWordJoint, AFirstPartWordBaseItem, AWordBaseItem) then AWordBaseItem := nil; if (AWordBaseItem <> nil) and (((ATakenPrefix <> nil) and ATakenPrefix.IsCompatibleWithFlag(FCompoundForbidFlag)) or ((ATakenSuffix <> nil) and ATakenSuffix.IsCompatibleWithFlag(FCompoundForbidFlag))) then AWordBaseItem := nil; if IsForbiddenOrNoSuggestWord(AWordBaseItem, ASuggest) then begin Result := nil; Exit; end; if IsHungarian then begin Inc(ASyllableCount, GetSyllableCount(AWord + AInitialWordJoint, StrLen(AWord + AInitialWordJoint))); if FSavedSuffixInfo.AppendString <> nil then Dec(ASyllableCount, GetSyllableCount(FSavedSuffixInfo.AppendString, StrLen(FSavedSuffixInfo.AppendString))); if (ATakenPrefix <> nil) and (GetSyllableCount(ATakenPrefix.AppendString, StrLen(ATakenPrefix.AppendString)) > 1) then Inc(AWordPartIndex); if FSyllableNum <> nil then case Chr(FSavedSuffixInfo.Flag) of 'c': Inc(ASyllableCount, 2); 'J': Inc(ASyllableCount, 1); 'I': if AWordBaseItem.IsCompatibleWithFlag(Ord('J')) then Inc(ASyllableCount, 1); end; end; CompoundRootCheck(AWordBaseItem, AWordPartIndex); if (AWordBaseItem <> nil) and (((FCompoundWordMax = -1) or (AWordPartIndex + 1 < FCompoundWordMax)) or ((FMaxCompoundSyllable <> 0) and (ASyllableCount <= FMaxCompoundSyllable))) and (not FCheckCompoundDup or (AWordBaseItem <> AFirstPartWordBaseItem)) then begin Result := AFirstPartWordBaseItem; Exit; end; ASyllableCount := ASyllableCountStore2; AWordPartIndex := AWordPartIndexStore2; if AWordPartIndex < AWordPartMaxCount then begin AWordBaseItem := CompoundCheck(AInitialWordForm + AInitialWordJoint, AWordPartIndex + 1, ASyllableCount, AWordPartMaxCount, wnum + 1, words, False, ASuggest); if (AWordBaseItem <> nil) and ( not ((ATableIndex = 0) xor IsForbiddenByCheckCompoundPattern(AWord, AInitialWordJoint, AFirstPartWordBaseItem, AWordBaseItem))) then AWordBaseItem := nil; end else AWordBaseItem := nil; if AWordBaseItem <> nil then begin Result := AFirstPartWordBaseItem; Exit; end; until not ADoubleCharacter or ACheckWordWithoutTripleRemoving; if ACheckWordWithoutTripleRemoving then begin Inc(ACompoundWordJoint); ACheckWordWithoutTripleRemoving := False; ADoubleCharacter := False; end; end; Inc(ATableIndex); until not (FCheckCompoundPatternTable.IsSimplified and (ATableIndex <= FCheckCompoundPatternTable.Count)); ATableIndex := 0; AWordPartIndex := AWordPartIndexStore; ASyllableCount := ASyllableCountStore; Inc(ACompoundWordJoint); end; end; function TdxHunspellAffixManager.ProcessSuffixCheck(const AWord: PAnsiChar; AWordLength, AOptions: Integer; APrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; wlst: PPAnsiChar; maxSug: Integer; ns: PInteger; const ACompatibleFlag, ACompoundPartFlag: Word; ACompoundWordPart: TdxCompoundWordPart): TdxHunspellWordItem; var ASuffix: TdxSuffix; AIncompatibleFlag: Word; begin Result := nil; AIncompatibleFlag := IfThen(ACompoundWordPart <> cwpNone, NullFlag, FOnlyInCompound); ASuffix := TdxSuffix(FSuffixTableArrangedByAppendString[0]); while (ASuffix <> nil) and (Result = nil) do begin if (ACompatibleFlag = 0) or (ASuffix.CompatibleFlags <> nil) then begin if ASuffix.CanUseInTheMiddleOfCompoundWords(ACompoundWordPart) and AreAffixesBothCircumfixesOrNot(APrefix, ASuffix) and ASuffix.CanUseInSimpleWordsOrIsFogemorpheme(ACompoundWordPart) and ((ACompatibleFlag <> NullFlag) or not ASuffix.IsCompatibleWithFlag(FNeedAffix) or (APrefix <> nil) and not APrefix.IsCompatibleWithFlag(FNeedAffix)) then begin Result := ASuffix.CheckWord(AWord, AWordLength, AOptions, APrefix, wlst, maxSug, ns, ACompatibleFlag, ACompoundPartFlag, AIncompatibleFlag); if Result <> nil then ATakenSuffix := ASuffix; end; end; ASuffix := TdxSuffix(ASuffix.Next); end; if Result = nil then begin ASuffix := TdxSuffix(FSuffixTableArrangedByAppendString[Ord((AWord + AWordLength - 1)^)]); while (ASuffix <> nil) and (Result = nil) do begin if IsTrailingReversedSubset(ASuffix.AppendString, AWord + AWordLength - 1, AWordLength) then begin if ASuffix.CanUseInTheMiddleOfCompoundWords(ACompoundWordPart) and AreAffixesBothCircumfixesOrNot(APrefix, ASuffix) and ASuffix.CanUseInSimpleWordsOrIsFogemorpheme(ACompoundWordPart) and ((ACompatibleFlag <> 0) or not ASuffix.IsCompatibleWithFlag(FNeedAffix) or (APrefix <> nil) and not APrefix.IsCompatibleWithFlag(FNeedAffix)) then begin Result := ASuffix.CheckWord(AWord, AWordLength, AOptions, APrefix, wlst, maxSug, ns, ACompatibleFlag, ACompoundPartFlag, AIncompatibleFlag); if Result <> nil then begin ATakenSuffix := ASuffix; FSavedSuffixInfo.Flag := ASuffix.Flag; if ASuffix.CompatibleFlags = nil then FSavedSuffixInfo.AppendString := ASuffix.AppendString; end; end; ASuffix := TdxSuffix(ASuffix.NextSimilar); end else ASuffix := TdxSuffix(ASuffix.NextDifferent); end; end; end; function TdxHunspellAffixManager.ProcessTwoSuffixCheck(const AWord: PAnsiChar; AWordLength, AOptions: Integer; APrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; const ACompoundPartFlag: Word): TdxHunspellWordItem; var ASuffix: TdxSuffix; begin Result := nil; ASuffix := TdxSuffix(FSuffixTableArrangedByAppendString[0]); while (ASuffix <> nil) and (Result = nil) do begin if FCompatibleFlags[ASuffix.Flag] then Result := ASuffix.MakeInitialWordFormAndProcessSuffixCheck(AWord, AWordLength, AOptions, APrefix, ATakenSuffix, ACompoundPartFlag); ASuffix := TdxSuffix(ASuffix.Next); end; if Result = nil then begin ASuffix := TdxSuffix(FSuffixTableArrangedByAppendString[Ord((AWord + AWordLength - 1)^)]); while (ASuffix <> nil) and (Result = nil) do begin if IsTrailingReversedSubset(ASuffix.AppendString, AWord + AWordLength - 1, AWordLength) then begin if FCompatibleFlags[ASuffix.Flag] then begin Result := ASuffix.MakeInitialWordFormAndProcessSuffixCheck(AWord, AWordLength, AOptions, APrefix, ATakenSuffix, ACompoundPartFlag); if Result <> nil then begin FSavedSuffixInfo.Flag := ASuffix.Flag; if ASuffix.CompatibleFlags = nil then FSavedSuffixInfo.AppendString := ASuffix.AppendString; end; end; ASuffix := TdxSuffix(ASuffix.NextSimilar); end else ASuffix := TdxSuffix(ASuffix.NextDifferent); end; end; end; function TdxHunspellAffixManager.AffixCheck(const AWord: PAnsiChar; AWordLength: Integer; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; const ACompoundPartFlag: Word; ACompoundWordPart: TdxCompoundWordPart): TdxHunspellWordItem; begin Result := ProcessPrefixCheck(AWord, AWordLength, ATakenPrefix, ATakenSuffix, ACompoundWordPart, ACompoundPartFlag); if Result = nil then begin Result := ProcessSuffixCheck(AWord, AWordLength, 0, nil, ATakenSuffix, nil, 0, nil, NullFlag, ACompoundPartFlag, ACompoundWordPart); if FCompatibleFlagsExist then begin ATakenSuffix := nil; ATakenPrefix := nil; if Result = nil then begin Result := ProcessTwoSuffixCheck(AWord, AWordLength, 0, nil, ATakenSuffix, ACompoundPartFlag); if Result = nil then Result := ProcessPrefixWithSuffixesCheck(AWord, AWordLength, ATakenPrefix, ATakenSuffix, cwpNone, ACompoundPartFlag); end; end; end; end; function TdxHunspellAffixManager.EncodeFlag(AFlag: Word): PAnsiChar; begin Result := FWordBaseManager.EncodeFlag(AFlag); end; procedure TdxHunspellAffixManager.SetCompatibleFlags(AIndex: Integer; AValue: Boolean); begin FCompatibleFlags[AIndex] := AValue; end; function TdxHunspellAffixManager.Lookup(const AWord: PAnsiChar): TdxHunspellWordItem; begin Result := FWordBaseManager.Lookup(AWord); end; function TdxHunspellAffixManager.ParseCompoundSyllable(ALine: PAnsiChar; AAffixFileManager: TdxHunspellReader): Boolean; var ALineCursor, APiece: PAnsiChar; I, APartCount: Integer; begin Result := False; ALineCursor := ALine; APiece := StrSeparate(@ALineCursor, #0); APartCount := 0; I := 0; while APiece <> nil do begin if APiece^ <> #0 then begin case I of 0: Inc(APartCount); 1: begin FMaxCompoundSyllable := StrInt(APiece); Inc(APartCount); end; 2: begin if not IsUTF8 then FCompoundVowels := StrNew(APiece) else begin //TODO: end; Inc(APartCount); end; end; Inc(I); end; APiece := StrSeparate(@ALineCursor, #0); end; if APartCount < 2 then begin Result := True; Exit; end; if APartCount = 2 then FCompoundVowels := StrNew(PAnsiChar('aeiouAEIOU')); end; function TdxHunspellAffixManager.ParseRep(ALine: PAnsiChar; AAffixFileManager: TdxHunspellReader): Boolean; begin Result := not FReplaceTable.ReadData(ALine, AAffixFileManager); end; function TdxHunspellAffixManager.AreAffixesBothCircumfixesOrNot(APrefix, ASuffix: TdxAffix): Boolean; begin Result := (FCircumfix = NullFlag) or not (((APrefix <> nil) and APrefix.IsCircumfix) xor ASuffix.IsCircumfix); end; function TdxHunspellAffixManager.CanBeFirstPartInCompoundWord( AWordBaseItem: TdxHunspellWordItem; AWordPartIndex: Cardinal): Boolean; begin Result := ((FCompoundFlag <> 0) and AWordBaseItem.IsCompatibleWithFlag(FCompoundFlag)) or ((AWordPartIndex = 0) and (FCompoundBegin <> 0) and AWordBaseItem.IsCompatibleWithFlag(FCompoundBegin)) or ((AWordPartIndex > 0) and (FCompoundMiddle <> 0) and AWordBaseItem.IsCompatibleWithFlag(FCompoundMiddle)); end; procedure TdxHunspellAffixManager.CompoundRootCheck( AWordBaseItem: TdxHunspellWordItem; var AWordPartIndex: SmallInt); begin if (AWordBaseItem <> nil) and (FCompoundRoot <> 0) and AWordBaseItem.IsCompatibleWithFlag(FCompoundRoot) then Inc(AWordPartIndex); end; function TdxHunspellAffixManager.IsForbiddenOrNoSuggestWord( AWordBaseItem: TdxHunspellWordItem; ASuggest: Boolean): Boolean; begin Result := (AWordBaseItem <> nil) and (AWordBaseItem.IsCompatibleWithFlag(FForbiddenWord) or (ASuggest and (FNoSuggest <> 0) and AWordBaseItem.IsCompatibleWithFlag(FNoSuggest))); end; function TdxHunspellAffixManager.IsWordPartSuitsCheckCompoundPatternTable( AWordBaseItem: TdxHunspellWordItem; ATableIndex: Integer; AIsFirstWordPart: Boolean): Boolean; var AFlag: Word; begin Result := ATableIndex = 0; if not Result then begin if AIsFirstWordPart then AFlag := FCheckCompoundPatternTable[ATableIndex - 1].Condition else AFlag := FCheckCompoundPatternTable[ATableIndex - 1].Condition2; Result := (AFlag = NullFlag) or AWordBaseItem.IsCompatibleWithFlag(AFlag); end; end; function TdxHunspellAffixManager.IsWordPartSuitsCompoundFlags(AWordBaseItem: TdxHunspellWordItem; AWordPartIndex: SmallInt; AIsFirstWordPart: Boolean; words: PdxHunspellWordBaseTable): Boolean; begin Result := (words = nil) and AWordBaseItem.IsCompatibleWithFlag(FCompoundFlag); if not Result then begin if AIsFirstWordPart then Result := ((AWordPartIndex = 0) and AWordBaseItem.IsCompatibleWithFlag(FCompoundBegin)) or ((AWordPartIndex <> 0) and (words = nil) and AWordBaseItem.IsCompatibleWithFlag(FCompoundMiddle)) else Result := (words = nil) and AWordBaseItem.IsCompatibleWithFlag(FCompoundEnd); end; end; function TdxHunspellAffixManager.IsTripleLettersExistAndForbid(AWord: PAnsiChar; AJoint: Cardinal): Boolean; begin Result := FCheckCompoundTriple and (AWord[AJoint - 1] = AWord[AJoint]) and ( ((AJoint > 1) and (AWord[AJoint - 1] = AWord[AJoint - 2])) or ((AJoint + 1 < StrLen(AWord)) and (AWord[AJoint - 1] = AWord[AJoint + 1]))); end; function TdxHunspellAffixManager.UseWithAffixOnly(AWordBaseItem: TdxHunspellWordItem): Boolean; begin Result := (FNeedAffix <> 0) and AWordBaseItem.IsCompatibleWithFlag(FNeedAffix); end; function TdxHunspellAffixManager.HungarianCheck(AWordBaseItem: TdxHunspellWordItem; hu_mov_rule: Boolean): Boolean; begin Result := IsHungarian and hu_mov_rule and ( AWordBaseItem.IsCompatibleWithFlag(Ord('F')) or AWordBaseItem.IsCompatibleWithFlag(Ord('G')) or AWordBaseItem.IsCompatibleWithFlag(Ord('H'))); end; function TdxHunspellAffixManager.HungarianCheck2(var AWordBaseItem: TdxHunspellWordItem; AInitialWordForm: TWordFormArray; ACompoundWordJoint: Integer; hu_mov_rule: Boolean; var ATakenPrefix: TdxPrefix; var ATakenSuffix: TdxSuffix): Boolean; function DoAffixCheck: Boolean; begin AWordBaseItem := AffixCheck(AInitialWordForm, ACompoundWordJoint, ATakenPrefix, ATakenSuffix); Result := AWordBaseItem <> nil; end; begin Result := (AWordBaseItem = nil) and IsHungarian and hu_mov_rule and DoAffixCheck and ((ATakenSuffix <> nil) and (ATakenSuffix.IsCompatibleWithFlag(Ord('x')) or ATakenSuffix.IsCompatibleWithFlag(Ord('%')))); end; function TdxHunspellAffixManager.ParseAffix(ALine: PAnsiChar; const IsPrefix: Boolean; AAffixFileManager: TdxHunspellReader; AExistentAffixFlags: TdxExistentAffixFlags): Boolean; var AAffixItem: TdxAffixItemTable; begin if IsPrefix then AAffixItem := TdxPrefixItemTable.Create(FWordBaseManager, Self, AExistentAffixFlags) else AAffixItem := TdxSuffixItemTable.Create(FWordBaseManager, Self, AExistentAffixFlags); try Result := not AAffixItem.ReadData(ALine, AAffixFileManager); if not Result then AAffixItem.BuildAffixes else dxHunspellError(AAffixItem.Flag + ' affix is invalid in file ' + FAffixFilePath); finally AAffixItem.Free; end; end; function TdxHunspellAffixManager.GetIsCompoundWordsAvailable: Boolean; begin Result := (FCompoundFlag <> NullFlag) or (FCompoundBegin <> NullFlag) or (FCompoundRuleTable.Count > 0); end; function TdxHunspellAffixManager.GetParseClasses: TObjectList; begin Result := TObjectList.Create; Result.Add(TTryReader.Create(Self)); Result.Add(TSetReader.Create(Self)); Result.Add(TComplexPrefixesReader.Create(Self)); Result.Add(TCompoundFlagReader.Create(Self)); Result.Add(TCompoundBeginReader.Create(Self)); Result.Add(TCompoundEndReader.Create(Self)); Result.Add(TCompoundMiddleReader.Create(Self)); Result.Add(TCompoundWordMaxReader.Create(Self)); Result.Add(TCompoundRootReader.Create(Self)); Result.Add(TCompoundPermitFlagReader.Create(Self)); Result.Add(TCompoundForbidFlagReader.Create(Self)); Result.Add(TCheckCompoundDupReader.Create(Self)); Result.Add(TCheckCompoundRepReader.Create(Self)); Result.Add(TCheckCompoundTripleReader.Create(Self)); Result.Add(TSimplifiedTripleReader.Create(Self)); Result.Add(TCheckCompoundCaseReader.Create(Self)); Result.Add(TNoSuggestReader.Create(Self)); Result.Add(TForbiddenWordReader.Create(Self)); Result.Add(TLemmaPresentReader.Create(Self)); Result.Add(TCircumfixReader.Create(Self)); Result.Add(TOnlyInCompoundReader.Create(Self)); Result.Add(TPseudoRootReader.Create(Self)); Result.Add(TNeedAffixReader.Create(Self)); Result.Add(TCompoundMinReader.Create(Self)); Result.Add(TCompoundSyllableReader.Create(Self)); Result.Add(TSyllableNumReader.Create(Self)); Result.Add(TWordCharsReader.Create(Self)); Result.Add(TIgnoreReader.Create(Self)); Result.Add(TRepReader.Create(Self)); Result.Add(TIConvReader.Create(Self)); Result.Add(TOConvReader.Create(Self)); Result.Add(TCheckCompoundPatternReader.Create(Self)); Result.Add(TCompoundRuleReader.Create(Self)); Result.Add(TMapReader.Create(Self)); Result.Add(TBreakReader.Create(Self)); Result.Add(TLangReader.Create(Self)); Result.Add(TVersionReader.Create(Self)); Result.Add(TNoSplitSugsReader.Create(Self)); Result.Add(TFullStripReader.Create(Self)); Result.Add(TSugsWithDotsReader.Create(Self)); Result.Add(TKeepCaseReader.Create(Self)); Result.Add(TSubStandardReader.Create(Self)); Result.Add(TCheckSharpsReader.Create(Self)); Result.Add(TPrefixReader.Create(Self)); Result.Add(TSuffixReader.Create(Self)); end; procedure TdxHunspellAffixManager.SetLanguage(Value: Integer); begin FLanguage := Value; FIsHungarian := Language = LanguageHu; //TODO: end; { TdxAffix } constructor TdxAffix.Create(AAffixManager: TdxHunspellAffixManager; AAffixItem: PdxAffixItem; AFlag: Word; AOptions: ShortInt); begin FAffixManager := AAffixManager; FFlag := AFlag; FStripString := AAffixItem.FStripString; FAppendString := AAffixItem.FAppendString; FStripStringLength := AAffixItem.FStripStringLength; FAppendStringLength := AAffixItem.FAppendStringLength; FConditionLength := AAffixItem.FConditionLength; FOptions := AOptions; FConditions := AllocMem(SizeOf(TdxAffixCondition)); if FOptions and aeLONGCOND <> 0 then begin Move(AAffixItem.ConditionFirstPart, FConditions.Condition, MaxConditionLength); FConditions.ConditionSecondPart := AAffixItem.ConditionSecondPart; end else Move(AAffixItem.Condition, FConditions.Condition, MaxConditionLength); FMorphologicalDescription := AAffixItem.FMorphologicalDescription; FCompatibleFlags := AAffixItem.FCompatibleFlags; end; destructor TdxAffix.Destroy; begin FFlag := 0; StrDispose(FAppendString); StrDispose(FStripString); FAppendString := nil; FStripString := nil; if FOptions and aeLONGCOND <> 0 then StrDispose(FConditions.ConditionSecondPart); FreeMem(FConditions, SizeOf(TdxAffixCondition)); if (FMorphologicalDescription <> nil) and (FOptions and aoAffixMorphologyTable = 0) then StrDispose(FMorphologicalDescription); FreeAndNil(FCompatibleFlags); inherited Destroy; end; function TdxAffix.CanUseInSimpleWordsOrIsFogemorpheme(ACompoundWordPart: TdxCompoundWordPart): Boolean; begin Result := (ACompoundWordPart <> cwpNone) or not IsCompatibleWithFlag(AffixManager.OnlyInCompound); end; function TdxAffix.CanUseInTheMiddleOfCompoundWords(ACompoundWordPart: TdxCompoundWordPart): Boolean; begin Result := CanUseInCompoundWordPart(ACompoundWordPart) or IsCompatibleWithFlag(AffixManager.FCompoundPermitFlag); end; function TdxAffix.GetNextConditionChar(AConditionCursor: PAnsiChar): PAnsiChar; begin Result := nil; if AConditionCursor <> nil then begin Inc(AConditionCursor); if FOptions and aeLONGCOND <> 0 then begin if AConditionCursor = FConditions.Condition + MaxConditionLength then AConditionCursor := FConditions.ConditionSecondPart; end else if AConditionCursor = FConditions.Condition + MaxConditionLength then Exit; if AConditionCursor^ <> #0 then Result := AConditionCursor; end; end; function TdxAffix.IsCompatibleWithFlag(AFlag: Word; CompatibleIfNull: Boolean = False): Boolean; begin Result := (CompatibleFlags <> nil) and CompatibleFlags.ContainsFlag(AFlag); if CompatibleIfNull then Result := Result or (AFlag = NullFlag); end; function TdxAffix.IsPrefixSuffixUnion(AOptions: ShortInt): Boolean; begin Result := AOptions and aoPrefixSuffixUnion <> 0 end; function TdxAffix.IsWithoutAffixWordLengthCorrect(ALengthWithoutAffix: Integer): Boolean; begin Result := ((ALengthWithoutAffix > 0) or (ALengthWithoutAffix = 0) and AffixManager.FullStrip) and (ALengthWithoutAffix + FStripStringLength >= FConditionLength); end; procedure TdxAffix.SearchConditionGroupEnd(var AConditionCursor: PAnsiChar); var ANextConditionChar: PAnsiChar; begin ANextConditionChar := GetNextConditionChar(AConditionCursor); while (AConditionCursor <> nil) and (AConditionCursor^ <> ']') and (ANextConditionChar <> nil) do begin AConditionCursor := ANextConditionChar; ANextConditionChar := GetNextConditionChar(AConditionCursor); end; end; function TdxAffix.IsCircumfix: Boolean; begin Result := IsCompatibleWithFlag(AffixManager.FCircumfix); end; function TdxAffix.GetAppendString: PAnsiChar; begin Result := FAppendString; end; function TdxAffix.GetPrefixSuffixUnion: Boolean; begin Result := IsPrefixSuffixUnion(FOptions); end; { TdxPrefix } function TdxPrefix.CanUseInCompoundWordPart(ACompoundWordPart: TdxCompoundWordPart): Boolean; begin Result := ACompoundWordPart <> cwpLast; end; function TdxPrefix.CheckWord(const AWord: PAnsiChar; AWordLength: Integer; var ATakenSuffix: TdxSuffix; ACompoundWordPart: TdxCompoundWordPart; const ACompoundPartFlag: Word = NullFlag): TdxHunspellWordItem; var AInitialWordFormLength: Integer; AWordBaseTableItem: TdxHunspellWordItem; AInitialWordForm: array[0..MAXWORDUTF8LEN + 3] of AnsiChar; begin Result := nil; AInitialWordFormLength := AWordLength - AppendStringLength; if IsWithoutAffixWordLengthCorrect(AInitialWordFormLength) then begin if FStripStringLength <> 0 then StrCopy(AInitialWordForm, FStripString); StrCopy(AInitialWordForm + FStripStringLength, AWord + AppendStringLength); if IsWordSuitableToCondition(AInitialWordForm) then begin Inc(AInitialWordFormLength, FStripStringLength); AWordBaseTableItem := AffixManager.Lookup(AInitialWordForm); if AWordBaseTableItem <> nil then repeat if AWordBaseTableItem.IsCompatibleWithFlag(FFlag) and not IsCompatibleWithFlag(AffixManager.FNeedAffix) and (AWordBaseTableItem.IsCompatibleWithFlag(ACompoundPartFlag, True) or IsCompatibleWithFlag(ACompoundPartFlag)) then Result := AWordBaseTableItem; AWordBaseTableItem := AWordBaseTableItem.NextHomonym; until (AWordBaseTableItem = nil) or (Result <> nil); if Result = nil then begin if PrefixSuffixUnion then Result := AffixManager.ProcessSuffixCheck(AInitialWordForm, AInitialWordFormLength, aoPrefixSuffixUnion, Self, ATakenSuffix, nil, 0, nil, NullFlag, ACompoundPartFlag, ACompoundWordPart); end; end; end; end; function TdxPrefix.MakeInitialWordFormAndProcessTwoSuffixCheck(const AWord: PAnsiChar; AWordLength: Integer; var ATakenSuffix: TdxSuffix; ACompoundWordPart: TdxCompoundWordPart; const ACompoundPartFlag: Word = NullFlag): TdxHunspellWordItem; var AInitialWordFormLength: Integer; AInitialWordForm: array[0..MAXWORDUTF8LEN + 3] of AnsiChar; begin Result := nil; AInitialWordFormLength := AWordLength - AppendStringLength; if IsWithoutAffixWordLengthCorrect(AInitialWordFormLength) then begin if FStripStringLength <> 0 then StrCopy(AInitialWordForm, FStripString); StrCopy(AInitialWordForm + FStripStringLength, AWord + AppendStringLength); if IsWordSuitableToCondition(AInitialWordForm) then begin Inc(AInitialWordFormLength, FStripStringLength); if PrefixSuffixUnion and (ACompoundWordPart <> cwpFirst) then Result := AffixManager.ProcessTwoSuffixCheck(AInitialWordForm, AInitialWordFormLength, aoPrefixSuffixUnion, Self, ATakenSuffix, ACompoundPartFlag); end; end; end; function TdxPrefix.IsWordSuitableToCondition(AWord: PAnsiChar): Boolean; procedure GoForward; begin Inc(AWord); while (FOptions and aeUTF8 <> 0) and (Ord(AWord^) and $C0 = $80) do Inc(AWord); end; var AWordCursor: PAnsiChar; ANegative: Boolean; AIsGroup: Boolean; AConditionCursor: PAnsiChar; begin Result := False; AWordCursor := nil; ANegative := False; AIsGroup := False; if FConditionLength = 0 then begin Result := True; Exit; end; AConditionCursor := FConditions.Condition; while True do begin case AConditionCursor^ of #0: begin Result := True; Exit; end; '[': begin ANegative := False; AIsGroup := False; AConditionCursor := GetNextConditionChar(AConditionCursor); AWordCursor := AWord; end; '^': begin AConditionCursor := GetNextConditionChar(AConditionCursor); ANegative := True; end; ']': begin if ANegative and AIsGroup or not ANegative and not AIsGroup then Exit; AWordCursor := nil; AConditionCursor := GetNextConditionChar(AConditionCursor); if not AIsGroup then GoForward; if (AWord^ = #0) and (AConditionCursor <> nil) then Exit; end; '.': if AWordCursor = nil then begin AConditionCursor := GetNextConditionChar(AConditionCursor); GoForward; if (AWord^ = #0) and (AConditionCursor <> nil) then Exit; end; else if AWord^ = AConditionCursor^ then begin Inc(AWord); AConditionCursor := GetNextConditionChar(AConditionCursor); if (FOptions and aeUTF8 <> 0) and (Ord((AWord - 1)^) and $80 <> 0) then begin while (AConditionCursor <> nil) and (Ord(AConditionCursor^) and $C0 = $80) do begin if (AConditionCursor^ <> AWord^) then begin if AWordCursor = nil then Exit; AWord := AWordCursor; Break; end; AConditionCursor := GetNextConditionChar(AConditionCursor); Inc(AWord); end; if (AWordCursor <> nil) and (AWord <> AWordCursor) then begin AIsGroup := True; SearchConditionGroupEnd(AConditionCursor); end; end else if AWordCursor <> nil then begin AIsGroup := True; SearchConditionGroupEnd(AConditionCursor); end; end else if AWordCursor <> nil then AConditionCursor := GetNextConditionChar(AConditionCursor) else Exit; end; if AConditionCursor = nil then begin Result := True; Exit; end; end; end; { TdxSuffix } constructor TdxSuffix.Create(AAffixManager: TdxHunspellAffixManager; AAffixItem: PdxAffixItem; AFlag: Word; AOptions: ShortInt); begin inherited Create(AAffixManager, AAffixItem, AFlag, AOptions); FReverseAppendString := StrCopyReverse(FAppendString); end; destructor TdxSuffix.Destroy; begin StrDispose(FReverseAppendString); inherited Destroy; end; function TdxSuffix.CanUseInCompoundWordPart(ACompoundWordPart: TdxCompoundWordPart): Boolean; begin Result := ACompoundWordPart <> cwpFirst; end; function TdxSuffix.GetWordWithAffix(const AWord: PAnsiChar; AWordLength: Integer): PAnsiChar; var ATempWord: array[0..MAXWORDUTF8LEN + 3] of AnsiChar; begin Result := nil; if ((AWordLength > FStripStringLength) or (AWordLength = 0) and (AffixManager.FullStrip)) and (AWordLength >= FConditionLength) and IsWordSuitableToCondition(AWord + AWordLength, AWord) and ((FStripStringLength = 0) or (StrComp(AWord + AWordLength - FStripStringLength, FStripString) = 0)) and (MAXWORDUTF8LEN + 4 > AWordLength + AppendStringLength - FStripStringLength) then begin StrCopy(ATempWord, AWord); if AppendStringLength <> 0 then StrCopy(ATempWord + AWordLength - FStripStringLength, FAppendString) else (ATempWord + AWordLength - FStripStringLength)^ := #0; Result := StrNew(ATempWord); end; end; function TdxSuffix.CheckWord(const AWord: PAnsiChar; AWordLength, AOptions: Integer; APrefix: TdxPrefix; wlst: PPAnsiChar; maxSug: Integer; ns: PInteger; const ACompatibleFlag: Word = NullFlag; const ACompoundPartFlag: Word = NullFlag; const AIncompatibleFlag: Word = NullFlag): TdxHunspellWordItem; function IsIncompatibleWithFlag(AWordBaseTableItem: TdxHunspellWordItem; AFlag: Word): Boolean; begin Result := (AFlag = 0) or not AWordBaseTableItem.IsCompatibleWithFlag(AFlag); end; var AInitialWordFormLength: Integer; AWordBaseTableItem: TdxHunspellWordItem; AInitialWordFormEndPointer: PAnsiChar; AInitialWordForm: array[0..MAXWORDUTF8LEN + 3] of AnsiChar; cwrd: Boolean; K: Integer; AElement: PPAnsiChar; begin Result := nil; if IsPrefixSuffixUnion(AOptions) and not PrefixSuffixUnion then Exit; AInitialWordFormLength := AWordLength - AppendStringLength; if IsWithoutAffixWordLengthCorrect(AInitialWordFormLength) then begin StrCopy(AInitialWordForm, AWord); AInitialWordFormEndPointer := AInitialWordForm + AInitialWordFormLength; if FStripStringLength <> 0 then begin StrCopy(AInitialWordFormEndPointer, FStripString); Inc(AInitialWordFormLength, FStripStringLength); AInitialWordFormEndPointer := AInitialWordForm + AInitialWordFormLength; end else AInitialWordFormEndPointer^ := #0; if IsWordSuitableToCondition(AInitialWordFormEndPointer, AInitialWordForm) then begin AWordBaseTableItem := AffixManager.Lookup(AInitialWordForm); if AWordBaseTableItem <> nil then begin repeat if (AWordBaseTableItem.IsCompatibleWithFlag(FFlag) or (APrefix <> nil) and APrefix.IsCompatibleWithFlag(FFlag)) and (not IsPrefixSuffixUnion(AOptions) or AWordBaseTableItem.IsCompatibleWithFlag(APrefix.Flag) or IsCompatibleWithFlag(APrefix.Flag)) and IsCompatibleWithFlag(ACompatibleFlag, True) and IsIncompatibleWithFlag(AWordBaseTableItem, AIncompatibleFlag) and ((ACompoundPartFlag = 0) or (AWordBaseTableItem.IsCompatibleWithFlag(ACompoundPartFlag, True) or IsCompatibleWithFlag(ACompoundPartFlag))) then Result := AWordBaseTableItem; AWordBaseTableItem := AWordBaseTableItem.NextHomonym; until (AWordBaseTableItem = nil) or (Result <> nil); end else if (wlst <> nil) and (ns^ < maxSug) then begin cwrd := True; for K := 0 to ns^ - 1 do if StrComp(AInitialWordForm, GetElementPointer(wlst, K)^) = 0 then cwrd := False; if cwrd then begin AElement := GetElementPointer(wlst, ns^); AElement^ := StrNew(AInitialWordForm); if AElement^ = nil then begin for K := 0 to ns^ - 1 do StrDispose(GetElementPointer(wlst, K)^); ns^ := -1; Exit; end; Inc(ns^); end; end; end; end; end; function TdxSuffix.MakeInitialWordFormAndProcessSuffixCheck(const AWord: PAnsiChar; AWordLength, AOptions: Integer; APrefix: TdxPrefix; var ATakenSuffix: TdxSuffix; const ACompoundPartFlag: Word = 0): TdxHunspellWordItem; var AInitialWordFormLength: Integer; AInitialWordFormEndPointer: PAnsiChar; AInitialWordForm: array[0..MAXWORDUTF8LEN + 3] of AnsiChar; begin Result := nil; if IsPrefixSuffixUnion(AOptions) and not PrefixSuffixUnion then Exit; AInitialWordFormLength := AWordLength - AppendStringLength; if IsWithoutAffixWordLengthCorrect(AInitialWordFormLength) then begin StrCopy(AInitialWordForm, AWord); AInitialWordFormEndPointer := AInitialWordForm + AInitialWordFormLength; if FStripStringLength <> 0 then begin StrCopy(AInitialWordFormEndPointer, FStripString); Inc(AInitialWordFormLength, FStripStringLength); AInitialWordFormEndPointer := AInitialWordForm + AInitialWordFormLength; end else AInitialWordFormEndPointer^ := #0; if IsWordSuitableToCondition(AInitialWordFormEndPointer, AInitialWordForm) then begin if APrefix <> nil then begin if IsCompatibleWithFlag(APrefix.Flag) then Result := AffixManager.ProcessSuffixCheck(AInitialWordForm, AInitialWordFormLength, 0, nil, ATakenSuffix, nil, 0, nil, FFlag, ACompoundPartFlag) else Result := AffixManager.ProcessSuffixCheck(AInitialWordForm, AInitialWordFormLength, AOptions, APrefix, ATakenSuffix, nil, 0, nil, FFlag, ACompoundPartFlag); end else Result := AffixManager.ProcessSuffixCheck(AInitialWordForm, AInitialWordFormLength, 0, nil, ATakenSuffix, nil, 0, nil, FFlag, ACompoundPartFlag); end; end; end; function TdxSuffix.GetAppendString: PAnsiChar; begin Result := FReverseAppendString; end; function TdxSuffix.getLM: TdxSuffix; begin Result := l_morph; end; function TdxSuffix.getRM: TdxSuffix; begin Result := r_morph; end; function TdxSuffix.getEQM: TdxSuffix; begin Result := eq_morph; end; function TdxSuffix.IsWordSuitableToCondition(AWordEnd, AWordBegin: PAnsiChar): Boolean; procedure GoForward; begin while (FOptions and aeUTF8 <> 0) and (AWordEnd >= AWordBegin) and (Ord(AWordEnd^) and $C0 = $80) do Dec(AWordEnd); end; var AWordCursor: PAnsiChar; ANegative: Boolean; AIsGroup: Boolean; AConditionCursor: PAnsiChar; I: Integer; begin Result := False; AWordCursor := nil; ANegative := False; AIsGroup := False; if FConditionLength = 0 then begin Result := True; Exit; end; AConditionCursor := FConditions.Condition; Dec(AWordEnd); I := 1; while True do begin case AConditionCursor^ of #0: begin Result := True; Exit; end; '[': begin AConditionCursor := GetNextConditionChar(AConditionCursor); AWordCursor := AWordEnd; end; '^': begin AConditionCursor := GetNextConditionChar(AConditionCursor); ANegative := True; end; ']': begin if not ANegative and not AIsGroup then Exit; Inc(I); if not AIsGroup then begin GoForward; Dec(AWordEnd); end; AWordCursor := nil; ANegative := False; AIsGroup := False; AConditionCursor := GetNextConditionChar(AConditionCursor); if (AWordEnd < AWordBegin) and (AConditionCursor <> nil) then Exit; end; '.': if AWordCursor = nil then begin AConditionCursor := GetNextConditionChar(AConditionCursor); Dec(AWordEnd); GoForward; if AWordEnd < AWordBegin then begin if AConditionCursor = nil then Result := True; Exit; end; if (FOptions and aeUTF8 <> 0) and (Ord(AWordEnd^) and $80 <> 0) then begin Dec(AWordEnd); if AWordEnd < AWordBegin then begin if AConditionCursor = nil then Result := True; Exit; end; end; end; else if AWordEnd^ = AConditionCursor^ then begin AConditionCursor := GetNextConditionChar(AConditionCursor); if (FOptions and aeUTF8 <> 0) and (Ord(AWordEnd^) and $80 <> 0) then begin Dec(AWordEnd); while (AConditionCursor <> nil) and (AWordEnd >= AWordBegin) do begin if AConditionCursor^ <> AWordEnd^ then begin if AWordCursor = nil then Exit; AWordEnd := AWordCursor; Break; end; if Ord(AConditionCursor^) and $C0 <> $80 then Break; AConditionCursor := GetNextConditionChar(AConditionCursor); Dec(AWordEnd); end; if (AWordCursor <> nil) and (AWordEnd <> AWordCursor) then begin if ANegative then Exit else if I = FConditionLength then begin Result := True; Exit; end; AIsGroup := True; SearchConditionGroupEnd(AConditionCursor); Dec(AWordEnd); end; if (AConditionCursor <> nil) and (AConditionCursor^ <> ']') then AConditionCursor := GetNextConditionChar(AConditionCursor); end else if AWordCursor <> nil then begin if ANegative then Exit else if I = FConditionLength then begin Result := True; Exit; end; AIsGroup := True; SearchConditionGroupEnd(AConditionCursor); Dec(AWordEnd); end; if AWordCursor = nil then begin Inc(I); Dec(AWordEnd); end; if (AWordEnd < AWordBegin) and (AConditionCursor <> nil) and (AConditionCursor^ <> ']') then Exit; end else if AWordCursor <> nil then AConditionCursor := GetNextConditionChar(AConditionCursor) else Exit; end; if AConditionCursor = nil then begin Result := True; Exit; end; end; end; { TdxAffixDataTable } constructor TdxSpellCheckerDataTable.Create; begin // do nothing end; constructor TdxSpellCheckerDataTable.Create(ADataIdentifier: PAnsiChar); begin inherited Create; FDataIdentifier := ADataIdentifier; end; destructor TdxSpellCheckerDataTable.Destroy; begin FreeData; inherited Destroy; end; function TdxSpellCheckerDataTable.AllocateData(ASize: Integer): Boolean; begin Result := True; FreeData; FSize := ASize; try FData := AllocMem(GetDataItemSize * ASize); except on EOutOfMemory do begin FSize := 0; Result := False; end; end; end; function TdxSpellCheckerDataTable.ReadData(ALine: PAnsiChar; AAffixFileManager: TdxHunspellReader): Boolean; begin Result := ReadDataHeader(ALine); if Result then Result := ParseData(AAffixFileManager); if not Result then FreeData; end; function TdxSpellCheckerDataTable.AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; begin Result := True; if APieceIndex = 0 then begin Result := CheckDataType(APiece); if Result then begin FCurrentDataIndex := ADataIndex; InitializeItem(ADataIndex); end; end else if FCurrentDataIndex <> ADataIndex then Result := False; end; function TdxSpellCheckerDataTable.IsAllPiecesFound(APieceIndex: Integer): Boolean; begin Result := APieceIndex >= GetPieceQuantity; end; function TdxSpellCheckerDataTable.CheckDataType(const APiece: PAnsiChar): Boolean; var ADataIdentifier: PAnsiChar; begin ADataIdentifier := FDataIdentifier; if ADataIdentifier = nil then ADataIdentifier := GetDataIdentifier; Result := StrLComp(APiece, ADataIdentifier, StrLen(ADataIdentifier)) = 0; end; procedure TdxSpellCheckerDataTable.InitializeItem(Index: Integer); begin // do nothing end; function TdxSpellCheckerDataTable.ReadDataHeader(ALine: PAnsiChar): Boolean; var ATableSize: Integer; begin Result := ParseHeader(ALine, ATableSize); if Result then AllocateData(ATableSize); end; function TdxSpellCheckerDataTable.ParseHeader(ALine: PAnsiChar; out ATableSize: Integer): Boolean; var APiece: PAnsiChar; begin ATableSize := 0; APiece := nil; Result := GetSecondPartOfString(ALine, APiece); if Result then begin ATableSize := StrInt(APiece); StrDispose(APiece); end; end; function TdxSpellCheckerDataTable.ParseData(AAffixFileManager: TdxHunspellReader): Boolean; var ADataIndex, APieceIndex: Integer; ALineStart, ALineCursor, APiece: PAnsiChar; begin Result := True; for ADataIndex := 0 to Count - 1 do begin ALineStart := AAffixFileManager.getline; try RemoveCRLF(ALineStart); ALineCursor := ALineStart; APieceIndex := 0; repeat APiece := StrSeparate(@ALineCursor, #0); if (APiece <> nil) and (APiece^ <> #0) then begin Result := AddDataPiece(APiece, ADataIndex, APieceIndex); Inc(APieceIndex); end; until (APiece = nil) or not Result; if not IsAllPiecesFound(APieceIndex) then begin Result := False; Break; end; finally StrDispose(ALineStart); end; end; end; procedure TdxSpellCheckerDataTable.FreeData; begin if FData <> nil then begin FreeDataItems; FreeMem(FData, GetDataItemSize * FSize); FData := nil; end; FSize := 0; end; { TdxAffixDataLinkedTable } constructor TdxSpellCheckerDataLinkedTable.Create(AWordBaseManager: TdxHunspellWordBaseManager; AAffixManager: TdxHunspellAffixManager); begin inherited Create; FWordBaseManager := AWordBaseManager; FAffixManager := AAffixManager; end; { TdxAffixItemTable } constructor TdxAffixItemTable.Create(AWordBaseManager: TdxHunspellWordBaseManager; AAffixManager: TdxHunspellAffixManager; AExistentAffixFlags: TdxExistentAffixFlags); begin inherited Create(AWordBaseManager, AAffixManager); FExistentAffixFlags := AExistentAffixFlags; end; destructor TdxAffixItemTable.Destroy; begin StrDispose(FFlag); inherited Destroy; end; function TdxAffixItemTable.AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; begin Result := inherited AddDataPiece(APiece, ADataIndex, APieceIndex); if Result then case APieceIndex of 1: Result := CheckFlag(APiece); 2: ParseStripString(APiece, ADataIndex); 3: Result := ParseAppendStringWithAffixes(APiece, ADataIndex); 4: Result := ParseCondition(APiece, ADataIndex); 5: Result := ParseMorphologicDescription(APiece, ADataIndex); 6: AddLineRemainderToMorphologicDescription(APiece, ADataIndex); end; end; function TdxAffixItemTable.IsAllPiecesFound(APieceIndex: Integer): Boolean; begin Result := True; end; function TdxAffixItemTable.IsComplexPrefixes: Boolean; begin Result := FAffixManager.ComplexPrefixes; end; procedure TdxAffixItemTable.BuildAffixes; begin FAffixesIsCreated := True; end; function TdxAffixItemTable.GetDataItemSize: Integer; begin Result := SizeOf(TdxAffixItem); end; function TdxAffixItemTable.GetDataIdentifier: PAnsiChar; begin if IsPrefix then Result := 'PFX' else Result := 'SFX'; end; function TdxAffixItemTable.GetPieceQuantity: Integer; begin Result := 7; end; procedure TdxAffixItemTable.FreeDataItems; var I: Integer; begin if not FAffixesIsCreated then for I := 0 to Count - 1 do begin StrDispose(Data[I].FStripString); StrDispose(Data[I].FAppendString); if not HasAffixMorphologicAliases then StrDispose(Data[I].FMorphologicalDescription); if Data[I].LongCondition then StrDispose(Data[I].ConditionSecondPart); FreeAndNil(Data[I].FCompatibleFlags); end; end; function TdxAffixItemTable.ParseHeader(ALine: PAnsiChar; out ATableSize: Integer): Boolean; var I: Integer; APiece: PAnsiChar; begin Result := True; APiece := StrSeparate(@ALine, #0); I := 0; while (APiece <> nil) and Result and (I < 4) do begin if APiece^ <> #0 then begin case I of 1: Result := ParseFlag(APiece); 2: ParsePrefixSuffixUnion(APiece); 3: Result := ParseTableSize(APiece, ATableSize); end; Inc(I); end; APiece := StrSeparate(@ALine, #0); end; end; function TdxAffixItemTable.GetData: PdxAffixItemArray; begin Result := PdxAffixItemArray(FData); end; function TdxAffixItemTable.GetItem(Index: Integer): TdxAffixItem; begin Result := Data[Index]; end; procedure TdxAffixItemTable.AddLineRemainderToMorphologicDescription( ALine: PAnsiChar; ADataIndex: Integer); var ANewDescription: PAnsiChar; ALength: Integer; begin if not HasAffixMorphologicAliases then begin ALength := StrLen(Data[ADataIndex].FMorphologicalDescription) + StrLen(ALine) + 2; {$IFDEF DELPHI12} ANewDescription := AnsiStrAlloc(ALength); {$ELSE} ANewDescription := StrAlloc(ALength); {$ENDIF} StrCopy(ANewDescription, Data[ADataIndex].FMorphologicalDescription); ALength := StrLen(ANewDescription); StrCopy(ANewDescription + ALength, ' '); StrCopy(ANewDescription + ALength + 1, ALine); StrDispose(Data[ADataIndex].FMorphologicalDescription); Data[ADataIndex].FMorphologicalDescription := ANewDescription; end; end; function TdxAffixItemTable.CheckFlag(ALine: PAnsiChar): Boolean; begin Result := (WordBaseManager.DecodeFlag(ALine) = FDecodedFlag) or FlagIsNil; end; procedure TdxSuffixItemTable.BuildAffixes; var I: Integer; begin inherited BuildAffixes; for I := 0 to Count - 1 do AffixManager.BuildAffixTree(TdxSuffix.Create(AffixManager, @Data[I], FDecodedFlag, GetOptions(Data[I])), AffixManager.FSuffixTableArrangedByAppendString, AffixManager.FSuffixTableIndexedByFlag); end; function TdxSuffixItemTable.ConditionContainsDuplicateInformation( strip: PAnsiChar; stripl: Integer; const ACondition: PAnsiChar; out Contains: Boolean): Boolean; var AConditionLength, I, J: Integer; ANegative, AIsCharactersAgreed: Boolean; begin Result := True; Contains := False; AConditionLength := StrLen(ACondition); if (stripl >= AConditionLength) and (StrComp(strip + stripl - AConditionLength, ACondition) = 0) then Contains := True; if not Contains then begin I := stripl - 1; J := AConditionLength - 1; while (I >= 0) and (J >= 0) and Result do begin if (ACondition + J)^ <> ']' then begin Result := (ACondition + J)^ = (strip + I)^; end else begin AIsCharactersAgreed := False; repeat Dec(J); if (strip + I)^ = (ACondition + J)^ then AIsCharactersAgreed := True; until not((J > 0) and ((ACondition + J)^ <> '[')); Result := not((J = 0) and ((ACondition + J)^ <> '[')); if Result then begin ANegative := (ACondition + J + 1)^ = '^'; Result := ANegative xor AIsCharactersAgreed; end; end; Dec(I); Dec(J); end; if Result and (J < 0) then Contains := True; end; end; function TdxAffixItemTable.ConditionLength(ALine: PAnsiChar): Integer; var AInGroup: Boolean; begin Result := 0; AInGroup := False; while ALine^ <> #0 do begin if ALine^ = '[' then begin AInGroup := True; Inc(Result); end else if ALine^ = ']' then AInGroup := False else if not AInGroup then Inc(Result); Inc(ALine); end; end; function TdxAffixItemTable.EncodeAffixCondition(ALine: PAnsiChar; ADataIndex: Integer): Boolean; begin Result := True; if StrComp(ALine, '.') <> 0 then begin Data[ADataIndex].FConditionLength := ConditionLength(ALine); StrLCopy(Data[ADataIndex].Condition, ALine, MaxConditionLength); if (Data[ADataIndex].Condition[MaxConditionLength - 1] <> #0) and ((ALine + MaxConditionLength)^ <> #0) then begin Data[ADataIndex].LongCondition := True; Data[ADataIndex].ConditionSecondPart := StrNew(ALine + MaxConditionLength); Result := Data[ADataIndex].ConditionSecondPart <> nil; end; end else begin Data[ADataIndex].FConditionLength := 0; Data[ADataIndex].Condition[0] := #0; end; end; function TdxAffixItemTable.HasAffixFlagAliases: Boolean; begin Result := WordBaseManager.HasAffixFlagAliases; end; function TdxAffixItemTable.HasAffixMorphologicAliases: Boolean; begin Result := WordBaseManager.HasAffixMorphologicAliases; end; function TdxAffixItemTable.FlagIsNil: Boolean; var AEncodedFlag: PAnsiChar; begin AEncodedFlag := WordBaseManager.EncodeFlag(FDecodedFlag); try Result := AEncodedFlag = nil; finally StrDispose(AEncodedFlag); end; end; function TdxAffixItemTable.FlagIsUnique(AFlag: Word): Boolean; begin Result := IsPrefix and (FExistentAffixFlags[FDecodedFlag] and ExistentPrefixFlag = 0) or (FExistentAffixFlags[FDecodedFlag] and ExistentSuffixFlag = 0); end; procedure TdxAffixItemTable.FreeIfZero(var AString: TdxStringData); begin if StrComp(AString.Data, '0') = 0 then begin StrDispose(AString.Data); AString.Data := StrNew(PAnsiChar('')); AString.Length := 0; end; end; function TdxAffixItemTable.GetOptions(AItem: TdxAffixItem): ShortInt; begin Result := 0; if FPrefixSuffixUnion then Result := Result + aoPrefixSuffixUnion; if HasAffixFlagAliases then Result := Result + aoAffixFlagAliasTable; if WordBaseManager.HasAffixMorphologicAliases then Result := Result + aoAffixMorphologyTable; if AItem.LongCondition then Result := Result + aeLONGCOND; end; function TdxAffixItemTable.IsReverseWritingDirection: Boolean; begin Result := AffixManager.ComplexPrefixes; end; procedure TdxAffixItemTable.ParseAppendString(const ALine: PAnsiChar; ADataIndex: Integer); begin ProcessIgnoreChars(ALine); ProcessReverseString(ALine); Data[ADataIndex].FAppendString := StrNew(ALine); end; function TdxAffixItemTable.ParseAppendStringWithAffixes(const ALine: PAnsiChar; ADataIndex: Integer): Boolean; var ACompatibleFlagCursor: PAnsiChar; ATempStringData: TdxStringData; begin Result := True; ACompatibleFlagCursor := StrScan(ALine, '/'); if ACompatibleFlagCursor <> nil then begin ACompatibleFlagCursor^ := #0; ParseAppendString(ALine, ADataIndex); Result := ParseCompatibleFlags(ACompatibleFlagCursor + 1, ADataIndex); ACompatibleFlagCursor^ := '/'; if Result then RegisterCompatibleFlags(ADataIndex); end else ParseAppendString(ALine, ADataIndex); Data[ADataIndex].FAppendStringLength := StrLen(Data[ADataIndex].FAppendString); if Result then begin ATempStringData.Data := Data[ADataIndex].FAppendString; ATempStringData.Length := Data[ADataIndex].FAppendStringLength; FreeIfZero(ATempStringData); Data[ADataIndex].FAppendString := ATempStringData.Data; Data[ADataIndex].FAppendStringLength := ATempStringData.Length; end; end; function TdxAffixItemTable.ParseCompatibleFlags(const ALine: PAnsiChar; ADataIndex: Integer): Boolean; begin Result := True; Data[ADataIndex].FCompatibleFlags := TdxHunspellFlags.Create(FWordBaseManager); if HasAffixFlagAliases then Result := Data[ADataIndex].FCompatibleFlags.InitializeByAlias(ALine) else begin Data[ADataIndex].FCompatibleFlags.Decode(ALine); Data[ADataIndex].FCompatibleFlags.Sort; end; end; function TdxAffixItemTable.ParseCondition(ALine: PAnsiChar; ADataIndex: Integer): Boolean; var Contains: Boolean; begin Result := True; ProcessReverseString(ALine); if IsReverseWritingDirection then ReverseCondition(ALine); if (Data[ADataIndex].FStripStringLength <> 0) and (StrComp(ALine, '.') <> 0) then begin Result := ConditionContainsDuplicateInformation(Data[ADataIndex].FStripString, Data[ADataIndex].FStripStringLength, ALine, Contains); if Result and Contains then StrPCopy(ALine, '.'); end; if Result then begin if not IsPrefix then begin StrReverse(ALine); ReverseCondition(ALine); end; Result := EncodeAffixCondition(ALine, ADataIndex); end; end; function TdxAffixItemTable.ParseFlag(ALine: PAnsiChar): Boolean; begin FDecodedFlag := WordBaseManager.DecodeFlag(ALine); Result := FlagIsUnique(FDecodedFlag); if Result then begin SetExisting; FFlag := StrNew(ALine); end; end; function TdxAffixItemTable.ParseMorphologicDescription(ALine: PAnsiChar; ADataIndex: Integer): Boolean; begin Result := True; if HasAffixMorphologicAliases then Data[ADataIndex].FMorphologicalDescription := WordBaseManager.GetAffixMorphologyByAlias(StrInt(ALine)) else begin ProcessReverseString(ALine); Data[ADataIndex].FMorphologicalDescription := StrNew(ALine); Result := Data[ADataIndex].FMorphologicalDescription <> nil; end; end; procedure TdxAffixItemTable.ParsePrefixSuffixUnion(ALine: PAnsiChar); begin FPrefixSuffixUnion := ALine^ = 'Y'; end; procedure TdxAffixItemTable.ParseStripString(const ALine: PAnsiChar; ADataIndex: Integer); var ATempStringData: TdxStringData; begin ProcessReverseString(ALine); ATempStringData.Data := StrNew(ALine); ATempStringData.Length := StrLen(ATempStringData.Data); FreeIfZero(ATempStringData); Data[ADataIndex].FStripString := ATempStringData.Data; Data[ADataIndex].FStripStringLength := ATempStringData.Length; end; function TdxAffixItemTable.ParseTableSize(ALine: PAnsiChar; out ATableSize: Integer): Boolean; begin ATableSize := StrInt(ALine); Result := (ATableSize <> 0) or FlagIsNil; end; procedure TdxAffixItemTable.ProcessIgnoreChars(ALine: PAnsiChar); begin if AffixManager.Ignore <> nil then RemoveIgnoredChars(ALine, AffixManager.Ignore); end; procedure TdxAffixItemTable.ProcessReverseString(ALine: PAnsiChar); begin if IsReverseWritingDirection then StrReverse(ALine); end; procedure TdxAffixItemTable.RegisterCompatibleFlags(ADataIndex: Integer); var I: Integer; begin AffixManager.CompatibleFlagsExist := True; for I := 0 to Data[ADataIndex].FCompatibleFlags.Length - 1 do AffixManager.SetCompatibleFlags(Data[ADataIndex].FCompatibleFlags[I], True); end; procedure TdxAffixItemTable.ReverseCondition(ACondition: PAnsiChar); var ANegative: Boolean; AConditionCursor: PAnsiChar; begin ANegative := False; AConditionCursor := ACondition + StrLen(ACondition) - 1; while AConditionCursor >= ACondition do begin case AConditionCursor^ of '[': begin if ANegative then (AConditionCursor + 1)^ := '[' else AConditionCursor^ := ']'; end; ']': begin AConditionCursor^ := '['; if ANegative then (AConditionCursor + 1)^ := '^'; ANegative := False; end; '^': begin if (AConditionCursor + 1)^ = ']' then ANegative := True else (AConditionCursor + 1)^ := AConditionCursor^; end; else if ANegative then (AConditionCursor + 1)^ := AConditionCursor^; end; Dec(AConditionCursor); end; end; procedure TdxAffixItemTable.SetExisting; begin if IsPrefix then FExistentAffixFlags[FDecodedFlag] := FExistentAffixFlags[FDecodedFlag] or ExistentPrefixFlag else FExistentAffixFlags[FDecodedFlag] := FExistentAffixFlags[FDecodedFlag] or ExistentSuffixFlag; end; { TdxPrefixItemTable } procedure TdxPrefixItemTable.BuildAffixes; var I: Integer; begin inherited BuildAffixes; for I := 0 to Count - 1 do AffixManager.BuildAffixTree(TdxPrefix.Create(AffixManager, @Data[I], FDecodedFlag, GetOptions(Data[I])), AffixManager.FPrefixTableArrangedByAppendString, AffixManager.FPrefixTableIndexedByFlag); end; function TdxPrefixItemTable.ConditionContainsDuplicateInformation( strip: PAnsiChar; stripl: Integer; const ACondition: PAnsiChar; out Contains: Boolean): Boolean; var AConditionLength, I, J: Integer; ANegative, AIsCharactersAgreed: Boolean; begin Result := True; Contains := False; AConditionLength := StrLen(ACondition); if StrComp(strip, ACondition) = 0 then Contains := True; if not Contains then begin I := 0; J := 0; while (I < stripl) and (J < AConditionLength) and Result do begin if (ACondition + J)^ <> '[' then begin Result := (ACondition + J)^ = (strip + I)^; end else begin ANegative := (ACondition + J + 1)^ = '^'; AIsCharactersAgreed := False; repeat Inc(J); if (strip + I)^ = (ACondition + J)^ then AIsCharactersAgreed := True; until not((J < AConditionLength - 1) and ((ACondition + J)^ <> ']')); Result := not((J = AConditionLength - 1) and ((ACondition + J)^ <> ']')); if Result then Result := ANegative xor AIsCharactersAgreed; end; Inc(I); Inc(J); end; if Result and (J >= AConditionLength) then Contains := True; end; end; function TdxPrefixItemTable.IsPrefix: Boolean; begin Result := not IsComplexPrefixes; end; { TdxSuffixItemTable } function TdxSuffixItemTable.IsPrefix: Boolean; begin Result := IsComplexPrefixes; end; { TdxBreakTable } constructor TdxBreakTable.Create; begin inherited Create; FillDefaultData; end; procedure TdxBreakTable.FillDefaultData; var I: Integer; begin I := 3; AllocateData(I); Data[I - 3] := StrNew('-'); Data[I - 2] := StrNew('^-'); Data[I - 1] := StrNew('-$'); end; function TdxBreakTable.AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; begin Result := inherited AddDataPiece(APiece, ADataIndex, APieceIndex); if Result and (APieceIndex = 1) then Data[ADataIndex] := StrNew(APiece); end; procedure TdxBreakTable.FreeDataItems; var I: Integer; begin for I := 0 to Count - 1 do begin StrDispose(Data[I]); Data[I] := nil; end; end; function TdxBreakTable.GetDataIdentifier: PAnsiChar; begin Result := 'BREAK'; end; function TdxBreakTable.GetDataItemSize: Integer; begin Result := SizeOf(PAnsiChar); end; function TdxBreakTable.GetPieceQuantity: Integer; begin Result := 2; end; function TdxBreakTable.GetData: PdxPAnsiCharArray; begin Result := PdxPAnsiCharArray(FData); end; function TdxBreakTable.GetItem(Index: Integer): PAnsiChar; begin Result := Data[Index]; end; { TdxCheckCompoundPatternTable } function TdxCheckCompoundPatternTable.AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; begin Result := inherited AddDataPiece(APiece, ADataIndex, APieceIndex); if Result then case APieceIndex of 1: begin Data[ADataIndex].Pattern := StrNew(APiece); ExtractCondition(Data[ADataIndex].Pattern, Data[ADataIndex].Condition); end; 2: begin Data[ADataIndex].Pattern2 := StrNew(APiece); ExtractCondition(Data[ADataIndex].Pattern2, Data[ADataIndex].Condition2); end; 3: begin Data[ADataIndex].Pattern3 := StrNew(APiece); FIsSimplified := True; end; end; end; procedure TdxCheckCompoundPatternTable.FreeDataItems; var I: Integer; begin for I := 0 to Count - 1 do begin StrDispose(Data[I].Pattern); StrDispose(Data[I].Pattern2); StrDispose(Data[I].Pattern3); end; end; function TdxCheckCompoundPatternTable.GetDataIdentifier: PAnsiChar; begin Result := diCheckCompoundPattern; end; function TdxCheckCompoundPatternTable.GetDataItemSize: Integer; begin Result := SizeOf(TdxCompoundForbidPattern); end; function TdxCheckCompoundPatternTable.GetPieceQuantity: Integer; begin Result := 3; end; procedure TdxCheckCompoundPatternTable.InitializeItem(Index: Integer); begin Data[Index].Pattern := nil; Data[Index].Pattern2 := nil; Data[Index].Pattern3 := nil; Data[Index].Condition := NullFlag; Data[Index].Condition2 := NullFlag; end; procedure TdxCheckCompoundPatternTable.ExtractCondition(APattern: PAnsiChar; var ACondition: Word); var ADelimiter: PAnsiChar; begin ADelimiter := StrScan(APattern, '/'); if ADelimiter <> nil then begin ADelimiter^ := #0; ACondition := WordBaseManager.DecodeFlag(ADelimiter + 1); end; end; function TdxCheckCompoundPatternTable.GetData: PdxCompoundForbidPatternArray; begin Result := PdxCompoundForbidPatternArray(FData); end; function TdxCheckCompoundPatternTable.GetItem(Index: Integer): TdxCompoundForbidPattern; begin Result := Data[Index]; end; { TdxCompoundRuleTable } function TdxCompoundRuleTable.AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; begin Result := inherited AddDataPiece(APiece, ADataIndex, APieceIndex); if Result and (APieceIndex = 1) then Result := ParseFlags(APiece, ADataIndex); end; procedure TdxCompoundRuleTable.FreeDataItems; var I: Integer; begin for I := 0 to Count - 1 do FreeMem(Data[I].Pattern, Data[I].Count * SizeOf(Word)); end; function TdxCompoundRuleTable.GetDataIdentifier: PAnsiChar; begin Result := diCompoundRule; end; function TdxCompoundRuleTable.GetDataItemSize: Integer; begin Result := SizeOf(TdxCompoundFlag); end; function TdxCompoundRuleTable.GetPieceQuantity: Integer; begin Result := 2; end; procedure TdxCompoundRuleTable.InitializeItem(Index: Integer); begin Data[Index].Pattern := nil; end; function TdxCompoundRuleTable.GetData: PdxCompoundFlagArray; begin Result := PdxCompoundFlagArray(FData); end; function TdxCompoundRuleTable.GetItem(Index: Integer): TdxCompoundFlag; begin Result := Data[Index]; end; function TdxCompoundRuleTable.ParseFlags(APiece: PAnsiChar; AIndex: Integer): Boolean; var ABracketPosition: PAnsiChar; AIsEndOfLine: Boolean; AItem: PdxCompoundFlag; procedure AllocPattern(AItem: PdxCompoundFlag; ASize: Integer); begin AItem^.Count := ASize; AItem^.Pattern := AllocMem(AItem^.Count * SizeOf(Word)); end; procedure ReadFlags(APiece: PAnsiChar; AItem: PdxCompoundFlag); var AFlags: TdxHunspellFlags; I: Integer; begin AFlags := TdxHunspellFlags.Create(FWordBaseManager); try AFlags.Decode(APiece); for I := 0 to AFlags.Length - 1 do begin AItem^.Pattern[AItem^.Length] := AFlags[I]; Inc(AItem^.Length); end; finally AFlags.Free; end; end; begin AItem := @Data[AIndex]; AllocPattern(AItem, StrLen(APiece)); if StrScan(APiece, '(') <> nil then begin AIsEndOfLine := False; repeat ABracketPosition := APiece + 1; while (ABracketPosition^ <> '(') and (ABracketPosition^ <> ')') and (ABracketPosition^ <> #0) do Inc(ABracketPosition); if ABracketPosition^ = #0 then AIsEndOfLine := True else ABracketPosition^ := #0; if APiece^ = '(' then Inc(APiece); if (APiece^ = '*') or (APiece^ = '?') then begin AItem^.Pattern[AItem^.Length] := Ord(APiece^); Inc(AItem^.Length); end else if APiece^ <> #0 then ReadFlags(APiece, AItem); APiece := ABracketPosition + 1; until AIsEndOfLine; end else ReadFlags(APiece, AItem); Result := AItem^.Length <> 0; end; { TdxMapTable } function TdxMapTable.AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; begin Result := inherited AddDataPiece(APiece, ADataIndex, APieceIndex); if Result and (APieceIndex = 1) then begin Data[ADataIndex].CharacterSet := StrNew(APiece); Data[ADataIndex].Length := StrLen(Data[ADataIndex].CharacterSet); end; end; procedure TdxMapTable.FreeDataItems; var I: Integer; begin for I := 0 to Count - 1 do StrDispose(Data[I].CharacterSet); end; function TdxMapTable.GetDataIdentifier: PAnsiChar; begin Result := diMap; end; function TdxMapTable.GetDataItemSize: Integer; begin Result := SizeOf(TdxMapTableItem); end; function TdxMapTable.GetPieceQuantity: Integer; begin Result := 2; end; procedure TdxMapTable.InitializeItem(Index: Integer); begin Data[Index].CharacterSet := nil; Data[Index].Length := 0; end; function TdxMapTable.GetData: PdxMapArray; begin Result := PdxMapArray(FData); end; function TdxMapTable.GetItem(Index: Integer): TdxMapTableItem; begin Result := Data[Index]; end; { TdxReplaceTable } function TdxReplaceTable.AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; begin Result := inherited AddDataPiece(APiece, ADataIndex, APieceIndex); if Result then case APieceIndex of 1: Data[ADataIndex].Initial := StrReplace(StrNew(APiece), '_', ' '); 2: Data[ADataIndex].Replacement := StrReplace(StrNew(APiece), '_', ' '); end; end; procedure TdxReplaceTable.FreeDataItems; var I: Integer; begin for I := 0 to Count - 1 do begin StrDispose(Data[I].Initial); StrDispose(Data[I].Replacement); end; end; function TdxReplaceTable.GetDataIdentifier: PAnsiChar; begin Result := diRep; end; function TdxReplaceTable.GetDataItemSize: Integer; begin Result := SizeOf(TdxReplaceTableItem); end; function TdxReplaceTable.GetPieceQuantity: Integer; begin Result := 3; end; procedure TdxReplaceTable.InitializeItem(Index: Integer); begin Data[Index].Initial := nil; Data[Index].Replacement := nil; end; function TdxReplaceTable.GetData: PdxReplaceArray; begin Result := PdxReplaceArray(FData); end; function TdxReplaceTable.GetItem(Index: Integer): TdxReplaceTableItem; begin Result := Data[Index]; end; { TdxSortedReplaceTable } function TdxSortedReplaceTable.conv(const Aword: PAnsiChar; dest: PAnsiChar): Integer; var stl, I, n, l: Integer; pc: PAnsiChar; begin Result := 0; if Data = nil then Exit; stl := 0; I := 0; while I < Length(AWord) do begin n := near1(Aword + I); l := match(Aword + I, n); if l <> 0 then begin pc := Data[n].Replacement; StrCopy(dest + stl, pc); Inc(stl, Length(pc)); Inc(I, l - 1); Result := 1; end else begin (dest + stl)^ := (Aword + I)^; Inc(stl); end; Inc(I); end; (dest + stl)^ := #0; end; function TdxSortedReplaceTable.AddDataPiece(const APiece: PAnsiChar; ADataIndex, APieceIndex: Integer): Boolean; begin Result := inherited AddDataPiece(APiece, ADataIndex, APieceIndex); if Result and (APieceIndex = 2) then Sort(ADataIndex); end; function TdxSortedReplaceTable.match(const Aword: PAnsiChar; n: Integer): Integer; var P: TdxReplaceTableItem; begin Result := 0; P := Data[n]; if StrLComp(Aword, P.Initial, Length(P.Initial)) = 0 then Result := Length(P.Initial); end; function TdxSortedReplaceTable.near1(const Aword: PAnsiChar): Integer; var p1, p2, m, c: Integer; begin p1 := 0; p2 := Count; while p2 - p1 > 1 do begin m := Round((p1 + p2) / 2); c := StrComp(Aword, Data[m].Initial); if c <= 0 then begin p2 := m; if c = 0 then p1 := m; end else p1 := m; end; Result := p1; end; procedure TdxSortedReplaceTable.Sort(AIndex: Integer); var I: Integer; AItem: TdxReplaceTableItem; begin for I := AIndex downto 1 do begin AItem := Data[I]; if StrComp(AItem.Initial, Data[I - 1].Initial) < 0 then begin Data[I] := Data[I - 1]; Data[I - 1] := AItem; end else Break; end; end; { TAffixFileReader } constructor TAffixFileReader.Create(AAffixManager: TdxHunspellAffixManager); begin FAffixManager := AAffixManager; end; function TAffixFileReader.ParseFlag(ALine: PAnsiChar; var ADecodedFlag: Word; AReader: TdxHunspellReader): Boolean; var AFlag: PAnsiChar; begin Result := True; AFlag := nil; if (ADecodedFlag <> NullFlag) and not(ADecodedFlag >= DEFAULTFLAGS) then begin Result := False; Exit; end; if not GetSecondPartOfString(ALine, AFlag) then begin Result := False; Exit; end; ADecodedFlag := FAffixManager.FWordBaseManager.DecodeFlag(AFlag); StrDispose(AFlag); end; function TAffixFileReader.ParseNumber(ALine: PAnsiChar; out ANumber: Integer): Boolean; var ASecondPart: PAnsiChar; begin Result := True; if ANumber <> - 1 then begin Result := False; Exit; end; ASecondPart := nil; try if GetSecondPartOfString(ALine, ASecondPart) then ANumber := StrInt(ASecondPart) else Result := False; finally StrDispose(ASecondPart); end; end; { TTryReader } function TTryReader.GetDataIdentifier: PAnsiChar; begin Result := diTry; end; function TTryReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := GetSecondPartOfString(ALine, FAffixManager.FTryChars); end; { TSetReader } function TSetReader.GetDataIdentifier: PAnsiChar; begin Result := diSet; end; function TSetReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; var AEncoding: PAnsiChar; begin AEncoding := nil; try Result := GetSecondPartOfString(ALine, AEncoding); FAffixManager.CodePage := GetCodePageByName(AEncoding); if Result and (StrComp(AEncoding, 'UTF-8') = 0) then //TODO: //TODO: // FAffixManager.FIsUTF8 := 1; raise Exception.Create('UTF-8 doesn''t supported'); finally StrDispose(AEncoding); end; end; { TComplexPrefixesReader } function TComplexPrefixesReader.GetDataIdentifier: PAnsiChar; begin Result := diComplexPrefixes; end; function TComplexPrefixesReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FComplexPrefixes := True; end; { TCompoundFlagReader } function TCompoundFlagReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundFlag; end; function TCompoundFlagReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FCompoundFlag, AReader); end; { TCompoundBeginReader } function TCompoundBeginReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundBegin; end; function TCompoundBeginReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin if FAffixManager.FComplexPrefixes then Result := ParseFlag(ALine, FAffixManager.FCompoundEnd, AReader) else Result := ParseFlag(ALine, FAffixManager.FCompoundBegin, AReader); end; { TCompoundEndReader } function TCompoundEndReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundEnd; end; function TCompoundEndReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin if FAffixManager.FComplexPrefixes then Result := ParseFlag(ALine, FAffixManager.FCompoundBegin, AReader) else Result := ParseFlag(ALine, FAffixManager.FCompoundEnd, AReader); end; { TCompoundMiddleReader } function TCompoundMiddleReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundMiddle; end; function TCompoundMiddleReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FCompoundMiddle, AReader); end; { TCompoundWordMaxReader } function TCompoundWordMaxReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundWordMax; end; function TCompoundWordMaxReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseNumber(ALine, FAffixManager.FCompoundWordMax); end; { TCompoundRootReader } function TCompoundRootReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundRoot; end; function TCompoundRootReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FCompoundRoot, AReader); end; { TCompoundPermitFlagReader } function TCompoundPermitFlagReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundPermitFlag; end; function TCompoundPermitFlagReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FCompoundPermitFlag, AReader); end; { TCompoundForbidFlagReader } function TCompoundForbidFlagReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundForbidFlag; end; function TCompoundForbidFlagReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FCompoundForbidFlag, AReader); end; { TCheckCompoundDupReader } function TCheckCompoundDupReader.GetDataIdentifier: PAnsiChar; begin Result := diCheckCompoundDup; end; function TCheckCompoundDupReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FCheckCompoundDup := True; end; { TCheckCompoundRepReader } function TCheckCompoundRepReader.GetDataIdentifier: PAnsiChar; begin Result := diCheckCompoundRep; end; function TCheckCompoundRepReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FCheckCompoundRep := True; end; { TCheckCompoundTripleReader } function TCheckCompoundTripleReader.GetDataIdentifier: PAnsiChar; begin Result := diCheckCompoundTriple; end; function TCheckCompoundTripleReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FCheckCompoundTriple := True; end; { TSimplifiedTripleReader } function TSimplifiedTripleReader.GetDataIdentifier: PAnsiChar; begin Result := diSimplifiedTriple; end; function TSimplifiedTripleReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FSimplifiedTriple := True; end; { TCheckCompoundCaseReader } function TCheckCompoundCaseReader.GetDataIdentifier: PAnsiChar; begin Result := diCheckCompoundCase; end; function TCheckCompoundCaseReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FCheckCompoundCase := True; end; { TNoSuggestReader } function TNoSuggestReader.GetDataIdentifier: PAnsiChar; begin Result := diNoSuggest; end; function TNoSuggestReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FNoSuggest, AReader); end; { TForbiddenWordReader } function TForbiddenWordReader.GetDataIdentifier: PAnsiChar; begin Result := diForbiddenWord; end; function TForbiddenWordReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FForbiddenWord, AReader); end; { TLemmaPresentReader } function TLemmaPresentReader.GetDataIdentifier: PAnsiChar; begin Result := diLemmaPresent; end; function TLemmaPresentReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FLemmaPresent, AReader); end; { TCircumfixReader } function TCircumfixReader.GetDataIdentifier: PAnsiChar; begin Result := diCircumfix; end; function TCircumfixReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FCircumfix, AReader); end; { TOnlyInCompoundReader } function TOnlyInCompoundReader.GetDataIdentifier: PAnsiChar; begin Result := diOnlyInCompound; end; function TOnlyInCompoundReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FOnlyInCompound, AReader); end; { TPseudoRootReader } function TPseudoRootReader.GetDataIdentifier: PAnsiChar; begin Result := diPseudoRoot; end; function TPseudoRootReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FNeedAffix, AReader) end; { TNeedAffixReader } function TNeedAffixReader.GetDataIdentifier: PAnsiChar; begin Result := diNeedAffix; end; function TNeedAffixReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FNeedAffix, AReader); end; { TCompoundMinReader } function TCompoundMinReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundMin; end; function TCompoundMinReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseNumber(ALine, FAffixManager.FCompoundPartMin); if FAffixManager.FCompoundPartMin < 1 then FAffixManager.FCompoundPartMin := 1; end; { TCompoundSyllableReader } function TCompoundSyllableReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundSyllable; end; function TCompoundSyllableReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := FAffixManager.ParseCompoundSyllable(ALine, AReader); end; { TSyllableNumReader } function TSyllableNumReader.GetDataIdentifier: PAnsiChar; begin Result := diSyllableNum; end; function TSyllableNumReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := GetSecondPartOfString(ALine, FAffixManager.FSyllableNum); end; { TWordCharsReader } function TWordCharsReader.GetDataIdentifier: PAnsiChar; begin Result := diWordChars; end; function TWordCharsReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := False; //TODO: end; { TIgnoreReader } function TIgnoreReader.GetDataIdentifier: PAnsiChar; begin Result := diIgnore; end; function TIgnoreReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseArray(ALine, FAffixManager.FIgnore, FAffixManager.IsUTF8); end; { TRepReader } function TRepReader.GetDataIdentifier: PAnsiChar; begin Result := diRep; end; function TRepReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := FAffixManager.ParseRep(ALine, AReader); end; { TIConvReader } function TIConvReader.GetDataIdentifier: PAnsiChar; begin Result := diIConv; end; function TIConvReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := FAffixManager.FInputReplaceTable.ReadData(ALine, AReader); end; { TOConvReader } function TOConvReader.GetDataIdentifier: PAnsiChar; begin Result := diOConv; end; function TOConvReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := FAffixManager.FOutputReplaceTable.ReadData(ALine, AReader); end; { TCheckCompoundPatternReader } function TCheckCompoundPatternReader.GetDataIdentifier: PAnsiChar; begin Result := diCheckCompoundPattern; end; function TCheckCompoundPatternReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := FAffixManager.FCheckCompoundPatternTable.ReadData(ALine, AReader); end; { TCompoundRuleReader } function TCompoundRuleReader.GetDataIdentifier: PAnsiChar; begin Result := diCompoundRule; end; function TCompoundRuleReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := FAffixManager.FCompoundRuleTable.ReadData(ALine, AReader); end; { TMapReader } function TMapReader.GetDataIdentifier: PAnsiChar; begin Result := diMap; end; function TMapReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := FAffixManager.FMapTable.ReadData(ALine, AReader); end; { TBreakReader } function TBreakReader.GetDataIdentifier: PAnsiChar; begin Result := diBreak; end; function TBreakReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := FAffixManager.FBreakTable.ReadData(ALine, AReader); end; { TLangReader } function TLangReader.GetDataIdentifier: PAnsiChar; begin Result := diLang; end; function TLangReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; var ALanguage: PAnsiChar; begin ALanguage := nil; try Result := GetSecondPartOfString(ALine, ALanguage); if Result then FAffixManager.FLanguage := GetLanguageID(ALanguage); finally StrDispose(ALanguage); end; end; { TVersionReader } function TVersionReader.GetDataIdentifier: PAnsiChar; begin Result := diVersion; end; function TVersionReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; ALine := ALine + 7; while (ALine^ = ' ') or (ALine^ = #9) do Inc(ALine); if ALine^ <> #0 then FAffixManager.FVersion := StrNew(ALine) else FAffixManager.FVersion := nil; end; { TNoSplitSugsReader } function TNoSplitSugsReader.GetDataIdentifier: PAnsiChar; begin Result := diNoSplitSugs; end; function TNoSplitSugsReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FNoSplitSugs := True; end; { TFullStripReader } function TFullStripReader.GetDataIdentifier: PAnsiChar; begin Result := diFullStrip; end; function TFullStripReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FFullStrip := True; end; { TSugsWithDotsReader } function TSugsWithDotsReader.GetDataIdentifier: PAnsiChar; begin Result := diSugsWithDots; end; function TSugsWithDotsReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FSugsWithDots := True; end; { TKeepCaseReader } function TKeepCaseReader.GetDataIdentifier: PAnsiChar; begin Result := diKeepCase; end; function TKeepCaseReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FKeepcase, AReader); end; { TSubStandardReader } function TSubStandardReader.GetDataIdentifier: PAnsiChar; begin Result := diSubStandard; end; function TSubStandardReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := ParseFlag(ALine, FAffixManager.FSubstandard, AReader); end; { TCheckSharpsReader } function TCheckSharpsReader.GetDataIdentifier: PAnsiChar; begin Result := diCheckSharps; end; function TCheckSharpsReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; begin Result := True; FAffixManager.FCheckSharps := 1; end; { TPrefixReader } function TPrefixReader.GetDataIdentifier: PAnsiChar; begin Result := diPrefix; end; function TPrefixReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; var AAffixType: TdxAffixType; begin AAffixType := atPrefix; if FAffixManager.FComplexPrefixes then AAffixType := atSuffix; Result := FAffixManager.DoAffixParsing(ALine, AReader, AAffixType); end; { TSuffixReader } function TSuffixReader.GetDataIdentifier: PAnsiChar; begin Result := diSuffix; end; function TSuffixReader.Process(ALine: PAnsiChar; AReader: TdxHunspellReader): Boolean; var AAffixType: TdxAffixType; begin AAffixType := atSuffix; if FAffixManager.FComplexPrefixes then AAffixType := atPrefix; Result := FAffixManager.DoAffixParsing(ALine, AReader, AAffixType); end; end.