Start of smart playlists

This commit is contained in:
Ron Pedde 2003-12-04 23:20:51 +00:00
parent a98bdfbf66
commit 377356bd06
6 changed files with 332 additions and 1 deletions

View File

@ -8,6 +8,8 @@ AM_INIT_AUTOMAKE(mt-daapd,0.1.0)
dnl Checks for programs.
AC_PROG_CC
AC_PROG_YACC
AC_PROG_LEX
AC_CANONICAL_HOST

View File

@ -1,5 +1,9 @@
# $Id$
#
BUILT_SOURCES=parser.h
AM_YFLAGS=-d
sbin_PROGRAMS = mt-daapd
if COND_REND_POSIX
@ -17,7 +21,8 @@ endif
mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
webserver.h configfile.c configfile.h err.c err.h restart.c restart.h \
daap-proto.c daap-proto.h daap.c daap.h db-memory.c db-memory.h \
mp3-scanner.h mp3-scanner.c $(RENDSRC)
mp3-scanner.h mp3-scanner.c playlist.c playlist.h \
lexer.l parser.y $(RENDSRC)
EXTRA_DIST = mdns/mDNS.c mdns/mDNSClientAPI.h mdns/mDNSDebug.h mdns/mDNSPosix.c \
mdns/mDNSUNP.c rend-howl.c rend-posix.c rend-osx.c

84
src/lexer.l Normal file
View File

@ -0,0 +1,84 @@
%{
/* $Id$
*
* Simple playlist lexer
*/
#include <string.h>
#include "playlist.h"
#include "parser.h"
extern int yydebug;
%}
%option yylineno
%%
[\n\t ]+
artist { yylval.ival=TOK_ARTIST; return(TOK_ARTIST); }
album { yylval.ival=TOK_ALBUM; return(TOK_ALBUM); }
genre { yylval.ival=TOK_GENRE; return(TOK_GENRE); }
is |
= { yylval.ival=TOK_IS; return(TOK_IS); }
includes |
=~ |
~= { yylval.ival=TOK_INCLUDES; return(TOK_INCLUDES); }
or |
\|\| { yylval.ival=TOK_OR; return(TOK_OR); }
and |
&& { yylval.ival=TOK_AND; return(TOK_AND); }
not |
! { yylval.ival=1; return(TOK_NOT); }
\"[A-Za-z0-9 ]*\" { yylval.cval=strdup(yytext); return(TOK_ID); }
. { return yytext[0]; }
%%
int yywrap(void) {
return 1;
}
void yyerror(char *msg) {
printf("\nParser Error: Line %d: %s\n",yylineno, msg);
printf("Wish I could be more helpful. :(\n\n");
printf("If you know how to generate good yacc errors, please\n");
printf("Let me know!\n\n");
exit(0);
}
/*
int main(int argc, char *argv[]) {
FILE *fin;
int result;
yydebug=1;
fin=fopen(argv[1],"r");
if(!fin) {
perror("fopen");
return 1;
}
yyin=fin;
result=yyparse();
printf("Parsed... result = %d\n", result);
pl_dump();
return 0;
}
*/

116
src/parser.y Normal file
View File

