39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Copyright (c) 2007 D. Richard Hipp 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** This program is free software; you can redistribute it and/or 39aa870f8f 2007-10-14 drh: ** modify it under the terms of the GNU General Public 39aa870f8f 2007-10-14 drh: ** License version 2 as published by the Free Software Foundation. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** This program is distributed in the hope that it will be useful, 39aa870f8f 2007-10-14 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of 39aa870f8f 2007-10-14 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 39aa870f8f 2007-10-14 drh: ** General Public License for more details. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** You should have received a copy of the GNU General Public 39aa870f8f 2007-10-14 drh: ** License along with this library; if not, write to the 39aa870f8f 2007-10-14 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, 39aa870f8f 2007-10-14 drh: ** Boston, MA 02111-1307, USA. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** Author contact information: 39aa870f8f 2007-10-14 drh: ** drh@hwaci.com 39aa870f8f 2007-10-14 drh: ** http://www.hwaci.com/drh/ 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ******************************************************************************* 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** This file contains an implementation of the "subscript" interpreter. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** Subscript attempts to be an extremely light-weight scripting 39aa870f8f 2007-10-14 drh: ** language. It contains the barest of bare essentials. It is 39aa870f8f 2007-10-14 drh: ** stack-based and forth-like. Everything is in a single global 39aa870f8f 2007-10-14 drh: ** namespace. There is only a single datatype of zero-terminated 39aa870f8f 2007-10-14 drh: ** string. The stack is of fixed, limited depth. The hash table 39aa870f8f 2007-10-14 drh: ** is of a limited and fixed size. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** This module attempts to be completely self-contained so that it can 39aa870f8f 2007-10-14 drh: ** be portable to other projects. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: #include "config.h" 39aa870f8f 2007-10-14 drh: #include "subscript.h" 39aa870f8f 2007-10-14 drh: #include <assert.h> 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Configuration constants 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: #define SBSCONFIG_NHASH 41 /* Size of the hash table */ 39aa870f8f 2007-10-14 drh: #define SBSCONFIG_NSTACK 10 /* Maximum stack depth */ 39aa870f8f 2007-10-14 drh: #define SBSCONFIG_ERRSIZE 100 /* Maximum size of an error message */ 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Available token types: 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: #define SBSTT_WHITESPACE 1 /* ex: \040 */ 39aa870f8f 2007-10-14 drh: #define SBSTT_NAME 2 /* ex: /abcde */ 39aa870f8f 2007-10-14 drh: #define SBSTT_VERB 3 /* ex: abcde */ 39aa870f8f 2007-10-14 drh: #define SBSTT_STRING 4 /* ex: {...} */ 39aa870f8f 2007-10-14 drh: #define SBSTT_INTEGER 5 /* Integer including option sign */ 39aa870f8f 2007-10-14 drh: #define SBSTT_INCOMPLETE 6 /* Unterminated string token */ 39aa870f8f 2007-10-14 drh: #define SBSTT_UNKNOWN 7 /* Unknown token */ 39aa870f8f 2007-10-14 drh: #define SBSTT_EOF 8 /* End of input */ 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Given an input string z of length n, identify the token that 39aa870f8f 2007-10-14 drh: ** starts at z[0]. Write the token type into *pTokenType and 39aa870f8f 2007-10-14 drh: ** return the length of the token. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: static int sbs_next_token(const char *z, int n, int *pTokenType){ 39aa870f8f 2007-10-14 drh: int c; 39aa870f8f 2007-10-14 drh: if( n<=0 || z[0]==0 ){ 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_EOF; 39aa870f8f 2007-10-14 drh: return 0; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: c = z[0]; 39aa870f8f 2007-10-14 drh: if( isspace(c) ){ 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_WHITESPACE; 39aa870f8f 2007-10-14 drh: for(i=1; i<n && isspace(z[i]); i++){} 39aa870f8f 2007-10-14 drh: return i; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: if( c=='#' ){ 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: for(i=1; i<n && z[i] && z[i-1]!='\n'; i++){} 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_WHITESPACE; 39aa870f8f 2007-10-14 drh: return i; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: if( c=='{' ){ 39aa870f8f 2007-10-14 drh: int depth = 1; 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: for(i=1; i<n && z[i]; i++){ 39aa870f8f 2007-10-14 drh: if( z[i]=='{' ){ 39aa870f8f 2007-10-14 drh: depth++; 39aa870f8f 2007-10-14 drh: }else if( z[i]=='}' ){ 39aa870f8f 2007-10-14 drh: depth--; 39aa870f8f 2007-10-14 drh: if( depth==0 ){ 39aa870f8f 2007-10-14 drh: i++; 39aa870f8f 2007-10-14 drh: break; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: if( depth ){ 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_INCOMPLETE; 39aa870f8f 2007-10-14 drh: }else{ 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_STRING; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: return i; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: if( c=='/' && n>=2 && isalpha(z[1]) ){ 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: for(i=2; i<n && (isalnum(z[i]) || z[i]=='_'); i++){} 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_NAME; 39aa870f8f 2007-10-14 drh: return i; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: if( isalpha(c) ){ 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: for(i=1; i<n && (isalnum(z[i]) || z[i]=='_'); i++){} 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_VERB; 39aa870f8f 2007-10-14 drh: return i; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: if( isdigit(c) || ((c=='-' || c=='+') && n>=2 && isdigit(z[1])) ){ 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: for(i=1; i<n && isdigit(z[i]); i++){} 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_INTEGER; 39aa870f8f 2007-10-14 drh: return i; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: *pTokenType = SBSTT_UNKNOWN; 39aa870f8f 2007-10-14 drh: return 1; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Values are stored in the hash table as instances of the following 39aa870f8f 2007-10-14 drh: ** structure. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: typedef struct SbSValue SbSValue; 39aa870f8f 2007-10-14 drh: struct SbSValue { 39aa870f8f 2007-10-14 drh: int flags; /* Bitmask of SBSVAL_* values */ 39aa870f8f 2007-10-14 drh: union { 39aa870f8f 2007-10-14 drh: struct { 39aa870f8f 2007-10-14 drh: int size; /* Number of bytes in string, not counting final zero */ 39aa870f8f 2007-10-14 drh: char *z; /* Pointer to string content */ 39aa870f8f 2007-10-14 drh: } str; /* Value if SBSVAL_STR */ 39aa870f8f 2007-10-14 drh: struct { 39aa870f8f 2007-10-14 drh: int (*xVerb)(Subscript*), void*); /* Function to do the work */ 39aa870f8f 2007-10-14 drh: void *pArg; /* 2nd parameter to xVerb */ 39aa870f8f 2007-10-14 drh: } verb; /* Value if SBSVAL_VERB */ 39aa870f8f 2007-10-14 drh: } u; 39aa870f8f 2007-10-14 drh: }; 39aa870f8f 2007-10-14 drh: #define SBSVAL_VERB 0x0001 /* Value stored in u.verb */ 39aa870f8f 2007-10-14 drh: #define SBSVAL_STR 0x0002 /* Value stored in u.str */ 39aa870f8f 2007-10-14 drh: #define SBSVAL_DYN 0x0004 /* u.str.z is dynamically allocated */ 39aa870f8f 2007-10-14 drh: #define SBSVAL_EXEC 0x0008 /* u.str.z is a script */ 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Release any memory allocated by a value. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: static void sbs_value_reset(SbSValue *p){ 39aa870f8f 2007-10-14 drh: if( p->flags & SBSVAL_DYN ){ 39aa870f8f 2007-10-14 drh: free(p->u.str.z); 39aa870f8f 2007-10-14 drh: p->flags = SBSVAL_STR; 39aa870f8f 2007-10-14 drh: p->u.str.z = ""; 39aa870f8f 2007-10-14 drh: p->u.str.size = 0; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** An entry in the hash table is an instance of this structure. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: typedef struct UsHashEntry UsHashEntry; 39aa870f8f 2007-10-14 drh: struct UsHashEntry { 39aa870f8f 2007-10-14 drh: UsHashEntry *pNext; /* Next entry with the same hash on zKey */ 39aa870f8f 2007-10-14 drh: SbSValue val; /* The payload */ 39aa870f8f 2007-10-14 drh: int nKey; /* Length of the key */ 39aa870f8f 2007-10-14 drh: char zKey[0]; /* The key */ 39aa870f8f 2007-10-14 drh: }; 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** A hash table is an instance of the following structure. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: typedef struct UsHashTab UsHashTab; 39aa870f8f 2007-10-14 drh: struct UsHashTab { 39aa870f8f 2007-10-14 drh: UsHashEntry *aHash[SBSCONFIG_NHASH]; /* The hash table */ 39aa870f8f 2007-10-14 drh: }; 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Compute a hash on a string. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: static int sbs_hash(const char *z, int n){ 39aa870f8f 2007-10-14 drh: int h = 0; 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: for(i=0; i<n; i++){ 39aa870f8f 2007-10-14 drh: h ^= (h<<1) | z[i]; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: h &= 0x7ffffff; 39aa870f8f 2007-10-14 drh: return h % SBSCONFIG_NHASH; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Look up a value in the hash table. Return a pointer to the value. 39aa870f8f 2007-10-14 drh: ** Return NULL if not found. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: static const SbSValue *sbs_fetch( 39aa870f8f 2007-10-14 drh: sbs_hash *pHash, 39aa870f8f 2007-10-14 drh: const char *zKey, 39aa870f8f 2007-10-14 drh: int nKey 39aa870f8f 2007-10-14 drh: ){ 39aa870f8f 2007-10-14 drh: int h; 39aa870f8f 2007-10-14 drh: UsHashEntry *p; 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: if( nKey<0 ) nKey = strlen(zKey); 39aa870f8f 2007-10-14 drh: h = sbs_hash(zKey, nKey); 39aa870f8f 2007-10-14 drh: for(p = pHash->aHash[h]; p; p=p->pNext){ 39aa870f8f 2007-10-14 drh: if( p->nKey==nKey && memcmp(p->zKey,zKey,nKey)==0 ){ 39aa870f8f 2007-10-14 drh: return &p->val; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: return 0; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Store a value in the hash table. Overwrite any prior value stored 39aa870f8f 2007-10-14 drh: ** under the same name. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** If the value in the 4th argument needs to be reset or freed, 39aa870f8f 2007-10-14 drh: ** the hash table will take over responsibiliity for doing so. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: static int sbs_store( 39aa870f8f 2007-10-14 drh: sbs_hash *pHash, /* Insert into this hash table */ 39aa870f8f 2007-10-14 drh: const char *zKey, /* The key */ 39aa870f8f 2007-10-14 drh: int nKey, /* Size of the key */ 39aa870f8f 2007-10-14 drh: const SbSValue *pValue /* The value to be stored */ 39aa870f8f 2007-10-14 drh: ){ 39aa870f8f 2007-10-14 drh: int h; 39aa870f8f 2007-10-14 drh: UsHashEntry *p; 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: if( nKey<0 ) nKey = strlen(zKey); 39aa870f8f 2007-10-14 drh: h = sbs_hash(zKey, nKey); 39aa870f8f 2007-10-14 drh: for(p = pHash->aHash[h]; p; p=p->pNext){ 39aa870f8f 2007-10-14 drh: if( p->nKey==nKey && memcmp(p->zKey,zKey,nKey)==0 ){ 39aa870f8f 2007-10-14 drh: sbs_value_reset(&p->val); 39aa870f8f 2007-10-14 drh: memcpy(&p->val, pValue, sizeof(p->val)); 39aa870f8f 2007-10-14 drh: return SBS_OK; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: pNew = malloc( sizeof(*pNew) + nKey ); 39aa870f8f 2007-10-14 drh: if( pNew ){ 39aa870f8f 2007-10-14 drh: pNew->nKey = nKey; 39aa870f8f 2007-10-14 drh: memcpy(pNew->zKey, zKey, nKey+1); 39aa870f8f 2007-10-14 drh: memcpy(&pNew->val, pValue, sizeof(pNew->val)); 39aa870f8f 2007-10-14 drh: pNew->pNext = pHash->aHash[h]; 39aa870f8f 2007-10-14 drh: pHash->aHash[h] = pNew; 39aa870f8f 2007-10-14 drh: return SBS_OK; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: return SBS_ERROR; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Reset a hash table. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: static void sbs_hash_reset(sbs_hash *pHash){ 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: UsHashEntry *p, *pNext; 39aa870f8f 2007-10-14 drh: for(i=0; i<SBSCONFIG_NHASH; i++){ 39aa870f8f 2007-10-14 drh: for(p=pHash->aHash[i]; p; p=pNext){ 39aa870f8f 2007-10-14 drh: pNext = p->pNext; 39aa870f8f 2007-10-14 drh: sbs_value_reset(&p->val); 39aa870f8f 2007-10-14 drh: free(p); 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: memset(pHash, 0, sizeof(*pHash)); 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** An instance of the Subscript interpreter 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: struct Subscript { 39aa870f8f 2007-10-14 drh: int nStack; /* Number of entries on stack */ 39aa870f8f 2007-10-14 drh: UsHashTab symTab; /* The symbol table */ 39aa870f8f 2007-10-14 drh: char zErrMsg[SBSCONFIG_ERRSIZE]; /* Space to write an error message */ 39aa870f8f 2007-10-14 drh: SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */ 39aa870f8f 2007-10-14 drh: }; 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Push a value onto the stack of an interpreter 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: static int sbs_push(Subscript *p, SbSValue *pVal){ 39aa870f8f 2007-10-14 drh: SbSValue *pStk; 39aa870f8f 2007-10-14 drh: if( p->nStack>=SBSCONFIG_NSTACK ){ 39aa870f8f 2007-10-14 drh: sqlite3_snprintf(SBSCONFIG_ERRSIZE, p->zErrMsg, "stack overflow"); 39aa870f8f 2007-10-14 drh: return SBS_ERROR; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: p->aStack[p->nStack++] = *pVal; 39aa870f8f 2007-10-14 drh: return SBS_OK; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Destroy an underscore interpreter 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: void SbS_Destroy(struct Subscript *p){ 39aa870f8f 2007-10-14 drh: int i; 39aa870f8f 2007-10-14 drh: sbs_hash_reset(&p->symTab); 39aa870f8f 2007-10-14 drh: for(i=0; i<p->nStack; i++){ 39aa870f8f 2007-10-14 drh: sbs_value_reset(&p->aStack[i]); 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: free(p); 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Set the error message for an interpreter. Verb implementations 39aa870f8f 2007-10-14 drh: ** use this routine when they encounter an error. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: void SbS_SetErrorMessage(struct Subscript *p, const char *zErr){ 39aa870f8f 2007-10-14 drh: int nErr = strlen(zErr); 39aa870f8f 2007-10-14 drh: if( nErr>sizeof(p->zErrMsg)-1 ){ 39aa870f8f 2007-10-14 drh: nErr = sizeof(p->zErrMsg)-1; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: memcpy(p->zErrMsg, zErr, nErr); 39aa870f8f 2007-10-14 drh: p->zErrMsg[nErr] = 0; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Return a pointer to the current error message for the 39aa870f8f 2007-10-14 drh: ** interpreter. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: const char *SbS_GetErrorMessage(struct Subscript *p){ 39aa870f8f 2007-10-14 drh: return p->zErrMsg; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Add a new verb the given interpreter 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: int SbS_AddVerb( 39aa870f8f 2007-10-14 drh: struct Subscript *p, 39aa870f8f 2007-10-14 drh: const char *zVerb, 39aa870f8f 2007-10-14 drh: int (*xVerb)(struct Subscript*,void*), 39aa870f8f 2007-10-14 drh: void *pArg 39aa870f8f 2007-10-14 drh: ){ 39aa870f8f 2007-10-14 drh: SbSValue v; 39aa870f8f 2007-10-14 drh: v.flags = SBSVAL_VERB; 39aa870f8f 2007-10-14 drh: v.u.verb.xVerb = xVerb; 39aa870f8f 2007-10-14 drh: v.u.verb.pArg = pArg; 39aa870f8f 2007-10-14 drh: return sbs_store(&p->symTab, zVerb, -1, &v); 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Push a string value onto the stack. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** If the 4th parameter is 0, then the string is static. 39aa870f8f 2007-10-14 drh: ** If the 4th parameter is non-zero then the string was obtained 39aa870f8f 2007-10-14 drh: ** from malloc and Subscript will take responsibility for freeing 39aa870f8f 2007-10-14 drh: ** it. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** Return 0 on success and non-zero if there is an error. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: int SbS_Push( 39aa870f8f 2007-10-14 drh: struct Subscript *p, /* Push onto this interpreter */ 39aa870f8f 2007-10-14 drh: const char *z, /* String value to push */ 39aa870f8f 2007-10-14 drh: int n, /* Length of the string, or -1 */ 39aa870f8f 2007-10-14 drh: int dyn /* If true, z was obtained from malloc */ 39aa870f8f 2007-10-14 drh: ){ 39aa870f8f 2007-10-14 drh: SbSValue v; 39aa870f8f 2007-10-14 drh: v.flags = SBSVAL_STR; 39aa870f8f 2007-10-14 drh: if( needToFree ){ 39aa870f8f 2007-10-14 drh: v.flags |= SBSVAL_DYN; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: if( n<0 ) n = strlen(z); 39aa870f8f 2007-10-14 drh: v.u.str.size = n; 39aa870f8f 2007-10-14 drh: v.u.str.z = z; 39aa870f8f 2007-10-14 drh: return sbs_push(p, &v); 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Push an integer value onto the stack. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** This routine really just converts the integer into a string 39aa870f8f 2007-10-14 drh: ** then calls SbS_Push. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: int SbS_PushInt(struct Subscript *p, int iVal){ 39aa870f8f 2007-10-14 drh: if( iVal==0 ){ 39aa870f8f 2007-10-14 drh: return SbS_Push(p, "0", 1, 0); 39aa870f8f 2007-10-14 drh: }else if( iVal==1 ){ 39aa870f8f 2007-10-14 drh: return SbS_Push(p, "1", 1, 0); 39aa870f8f 2007-10-14 drh: }else{ 39aa870f8f 2007-10-14 drh: char *z; 39aa870f8f 2007-10-14 drh: int n; 39aa870f8f 2007-10-14 drh: char zVal[50]; 39aa870f8f 2007-10-14 drh: sprintf(zVal, "%d", iVal); 39aa870f8f 2007-10-14 drh: n = strlen(zVal); 39aa870f8f 2007-10-14 drh: z = malloc( n+1 ); 39aa870f8f 2007-10-14 drh: if( z ){ 39aa870f8f 2007-10-14 drh: strcpy(z, zVal); 39aa870f8f 2007-10-14 drh: return SbS_Push(p, z, n, 1); 39aa870f8f 2007-10-14 drh: }else{ 39aa870f8f 2007-10-14 drh: return SBS_ERROR; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Pop and destroy zero or more values from the stack. 39aa870f8f 2007-10-14 drh: ** Return the number of values remaining on the stack after 39aa870f8f 2007-10-14 drh: ** the pops occur. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: int SbS_Pop(struct Subscript *p, int N){ 39aa870f8f 2007-10-14 drh: while( N>0 && p->nStack>0 ){ 39aa870f8f 2007-10-14 drh: p->nStack--; 39aa870f8f 2007-10-14 drh: sbs_value_reset(&p->aStack[p->nStack]); 39aa870f8f 2007-10-14 drh: N--; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: return p->nStack; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Return the N-th element of the stack. 0 is the top of the stack. 39aa870f8f 2007-10-14 drh: ** 1 is the first element down. 2 is the second element. And so forth. 39aa870f8f 2007-10-14 drh: ** Return NULL if there is no N-th element. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** The pointer returned is only valid until the value is popped 39aa870f8f 2007-10-14 drh: ** from the stack. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: const char *SbS_StackValue(struct Subscript *p, int N, int *pSize){ 39aa870f8f 2007-10-14 drh: SbSValue *pVal; 39aa870f8f 2007-10-14 drh: if( N<0 || N>=p->nStack ){ 39aa870f8f 2007-10-14 drh: return 0; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: pVal = &p->aStack[p->nStack-N-1]; 39aa870f8f 2007-10-14 drh: if( (pVal->flags & SBSVAL_STR)==0 ){ 39aa870f8f 2007-10-14 drh: return 0; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: *pSize = pVal->u.str.size; 39aa870f8f 2007-10-14 drh: return pVal->u.str.z; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** A convenience routine for extracting an integer value from the 39aa870f8f 2007-10-14 drh: ** stack. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: int SbS_StackValueInt(struct Subscript *p, int N){ 39aa870f8f 2007-10-14 drh: int n, v; 39aa870f8f 2007-10-14 drh: int isNeg = 0; 39aa870f8f 2007-10-14 drh: char *z = SbS_StackValue(p, N, &n); 39aa870f8f 2007-10-14 drh: v = 0; 39aa870f8f 2007-10-14 drh: if( n==0 ) return 0; 39aa870f8f 2007-10-14 drh: if( z[0]=='-' ){ 39aa870f8f 2007-10-14 drh: isNeg = 1; 39aa870f8f 2007-10-14 drh: z++; 39aa870f8f 2007-10-14 drh: n--; 39aa870f8f 2007-10-14 drh: }else if( z[0]=='+' ){ 39aa870f8f 2007-10-14 drh: z++; 39aa870f8f 2007-10-14 drh: n--; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: while( n>0 && isdigit(z[0]) ){ 39aa870f8f 2007-10-14 drh: v = v*10 + z[0] - '0'; 39aa870f8f 2007-10-14 drh: z++; 39aa870f8f 2007-10-14 drh: n--; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: if( isNeg ){ 39aa870f8f 2007-10-14 drh: v = -v; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: return v; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Retrieve the value of a variable from the interpreter. Return 39aa870f8f 2007-10-14 drh: ** NULL if no such variable is defined. 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** The returned string is not necessarily (probably not) zero-terminated. 39aa870f8f 2007-10-14 drh: ** The string may be deallocated the next time anything is done to 39aa870f8f 2007-10-14 drh: ** the interpreter. Make a copy if you need it to persist. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: const char *SbS_Fetch( 39aa870f8f 2007-10-14 drh: struct Subscript *p, /* The interpreter we are interrogating */ 39aa870f8f 2007-10-14 drh: const char *zKey, /* Name of the variable. Case sensitive */ 39aa870f8f 2007-10-14 drh: int *pLength /* Write the length here */ 39aa870f8f 2007-10-14 drh: ){ 39aa870f8f 2007-10-14 drh: SbSValue *pVal; 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: pVal = sbs_fetch(&p->symTab, zKey, -1); 39aa870f8f 2007-10-14 drh: if( pVal==0 || (pVal->flags & SBSVAL_STR)==0 ){ 39aa870f8f 2007-10-14 drh: *pLength = 0; 39aa870f8f 2007-10-14 drh: return 0; 39aa870f8f 2007-10-14 drh: }else{ 39aa870f8f 2007-10-14 drh: *pLength = pVal->u.str.size; 39aa870f8f 2007-10-14 drh: return pVal->u.str.z; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Generate an error and return non-zero if the stack has 39aa870f8f 2007-10-14 drh: ** fewer than N elements. This is utility routine used in 39aa870f8f 2007-10-14 drh: ** the implementation of verbs. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: int SbS_RequireStack(struct Subscript *p, int N, const char *zCmd){ 39aa870f8f 2007-10-14 drh: if( p->nStack>N ) return 0; 39aa870f8f 2007-10-14 drh: sqlite3_snprintf(sizeof(p->zErrMsg), p->zErrMsg, 39aa870f8f 2007-10-14 drh: "\"%s\" requires at least %d stack elements - only found %d", 39aa870f8f 2007-10-14 drh: zCmd, N, p->nStack); 39aa870f8f 2007-10-14 drh: return 1; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** The built-in "set" command: 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** STRING NAME set 39aa870f8f 2007-10-14 drh: ** 39aa870f8f 2007-10-14 drh: ** Write the value of STRING into variable called NAME. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: static int setCmd(Subscript *p, void *pNotUsed){ 39aa870f8f 2007-10-14 drh: SbSValue *pTos; 39aa870f8f 2007-10-14 drh: SbSValue *pNos; 39aa870f8f 2007-10-14 drh: if( SbS_RequireStack(p, 2, "set") ) return SBS_ERROR; 39aa870f8f 2007-10-14 drh: pTos = p->aStack[--p->nStack]; 39aa870f8f 2007-10-14 drh: pNos = p->aStack[--p->nStack]; 39aa870f8f 2007-10-14 drh: sbs_store(&p->symTab, pTos->u.str.z, pTos->u.str.size, pNos); 39aa870f8f 2007-10-14 drh: sbs_value_reset(pTos); 39aa870f8f 2007-10-14 drh: return 0; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Create a new underscore interpreter 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: struct Subscript *SbS_Create(void){ 39aa870f8f 2007-10-14 drh: Subscript *p; 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: p = malloc( sizeof(*p) ); 39aa870f8f 2007-10-14 drh: if( p ){ 39aa870f8f 2007-10-14 drh: memset(p, 0, sizeof(*p)); 39aa870f8f 2007-10-14 drh: SbS_AddVerb(p, "set", setCmd, 0); 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: return p; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: 39aa870f8f 2007-10-14 drh: /* 39aa870f8f 2007-10-14 drh: ** Evaluate the script given by the first nScript bytes of zScript[]. 39aa870f8f 2007-10-14 drh: ** Return 0 on success and non-zero for an error. 39aa870f8f 2007-10-14 drh: */ 39aa870f8f 2007-10-14 drh: int SbS_Eval(struct Subscript *p, const char *zScript, int nScript){ 39aa870f8f 2007-10-14 drh: int rc = SBS_OK; 39aa870f8f 2007-10-14 drh: if( nScript<0 ) nScript = strlen(zScript); 39aa870f8f 2007-10-14 drh: while( nScript>0 && rc==SBS_OK ){ 39aa870f8f 2007-10-14 drh: int n; 39aa870f8f 2007-10-14 drh: int ttype; 39aa870f8f 2007-10-14 drh: n = sbs_token_type(zScript, nScript, &ttype); 39aa870f8f 2007-10-14 drh: switch( ttype ){ 39aa870f8f 2007-10-14 drh: case SBSTT_WHITESPACE: { 39aa870f8f 2007-10-14 drh: break; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: case SBSTT_EOF: { 39aa870f8f 2007-10-14 drh: nScript = 0; 39aa870f8f 2007-10-14 drh: break; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: case SBSTT_INCOMPLETE: 39aa870f8f 2007-10-14 drh: case SBSTT_UNKNOWN: { 39aa870f8f 2007-10-14 drh: rc = SBS_ERROR; 39aa870f8f 2007-10-14 drh: nScript = n; 39aa870f8f 2007-10-14 drh: break; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: case SBSTT_INTEGER: { 39aa870f8f 2007-10-14 drh: rc = sbs_push(p, zScript, n); 39aa870f8f 2007-10-14 drh: break; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: case SBSTT_NAME: { 39aa870f8f 2007-10-14 drh: rc = sbs_push(p, &zScript[1], n-1); 39aa870f8f 2007-10-14 drh: break; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: case SBSTT_STRING: { 39aa870f8f 2007-10-14 drh: rc = sbs_push(p, &zScript[1], n-2); 39aa870f8f 2007-10-14 drh: break; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: case SBSTT_VERB: { 39aa870f8f 2007-10-14 drh: SbSValue *pVal = sbs_fetch(p->pHash, zScript, nScript); 39aa870f8f 2007-10-14 drh: if( pVal==0 ){ 39aa870f8f 2007-10-14 drh: rc = SBS_ERROR; 39aa870f8f 2007-10-14 drh: }else if( pVal->flags & SBSVAL_VERB ){ 39aa870f8f 2007-10-14 drh: rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg); 39aa870f8f 2007-10-14 drh: }else if( pVal->flags & SBSVAL_EXEC ){ 39aa870f8f 2007-10-14 drh: rc = SbS_Eval(p, pVal->u.str.z, pVal->u.str.size); 39aa870f8f 2007-10-14 drh: }else{ 39aa870f8f 2007-10-14 drh: rc = sbs_push(p, pVal->u.str.z, pVal->u.str.size); 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: break; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: zScript += n; 39aa870f8f 2007-10-14 drh: nScript -= n; 39aa870f8f 2007-10-14 drh: } 39aa870f8f 2007-10-14 drh: return rc; 39aa870f8f 2007-10-14 drh: }