Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/openal-0.0.8/src/al_config.c @ 17

Last change on this file since 17 was 17, checked in by landauf, 16 years ago

added openal

File size: 28.3 KB
Line 
1/* -*- mode: C; tab-width:8; c-basic-offset:8 -*-
2 * vi:set ts=8:
3 *
4 * al_config.c
5 *
6 * Handling of the configuration file and alrc configuration variables.
7 *
8 * FIXME: make thread safe
9 *        needs to be more robust.
10 *        leaks memory
11 *        needs gc
12 *
13 */
14#include "al_siteconfig.h"
15
16#include <AL/al.h>
17#include <ctype.h>
18#include <limits.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <sys/stat.h>
22#include <unistd.h>
23#include <string.h>
24
25#include "al_main.h"
26#include "al_ext.h"
27#include "al_config.h"
28#include "al_error.h"
29#include "al_debug.h"
30
31#ifndef __MORPHOS__
32#define _AL_FNAME "openalrc"
33#else
34#define _AL_FNAME "OpenAL.prefs"
35#endif
36
37#ifndef PATH_MAX
38#define PATH_MAX 4096
39#endif
40
41#define alrc_cadr(ls) alrc_car(alrc_cdr(ls))
42
43/*
44 * our symbol table definition.  Simple binary tree.
45 */
46typedef struct _AL_SymTab {
47        char str[ALRC_MAXSTRLEN + 1];
48        AL_rctree *datum;
49
50        struct _AL_SymTab *left;
51        struct _AL_SymTab *right;
52} AL_SymTab;
53
54/*
55 * root is our scratch space for evaluating expressions.
56 */
57static AL_rctree *root = NULL;
58
59/*
60 * symbols with global scope.
61 */
62static AL_SymTab *glsyms;
63
64/*
65 * _alOpenRcFile( void );
66 *
67 * Opens any openalrc file and returns its contents.
68 */
69static char *_alOpenRcFile( void );
70
71/*
72 * _alEval( AL_rctree *head )
73 *
74 * Evaluate an expression in AL_rctree form.
75 */
76static AL_rctree *_alEval( AL_rctree *head );
77
78/*
79 * _alSymbolTableAlloc( void )
80 *
81 * Allocate and return a new symbol table object.
82 */
83static AL_SymTab *_alSymbolTableAlloc( void );
84
85/*
86 * _alSymbolTableAdd( AL_SymTab *table,
87 *                    const char *symname,
88 *                    AL_rctree *datum )
89 *
90 * Adds binding for symbol named by str to table, returning the table.
91 */
92static AL_SymTab *_alSymbolTableAdd( AL_SymTab *table,
93                                          const char *symname,
94                                          AL_rctree *datum );
95
96/*
97 * _alSymbolTableRemove( AL_SymTab *table, const char *sym )
98 *
99 * Removes binding for symbol named by sym from table.
100 */
101static AL_SymTab *_alSymbolTableRemove( AL_SymTab *table, const char *sym );
102
103/*
104 * _alSymbolTableGet( AL_SymTab *head, const char *str )
105 *
106 * Returns the definition associated with the symbol named by str, or NULL if
107 * no such definition exists.
108 */
109static AL_rctree *_alSymbolTableGet( AL_SymTab *head, const char *str );
110
111/*
112 * _alSymbolTableDestroy( AL_SymTab *head )
113 *
114 * Destroys the symbol table head.
115 */
116static void _alSymbolTableDestroy( AL_SymTab *head );
117
118/*
119 * is_string( const char *tokenname )
120 *
121 * Returns AL_TRUE if tokenname describes a string, AL_FALSE otherwise.  A
122 * string in this context means any data contained between two quotation
123 * marks.
124 */
125static ALboolean is_string( const char *tokenname );
126
127/*
128 * is_int( const char *tokenname )
129 *
130 * Returns AL_TRUE if tokenname describes an integer (either base 10 or 16),
131 * AL_FALSE otherwise.
132 */
133static ALboolean is_int( const char *tokenname );
134
135/*
136 * is_float( const char *tokenname )
137 *
138 * Returns AL_TRUE if tokenname describes a float, AL_FALSE otherwise.
139 */
140static ALboolean is_float( const char *tokenname );
141
142/*
143 * is_lispchar( int ch )
144 *
145 * Returns AL_TRUE if ch is ( or ), AL_FALSE otherwise.
146 */
147static ALboolean is_lispchar( int ch );
148
149/*
150 * is_whitespace( int ch )
151 *
152 * Returns AL_TRUE if ch is any form of whitespace, AL_FALSE otherwise.
153 */
154static ALboolean is_whitespace( int ch );
155
156/*
157 * AL_rctree_copy( AL_rctree *src )
158 *
159 * Returns a copy of src, including car and cdr sections.
160 */
161static AL_rctree *AL_rctree_copy( AL_rctree *src );
162
163/*
164 * buildExp( const char *tokenstr, unsigned int *offset )
165 *
166 * Builds an AL_rctree representation of the alrc expression in
167 * tokenstr[*offset], setting *offset to the last scanned position, or NULL if
168 * tokenstr[*offset...] does not describe a valid alrc expression.
169 */
170static AL_rctree *buildExp( const char *tokenstr, unsigned int *offset );
171
172/*
173 * getTokenStr( const char *data, char *retbuf,
174 *              ALuint *offset, ALuint size )
175 *
176 * copies the next alrc token from data[*offset] to retbuf, not exceeding
177 * size ( size is the length of retbuf ), and returning the length of the
178 * token.  -1 is returned on error.
179 *
180 */
181static int getTokenStr( const char *data, char *retbuf,
182                        ALuint *offset, ALuint size );
183
184/*
185 * literalExp( const char *foo )
186 *
187 * Creates and returns an AL_rctree * with the value described by foo.  Let's
188 * just say that foo had better describe self-evaluating.
189 */
190static AL_rctree *literalExp( const char *foo );
191
192/*
193 * selfEvaluating( AL_rctree *head )
194 *
195 * Return AL_TRUE if the alrc token head is self-evaluating ( integer, float,
196 * string, bool, or primitive ), AL_FALSE otherwise.
197 */
198static ALboolean selfEvaluating( AL_rctree *head );
199
200/*
201 * apply( AL_rctree *proc, AL_rctree *args )
202 *
203 * Calls procedure proc with arguments args, returning return.
204 */
205static AL_rctree *apply( AL_rctree *proc, AL_rctree *args );
206
207/*
208 * length( AL_rctree *ls )
209 *
210 * Returns length of list ls, or 0 if ls is not a cons cell.
211 */
212static ALuint length( AL_rctree *ls );
213
214/**
215 * primitives
216 */
217
218/*
219 * and_prim( AL_rctree *env, AL_rctree *args )
220 *
221 * Evaluates each car in the list args, returning NULL if any evaluation is
222 * NULL or #f, something else otherwise.
223 */
224static AL_rctree *and_prim( AL_rctree *env, AL_rctree *args );
225
226/*
227 * define_prim( AL_rctree *env, AL_rctree *args )
228 *
229 * Defines the car of args to be the evaluation of the cadr of args,
230 * returning said evaluation.
231 */
232static AL_rctree *define_prim( AL_rctree *env, AL_rctree *args );
233
234/*
235 * load_ext_prim( AL_rctree *env, AL_rctree *args );
236 *
237 * Loads an extension library named by ( eval ( car args ) ).
238 */
239static AL_rctree *load_ext_prim( AL_rctree *env, AL_rctree *args );
240
241/*
242 * quote_prim( AL_rctree *env, AL_rctree *args )
243 *
244 * Evaluates to args.
245 */
246static AL_rctree *quote_prim( AL_rctree *env, AL_rctree *args );
247
248/* symbols to be defined as primitives */
249static struct _global_table {
250        const char *symname;
251        alrc_prim datum;
252} global_primitive_table[] = {
253        { "and",             and_prim      },
254        { "define",          define_prim   },
255        { "load-extension",  load_ext_prim },
256        { "quote",           quote_prim    },
257        {  NULL,  NULL }
258};
259
260/* string defining the default environment */
261static const char *default_environment =
262        "(define speaker-num 2)"
263        "(define display-banner #t)"
264        "(define source-gain 1.0)";
265
266/* FIXME: get rid of this */
267const AL_rctree scmtrue = { ALRC_BOOL, { 1 } };
268
269/*
270 * _alParseConfig( void )
271 *
272 * Parse the openalrc config file, if any.  Returns AL_TRUE if one was found
273 * and contained valid openalrc syntax, AL_FALSE otherwise.
274 *
275 * FIXME: clean this up.
276 */
277ALboolean _alParseConfig( void ) {
278        AL_rctree *temp;
279        ALboolean retval;
280        char *rcbuf;
281        int i;
282
283        if(root != NULL) {
284                /* already been here */
285                return AL_TRUE;
286        }
287
288        for(i = 0; global_primitive_table[i].symname != NULL; i++) {
289                temp = _alRcTreeAlloc();
290
291                temp->type = ALRC_PRIMITIVE;
292                temp->data.proc = global_primitive_table[i].datum;
293
294                glsyms = _alSymbolTableAdd( glsyms,
295                                   global_primitive_table[i].symname,
296                                   temp );
297        }
298
299        /* now, evaluate our default environment */
300        root = _alEvalStr( default_environment );
301        if(root == NULL) {
302                _alDebug(ALD_CONFIG, __FILE__, __LINE__, "Invalid default");
303                return AL_FALSE;
304        }
305
306        _alRcTreeFree( root );
307        root = NULL;
308
309        /* now, parse user's config */
310        rcbuf = _alOpenRcFile();
311        if(rcbuf == NULL) {
312                return AL_FALSE;
313        }
314
315        root = _alEvalStr( rcbuf );
316
317        retval = AL_TRUE;
318        if(root == NULL) {
319                retval = AL_FALSE;
320        }
321
322        _alRcTreeFree( root );
323        root = NULL;
324
325        free( rcbuf );
326
327        return retval;
328}
329
330/*
331 * _alOpenRcFile( void );
332 *
333 * Opens any openalrc file and returns its contents.
334 */
335static char *_alOpenRcFile( void ) {
336        FILE *fh = NULL;
337        struct stat buf;
338        static char pathname[PATH_MAX];
339        char *retval = NULL;
340        unsigned long filelen = 0;
341        int i;
342
343#ifndef __MORPHOS__
344        /*
345         * try home dir
346         */
347        snprintf(pathname, sizeof(pathname), "%s/.%s", getenv("HOME"), _AL_FNAME);
348#else
349        snprintf(pathname, sizeof(pathname), "ENV:%s", _AL_FNAME);
350#endif
351        if(stat(pathname, &buf) != -1) {
352                fh = fopen(pathname, "rb");
353
354                /* for later malloc, get size */
355                filelen = buf.st_size;
356        } else {
357                /* try system wide OpenAL config file */
358                snprintf(pathname, sizeof(pathname), "/etc/%s", _AL_FNAME);
359                if(stat(pathname, &buf) != -1) {
360                        fh = fopen(pathname, "rb");
361
362                        /* for later malloc, get size */
363                        filelen = buf.st_size;
364                }
365        }
366
367        if( fh == NULL ) {
368                return NULL;
369        }
370
371        retval = malloc(filelen + 1);
372        if(retval == NULL) {
373                return NULL;
374        }
375
376        fread(retval, filelen, 1, fh);
377        retval[filelen] = '\0';
378
379        fclose( fh );
380
381        i = strlen( retval );
382
383        /* trim newlines */
384        while(retval[--i] == '\n') {
385                retval[i] = '\0';
386        }
387
388        return retval;
389}
390
391/*
392 * is_float( const char *tokenname )
393 *
394 * Returns AL_TRUE if tokenname describes a float, AL_FALSE otherwise.
395 */
396static ALboolean is_float( const char *tokenname ) {
397        int i = strlen( tokenname );
398        int c;
399
400        while(i--) {
401                c = tokenname[i];
402
403                if((isdigit(c) == 0) &&
404                        (c != '-')   &&
405                        (c != '.')) {
406                        return AL_FALSE;
407                }
408        }
409
410        return AL_TRUE;
411}
412
413/*
414 * is_int( const char *tokenname )
415 *
416 * Returns AL_TRUE if tokenname describes an integer (either base 10 or 16),
417 * AL_FALSE otherwise.
418 */
419ALboolean is_int( const char *tokenname ) {
420        int i = strlen(tokenname);
421        int c;
422
423        while(i--) {
424                c = tokenname[i];
425
426                if(isdigit(c) == 0) {
427                        return AL_FALSE;
428                }
429        }
430
431        return AL_TRUE;
432}
433
434/*
435 * is_string( const char *tokenname )
436 *
437 * Returns AL_TRUE if tokenname describes a string, AL_FALSE otherwise.  A
438 * string in this context means any data contained between two quotation
439 * marks.
440 */
441ALboolean is_string( const char *tokenname ) {
442        int i = strlen( tokenname );
443        int c;
444
445        if(tokenname[0] != '"') {
446                return AL_FALSE;
447        }
448
449        while(i--) {
450                c = tokenname[i];
451
452                if((isgraph(c) == 0) &&
453                   (isspace(c) == 0)) {
454                        _alDebug(ALD_CONFIG, __FILE__, __LINE__,
455                                "tokenname %s failed at %d '%c'",
456                                tokenname, i, tokenname[i]);
457
458                        return AL_FALSE;
459                }
460        }
461
462        return AL_TRUE;
463}
464
465/*
466 * is_lispchar( int ch )
467 *
468 * Returns AL_TRUE if ch is ( or ), AL_FALSE otherwise.
469 */
470static ALboolean is_lispchar( int ch ) {
471        switch(ch) {
472                case ')':
473                case '(':
474                        return AL_TRUE;
475                default:
476                        break;
477        }
478
479        return AL_FALSE;
480}
481
482/*
483 * is_whitespace( int ch )
484 *
485 * Returns AL_TRUE if ch is any form of whitespace, AL_FALSE otherwise.
486 */
487static ALboolean is_whitespace( int ch ) {
488        switch(ch) {
489                case ' ':
490                case '\t':
491                case '\n':
492                case '\r':
493                        return AL_TRUE;
494                default:
495                        break;
496        }
497
498        return AL_FALSE;
499}
500
501/*
502 * Returns AL_TRUE if foo[offset...] describes a float.  Instead of simply
503 * being limited to the NUL character, any form of whitespace or moving into
504 * foo[len] is also a terminating condition.
505 */
506static int is_floatWS( const char *foo, ALuint offset, size_t len ) {
507        int decimalPoints = 0;
508        ALuint i = offset;
509
510        if ( offset >= len ) {
511                return -1;
512        }
513
514        if( foo[i] == '-' ) {
515                i++;
516        }
517
518        while( foo[i] && !is_whitespace( foo[i] ) && ( i < len ) ) {
519                if(!isdigit( (int) foo[i] )) {
520                        if( foo[i] == '.' ) {
521                                if( decimalPoints > 1 ) {
522                                        return -1;
523                                }
524
525                                decimalPoints++;
526                        } else {
527                                return -1;
528                        }
529                }
530
531                i++;
532        }
533
534        return i - offset;
535}
536
537/*
538 * _alGlobalBinding( const char *str )
539 *
540 * If str names an existing alrc symbol, return a pointer to the value
541 * associated with that symbol.  Otherwise, return NULL.
542 *
543 */
544AL_rctree *_alGlobalBinding( const char *str ) {
545        AL_rctree *retval;
546
547        retval =  _alSymbolTableGet( glsyms, str );
548
549        if( retval == NULL ) {
550                _alDebug( ALD_CONFIG, __FILE__, __LINE__,
551                          "could not resolve %s", str );
552        }
553
554        return retval;
555}
556
557/*
558 * _alSymbolTableGet( AL_SymTab *head, const char *str )
559 *
560 * Returns the definition associated with the symbol named by str, or NULL if
561 * no such definition exists.
562 */
563static AL_rctree *_alSymbolTableGet( AL_SymTab *head, const char *str ) {
564        int i;
565
566        if(head == NULL) {
567                return NULL;
568        }
569
570        i = strncmp(head->str, str, ALRC_MAXSTRLEN);
571
572        if(i < 0) {
573                return _alSymbolTableGet(head->left, str);
574        } else if (i == 0) {
575                return head->datum;
576        } else if (i > 0) {
577                return _alSymbolTableGet(head->right, str);
578        }
579
580        return NULL;
581}
582
583/*
584 * _alSymbolTableAdd( AL_SymTab *table, const char *sym, AL_rctree *datum )
585 *
586 * Adds binding for symbol named by sym to table, returning the table.
587 *
588 * Since AL_SymTab is a simple binary tree, this is a simple recursive
589 * function.
590 */
591static AL_SymTab *_alSymbolTableAdd( AL_SymTab *head, const char *sym,
592                                     AL_rctree *datum ) {
593        int i;
594
595        if(head == NULL) {
596                head = _alSymbolTableAlloc();
597
598                strncpy( head->str, sym, ALRC_MAXSTRLEN );
599
600                head->datum = AL_rctree_copy( datum );
601
602                return head;
603        }
604
605        i = strncmp(head->str, sym, ALRC_MAXSTRLEN);
606
607        if(i < 0) {
608                head->left = _alSymbolTableAdd( head->left, sym, datum);
609
610                return head;
611        }
612
613        if(i == 0) {
614                strncpy(head->str, sym, ALRC_MAXSTRLEN);
615
616                head->datum = AL_rctree_copy( datum );
617
618                return head;
619        }
620
621        if(i > 0) {
622                head->right = _alSymbolTableAdd( head->right, sym, datum );
623                return head;
624        }
625
626        return NULL;
627}
628
629/*
630 * _alSymbolTableAlloc( void )
631 *
632 * Allocate and return a new symbol table object.
633 */
634AL_SymTab *_alSymbolTableAlloc( void ) {
635        AL_SymTab *retval;
636
637        retval = malloc( sizeof *retval );
638        if(retval == NULL) {
639                return NULL;
640        }
641
642        memset( retval->str, 0, ALRC_MAXSTRLEN + 1 );
643
644        retval->datum = NULL;
645        retval->left  = NULL;
646        retval->right = NULL;
647
648        return retval;
649}
650
651/*
652 * define_prim( AL_rctree *env, AL_rctree *args )
653 *
654 * Defines the car of args to be the evaluation of the cadr of args,
655 * returning said evaluation.
656 */
657static AL_rctree *define_prim( UNUSED(AL_rctree *env), AL_rctree *args ) {
658        AL_rctree *symbol;
659        AL_rctree *retval;
660
661        symbol = alrc_car( args );
662        retval = _alEval( alrc_cadr( args ) );
663
664        if((symbol == NULL) || (retval == NULL)) {
665                _alDebug(ALD_CONFIG, __FILE__, __LINE__,
666                        "define_prim fail" );
667
668                return NULL;
669        }
670
671        glsyms = _alSymbolTableAdd( glsyms,
672                                symbol->data.str.c_str,
673                                retval );
674
675        _alDebug( ALD_CONFIG, __FILE__, __LINE__,
676                        "define %s", symbol->data.str.c_str );
677
678        return retval;
679}
680
681/*
682 * and_prim( AL_rctree *env, AL_rctree *args )
683 *
684 * Evaluates each car in the list args, returning NULL if any evaluation is
685 * NULL or #f, something else otherwise.
686 */
687static AL_rctree *and_prim( UNUSED(AL_rctree *env), AL_rctree *args ) {
688        AL_rctree *result = NULL;
689        AL_rctree *itr;
690        AL_rctree *temp;
691        ALboolean keepgoing = AL_TRUE;
692
693        itr = args;
694        while(itr && (keepgoing == AL_TRUE)) {
695                temp = alrc_cdr( itr );
696
697                result = _alEval( alrc_car( itr ) );
698
699                if( result == NULL ) {
700                        result         = _alRcTreeAlloc();
701                        result->type   = ALRC_BOOL;
702                        result->data.i = AL_FALSE;
703
704                        _alDebug( ALD_CONFIG, __FILE__, __LINE__,
705                                "and_prim false" );
706
707                        return result;
708                }
709
710                itr = temp;
711
712        }
713
714        result         = _alRcTreeAlloc();
715        result->type   = ALRC_BOOL;
716        result->data.i = AL_TRUE;
717
718        return result;
719}
720
721
722/*
723 * _alDestroyConfig( void )
724 *
725 * Deallocate the memory reserved in the call to _alParseConfig, as well as
726 * any alrc objects that have been created since that point.
727 */
728void _alDestroyConfig( void ) {
729        _alSymbolTableDestroy( glsyms );
730        glsyms = NULL;
731
732        _alRcTreeDestroyAll(); /* gc replacement.  sigh */
733
734        return;
735}
736
737/*
738 * _alSymbolTableDestroy( AL_SymTab *head )
739 *
740 * Destroys the symbol table head.
741 */
742static void _alSymbolTableDestroy( AL_SymTab *head ) {
743        if( head == NULL ) {
744                return;
745        }
746
747        if( head->left ) {
748                _alSymbolTableDestroy( head->left );
749        }
750        if( head->right ) {
751                _alSymbolTableDestroy( head->right );
752        }
753
754        free( head );
755
756        return;
757}
758
759
760
761/*
762 * load_ext_prim( AL_rctree *env, AL_rctree *args );
763 *
764 * Loads an extension library named by ( eval ( car args ) ).
765 *
766 * FIXME: return #t or something?
767 */
768static AL_rctree *load_ext_prim(UNUSED(AL_rctree *env), AL_rctree *args) {
769        AL_rctree *retval;
770        static char fname[128]; /* FIXME */
771        char *symname;
772        size_t len;
773
774        if(args->type != ALRC_STRING) {
775                _alDebug(ALD_CONFIG, __FILE__, __LINE__,
776                        "syntax error: load_ext_prim passed type is 0x%x",
777                        args->type);
778
779                return NULL;
780        }
781
782        /* skip first and last quote */
783        symname = args->data.str.c_str;
784        len     = args->data.str.len;
785
786        /* copy data */
787        memcpy(fname, symname, len);
788        fname[len] = '\0';
789
790        /* prepare retval */
791
792        retval         = _alRcTreeAlloc();
793        retval->type   = ALRC_BOOL;
794        retval->data.i = AL_TRUE;
795
796        if(_alLoadDL(fname) == AL_FALSE)
797        {
798                _alDebug(ALD_CONFIG, __FILE__, __LINE__,
799                         "Couldn't load %s", fname);
800
801                retval->data.i = AL_FALSE;
802        }
803
804        return retval;
805}
806
807/*
808 * quote_prim( AL_rctree *env, AL_rctree *args )
809 *
810 * Evaluates to args.
811 */
812static AL_rctree *quote_prim(UNUSED(AL_rctree *env), AL_rctree *args) {
813        return args;
814}
815
816/*
817 * _alGetGlobalScalar( const char *str, ALRcEnum type, ALvoid *retref )
818 *
819 * If str names an existing alrc symbol, and type matches the type of that
820 * symbol, *retref is populated with the value of that symbol and this call
821 * returns AL_TRUE.  Otherwise, AL_FALSE is returned.
822 *
823 * NOTE: future revisions should replace this with a call to rc_lookup.
824 *
825 * FIXME: fill out literal support
826 */
827ALboolean _alGetGlobalScalar( const char *str, ALRcEnum type, ALvoid *retref ) {
828        AL_rctree *sym;
829        ALfloat *fvp;
830        ALint   *ivp;
831
832        if(retref == NULL) {
833                return AL_FALSE;
834        }
835
836        /* [fi]vp, the dereference helper */
837        fvp = retref;
838        ivp = retref;
839
840        sym = _alGlobalBinding(str);
841        if(sym == NULL) {
842                return AL_FALSE;
843        }
844
845        switch(sym->type) {
846                case ALRC_INTEGER:
847                case ALRC_BOOL:
848                        switch(type) {
849                                case ALRC_INTEGER:
850                                case ALRC_BOOL:
851                                        *ivp = sym->data.i;
852                                        return AL_TRUE;
853                                case ALRC_FLOAT:
854                                        *fvp = sym->data.i;
855                                        return AL_TRUE;
856                                default:
857                                        return AL_FALSE;
858                        }
859                case ALRC_FLOAT:
860                        switch(type) {
861                                case ALRC_INTEGER:
862                                case ALRC_BOOL:
863                                        *ivp = sym->data.f;
864                                        return AL_TRUE;
865                                case ALRC_FLOAT:
866                                        *fvp = sym->data.f;
867                                        return AL_TRUE;
868                                default:
869                                        return AL_FALSE;
870                        }
871                default:
872                        return AL_FALSE;
873        }
874}
875
876/*
877 * _alDefine( const char *symname, AL_rctree *value )
878 *
879 * Bind a symbol, named by symname, to the evaluation of value.
880 */
881AL_rctree *_alDefine( const char *symname, AL_rctree *value ) {
882        glsyms = _alSymbolTableAdd( glsyms, symname, _alEval( value ));
883
884        _alDebug( ALD_CONFIG, __FILE__, __LINE__, "defining %s", symname );
885
886        return value;
887}
888
889/*
890 * selfEvaluating( AL_rctree *head )
891 *
892 * Return AL_TRUE if the alrc token head is self-evaluating ( integer, float,
893 * string, bool, or primitive ), AL_FALSE otherwise.
894 */
895static ALboolean selfEvaluating( AL_rctree *head ) {
896        switch( head->type ) {
897                case ALRC_INVALID:
898                case ALRC_INTEGER:
899                case ALRC_FLOAT:
900                case ALRC_STRING:
901                case ALRC_BOOL:
902                case ALRC_PRIMITIVE:
903                        return AL_TRUE;
904                        break;
905                case ALRC_SYMBOL:
906                case ALRC_CONSCELL:
907                default:
908                        break;
909        }
910
911        return AL_FALSE;
912}
913
914/*
915 * alrc_cons( AL_rctree *ls1, AL_rctree *ls2 )
916 *
917 * Create and return a cons cell, with the car section pointing to ls1 and the
918 * cdr section pointing to ls2.
919 */
920AL_rctree *alrc_cons( AL_rctree *ls1, AL_rctree *ls2 ) {
921        AL_rctree *newc;
922
923        assert( ls1->type == ALRC_CONSCELL );
924
925        if( ls1->data.ccell.cdr == NULL ) {
926                newc = ls1->data.ccell.cdr = _alRcTreeAlloc();
927                newc->type = ALRC_CONSCELL;
928
929                newc->data.ccell.car = ls2;
930
931                return newc;
932        }
933
934        alrc_cons( alrc_cdr(ls1), ls2 );
935
936        return ls1;
937}
938
939/*
940 * alrc_car( AL_rctree *ls )
941 *
942 * Return the car section of the cons cell named by ls, or NULL if ls is not a
943 * cons cell.
944 */
945AL_rctree *alrc_car( AL_rctree *ls ) {
946        assert( ls->type == ALRC_CONSCELL );
947
948        return ls->data.ccell.car;
949}
950
951/*
952 * alrc_cdr( AL_rctree *ls )
953 *
954 * Return the cdr section of the cons cell named by ls, or NULL if ls is not a
955 * cons cell.
956 */
957AL_rctree *alrc_cdr( AL_rctree *ls ) {
958        assert( ls->type == ALRC_CONSCELL );
959
960        return ls->data.ccell.cdr;
961}
962
963/*
964 * apply( AL_rctree *proc, AL_rctree *args )
965 *
966 * Calls procedure proc with arguments args, returning return.
967 */
968AL_rctree *apply( AL_rctree *procobj, AL_rctree *args ) {
969        AL_rctree *lobj;
970        AL_rctree *prototype;
971        AL_rctree *body;
972        AL_rctree *retval;
973        int i;
974
975        if( procobj->type == ALRC_PRIMITIVE ) {
976                alrc_prim proc = procobj->data.proc;
977
978                return proc(root, args);
979        }
980
981        if( procobj->type != ALRC_CONSCELL) {
982                assert(0);
983
984                return NULL;
985        }
986
987        lobj      = alrc_cdr( procobj );
988
989        prototype = alrc_car( lobj );
990        body      = alrc_cadr( lobj );
991
992        /* lambda expression */
993        assert(length(prototype) == length(args));
994
995        /* build bindings */
996        i = length( prototype );
997        while(i--) {
998                glsyms = _alSymbolTableAdd(glsyms,
999                        alrc_car(prototype)->data.str.c_str,
1000                        _alEval( alrc_car( args )));
1001
1002
1003                prototype = alrc_cdr(prototype);
1004                args = alrc_cdr(args);
1005        }
1006
1007        retval = _alEval( body );
1008
1009        /* remove bindings */
1010        prototype = alrc_car( lobj );
1011        i = length( prototype );
1012        while(i--) {
1013                glsyms = _alSymbolTableRemove( glsyms,
1014                                   alrc_car( prototype )->data.str.c_str );
1015
1016                prototype = alrc_cdr( prototype );
1017        }
1018
1019        return retval;
1020}
1021
1022/*
1023 * length( AL_rctree *ls )
1024 *
1025 * Returns length of list ls, or 0 if ls is not a cons cell.
1026 */
1027static ALuint
1028length( AL_rctree *ls )
1029{
1030        return ( ls == NULL || ls->type != ALRC_CONSCELL ) ?
1031                0 :
1032                ( 1 + length( alrc_cdr( ls ) ));
1033}
1034
1035/*
1036 * _alSymbolTableRemove( AL_SymTab *table, const char *sym )
1037 *
1038 * Removes binding for symbol named by sym from table.
1039 */
1040AL_SymTab *_alSymbolTableRemove( AL_SymTab *head, const char *sym ) {
1041        int i;
1042
1043        if(head == NULL) {
1044                return NULL;
1045        }
1046
1047        i = strncmp( head->str, sym, ALRC_MAXSTRLEN );
1048        if(i < 0) {
1049                head->left = _alSymbolTableRemove( head->left, sym );
1050                return head;
1051        }
1052        if(i == 0) {
1053                free( head );
1054
1055                return NULL;
1056        }
1057        if(i > 0) {
1058                head->right = _alSymbolTableRemove( head->right, sym );
1059                return head;
1060        }
1061
1062        return head;
1063}
1064
1065/*
1066 * AL_rctree_copy( AL_rctree *src )
1067 *
1068 * Returns a copy of src, including car and cdr sections.
1069 */
1070static AL_rctree *AL_rctree_copy( AL_rctree *src ) {
1071        AL_rctree *retval;
1072
1073        if( src == NULL ) {
1074                return NULL;
1075        }
1076
1077        retval = _alRcTreeAlloc();
1078
1079        if( src->type == ALRC_CONSCELL ) {
1080                retval->type = ALRC_CONSCELL;
1081
1082                retval->data.ccell.car = AL_rctree_copy( src->data.ccell.car );
1083                retval->data.ccell.cdr = AL_rctree_copy( src->data.ccell.cdr );
1084
1085                return retval;
1086        }
1087
1088        *retval = *src;
1089
1090        return retval;
1091}
1092
1093/*
1094 * buildExp( const char *tokenstr, unsigned int *offset )
1095 *
1096 * Builds an AL_rctree representation of the alrc expression in
1097 * tokenstr[*offset], setting *offset to the last scanned position, or NULL if
1098 * tokenstr[*offset...] does not describe a valid alrc expression.
1099 */
1100static AL_rctree *buildExp( const char *tokenstr, unsigned int *offset ) {
1101        AL_rctree *retval = NULL;
1102        unsigned int len = strlen(tokenstr);
1103        char *buffer;
1104
1105        while(is_whitespace(tokenstr[*offset]) && (*offset < len)) {
1106                (*offset)++;
1107        }
1108
1109        /* skip comments */
1110        while(tokenstr[*offset] == ';') {
1111                /* FIXME: do dos crlf as well */
1112                while((tokenstr[*offset] != '\n') && (*offset < len))
1113                {
1114                        (*offset)++;
1115                }
1116
1117                while(is_whitespace(tokenstr[*offset]) && (*offset < len)) {
1118                        (*offset)++;
1119                }
1120        }
1121
1122        if((len == 0) || (*offset >= len)) {
1123                _alDebug( ALD_CONFIG, __FILE__, __LINE__, "NULL here");
1124
1125                return NULL;
1126        }
1127
1128        if(tokenstr[*offset] == '\'') {
1129                /* quoted */
1130                (*offset)++;
1131
1132                retval = _alRcTreeAlloc();
1133                retval->type = ALRC_CONSCELL;
1134
1135                retval->data.ccell.car = _alRcTreeAlloc();
1136                retval->data.ccell.car->type = ALRC_SYMBOL;
1137                snprintf(retval->data.ccell.car->data.str.c_str, ALRC_MAXSTRLEN,
1138                        "quote");
1139                retval->data.ccell.car->data.str.len = 5;
1140
1141                retval->data.ccell.cdr = buildExp( tokenstr, offset );
1142
1143                return retval;
1144        }
1145
1146        if(tokenstr[*offset] == '(') {
1147                /* it's a list */
1148                AL_rctree *foo = NULL;
1149                AL_rctree *lp  = NULL;
1150                AL_rctree *rp  = NULL;
1151
1152                (*offset)++;
1153
1154                retval = rp = _alRcTreeAlloc();
1155                retval->type = ALRC_CONSCELL;
1156
1157                /* cdr */
1158                while((foo = buildExp(tokenstr, offset)) != NULL) {
1159                        /*car(rp, foo); */
1160                        rp->data.ccell.car = foo;
1161
1162                        /* cdr(rp, l_conscell_alloc(NULL, NULL));*/
1163                        rp->data.ccell.cdr = _alRcTreeAlloc();
1164                        rp->data.ccell.cdr->type = ALRC_CONSCELL;
1165
1166                        lp = rp;
1167                        rp = rp->data.ccell.cdr;
1168                }
1169
1170                if( lp != NULL ) {
1171                        _alRcTreeFree( lp->data.ccell.cdr );
1172                        lp->data.ccell.cdr = NULL;
1173                }
1174
1175                return (AL_rctree *) retval;
1176        }
1177
1178        if(tokenstr[*offset] == ')') {
1179                (*offset)++;
1180
1181                return NULL;
1182        }
1183
1184        buffer = malloc( len + 1 );
1185
1186        getTokenStr(tokenstr, buffer, offset, len);
1187
1188        retval = literalExp( buffer );
1189
1190        free( buffer );
1191
1192        return retval;
1193}
1194
1195/*
1196 * literalExp( const char *foo )
1197 *
1198 * Creates and returns an AL_rctree * with the value described by foo.  Let's
1199 * just say that foo had better describe self-evaluating.
1200 */
1201static AL_rctree *literalExp( const char *foo ) {
1202        AL_rctree *retval = _alRcTreeAlloc();
1203
1204        assert(foo[0] != '(');
1205        assert(foo[0] != '\'');
1206        assert(foo[0] != '(');
1207
1208        if ((foo[0] == '#') && (foo[1] == 'p'))
1209        {
1210                long foop = strtol( &foo[2], NULL, 0 );
1211
1212                retval->type = ALRC_POINTER;
1213                retval->data.p = (void *) foop;
1214        }
1215        else if ((foo[0] == '#') && ((foo[1] == 't') || (foo[1] == 'f')))
1216        {
1217                switch(foo[1]) {
1218                        case 'f':
1219                                retval->data.b = AL_FALSE;
1220                                break;
1221                        case 't':
1222                                retval->data.b = AL_TRUE;
1223                                break;
1224                        default:
1225                                assert( 0 );
1226                                _alRcTreeFree( retval );
1227
1228                                return NULL;
1229                }
1230
1231                retval->type = ALRC_BOOL;
1232        }
1233        else if (is_int(foo))
1234        {
1235                retval->type = ALRC_INTEGER;
1236                retval->data.i = atoi( foo );
1237        }
1238        else if (is_float( foo ))
1239        {
1240                retval->type = ALRC_FLOAT;
1241                retval->data.f = atof( foo );
1242        }
1243        else if (is_string(foo))
1244        {
1245                retval->type = ALRC_STRING;
1246                snprintf( retval->data.str.c_str, ALRC_MAXSTRLEN, &foo[1] );
1247
1248                retval->data.str.len = strlen( foo ) - 2;
1249        }
1250        else
1251        {
1252                retval->type = ALRC_SYMBOL;
1253                snprintf( retval->data.str.c_str, ALRC_MAXSTRLEN, foo );
1254
1255                retval->data.str.len = strlen( foo );
1256        }
1257
1258        return retval;
1259}
1260
1261/*
1262 * _alEval( AL_rctree *head )
1263 *
1264 * Evaluate an expression in AL_rctree form.
1265 */
1266static AL_rctree *_alEval( AL_rctree *head ) {
1267        AL_rctree *retval;
1268
1269        if(head == NULL) {
1270                return NULL;
1271        }
1272
1273        if( selfEvaluating( head ) == AL_TRUE ) {
1274                return head;
1275        }
1276
1277        if( head->type == ALRC_CONSCELL ) {
1278                AL_rctree *procsym = alrc_car( head );
1279                AL_rctree *args;
1280                AL_rctree *proc;
1281
1282                if( procsym == NULL ) {
1283                        _alDebug( ALD_CONFIG, __FILE__, __LINE__,
1284                                "trouble" );
1285
1286                        return NULL;
1287                }
1288
1289                proc =  _alGlobalBinding( procsym->data.str.c_str );
1290                args =  alrc_cdr(head);
1291
1292                if( proc == NULL ) {
1293                        _alDebug( ALD_CONFIG, __FILE__, __LINE__,
1294                                "could not apply %s",
1295                                alrc_car(head)->data.str.c_str );
1296
1297                        return NULL;
1298                }
1299
1300                return apply( proc, args );
1301        } else {
1302                /* symbols are resolved */
1303                retval = _alGlobalBinding( head->data.str.c_str );
1304                if(retval == NULL) {
1305                        _alDebug( ALD_CONFIG, __FILE__, __LINE__,
1306                                "invalid symbol %s", head->data.str.c_str );
1307                }
1308
1309                return retval;
1310        }
1311}
1312
1313/*
1314 * getTokenStr( const char *data, char *retbuf,
1315 *              ALuint *offset, ALuint size )
1316 *
1317 * copies the next alrc token from data[*offset] to retbuf, not exceeding
1318 * size ( size is the length of retbuf ), and returning the length of the
1319 * token.  -1 is returned on error.
1320 *
1321 */
1322static int getTokenStr( const char *data, char *outp,
1323                 ALuint *offsetp, ALuint size ) {
1324        ALuint offset = *offsetp;
1325        int start  = 0;
1326        int end    = 0;
1327        int tokenlen = 0;
1328        size_t retlen = 0;
1329
1330        while(is_whitespace(data[offset]) && (offset < size)) {
1331                offset++;
1332        }
1333
1334        if((data[offset] == '\'') ||
1335           (data[offset] == '(')  ||
1336           (data[offset] == ')'))
1337        {
1338                start = offset++;
1339                end = offset;
1340
1341        } else if((data[offset] == '#') && (data[offset+1] == 'p')) {
1342                /* pointer value */
1343                start = offset;
1344
1345                offset += 2;
1346
1347                if((data[offset] == '0') && (data[offset+1] == 'x')) {
1348                        /* in hex */
1349                        offset += 2;
1350                }
1351
1352                while( isxdigit( (int) data[offset]) && (offset < size))
1353                {
1354                        offset++;
1355                }
1356        } else if((data[offset] == '#') && ((data[offset+1] == 'f') || (data[offset+1] == 't'))) {
1357                /* boolean */
1358                start = offset;
1359
1360                offset += 2;
1361        } else if((data[offset] == '0') && (data[offset+1] == 'x')) {
1362                /* hex numbers */
1363                start = offset;
1364
1365                while(isdigit( (int) data[offset]) && (offset < size)) {
1366                        offset++;
1367                }
1368        } else if((tokenlen = is_floatWS( data, offset, size )) > 0)  {
1369                /* float */
1370                start = offset;
1371                offset += tokenlen;
1372
1373        } else if(data[offset] == '"') {
1374                /* it's a string */
1375                start = offset;
1376
1377                offset++;
1378
1379                while((data[offset] != '"') && (offset < size)) {
1380                        offset++;
1381                }
1382
1383                offset++; /* get last one too */
1384        } else {
1385                start = offset;
1386
1387                while(!is_whitespace(data[offset]) &&
1388                      !is_lispchar(data[offset])   &&
1389                      (offset < size)) {
1390                        offset++;
1391                }
1392
1393        }
1394
1395        if(offset > size) {
1396                *offsetp = size;
1397
1398                /* invalid expression */
1399                return 0;
1400        }
1401
1402        end = offset;
1403
1404        retlen = end - start;
1405
1406        memcpy(outp, &data[start], retlen);
1407
1408        outp[retlen] = '\0';
1409
1410        *offsetp = offset;
1411
1412        return strlen( outp );
1413}
1414
1415/*
1416 * _alEvalStr( const char *expression )
1417 *
1418 * Evaluate an alrc expression (expression), returning result.
1419 */
1420AL_rctree *_alEvalStr( const char *expression ) {
1421        ALuint offset = 0;
1422        ALuint len = strlen( expression );
1423        AL_rctree *retval = NULL;
1424
1425        while( offset < len ) {
1426                retval = _alEval( buildExp( expression, &offset ) );
1427        }
1428
1429        return retval;
1430}
Note: See TracBrowser for help on using the repository browser.