Updated TODO (bug #1128 moved to 'Open bugs').
[ahxm.git] / compiler.l
blobedc855f917f541ab061356e2e4c80e7cd97f2b59
1 %{
2 /*
4     Ann Hell Ex Machina - Music Software
5     Copyright (C) 2003/2006 Angel Ortega <angel@triptico.com>
7     compiler.l - Scripting language [F]lex lexer
9     This program is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License
11     as published by the Free Software Foundation; either version 2
12     of the License, or (at your option) any later version.
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23     http://www.triptico.com
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31 #include "y.tab.h"
33 #include "ahxm.h"
35 void yyerror(char * s);
36 int yy_input_for_flex(char * buf, int max);
38 /* the notes */
39 static char * ascii_notes = "ccddeffggaab";
41 /* redefinition of input function for GNU Flex */
42 #undef YY_INPUT
43 #define YY_INPUT(b,r,m) (r = yy_input_for_flex(b,m))
45 struct code_stack
47         char * code;    /* code string */
48         int offset;     /* current offset */
51 static struct code_stack * code_stack = NULL;
52 static int code_stack_i = -1;
54 /* dynamic string manipulation macros */
55 #ifndef ds_init
56 struct ds { char * d; int p; int s; };
57 #define ds_init(x) do { x.d = (char *)0; x.p = x.s = 0; } while(0)
58 #define ds_rewind(x) x.p = 0;
59 #define ds_free(x) do { if(x.d) free(x.d); ds_init(x); } while(0)
60 #define ds_redim(x) do { if(x.p >= x.s) x.d = realloc(x.d, x.s += 32); } while(0)
61 #define ds_poke(x,c) do { ds_redim(x); x.d[x.p++] = c; } while(0)
62 #define ds_pokes(x,t) do { char *p = t; while(*p) ds_poke(x, *p++); } while(0)
63 #endif /* ds_init */
65 /* block dynstring */
66 static struct ds ds_blk;
68 /* count of parentheses */
69 static int ds_blk_i;
71 /* line number */
72 int yyline = 0;
76 DIGIT           [0-9]
77 P_INTEGER       {DIGIT}+
78 S_INTEGER       [-+]{P_INTEGER}
79 P_REAL          {DIGIT}*[\.]?{DIGIT}+
80 S_REAL          [-+]{P_REAL}
82 NOTE_P          [a-g]
83 NOTE_T3         \/3
84 NOTE_T5         \/5
86 MARKNAME        [-a-zA-Z0-9_]+
87 NEW_MARK        \^{MARKNAME}
88 GOTO_MARK       @{MARKNAME}
89 ASSERT_MARK     !{MARKNAME}
91 BLOCKNAME       [-a-zA-Z0-9_#&]+
93 WSPACE          [ \t]+
95 ALTSTR          A[-&#]*
97 %x REM XC REMXC BLK
101 {P_INTEGER}             {
102                                 /* integers without sign */
103                                 yylval.i = atoi(yytext);
104                                 return(P_INTEGER);
105                         }
106 {S_INTEGER}             {
107                                 /* signed integers */
108                                 yylval.i = atoi(yytext);
109                                 return(S_INTEGER);
110                         }
111 {P_REAL}                {
112                                 /* real numbers without sign */
113                                 yylval.d = atof(yytext);
114                                 return(P_REAL);
115                         }
116 {S_REAL}                {
117                                 /* signel real numbers */
118                                 yylval.d = atof(yytext);
119                                 return(S_REAL);
120                         }
122 {NOTE_P}                {
123                                 /* note pitch */
124                                 yylval.i = strchr(ascii_notes, *yytext) - ascii_notes;
125                                 return(NOTE_P);
126                         }
127 {NOTE_T3}               { return(NOTE_T3); }
128 {NOTE_T5}               { return(NOTE_T5); }
130 {NEW_MARK}              {
131                                 /* create new mark */
132                                 yylval.p = yytext + 1;
133                                 return(NEW_MARK);
134                         }
135 {GOTO_MARK}             {
136                                 /* go to mark */
137                                 yylval.p = yytext + 1;
138                                 return(GOTO_MARK);
139                         }
140 {ASSERT_MARK}           {
141                                 /* assert mark */
142                                 yylval.p = yytext + 1;
143                                 return(ASSERT_MARK);
144                         }
146 {ALTSTR}                {
147                                 /* alteration string */
148                                 yylval.p = yytext + 1;
149                                 return(ALTSTR);
150                         }
152 {WSPACE}                { ; /* ignore blanks */ }
154 \n                      { yyline++; }
156 \/\*                    { BEGIN REM; /* C-like comments */ }
157 <REM>\*\/               { BEGIN 0; }
158 <REM>\n                 { yyline++; }
159 <REM>.                  { ; /* drop anything inside a comment */ }
161 \{                      { BEGIN XC; /* start of extended commands */ }
162 <XC>\"[^\"]*\"          {
163                                 /* double-quoted string */
164                                 yytext[yyleng - 1] = '\0';
165                                 yylval.p = strdup(yytext + 1);
166                                 return(XC_STR);
167                         }
168 <XC>\'[^\']*\'          {
169                                 /* single-quoted string */
170                                 yytext[yyleng - 1] = '\0';
171                                 yylval.p = strdup(yytext + 1);
172                                 return(XC_STR);
173                         }
174 <XC>{P_INTEGER}         {
175                                 yylval.i = atoi(yytext);
176                                 return(P_INTEGER);
177                         }
178 <XC>{S_INTEGER}         {
179                                 yylval.i = atoi(yytext);
180                                 return(S_INTEGER);
181                         }
182 <XC>{P_REAL}            {
183                                 yylval.d = atof(yytext);
184                                 return(P_REAL);
185                         }
186 <XC>{S_REAL}            {
187                                 yylval.d = atof(yytext);
188                                 return(S_REAL);
189                         }
190 <XC>{P_REAL}s           {
191                                 /* frame count, in seconds */
192                                 yytext[yyleng - 1] = '\0';
193                                 yylval.d = atof(yytext) * 1000;
194                                 return(XC_MSECS);
195                         }
196 <XC>{P_REAL}ms          {
197                                 /* frame count, in milliseconds */
198                                 yytext[yyleng - 2] = '\0';
199                                 yylval.d = atof(yytext);
200                                 return(XC_MSECS);
201                         }
202 <XC>{NOTE_P}[\#\&]?{P_INTEGER} {
203                                 char * ptr = yytext;
205                                 /* process note */
206                                 yylval.i = strchr(ascii_notes, *ptr) - ascii_notes;
207                                 ptr++;
209                                 /* process optional sharps or flats */
210                                 if(*ptr == '#')
211                                 {
212                                         yylval.i++;
213                                         ptr++;
214                                 }
215                                 else
216                                 if(*ptr == '&')
217                                 {
218                                         yylval.i--;
219                                         ptr++;
220                                 }
222                                 /* process octave */
223                                 yylval.i += atoi(ptr) * 12;
225                                 return(XC_ABSNOTE);
226                         }
228 <XC>wav                 { return(SS_WAV); }
229 <XC>pat                 { return(SS_PAT); }
230 <XC>sustain             { return(SS_SUSTAIN); }
231 <XC>attack              { return(SS_ATTACK); }
232 <XC>vibrato             { return(SS_VIBRATO); }
233 <XC>portamento          { return(SS_PORTAMENTO); }
234 <XC>channel             { return(SS_CHANNEL); }
235 <XC>vol                 { return(SS_VOL); }
237 <XC>delay               { return(SS_EFF_DELAY); }
238 <XC>echo                { return(SS_EFF_ECHO); }
239 <XC>comb                { return(SS_EFF_COMB); }
240 <XC>allpass             { return(SS_EFF_ALLPASS); }
241 <XC>flanger             { return(SS_EFF_FLANGER); }
242 <XC>wobble              { return(SS_EFF_WOBBLE); }
243 <XC>square_wobble       { return(SS_EFF_SQWOBBLE); }
244 <XC>half_wobble         { return(SS_EFF_HFWOBBLE); }
245 <XC>fader               { return(SS_EFF_FADER); }
246 <XC>reverb              { return(SS_EFF_REVERB); }
247 <XC>foldback            { return(SS_EFF_FOLDBACK); }
248 <XC>atan                { return(SS_EFF_ATAN); }
249 <XC>distort             { return(SS_EFF_DISTORT); }
250 <XC>overdrive           { return(SS_EFF_OVERDRIVE); }
251 <XC>off                 { return(SS_EFF_OFF); }
253 <XC>pitch_stretch       { return(SS_PITCH_STRETCH); }
254 <XC>time_stretch        { return(SS_TIME_STRETCH); }
255 <XC>print_wave_tempo    { return(SS_PRINT_WAVE_TEMPO); }
257 <XC>song_info           { return(SONG_INFO); }
259 <XC>midi_channel        { return(MIDI_CHANNEL); }
260 <XC>midi_program        { return(MIDI_PROGRAM); }
261 <XC>midi_generic        { return(MIDI_GENERIC); }
263 <XC>\/\*                { BEGIN REMXC; /* C-like comments inside XC */ }
264 <REMXC>\*\/             { BEGIN XC; /* jump back to XC processing */ }
265 <REMXC>\n               { yyline++; }
266 <REMXC>.                { ; }
268 <XC>\}                  { BEGIN 0; }
269 <XC>{WSPACE}            { ; /* ignore blanks */ }
270 <XC>\n                  { yyline++; }
271 <XC>.                   { return(*yytext); }
273 \(                      {
274                                 /* start of block */
275                                 ds_init(ds_blk);
276                                 ds_blk_i = 1;
277                                 BEGIN BLK;
278                         }
279 <BLK>\(                 { ds_blk_i++; ds_poke(ds_blk, *yytext); }
280 <BLK>\)                 {
281                                 /* one parentheses less */
282                                 ds_blk_i--;
284                                 /* ok with block? */
285                                 if(ds_blk_i == 0)
286                                 {
287                                         ds_poke(ds_blk, '\0');
288                                         yylval.p = ds_blk.d;
290                                         BEGIN 0;
291                                         return(BLOCK);
292                                 }
293                                 else
294                                         ds_poke(ds_blk, ')');
295                         }
296 <BLK>\n                 { ds_poke(ds_blk, ' '); yyline++; }
297 <BLK>.                  { ds_poke(ds_blk, *yytext); }
299 =[ \t\n]*{BLOCKNAME}    {
300                                 /* block assignation */
301                                 yylval.p = yytext + 1;
303                                 /* skip (possible) spaces between
304                                    the = and the blockname; this lex
305                                    rule is written this way to avoid
306                                    having a global match for {BLOCKNAME},
307                                    that could swallow notes and such,
308                                    being mismatched as blocknames */
309                                 while(*yylval.p == ' ' ||
310                                       *yylval.p == '\n' ||
311                                       *yylval.p == '\t')
312                                         yylval.p++;
314                                 return(BLK_ASSIGN);
315                         }
316 \${BLOCKNAME}           {
317                                 /* block insertion */
318                                 yylval.p = yytext + 1;
319                                 return(BLK_INSERT);
320                         }
321 `.+`                    {
322                                 /* file inclusion */
323                                 yytext[yyleng - 1] = '\0';
324                                 yylval.p = yytext + 1;
325                                 return(FILE_INSERT);
326                         }
328 .                       { return(*yytext); }
332 int yywrap(void) { return(1); }
334 char * ds_load(char * file)
336         struct ds s;
337         int c;
338         FILE * f;
340         if((f = libpath_fopen(file, "r")) == NULL)
341                 return(NULL);
343         ds_init(s);
345         while((c = fgetc(f)) != EOF)
346                 ds_poke(s, c);
348         ds_poke(s, '\0');
349         fclose(f);
351         return(s.d);
355 int push_code(char * code)
357         struct code_stack * cs;
359         code_stack_i++;
360         GROW(code_stack, code_stack_i, struct code_stack);
362         cs = &code_stack[code_stack_i];
364         cs->code = code;
365         cs->offset = 0;
367         return(1);
371 int push_code_from_file(char * file)
373         char * c;
375         if((c = ds_load(file)) == NULL)
376                 return(0);
378         push_code(c);
379         return(1);
383 int code_getchar(void)
385         struct code_stack * cs;
386         int c = '\0';
388         while(c == '\0' && code_stack_i > -1)
389         {
390                 /* get current char */
391                 cs = &code_stack[code_stack_i];
392                 c = cs->code[cs->offset++];
394                 /* end of code? */
395                 if(c == '\0')
396                 {
397                         /* free */
398                         free(cs->code);
400                         /* move to previous */
401                         code_stack_i--;
403                         /* in any case, return a separator */
404                         c = ' ';
405                 }
406         }
408         return(c);
412 int yy_input_for_flex(char * buf, int max)
414         buf[0] = code_getchar();
416         return(buf[0] == '\0' ? 0 : 1);