2005-08-01 23:17:22 -04:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* This is really two parts -- the lexer and the parser. Converting
|
|
|
|
* a parse tree back to a format that works with the database backend
|
|
|
|
* is left to the db backend.
|
|
|
|
*
|
|
|
|
* Oh, and this is called "smart-parser" because it parses terms for
|
|
|
|
* specifying smart playlists, not because it is particularly smart. :)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "err.h"
|
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
typedef struct tag_token {
|
2005-08-14 23:16:36 -04:00
|
|
|
int token_id;
|
|
|
|
union {
|
2005-10-13 03:38:22 -04:00
|
|
|
char *cvalue;
|
|
|
|
int ivalue;
|
2005-08-14 23:16:36 -04:00
|
|
|
} data;
|
2005-10-13 03:38:22 -04:00
|
|
|
} SP_TOKEN;
|
2005-08-14 23:16:36 -04:00
|
|
|
|
2005-10-18 18:35:10 -04:00
|
|
|
typedef struct tag_sp_node {
|
|
|
|
union {
|
|
|
|
struct tag_sp_node *node;
|
|
|
|
char *field;
|
|
|
|
} left;
|
|
|
|
|
|
|
|
int op;
|
|
|
|
|
|
|
|
union {
|
|
|
|
struct tag_sp_node *node;
|
|
|
|
int ivalue;
|
|
|
|
char *cvalue;
|
|
|
|
} right;
|
|
|
|
} SP_NODE;
|
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
/*
|
2005-10-02 18:48:07 -04:00
|
|
|
#define T_ID 0x00
|
|
|
|
#define T_PATH 0x01
|
|
|
|
#define T_TITLE 0x02
|
|
|
|
#define T_ARTIST 0x03
|
|
|
|
#define T_ALBUM 0x04
|
|
|
|
#define T_GENRE 0x05
|
|
|
|
#define T_COMMENT 0x06
|
|
|
|
#define T_TYPE 0x07
|
|
|
|
#define T_COMPOSER 0x08
|
|
|
|
#define T_ORCHESTRA 0x09
|
|
|
|
#define T_GROUPING 0x0a
|
|
|
|
#define T_URL 0x0b
|
|
|
|
#define T_BITRATE 0x0c
|
|
|
|
#define T_SAMPLERATE 0x0d
|
|
|
|
#define T_SONG_LENGTH 0x0e
|
|
|
|
#define T_FILE_SIZE 0x0f
|
|
|
|
#define T_YEAR 0x10
|
|
|
|
#define T_TRACK 0x11
|
|
|
|
#define T_TOTAL_TRACKS 0x12
|
|
|
|
#define T_DISC 0x13
|
|
|
|
#define T_TOTAL_DISCS 0x14
|
|
|
|
#define T_BPM 0x15
|
|
|
|
#define T_COMPILATION 0x16
|
|
|
|
#define T_RATING 0x17
|
|
|
|
#define T_PLAYCOUNT 0x18
|
|
|
|
#define T_DATA_KIND 0x19
|
|
|
|
#define T_ITEM_KIND 0x1a
|
|
|
|
#define T_DESCRIPTION 0x1b
|
|
|
|
#define T_TIME_ADDED 0x1c
|
|
|
|
#define T_TIME_MODIFIED 0x0d
|
|
|
|
#define T_TIME_PLAYED 0x1d
|
|
|
|
#define T_TIME_STAMP 0x1e
|
|
|
|
#define T_DISABLED 0x1f
|
|
|
|
#define T_SAMPLE_COUNT 0x1e
|
|
|
|
#define T_FORCE_UPDATE 0x1f
|
|
|
|
#define T_CODECTYPE 0x20
|
|
|
|
#define T_IDX 0x21
|
2005-10-13 03:38:22 -04:00
|
|
|
*/
|
|
|
|
|
2005-10-18 18:35:10 -04:00
|
|
|
/**
|
2005-10-13 03:38:22 -04:00
|
|
|
* high 4 bits:
|
|
|
|
*
|
2005-10-18 18:35:10 -04:00
|
|
|
* 0x8000 -
|
2005-10-13 03:38:22 -04:00
|
|
|
* 0x4000 -
|
|
|
|
* 0x2000 - data is string
|
|
|
|
* 0x1000 - data is int
|
2005-10-17 00:57:06 -04:00
|
|
|
*
|
2005-10-18 18:35:10 -04:00
|
|
|
* 0x0800 -
|
2005-10-17 00:57:06 -04:00
|
|
|
* 0x0400 -
|
|
|
|
* 0x0200 -
|
|
|
|
* 0x0100 -
|
2005-10-13 03:38:22 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
#define T_STRING 0x2001
|
2005-10-14 00:11:06 -04:00
|
|
|
#define T_INT_FIELD 0x2002
|
2005-10-13 03:38:22 -04:00
|
|
|
#define T_STRING_FIELD 0x2003
|
2005-10-14 00:11:06 -04:00
|
|
|
#define T_DATE_FIELD 0x2004
|
2005-10-02 18:48:07 -04:00
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
#define T_OPENPAREN 0x0005
|
|
|
|
#define T_CLOSEPAREN 0x0006
|
2005-10-17 00:57:06 -04:00
|
|
|
#define T_LESS 0x0007
|
|
|
|
#define T_LESSEQUAL 0x0008
|
|
|
|
#define T_GREATER 0x0009
|
|
|
|
#define T_GREATEREQUAL 0x000a
|
|
|
|
#define T_EQUAL 0x000b
|
|
|
|
#define T_OR 0x000c
|
|
|
|
#define T_AND 0x000d
|
|
|
|
#define T_QUOTE 0x000e
|
|
|
|
#define T_NUMBER 0x000f
|
|
|
|
#define T_LAST 0x0010
|
|
|
|
|
|
|
|
#define T_EOF 0x00fd
|
|
|
|
#define T_BOF 0x00fe
|
|
|
|
#define T_ERROR 0x00ff
|
|
|
|
|
|
|
|
char *sp_token_descr[] = {
|
|
|
|
"unknown",
|
|
|
|
"literal string",
|
|
|
|
"integer field",
|
|
|
|
"string field",
|
|
|
|
"date field",
|
|
|
|
"(",
|
|
|
|
")",
|
|
|
|
"<",
|
|
|
|
"<=",
|
|
|
|
">",
|
|
|
|
">=",
|
|
|
|
"=",
|
|
|
|
"or",
|
|
|
|
"and",
|
|
|
|
"quote",
|
|
|
|
"number"
|
|
|
|
};
|
2005-10-13 03:38:22 -04:00
|
|
|
|
|
|
|
typedef struct tag_fieldlookup {
|
|
|
|
int type;
|
|
|
|
char *name;
|
|
|
|
} FIELDLOOKUP;
|
|
|
|
|
|
|
|
FIELDLOOKUP sp_fields[] = {
|
|
|
|
{ T_INT_FIELD, "id" },
|
|
|
|
{ T_STRING_FIELD, "path" },
|
|
|
|
{ T_STRING_FIELD, "title" },
|
|
|
|
{ T_STRING_FIELD, "artist" },
|
|
|
|
{ T_STRING_FIELD, "album" },
|
|
|
|
{ T_STRING_FIELD, "genre" },
|
|
|
|
{ T_STRING_FIELD, "comment" },
|
|
|
|
{ T_STRING_FIELD, "type" },
|
|
|
|
{ T_STRING_FIELD, "composer" },
|
|
|
|
{ T_STRING_FIELD, "orchestra" },
|
|
|
|
{ T_STRING_FIELD, "grouping" },
|
|
|
|
{ T_STRING_FIELD, "url" },
|
|
|
|
{ T_INT_FIELD, "bitrate" },
|
|
|
|
{ T_INT_FIELD, "samplerate" },
|
|
|
|
{ T_INT_FIELD, "songlength" },
|
|
|
|
{ T_INT_FIELD, "filesize" },
|
|
|
|
{ T_INT_FIELD, "year" },
|
|
|
|
{ T_INT_FIELD, "track" },
|
|
|
|
{ T_INT_FIELD, "totaltracks" },
|
|
|
|
{ T_INT_FIELD, "disc" },
|
|
|
|
{ T_INT_FIELD, "totaldiscs" },
|
|
|
|
{ T_INT_FIELD, "bpm" },
|
|
|
|
{ T_INT_FIELD, "compilation" },
|
|
|
|
{ T_INT_FIELD, "rating" },
|
|
|
|
{ T_INT_FIELD, "playcount" },
|
|
|
|
{ T_INT_FIELD, "datakind" },
|
|
|
|
{ T_INT_FIELD, "itemkind" },
|
|
|
|
{ T_STRING_FIELD, "description" },
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
/* end of db fields */
|
|
|
|
{ T_OR, "or" },
|
|
|
|
{ T_AND, "and" },
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
/* end */
|
2005-10-13 03:38:22 -04:00
|
|
|
{ 0, NULL },
|
2005-08-14 23:16:36 -04:00
|
|
|
};
|
|
|
|
|
2005-10-18 18:35:10 -04:00
|
|
|
typedef struct tag_parsetree {
|
2005-08-01 23:17:22 -04:00
|
|
|
char *term;
|
2005-10-06 00:48:04 -04:00
|
|
|
char *current;
|
2005-10-13 03:38:22 -04:00
|
|
|
SP_TOKEN token;
|
|
|
|
SP_TOKEN next_token;
|
2005-08-01 23:17:22 -04:00
|
|
|
} PARSESTRUCT, *PARSETREE;
|
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
/* Forwards */
|
|
|
|
int sp_parse_phrase(PARSETREE tree);
|
|
|
|
int sp_parse_aexpr(PARSETREE tree);
|
|
|
|
int sp_parse_oexpr(PARSETREE tree);
|
|
|
|
int sp_parse_expr(PARSETREE tree);
|
|
|
|
int sp_parse_criterion(PARSETREE tree);
|
|
|
|
int sp_parse_string_criterion(PARSETREE tree);
|
|
|
|
int sp_parse_int_criterion(PARSETREE tree);
|
|
|
|
int sp_parse_date_criterion(PARSETREE tree);
|
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
/**
|
|
|
|
* scan the input, returning the next available token.
|
|
|
|
*
|
|
|
|
* @param tree current working parse tree.
|
|
|
|
* @returns next token (token, not the value)
|
|
|
|
*/
|
2005-08-14 23:16:36 -04:00
|
|
|
int sp_scan(PARSETREE tree) {
|
2005-10-17 00:57:06 -04:00
|
|
|
int is_string=0;
|
|
|
|
char *terminator=NULL;
|
2005-10-06 00:48:04 -04:00
|
|
|
char *tail;
|
2005-10-13 03:38:22 -04:00
|
|
|
int advance=0;
|
|
|
|
FIELDLOOKUP *pfield=sp_fields;
|
|
|
|
int len;
|
2005-10-17 00:57:06 -04:00
|
|
|
int found;
|
2005-10-13 03:38:22 -04:00
|
|
|
|
|
|
|
if(tree->token.token_id & 0x2000) {
|
2005-10-17 00:57:06 -04:00
|
|
|
if(tree->token.data.cvalue)
|
|
|
|
free(tree->token.data.cvalue);
|
2005-10-13 03:38:22 -04:00
|
|
|
}
|
2005-10-06 00:48:04 -04:00
|
|
|
|
|
|
|
tree->token=tree->next_token;
|
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
if(tree->token.token_id == T_EOF) {
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Returning token T_EOF\n");
|
2005-10-13 03:38:22 -04:00
|
|
|
return T_EOF;
|
2005-10-17 00:57:06 -04:00
|
|
|
}
|
2005-10-06 00:48:04 -04:00
|
|
|
|
|
|
|
/* keep advancing until we have a token */
|
2005-10-14 00:11:06 -04:00
|
|
|
while(*(tree->current) && strchr(" \t\n\r",*(tree->current)))
|
2005-10-13 03:38:22 -04:00
|
|
|
tree->current++;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
if(!*(tree->current)) {
|
|
|
|
tree->next_token.token_id = T_EOF;
|
2005-10-17 00:57:06 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Returning token %04x\n",tree->token.token_id);
|
2005-10-13 03:38:22 -04:00
|
|
|
return tree->token.token_id;
|
2005-10-06 00:48:04 -04:00
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-14 00:11:06 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Current offset: %d, char: %c\n",
|
|
|
|
tree->current - tree->term, *(tree->current));
|
2005-10-06 00:48:04 -04:00
|
|
|
|
|
|
|
/* check singletons */
|
2005-10-13 03:38:22 -04:00
|
|
|
switch(*(tree->current)) {
|
2005-10-17 00:57:06 -04:00
|
|
|
case '|':
|
|
|
|
if((*(tree->current + 1) == '|')) {
|
|
|
|
advance = 2;
|
|
|
|
tree->next_token.token_id = T_OR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '&':
|
|
|
|
if((*(tree->current + 1) == '&')) {
|
|
|
|
advance = 2;
|
|
|
|
tree->next_token.token_id = T_AND;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
case '=':
|
|
|
|
advance=1;
|
|
|
|
tree->next_token.token_id = T_EQUAL;
|
|
|
|
break;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
case '<':
|
|
|
|
if((*(tree->current + 1)) == '=') {
|
|
|
|
advance = 2;
|
|
|
|
tree->next_token.token_id = T_LESSEQUAL;
|
|
|
|
} else {
|
|
|
|
advance = 1;
|
|
|
|
tree->next_token.token_id = T_LESS;
|
|
|
|
}
|
|
|
|
break;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
case '>':
|
|
|
|
if((*(tree->current + 1)) == '=') {
|
|
|
|
advance = 2;
|
|
|
|
tree->next_token.token_id = T_GREATEREQUAL;
|
|
|
|
} else {
|
|
|
|
advance = 1;
|
|
|
|
tree->next_token.token_id = T_GREATER;
|
|
|
|
}
|
|
|
|
break;
|
2005-10-06 00:48:04 -04:00
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
case '(':
|
|
|
|
advance=1;
|
|
|
|
tree->next_token.token_id = T_OPENPAREN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ')':
|
|
|
|
advance=1;
|
|
|
|
tree->next_token.token_id = T_CLOSEPAREN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '"':
|
|
|
|
advance=1;
|
|
|
|
tree->next_token.token_id = T_QUOTE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
if(advance) { /* singleton */
|
2005-10-13 03:38:22 -04:00
|
|
|
tree->current += advance;
|
|
|
|
} else { /* either a keyword token or a quoted string */
|
2005-10-14 00:11:06 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"keyword or string!\n");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
/* walk to a terminator */
|
|
|
|
tail = tree->current;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
terminator = " \t\n\r\"<>=()|&";
|
|
|
|
if(tree->token.token_id == T_QUOTE) {
|
|
|
|
is_string=1;
|
|
|
|
terminator="\"";
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
while((*tail) && (!strchr(terminator,*tail))) {
|
2005-10-13 03:38:22 -04:00
|
|
|
tail++;
|
|
|
|
}
|
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
found=0;
|
2005-10-13 03:38:22 -04:00
|
|
|
len = tail - tree->current;
|
2005-10-17 00:57:06 -04:00
|
|
|
|
|
|
|
if(!is_string) {
|
|
|
|
/* find it in the token list */
|
|
|
|
pfield=sp_fields;
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Len is %d\n",len);
|
|
|
|
while(pfield->name) {
|
|
|
|
if(strlen(pfield->name) == len) {
|
|
|
|
if(strncasecmp(pfield->name,tree->current,len) == 0) {
|
|
|
|
found=1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pfield++;
|
2005-10-13 03:38:22 -04:00
|
|
|
}
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
if(found) {
|
2005-10-13 03:38:22 -04:00
|
|
|
tree->next_token.token_id = pfield->type;
|
2005-10-14 00:11:06 -04:00
|
|
|
} else {
|
|
|
|
tree->next_token.token_id = T_STRING;
|
|
|
|
}
|
2005-10-13 03:38:22 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
if(tree->next_token.token_id & 0x2000) {
|
|
|
|
tree->next_token.data.cvalue = malloc(len + 1);
|
|
|
|
if(!tree->next_token.data.cvalue) {
|
|
|
|
/* fail on malloc error */
|
|
|
|
DPRINTF(E_FATAL,L_PARSE,"Malloc error.\n");
|
|
|
|
}
|
|
|
|
strncpy(tree->next_token.data.cvalue,tree->current,len);
|
|
|
|
tree->next_token.data.cvalue[len] = '\x0';
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
/* check for numberic? */
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-14 00:11:06 -04:00
|
|
|
tree->current=tail;
|
2005-10-13 03:38:22 -04:00
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Returning token %04x\n",tree->token.token_id);
|
|
|
|
if(tree->token.token_id & 0x2000)
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"String val: %s\n",tree->token.data.cvalue);
|
|
|
|
if(tree->token.token_id & 0x1000)
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Int val: %d\n",tree->token.data.ivalue);
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-13 03:38:22 -04:00
|
|
|
return tree->token.token_id;
|
2005-08-14 23:16:36 -04:00
|
|
|
}
|
2005-08-01 23:17:22 -04:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set up the initial parse tree
|
2005-10-18 18:35:10 -04:00
|
|
|
*
|
2005-08-01 23:17:22 -04:00
|
|
|
* @returns opaque parsetree struct
|
|
|
|
*/
|
|
|
|
PARSETREE sp_init(void) {
|
|
|
|
PARSETREE ptree;
|
|
|
|
|
|
|
|
ptree = (PARSETREE)malloc(sizeof(PARSESTRUCT));
|
2005-10-18 18:35:10 -04:00
|
|
|
if(!ptree)
|
2005-10-13 03:38:22 -04:00
|
|
|
DPRINTF(E_FATAL,L_PARSE,"Alloc error\n");
|
2005-08-01 23:17:22 -04:00
|
|
|
|
|
|
|
memset(ptree,0,sizeof(PARSESTRUCT));
|
|
|
|
return ptree;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse a term or phrase into a tree.
|
|
|
|
*
|
2005-10-17 00:57:06 -04:00
|
|
|
* I'm not a language expert, so I'd welcome suggestions on the
|
2005-10-16 02:55:42 -04:00
|
|
|
* following production rules:
|
|
|
|
*
|
|
|
|
* phrase -> aexpr T_EOF
|
|
|
|
* aexpr -> oexpr { T_AND oexpr }
|
2005-10-18 18:35:10 -04:00
|
|
|
* oexpr -> expr { T_OR expr }
|
2005-10-17 00:57:06 -04:00
|
|
|
* expr -> T_OPENPAREN aexpr T_CLOSEPAREN | criterion
|
|
|
|
* criterion -> field op value
|
2005-10-16 02:55:42 -04:00
|
|
|
*
|
|
|
|
* field -> T_STRINGFIELD, T_INTFIELD, T_DATEFIELD
|
|
|
|
* op -> T_EQUAL, T_GREATEREQUAL, etc
|
|
|
|
* value -> T_NUMBER, T_STRING, or T_DATE, as appropriate
|
|
|
|
*
|
2005-08-01 23:17:22 -04:00
|
|
|
* @param tree parsetree previously created with sp_init
|
|
|
|
* @param term term or phrase to parse
|
|
|
|
* @returns 1 if successful, 0 if not
|
|
|
|
*/
|
|
|
|
int sp_parse(PARSETREE tree, char *term) {
|
|
|
|
tree->term = strdup(term); /* will be destroyed by parsing */
|
2005-10-06 00:48:04 -04:00
|
|
|
tree->current=tree->term;
|
2005-10-13 03:38:22 -04:00
|
|
|
tree->token.token_id=T_BOF;
|
|
|
|
tree->next_token.token_id=T_BOF;
|
2005-10-14 00:11:06 -04:00
|
|
|
sp_scan(tree);
|
2005-10-16 02:55:42 -04:00
|
|
|
sp_scan(tree);
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
if(sp_parse_phrase(tree)) {
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Parsed successfully\n");
|
|
|
|
} else {
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Parsing error\n");
|
2005-08-01 23:17:22 -04:00
|
|
|
}
|
2005-08-14 23:16:36 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
return 1;
|
2005-08-01 23:17:22 -04:00
|
|
|
}
|
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* parse for a phrase
|
|
|
|
*
|
|
|
|
* phrase -> aexpr T_EOF
|
|
|
|
*
|
|
|
|
* @param tree tree we are parsing (and building)
|
|
|
|
* @returns 1 if successful, 0 otherwise
|
|
|
|
*/
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
int sp_parse_phrase(PARSETREE tree) {
|
|
|
|
int result=0;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Entering sp_parse_phrase\n");
|
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
if(sp_parse_aexpr(tree) && (tree->token.token_id == T_EOF))
|
2005-10-16 02:55:42 -04:00
|
|
|
result=1;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Exiting sp_parse_phrase: %s\n",result ?
|
|
|
|
"success" : "fail");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
|
|
|
return result;
|
2005-10-16 02:55:42 -04:00
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
/**
|
|
|
|
* parse for an ANDed expression
|
|
|
|
*
|
|
|
|
* aexpr -> oexpr { T_AND oexpr }
|
|
|
|
*
|
|
|
|
* @param tree tree we are building
|
|
|
|
* @returns 1 if successful, 0 otherwise
|
|
|
|
*/
|
|
|
|
int sp_parse_aexpr(PARSETREE tree) {
|
|
|
|
int result=0;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Entering sp_parse_aexpr\n");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
while(1) {
|
|
|
|
result = sp_parse_oexpr(tree);
|
2005-10-17 00:57:06 -04:00
|
|
|
if((!result) || (tree->token.token_id != T_AND)) break;
|
2005-10-16 02:55:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Exiting sp_parse_aexpr: %s\n",result ?
|
|
|
|
"success" : "fail");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse for an ORed expression
|
|
|
|
*
|
|
|
|
* oexpr -> expr { T_OR expr }
|
|
|
|
*
|
|
|
|
* @param tree tree we are building
|
|
|
|
* @returns 1 if successful, 0 otherwise
|
|
|
|
*/
|
|
|
|
int sp_parse_oexpr(PARSETREE tree) {
|
|
|
|
int result=0;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Entering sp_parse_oexpr\n");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
while(1) {
|
|
|
|
result = sp_parse_expr(tree);
|
2005-10-17 00:57:06 -04:00
|
|
|
if((!result) || (tree->token.token_id != T_OR)) break;
|
2005-10-16 02:55:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Exiting sp_parse_oexpr: %s\n",result ?
|
|
|
|
"success" : "fail");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-16 02:55:42 -04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse for an expression
|
|
|
|
*
|
|
|
|
* expr -> T_OPENPAREN aexpr T_CLOSEPAREN | criteria
|
2005-10-18 18:35:10 -04:00
|
|
|
*
|
2005-10-16 02:55:42 -04:00
|
|
|
* @param tree tree we are building
|
|
|
|
* @returns 1 if successful, 0 otherwise
|
|
|
|
*/
|
|
|
|
int sp_parse_expr(PARSETREE tree) {
|
2005-10-17 00:57:06 -04:00
|
|
|
int result=0;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Entering sp_parse_expr\n");
|
|
|
|
if(tree->token.token_id == T_OPENPAREN) {
|
|
|
|
sp_scan(tree);
|
|
|
|
result = sp_parse_aexpr(tree);
|
|
|
|
if((result) && (tree->token.token_id == T_OPENPAREN)) {
|
|
|
|
sp_scan(tree);
|
|
|
|
} else {
|
|
|
|
/* Error: expecting close paren */
|
|
|
|
result=0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result = sp_parse_criterion(tree);
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Exiting sp_parse_expr: %s\n",result ?
|
|
|
|
"success" : "fail");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
return result;
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
/**
|
|
|
|
* parse for a criterion
|
|
|
|
*
|
|
|
|
* criterion -> field op value
|
|
|
|
*
|
|
|
|
* @param tree tree we are building
|
|
|
|
* @returns 1 if successful, 0 otherwise
|
|
|
|
*/
|
|
|
|
int sp_parse_criterion(PARSETREE tree) {
|
|
|
|
int result=0;
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Entering sp_parse_criterion\n");
|
|
|
|
|
|
|
|
switch(tree->token.token_id) {
|
|
|
|
case T_STRING_FIELD:
|
|
|
|
result = sp_parse_string_criterion(tree);
|
|
|
|
break;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
case T_INT_FIELD:
|
|
|
|
result = sp_parse_int_criterion(tree);
|
|
|
|
break;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
case T_DATE_FIELD:
|
|
|
|
result = sp_parse_date_criterion(tree);
|
|
|
|
break;
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
default:
|
|
|
|
/* Error: expecting field */
|
|
|
|
result = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Exiting sp_parse_criterion: %s\n",result ?
|
|
|
|
"success" : "fail");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
return result;
|
2005-10-16 02:55:42 -04:00
|
|
|
}
|
2005-10-17 00:57:06 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* parse for a string criterion
|
|
|
|
*
|
|
|
|
* @param tree tree we are building
|
|
|
|
* @returns 1 if successful, 0 otherwise
|
|
|
|
*/
|
|
|
|
int sp_parse_string_criterion(PARSETREE tree) {
|
|
|
|
int result=0;
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Entering sp_parse_string_criterion\n");
|
|
|
|
|
|
|
|
sp_scan(tree); /* scan past the string field we know is there */
|
|
|
|
|
|
|
|
switch(tree->token.token_id) {
|
|
|
|
case T_EQUAL:
|
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Error: expecting legal string comparison operator */
|
|
|
|
break;
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
if(result) {
|
|
|
|
sp_scan(tree);
|
|
|
|
/* should be sitting on quote literal string quote */
|
|
|
|
|
|
|
|
if(tree->token.token_id == T_QUOTE) {
|
|
|
|
sp_scan(tree);
|
|
|
|
if(tree->token.token_id == T_STRING) {
|
|
|
|
sp_scan(tree);
|
|
|
|
if(tree->token.token_id == T_QUOTE) {
|
|
|
|
result=1;
|
|
|
|
sp_scan(tree);
|
|
|
|
} else {
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Expecting closign quote\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Expecting literal string\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Expecting opening quote\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Exiting sp_parse_string_criterion: %s\n",result ?
|
|
|
|
"success" : "fail");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
return result;
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
/**
|
|
|
|
* parse for an int criterion
|
|
|
|
*
|
|
|
|
* @param tree tree we are building
|
|
|
|
* @returns 1 if successful, 0 otherwise
|
|
|
|
*/
|
|
|
|
int sp_parse_int_criterion(PARSETREE tree) {
|
|
|
|
int result=0;
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Entering sp_parse_int_criterion\n");
|
|
|
|
|
|
|
|
sp_scan(tree); /* scan past the int field we know is there */
|
|
|
|
|
|
|
|
switch(tree->token.token_id) {
|
|
|
|
case T_LESSEQUAL:
|
|
|
|
case T_LESS:
|
|
|
|
case T_GREATEREQUAL:
|
|
|
|
case T_GREATER:
|
|
|
|
case T_EQUAL:
|
|
|
|
result = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Error: expecting legal string comparison operator */
|
|
|
|
DPRINTF(E_LOG,L_PARSE,"Expecting string comparison op, got %04X\n",
|
|
|
|
tree->token.token_id);
|
|
|
|
break;
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
if(result) {
|
|
|
|
sp_scan(tree);
|
|
|
|
/* should be sitting on a literal string */
|
|
|
|
if(tree->token.token_id == T_NUMBER) {
|
|
|
|
result = 1;
|
|
|
|
sp_scan(tree);
|
|
|
|
} else {
|
|
|
|
/* Error: Expecting literal string */
|
|
|
|
DPRINTF(E_LOG,L_PARSE,"Expecting string literal, got %04X\n",
|
|
|
|
tree->token.token_id);
|
|
|
|
result = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Exiting sp_parse_int_criterion: %s\n",result ?
|
|
|
|
"success" : "fail");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse for a date criterion
|
|
|
|
*
|
|
|
|
* @param tree tree we are building
|
|
|
|
* @returns 1 if successful, 0 otherwise
|
|
|
|
*/
|
|
|
|
int sp_parse_date_criterion(PARSETREE tree) {
|
|
|
|
int result=0;
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Entering sp_parse_date_criterion\n");
|
|
|
|
|
|
|
|
DPRINTF(E_SPAM,L_PARSE,"Exiting sp_parse_date_criterion: %s\n",result ?
|
|
|
|
"success" : "fail");
|
2005-10-18 18:35:10 -04:00
|
|
|
|
2005-10-17 00:57:06 -04:00
|
|
|
return result;
|
|
|
|
}
|
2005-10-18 18:35:10 -04:00
|
|
|
|
|
|
|
|
2005-08-01 23:17:22 -04:00
|
|
|
/**
|
|
|
|
* dispose of an initialized tree
|
|
|
|
*
|
|
|
|
* @param tree tree to dispose
|
|
|
|
* @returns 1
|
|
|
|
*/
|
|
|
|
int sp_dispose(PARSETREE tree) {
|
|
|
|
if(tree->term)
|
2005-10-13 03:38:22 -04:00
|
|
|
free(tree->term);
|
2005-08-01 23:17:22 -04:00
|
|
|
|
|
|
|
free(tree);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* if there was an error in a previous action (parsing?)
|
2005-10-18 18:35:10 -04:00
|
|
|
* then return that error to the client. This does not
|
2005-08-01 23:17:22 -04:00
|
|
|
* clear the error condition -- multiple calls to sp_geterror
|
|
|
|
* will return the same value.
|
|
|
|
*
|
|
|
|
* memory handling is done on the smart-parser side.
|
|
|
|
*
|
|
|
|
* @param tree tree that generated the last error
|
|
|
|
* @returns text of the last error
|
|
|
|
*/
|
|
|
|
char *sp_geterror(PARSETREE tree) {
|
|
|
|
return "blah";
|
|
|
|
}
|
|
|
|
|