| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #include "fts3Int.h" |
| | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) |
| |
|
| | #include <assert.h> |
| | #include <stdlib.h> |
| | #include <stdio.h> |
| | #include <string.h> |
| |
|
| | #include "fts3_tokenizer.h" |
| |
|
| | |
| | |
| | |
| | typedef struct porter_tokenizer { |
| | sqlite3_tokenizer base; |
| | } porter_tokenizer; |
| |
|
| | |
| | |
| | |
| | typedef struct porter_tokenizer_cursor { |
| | sqlite3_tokenizer_cursor base; |
| | const char *zInput; |
| | int nInput; |
| | int iOffset; |
| | int iToken; |
| | char *zToken; |
| | int nAllocated; |
| | } porter_tokenizer_cursor; |
| |
|
| |
|
| | |
| | |
| | |
| | static int porterCreate( |
| | int argc, const char * const *argv, |
| | sqlite3_tokenizer **ppTokenizer |
| | ){ |
| | porter_tokenizer *t; |
| |
|
| | UNUSED_PARAMETER(argc); |
| | UNUSED_PARAMETER(argv); |
| |
|
| | t = (porter_tokenizer *) sqlite3_malloc(sizeof(*t)); |
| | if( t==NULL ) return SQLITE_NOMEM; |
| | memset(t, 0, sizeof(*t)); |
| | *ppTokenizer = &t->base; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | static int porterDestroy(sqlite3_tokenizer *pTokenizer){ |
| | sqlite3_free(pTokenizer); |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static int porterOpen( |
| | sqlite3_tokenizer *pTokenizer, |
| | const char *zInput, int nInput, |
| | sqlite3_tokenizer_cursor **ppCursor |
| | ){ |
| | porter_tokenizer_cursor *c; |
| |
|
| | UNUSED_PARAMETER(pTokenizer); |
| |
|
| | c = (porter_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); |
| | if( c==NULL ) return SQLITE_NOMEM; |
| |
|
| | c->zInput = zInput; |
| | if( zInput==0 ){ |
| | c->nInput = 0; |
| | }else if( nInput<0 ){ |
| | c->nInput = (int)strlen(zInput); |
| | }else{ |
| | c->nInput = nInput; |
| | } |
| | c->iOffset = 0; |
| | c->iToken = 0; |
| | c->zToken = NULL; |
| | c->nAllocated = 0; |
| |
|
| | *ppCursor = &c->base; |
| | return SQLITE_OK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static int porterClose(sqlite3_tokenizer_cursor *pCursor){ |
| | porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; |
| | sqlite3_free(c->zToken); |
| | sqlite3_free(c); |
| | return SQLITE_OK; |
| | } |
| | |
| | |
| | |
| | static const char cType[] = { |
| | 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, |
| | 1, 1, 1, 2, 1 |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int isVowel(const char*); |
| | static int isConsonant(const char *z){ |
| | int j; |
| | char x = *z; |
| | if( x==0 ) return 0; |
| | assert( x>='a' && x<='z' ); |
| | j = cType[x-'a']; |
| | if( j<2 ) return j; |
| | return z[1]==0 || isVowel(z + 1); |
| | } |
| | static int isVowel(const char *z){ |
| | int j; |
| | char x = *z; |
| | if( x==0 ) return 0; |
| | assert( x>='a' && x<='z' ); |
| | j = cType[x-'a']; |
| | if( j<2 ) return 1-j; |
| | return isConsonant(z + 1); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int m_gt_0(const char *z){ |
| | while( isVowel(z) ){ z++; } |
| | if( *z==0 ) return 0; |
| | while( isConsonant(z) ){ z++; } |
| | return *z!=0; |
| | } |
| |
|
| | |
| | |
| | |
| | static int m_eq_1(const char *z){ |
| | while( isVowel(z) ){ z++; } |
| | if( *z==0 ) return 0; |
| | while( isConsonant(z) ){ z++; } |
| | if( *z==0 ) return 0; |
| | while( isVowel(z) ){ z++; } |
| | if( *z==0 ) return 1; |
| | while( isConsonant(z) ){ z++; } |
| | return *z==0; |
| | } |
| |
|
| | |
| | |
| | |
| | static int m_gt_1(const char *z){ |
| | while( isVowel(z) ){ z++; } |
| | if( *z==0 ) return 0; |
| | while( isConsonant(z) ){ z++; } |
| | if( *z==0 ) return 0; |
| | while( isVowel(z) ){ z++; } |
| | if( *z==0 ) return 0; |
| | while( isConsonant(z) ){ z++; } |
| | return *z!=0; |
| | } |
| |
|
| | |
| | |
| | |
| | static int hasVowel(const char *z){ |
| | while( isConsonant(z) ){ z++; } |
| | return *z!=0; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static int doubleConsonant(const char *z){ |
| | return isConsonant(z) && z[0]==z[1]; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int star_oh(const char *z){ |
| | return |
| | isConsonant(z) && |
| | z[0]!='w' && z[0]!='x' && z[0]!='y' && |
| | isVowel(z+1) && |
| | isConsonant(z+2); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static int stem( |
| | char **pz, |
| | const char *zFrom, |
| | const char *zTo, |
| | int (*xCond)(const char*) |
| | ){ |
| | char *z = *pz; |
| | while( *zFrom && *zFrom==*z ){ z++; zFrom++; } |
| | if( *zFrom!=0 ) return 0; |
| | if( xCond && !xCond(z) ) return 1; |
| | while( *zTo ){ |
| | *(--z) = *(zTo++); |
| | } |
| | *pz = z; |
| | return 1; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ |
| | int i, mx, j; |
| | int hasDigit = 0; |
| | for(i=0; i<nIn; i++){ |
| | char c = zIn[i]; |
| | if( c>='A' && c<='Z' ){ |
| | zOut[i] = c - 'A' + 'a'; |
| | }else{ |
| | if( c>='0' && c<='9' ) hasDigit = 1; |
| | zOut[i] = c; |
| | } |
| | } |
| | mx = hasDigit ? 3 : 10; |
| | if( nIn>mx*2 ){ |
| | for(j=mx, i=nIn-mx; i<nIn; i++, j++){ |
| | zOut[j] = zOut[i]; |
| | } |
| | i = j; |
| | } |
| | zOut[i] = 0; |
| | *pnOut = i; |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ |
| | int i, j; |
| | char zReverse[28]; |
| | char *z, *z2; |
| | if( nIn<3 || nIn>=(int)sizeof(zReverse)-7 ){ |
| | |
| | |
| | copy_stemmer(zIn, nIn, zOut, pnOut); |
| | return; |
| | } |
| | for(i=0, j=sizeof(zReverse)-6; i<nIn; i++, j--){ |
| | char c = zIn[i]; |
| | if( c>='A' && c<='Z' ){ |
| | zReverse[j] = c + 'a' - 'A'; |
| | }else if( c>='a' && c<='z' ){ |
| | zReverse[j] = c; |
| | }else{ |
| | |
| | |
| | copy_stemmer(zIn, nIn, zOut, pnOut); |
| | return; |
| | } |
| | } |
| | memset(&zReverse[sizeof(zReverse)-5], 0, 5); |
| | z = &zReverse[j+1]; |
| |
|
| |
|
| | |
| | if( z[0]=='s' ){ |
| | if( |
| | !stem(&z, "sess", "ss", 0) && |
| | !stem(&z, "sei", "i", 0) && |
| | !stem(&z, "ss", "ss", 0) |
| | ){ |
| | z++; |
| | } |
| | } |
| |
|
| | |
| | z2 = z; |
| | if( stem(&z, "dee", "ee", m_gt_0) ){ |
| | |
| | }else if( |
| | (stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel)) |
| | && z!=z2 |
| | ){ |
| | if( stem(&z, "ta", "ate", 0) || |
| | stem(&z, "lb", "ble", 0) || |
| | stem(&z, "zi", "ize", 0) ){ |
| | |
| | }else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){ |
| | z++; |
| | }else if( m_eq_1(z) && star_oh(z) ){ |
| | *(--z) = 'e'; |
| | } |
| | } |
| |
|
| | |
| | if( z[0]=='y' && hasVowel(z+1) ){ |
| | z[0] = 'i'; |
| | } |
| |
|
| | |
| | switch( z[1] ){ |
| | case 'a': |
| | if( !stem(&z, "lanoita", "ate", m_gt_0) ){ |
| | stem(&z, "lanoit", "tion", m_gt_0); |
| | } |
| | break; |
| | case 'c': |
| | if( !stem(&z, "icne", "ence", m_gt_0) ){ |
| | stem(&z, "icna", "ance", m_gt_0); |
| | } |
| | break; |
| | case 'e': |
| | stem(&z, "rezi", "ize", m_gt_0); |
| | break; |
| | case 'g': |
| | stem(&z, "igol", "log", m_gt_0); |
| | break; |
| | case 'l': |
| | if( !stem(&z, "ilb", "ble", m_gt_0) |
| | && !stem(&z, "illa", "al", m_gt_0) |
| | && !stem(&z, "iltne", "ent", m_gt_0) |
| | && !stem(&z, "ile", "e", m_gt_0) |
| | ){ |
| | stem(&z, "ilsuo", "ous", m_gt_0); |
| | } |
| | break; |
| | case 'o': |
| | if( !stem(&z, "noitazi", "ize", m_gt_0) |
| | && !stem(&z, "noita", "ate", m_gt_0) |
| | ){ |
| | stem(&z, "rota", "ate", m_gt_0); |
| | } |
| | break; |
| | case 's': |
| | if( !stem(&z, "msila", "al", m_gt_0) |
| | && !stem(&z, "ssenevi", "ive", m_gt_0) |
| | && !stem(&z, "ssenluf", "ful", m_gt_0) |
| | ){ |
| | stem(&z, "ssensuo", "ous", m_gt_0); |
| | } |
| | break; |
| | case 't': |
| | if( !stem(&z, "itila", "al", m_gt_0) |
| | && !stem(&z, "itivi", "ive", m_gt_0) |
| | ){ |
| | stem(&z, "itilib", "ble", m_gt_0); |
| | } |
| | break; |
| | } |
| |
|
| | |
| | switch( z[0] ){ |
| | case 'e': |
| | if( !stem(&z, "etaci", "ic", m_gt_0) |
| | && !stem(&z, "evita", "", m_gt_0) |
| | ){ |
| | stem(&z, "ezila", "al", m_gt_0); |
| | } |
| | break; |
| | case 'i': |
| | stem(&z, "itici", "ic", m_gt_0); |
| | break; |
| | case 'l': |
| | if( !stem(&z, "laci", "ic", m_gt_0) ){ |
| | stem(&z, "luf", "", m_gt_0); |
| | } |
| | break; |
| | case 's': |
| | stem(&z, "ssen", "", m_gt_0); |
| | break; |
| | } |
| |
|
| | |
| | switch( z[1] ){ |
| | case 'a': |
| | if( z[0]=='l' && m_gt_1(z+2) ){ |
| | z += 2; |
| | } |
| | break; |
| | case 'c': |
| | if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){ |
| | z += 4; |
| | } |
| | break; |
| | case 'e': |
| | if( z[0]=='r' && m_gt_1(z+2) ){ |
| | z += 2; |
| | } |
| | break; |
| | case 'i': |
| | if( z[0]=='c' && m_gt_1(z+2) ){ |
| | z += 2; |
| | } |
| | break; |
| | case 'l': |
| | if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){ |
| | z += 4; |
| | } |
| | break; |
| | case 'n': |
| | if( z[0]=='t' ){ |
| | if( z[2]=='a' ){ |
| | if( m_gt_1(z+3) ){ |
| | z += 3; |
| | } |
| | }else if( z[2]=='e' ){ |
| | if( !stem(&z, "tneme", "", m_gt_1) |
| | && !stem(&z, "tnem", "", m_gt_1) |
| | ){ |
| | stem(&z, "tne", "", m_gt_1); |
| | } |
| | } |
| | } |
| | break; |
| | case 'o': |
| | if( z[0]=='u' ){ |
| | if( m_gt_1(z+2) ){ |
| | z += 2; |
| | } |
| | }else if( z[3]=='s' || z[3]=='t' ){ |
| | stem(&z, "noi", "", m_gt_1); |
| | } |
| | break; |
| | case 's': |
| | if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ |
| | z += 3; |
| | } |
| | break; |
| | case 't': |
| | if( !stem(&z, "eta", "", m_gt_1) ){ |
| | stem(&z, "iti", "", m_gt_1); |
| | } |
| | break; |
| | case 'u': |
| | if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ |
| | z += 3; |
| | } |
| | break; |
| | case 'v': |
| | case 'z': |
| | if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){ |
| | z += 3; |
| | } |
| | break; |
| | } |
| |
|
| | |
| | if( z[0]=='e' ){ |
| | if( m_gt_1(z+1) ){ |
| | z++; |
| | }else if( m_eq_1(z+1) && !star_oh(z+1) ){ |
| | z++; |
| | } |
| | } |
| |
|
| | |
| | if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){ |
| | z++; |
| | } |
| |
|
| | |
| | |
| | |
| | *pnOut = i = (int)strlen(z); |
| | zOut[i] = 0; |
| | while( *z ){ |
| | zOut[--i] = *(z++); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static const char porterIdChar[] = { |
| | |
| | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, |
| | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, |
| | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, |
| | }; |
| | #define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !porterIdChar[ch-0x30])) |
| |
|
| | |
| | |
| | |
| | |
| | static int porterNext( |
| | sqlite3_tokenizer_cursor *pCursor, |
| | const char **pzToken, |
| | int *pnBytes, |
| | int *piStartOffset, |
| | int *piEndOffset, |
| | int *piPosition |
| | ){ |
| | porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; |
| | const char *z = c->zInput; |
| |
|
| | while( c->iOffset<c->nInput ){ |
| | int iStartOffset, ch; |
| |
|
| | |
| | while( c->iOffset<c->nInput && isDelim(z[c->iOffset]) ){ |
| | c->iOffset++; |
| | } |
| |
|
| | |
| | iStartOffset = c->iOffset; |
| | while( c->iOffset<c->nInput && !isDelim(z[c->iOffset]) ){ |
| | c->iOffset++; |
| | } |
| |
|
| | if( c->iOffset>iStartOffset ){ |
| | int n = c->iOffset-iStartOffset; |
| | if( n>c->nAllocated ){ |
| | char *pNew; |
| | c->nAllocated = n+20; |
| | pNew = sqlite3_realloc64(c->zToken, c->nAllocated); |
| | if( !pNew ) return SQLITE_NOMEM; |
| | c->zToken = pNew; |
| | } |
| | porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes); |
| | *pzToken = c->zToken; |
| | *piStartOffset = iStartOffset; |
| | *piEndOffset = c->iOffset; |
| | *piPosition = c->iToken++; |
| | return SQLITE_OK; |
| | } |
| | } |
| | return SQLITE_DONE; |
| | } |
| |
|
| | |
| | |
| | |
| | static const sqlite3_tokenizer_module porterTokenizerModule = { |
| | 0, |
| | porterCreate, |
| | porterDestroy, |
| | porterOpen, |
| | porterClose, |
| | porterNext, |
| | 0 |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | void sqlite3Fts3PorterTokenizerModule( |
| | sqlite3_tokenizer_module const**ppModule |
| | ){ |
| | *ppModule = &porterTokenizerModule; |
| | } |
| |
|
| | #endif |
| |
|