@ -0,0 +1,116 @@
%{
#include <stdio.h>
#include "playlist.h"
#define YYERROR_VERBOSE 1
/* Forwards */
extern PL_NODE *pl_newpredicate(int tag, int op, char *value);
extern PL_NODE *pl_newexpr(PL_NODE *arg1, int op, PL_NODE *arg2);
extern int pl_addplaylist(char *name, PL_NODE *root);
%}
%left TOK_OR TOK_AND
%union {
unsigned int ival;
char *cval;
PL_NODE *plval;
}
%token <ival> TOK_ARTIST
%token <ival> TOK_ALBUM
%token <ival> TOK_GENRE
%token <ival> TOK_IS
%token <ival> TOK_INCLUDES
%token <ival> TOK_OR
%token <ival> TOK_AND
%token <ival> TOK_NOT
%token <cval> TOK_ID
%type <plval> expression
%type <plval> predicate
%type <ival> idtag
%type <ival> boolarg
%type <cval> value
%type <ival> playlist
%%
playlistlist: playlist
| playlistlist playlist
;
playlist: TOK_ID '{' expression '}' { $$ = pl_addplaylist($1, $3); }
;
expression: expression TOK_AND expression { $$=pl_newexpr($1,$2,$3); }
| expression TOK_OR expression { $$=pl_newexpr($1,$2,$3); }
| '(' expression ')' { $$=$2; }
| predicate
;
predicate: idtag boolarg value { $$=pl_newpredicate($1, $2, $3); }
idtag: TOK_ARTIST
| TOK_ALBUM
| TOK_GENRE
;
boolarg: TOK_IS
| TOK_INCLUDES
| TOK_NOT boolarg { $$=$2 | 0x80000000; }
;
value: TOK_ID
;
%%
PL_NODE *pl_newpredicate(int tag, int op, char *value) {
PL_NODE *pnew;
pnew=(PL_NODE*)malloc(sizeof(PL_NODE));
if(!pnew)
return NULL;
pnew->op=op;
pnew->arg1.ival=tag;
pnew->arg2.cval=value;
return pnew;
}
PL_NODE *pl_newexpr(PL_NODE *arg1, int op, PL_NODE *arg2) {
PL_NODE *pnew;
pnew=(PL_NODE*)malloc(sizeof(PL_NODE));
if(!pnew)
return NULL;
pnew->op=op;
pnew->arg1.plval=arg1;
pnew->arg2.plval=arg2;
return pnew;
}
int pl_addplaylist(char *name, PL_NODE *root) {
SMART_PLAYLIST *pnew;
pnew=(SMART_PLAYLIST *)malloc(sizeof(SMART_PLAYLIST));
if(!pnew)
return -1;
pnew->next=pl_smart.next;
pnew->name=name;
pnew->root=root;
pl_smart.next=pnew;
return 0;
}

92
src/playlist.c Normal file
View File

@ -0,0 +1,92 @@
/*
* $Id$
*
*/
#include <stdio.h>
#include "playlist.h"
#include "parser.h"
/* Globals */
SMART_PLAYLIST pl_smart = { NULL, NULL, NULL };
/* Forwards */
void pl_dump(void);
void pl_dump_node(PL_NODE *pnode, int indent);
/*
* pl_dump
*
* Dump the playlist list for debugging
*/
void pl_dump(void) {
SMART_PLAYLIST *pcurrent=pl_smart.next;
while(pcurrent) {
printf("Playlist %s:\n",pcurrent->name);
pl_dump_node(pcurrent->root,1);
pcurrent=pcurrent->next;
}
}
/*
* pl_dump_node
*
* recursively dump a node
*/
void pl_dump_node(PL_NODE *pnode, int indent) {
int index;
int not=0;
unsigned int boolarg;
for(index=0;index<indent;index++) {
printf(" ");
}
if(pnode->op == TOK_AND) {
printf("AND\n");
} else if (pnode->op == TOK_OR) {
printf("OR\n");
}
if((pnode->op == TOK_AND) || (pnode->op == TOK_OR)) {
pl_dump_node(pnode->arg1.plval,indent+1);
pl_dump_node(pnode->arg2.plval,indent+1);
return;
}
switch(pnode->arg1.ival) {
case TOK_ARTIST:
printf("ARTIST ");
break;
case TOK_ALBUM:
printf("ALBUM ");
break;
case TOK_GENRE:
printf("GENRE ");
break;
default:
printf ("<unknown tag> ");
break;
}
boolarg=(pnode->op) & 0x7FFFFFFF;
if(pnode->op & 0x80000000)
not=1;
switch(boolarg) {
case TOK_IS:
printf("%s",not? "IS " : "IS NOT ");
break;
case TOK_INCLUDES:
printf("%s",not? "INCLUDES " : "DOES NOT INCLUDE ");
break;
default:
printf("<unknown boolop> ");
break;
}
printf("%s\n",pnode->arg2.cval);
return;
}

32
src/playlist.h Normal file
View File

@ -0,0 +1,32 @@
/*
* $Id$
*
*/
#ifndef _PL_H_
#define _PL_H_
typedef struct tag_pl_node {
int op;
union {
int ival;
struct tag_pl_node *plval;
} arg1;
union {
char *cval;
struct tag_pl_node *plval;
} arg2;
} PL_NODE;
typedef struct tag_smart_playlist {
char *name;
PL_NODE *root;
struct tag_smart_playlist *next;
} SMART_PLAYLIST;
extern SMART_PLAYLIST pl_smart;
extern void pl_dump(void);
#endif /* _PL_H_ */