/[svn]/linuxsampler/trunk/src/scriptvm/scanner.l
ViewVC logotype

Annotation of /linuxsampler/trunk/src/scriptvm/scanner.l

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3573 - (hide annotations) (download)
Tue Aug 27 21:36:53 2019 UTC (4 years, 8 months ago) by schoenebeck
File size: 12166 byte(s)
NKSP: Introducing floating point support.

* NKSP language: Added support for NKSP real number literals and
  arithmetic operations on them (e.g. "(3.9 + 2.9) / 12.3 - 42.0").

* NKSP language: Added support for NKSP real number (floating point)
  script variables (declare ~foo := 3.4).

* NKSP language: Added support for NKSP real number (floating point)
  array script variables (declare ?foo[3] := ( 1.1, 2.7, 49.0 )).

* NKSP built-in script function "message()" accepts now real number
  argument as well.

* Added built-in NKSP script function "real_to_int()" and its short
  hand form "int()" for casting from real number to integer in NKSP
  scripts.

* Added built-in NKSP script function "int_to_real()" and its short
  hand form "real()" for casting from integer to real number in NKSP
  scripts.

* Bumped version (2.1.1.svn6).

1 schoenebeck 2581 /*
2 schoenebeck 3259 * Copyright (c) 2014-2017 Christian Schoenebeck and Andreas Persson
3 schoenebeck 2581 *
4     * http://www.linuxsampler.org
5     *
6     * This file is part of LinuxSampler and released under the same terms.
7     * See README file for details.
8     */
9    
10 schoenebeck 2888 /* Token scanner for NKSP real-time instrument script language. */
11 schoenebeck 2581
12     %{
13    
14     #include "parser_shared.h"
15     #include <math.h>
16     // reentrant scanner data context
17     #define YY_EXTRA_TYPE ParserContext*
18     // set line number each time a token is recognized
19     #define YY_USER_ACTION \
20     { \
21     yylloc->first_line = yylineno; \
22     yylloc->last_line = yylineno; \
23 schoenebeck 2888 yylloc->first_column = yycolumn; \
24     yycolumn += yyleng; \
25     yylloc->last_column = yycolumn - 1; \
26 schoenebeck 2581 /*printf("lex: line '%s'\n", yytext);*/ \
27     }
28     // custom (f)lex input for reading from std::istream object
29     #define YY_INPUT(buf,result,max_size) \
30     { \
31     char c = yyextra->is->get(); \
32     if (yyextra->is->eof()) \
33     result = YY_NULL; \
34     else { \
35     buf[0] = c; \
36     result = 1; \
37     } \
38     }
39    
40     static void scanner_error(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* err) {
41 schoenebeck 2889 context->addErr(locp->first_line, locp->last_line, locp->first_column, locp->last_column, err);
42 schoenebeck 2581 }
43    
44     static void scanner_warning(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* txt) {
45 schoenebeck 2889 context->addWrn(locp->first_line, locp->last_line, locp->first_column, locp->last_column, txt);
46 schoenebeck 2581 }
47    
48     #define SCANNER_ERR(txt) scanner_error(yylloc, yyextra, txt)
49     #define SCANNER_WRN(txt) scanner_warning(yylloc, yyextra, txt)
50    
51 schoenebeck 3054 // shut up warning that 'register' keyword is deprecated as of C++11
52     #if defined(__cplusplus) && __cplusplus >= 201103L
53     # define register
54     #endif
55    
56 schoenebeck 2581 using namespace LinuxSampler;
57    
58 schoenebeck 2888 static int countNewLineChars(const char* txt) {
59     int n = 0;
60     for (int i = 0; txt[i]; ++i)
61     if (txt[i] == '\n') ++n;
62     return n;
63     }
64    
65     static int countCharsPastLastNewLine(const char* txt) {
66 schoenebeck 3054 const int n = (int)strlen(txt);
67 schoenebeck 2888 for (int i = n - 1; i >= 0; --i)
68     if (txt[i] == '\n')
69     return n - i - 1;
70     return n;
71     }
72    
73     #define processLocation() { \
74     const int nl = countNewLineChars(yytext); \
75     yylineno += nl; \
76     if (nl) yycolumn = countCharsPastLastNewLine(yytext); \
77     }
78    
79 schoenebeck 3308 // if compiled for debugging, throw an exception instead of exiting on fatal
80     // lexer errors (so the debugger may pause with the appropriate back trace)
81     #if DEBUG
82     # include <stdexcept>
83     # define YY_FATAL_ERROR(msg) throw std::runtime_error(msg)
84     #endif
85    
86 schoenebeck 2581 %}
87    
88 schoenebeck 2888 /* use Flex's built-in support for line numbers
89     (disabled, because it seems to be unreliable, so we are using our own
90     tracking code in the respective scanner rules below) */
91     /*%option yylineno*/
92 schoenebeck 2581 /* generate a reentrant safe scanner */
93     %option reentrant
94     /* avoid symbol collision with other (i.e. future) scanner symbols */
95     %option prefix="InstrScript_"
96     /* bison-bridge adds an argument yylval to yylex, and bison-locations adds an
97     argument code yylloc for location tracking. */
98     %option bison-bridge
99     %option bison-locations
100     /* yywrap() would be called at EOF, we don't need it */
101     %option noyywrap
102     /* enable functions yy_push_state(), yy_pop_state(), yy_top_state() */
103     %option stack
104    
105     /* inclusive scanner conditions */
106     %s PREPROC_BODY_USE
107     /* exclusive scanner conditions */
108     %x PREPROC_SET_COND PREPROC_RESET_COND PREPROC_IF PREPROC_IF_NOT PREPROC_BODY_EAT PREPROC_PRE_BODY_USE PREPROC_PRE_BODY_EAT
109    
110     DIGIT [0-9]
111 schoenebeck 3561 ID [a-zA-Z][a-zA-Z0-9_]*
112     METRIC (k|h|(da)|d|c|m|u)
113     UNIT (s|(Hz)|B)
114 schoenebeck 2581
115     %%
116    
117     \"[^"]*\" {
118     yylval->sValue = strdup(yytext + 1);
119     yylval->sValue[strlen(yylval->sValue) - 1] = '\0';
120     return STRING;
121     }
122    
123     {DIGIT}+ {
124 schoenebeck 3557 if (sizeof(vmint) < 8)
125     yylval->iValue = atoi(yytext);
126     else
127     yylval->iValue = atoll(yytext);
128 schoenebeck 2581 return INTEGER;
129     }
130    
131 schoenebeck 3573 {DIGIT}+"."{DIGIT}+ {
132     yylval->fValue = atof(yytext);
133     return REAL;
134     }
135    
136 schoenebeck 3561 {DIGIT}+({METRIC}{1,2}|({METRIC}{0,2}{UNIT}?)) {
137     int pos = 0;
138    
139     // parse number portion
140     vmint value = 0;
141     for (; yytext[pos] >= '0' && yytext[pos] <= '9'; ++pos) {
142     value *= 10;
143     value += yytext[pos] - '0';
144     }
145     yylval->iUnitValue.iValue = value;
146    
147     // parse metric prefix portion
148     for (int i = 0; i < 2; ++i, ++pos) {
149     switch (yytext[pos]) {
150     case 'k': yylval->iUnitValue.prefix[i] = VM_KILO; continue;
151     case 'h': yylval->iUnitValue.prefix[i] = VM_HECTO; continue;
152     case 'c': yylval->iUnitValue.prefix[i] = VM_CENTI; continue;
153     case 'm': yylval->iUnitValue.prefix[i] = VM_MILLI; continue;
154     case 'u': yylval->iUnitValue.prefix[i] = VM_MICRO; continue;
155     case 'd':
156     if (yytext[pos+1] == 'a') {
157     yylval->iUnitValue.prefix[i] = VM_DECA;
158     ++pos;
159     } else {
160     yylval->iUnitValue.prefix[i] = VM_DECI;
161     }
162     continue;
163     default:
164     yylval->iUnitValue.prefix[i] = VM_NO_PREFIX;
165 schoenebeck 3573 goto parseIntStdUnit;
166 schoenebeck 3561 }
167     }
168    
169 schoenebeck 3573 parseIntStdUnit:
170 schoenebeck 3561
171     // parse standard measurement unit
172     switch (yytext[pos]) {
173     case 's': yylval->iUnitValue.unit = VM_SECOND; break;
174     case 'H': yylval->iUnitValue.unit = VM_HERTZ; break;
175     case 'B': yylval->iUnitValue.unit = VM_BEL; break;
176     default: yylval->iUnitValue.unit = VM_NO_UNIT; break;
177     }
178    
179     return INTEGER_UNIT;
180     }
181    
182 schoenebeck 3573 {DIGIT}+"."{DIGIT}+({METRIC}{1,2}|({METRIC}{0,2}{UNIT}?)) {
183     int pos = 0;
184 schoenebeck 2581
185 schoenebeck 3573 // parse number portion
186     for (; (yytext[pos] >= '0' && yytext[pos] <= '9') || yytext[pos] == '.'; ++pos) {
187     }
188     {
189     const char tmp = yytext[pos];
190     yytext[pos] = 0; // mark temporary end of string
191     yylval->fUnitValue.fValue = atof(yytext);
192     yytext[pos] = tmp; // restore
193     }
194 schoenebeck 2581
195 schoenebeck 3573 // parse metric prefix portion
196     for (int i = 0; i < 2; ++i, ++pos) {
197     switch (yytext[pos]) {
198     case 'k': yylval->fUnitValue.prefix[i] = VM_KILO; continue;
199     case 'h': yylval->fUnitValue.prefix[i] = VM_HECTO; continue;
200     case 'c': yylval->fUnitValue.prefix[i] = VM_CENTI; continue;
201     case 'm': yylval->fUnitValue.prefix[i] = VM_MILLI; continue;
202     case 'u': yylval->fUnitValue.prefix[i] = VM_MICRO; continue;
203     case 'd':
204     if (yytext[pos+1] == 'a') {
205     yylval->fUnitValue.prefix[i] = VM_DECA;
206     ++pos;
207     } else {
208     yylval->fUnitValue.prefix[i] = VM_DECI;
209     }
210     continue;
211     default:
212     yylval->fUnitValue.prefix[i] = VM_NO_PREFIX;
213     goto parseRealStdUnit;
214     }
215     }
216    
217     parseRealStdUnit:
218    
219     // parse standard measurement unit
220     switch (yytext[pos]) {
221     case 's': yylval->fUnitValue.unit = VM_SECOND; break;
222     case 'H': yylval->fUnitValue.unit = VM_HERTZ; break;
223     case 'B': yylval->fUnitValue.unit = VM_BEL; break;
224     default: yylval->fUnitValue.unit = VM_NO_UNIT; break;
225     }
226    
227     return REAL_UNIT;
228     }
229    
230    
231 schoenebeck 2581 /* Preprocessor statement: SET_CONDITION(name) */
232    
233     <*>"SET_CONDITION"[ \t]*"(" {
234 schoenebeck 2588 //printf("SET_CONDITION\n");
235 schoenebeck 2581 yy_push_state(PREPROC_SET_COND, yyscanner);
236     }
237     <PREPROC_SET_COND>{ID} {
238 schoenebeck 2588 //printf("preproc set condition '%s'\n", yytext);
239 schoenebeck 2581 bool success = yyextra->setPreprocessorCondition(yytext);
240     if (!success) {
241     SCANNER_WRN((String("Preprocessor: Condition '") +
242     yytext + "' is already set.").c_str());
243     }
244     }
245     <PREPROC_SET_COND>[ \t]*")" {
246 schoenebeck 2588 //printf("End of PREPROC_SET_COND\n");
247 schoenebeck 2581 yy_pop_state(yyscanner);
248     }
249    
250    
251     /* Preprocessor statement: RESET_CONDITION(name) */
252    
253     <*>"RESET_CONDITION"[ \t]*"(" {
254 schoenebeck 2588 //printf("RESET_CONDITION\n");
255 schoenebeck 2581 yy_push_state(PREPROC_RESET_COND, yyscanner);
256     }
257     <PREPROC_RESET_COND>{ID} {
258 schoenebeck 2588 //printf("preproc reset condition '%s'\n", yytext);
259 schoenebeck 2581 bool success = yyextra->resetPreprocessorCondition(yytext);
260     if (!success) {
261     SCANNER_ERR((String("Preprocessor: could not reset condition '") +
262     yytext + "' (either not set or a built-in condition).").c_str());
263     }
264     }
265     <PREPROC_RESET_COND>[ \t]*")" {
266 schoenebeck 2588 //printf("End of RESET_CONDITION\n");
267 schoenebeck 2581 yy_pop_state(yyscanner);
268     }
269    
270    
271     /* Preprocessor conditional statements:
272    
273     USE_CODE_IF(name)
274     ...
275     END_USE_CODE
276    
277     and:
278    
279     USE_CODE_IF_NOT(name)
280     ...
281     END_USE_CODE
282     */
283    
284     <*>"USE_CODE_IF"[ \t]*"(" {
285 schoenebeck 3308 //printf("{%s}\n", yytext);
286 schoenebeck 2581 yy_push_state(PREPROC_IF, yyscanner);
287     }
288 schoenebeck 3308 <PREPROC_BODY_EAT>"USE_CODE_IF"[ \t]*"("{ID}")" {
289     //printf("[EAT{%s}\n", yytext);
290     yy_push_state(PREPROC_BODY_EAT, yyscanner);
291     }
292 schoenebeck 2581 <*>"USE_CODE_IF_NOT"[ \t]*"(" {
293 schoenebeck 2588 //printf("USE_CODE_IF_NOT\n");
294 schoenebeck 2581 yy_push_state(PREPROC_IF_NOT, yyscanner);
295     }
296 schoenebeck 3308 <PREPROC_BODY_EAT>"USE_CODE_IF_NOT"[ \t]*"("{ID}")" {
297     //printf("[EAT{%s}\n", yytext);
298     yy_push_state(PREPROC_BODY_EAT, yyscanner);
299     }
300 schoenebeck 2581 <PREPROC_IF>{ID} {
301 schoenebeck 2588 //printf("preproc use code if '%s'\n", yytext);
302 schoenebeck 2581 yy_pop_state(yyscanner);
303     if (yyextra->isPreprocessorConditionSet(yytext))
304     yy_push_state(PREPROC_PRE_BODY_USE, yyscanner);
305     else
306     yy_push_state(PREPROC_PRE_BODY_EAT, yyscanner);
307     }
308     <PREPROC_IF_NOT>{ID} {
309 schoenebeck 2588 //printf("preproc use code if not '%s'\n", yytext);
310 schoenebeck 2581 yy_pop_state(yyscanner);
311     if (!yyextra->isPreprocessorConditionSet(yytext))
312     yy_push_state(PREPROC_PRE_BODY_USE, yyscanner);
313     else
314     yy_push_state(PREPROC_PRE_BODY_EAT, yyscanner);
315     }
316     <PREPROC_PRE_BODY_USE>[ \t]*")" {
317     yy_pop_state(yyscanner);
318     yy_push_state(PREPROC_BODY_USE, yyscanner);
319     }
320     <PREPROC_PRE_BODY_EAT>[ \t]*")" {
321 schoenebeck 3308 //printf("PREPROCESSOR EAT : {%s}\n", yytext);
322 schoenebeck 2581 yy_pop_state(yyscanner);
323     yy_push_state(PREPROC_BODY_EAT, yyscanner);
324     }
325 schoenebeck 3308 <PREPROC_BODY_EAT,PREPROC_BODY_USE>"END_USE_CODE" {
326 schoenebeck 2588 //printf("-->END_USE_CODE\n");
327 schoenebeck 2581 yy_pop_state(yyscanner);
328     }
329 schoenebeck 2888 <PREPROC_BODY_EAT>[ \t\r\n]* { /* eat up code block filtered out by preprocessor */
330 schoenebeck 3308 //printf("PREPROCESSOR EAT2 : {%s}\n", yytext);
331 schoenebeck 2888 processLocation();
332     }
333     <PREPROC_BODY_EAT>.* { /* eat up code block filtered out by preprocessor */
334 schoenebeck 3308 //printf("PREPROCESSOR EAT3 : {%s}\n", yytext);
335 schoenebeck 3285 yyextra->addPreprocessorComment(yylloc->first_line, yylloc->last_line,
336     yylloc->first_column+1, yylloc->last_column+1);
337 schoenebeck 2888 }
338 schoenebeck 2581
339    
340     /* Language keywords */
341    
342     "on" return ON;
343     "end" return END;
344     "note" return NOTE;
345     "init" return INIT;
346     "declare" return DECLARE;
347     "while" return WHILE;
348     "if" return IF;
349 schoenebeck 2935 ".or." return BITWISE_OR;
350 schoenebeck 2581 "or" return OR;
351     "release" return RELEASE;
352 schoenebeck 2935 ".and." return BITWISE_AND;
353 schoenebeck 2581 "and" return AND;
354 schoenebeck 2935 ".not." return BITWISE_NOT;
355 schoenebeck 2581 "not" return NOT;
356     "else" return ELSE;
357     "controller" return CONTROLLER;
358     "case" return CASE;
359     "select" return SELECT;
360     "to" return TO;
361     "<=" return LE;
362     ">=" return GE;
363 schoenebeck 2585 "const" return CONST_; // note: "CONST" is already defined for C/C++ compilers on Windows by default
364 schoenebeck 2581 "polyphonic" return POLYPHONIC;
365     "mod" return MOD;
366 schoenebeck 2951 "function" return FUNCTION;
367     "call" return CALL;
368 schoenebeck 3260 "synchronized" return SYNCHRONIZED;
369 schoenebeck 2581
370 schoenebeck 3561 [&,!()[\]<>=*+#\/-] {
371 schoenebeck 2935 return *yytext;
372 schoenebeck 2581 }
373    
374 schoenebeck 3573 ("$"|"@"|"%"|"~"|"?"){ID} {
375 schoenebeck 2581 yylval->sValue = strdup(yytext);
376     return VARIABLE;
377     }
378    
379     {ID} {
380     yylval->sValue = strdup(yytext);
381     return IDENTIFIER;
382     }
383    
384     ":=" return ASSIGNMENT;
385    
386     \n+ {
387 schoenebeck 2888 yylineno += countNewLineChars(yytext);
388     yycolumn = 0;
389 schoenebeck 2581 //printf("lex: new line %d\n", yylineno, yytext);
390     //return LF;
391     }
392    
393 schoenebeck 2888 "{"[^}]*"}" { /* eat up comments */
394     processLocation();
395     }
396 schoenebeck 2581
397     [ \t\r]+ /* eat up whitespace */
398    
399 schoenebeck 3259 . {
400     printf( "Unrecognized character: '%s' (line %d, column %d)\n", yytext, yylineno, yycolumn);
401     return UNKNOWN_CHAR;
402     }
403 schoenebeck 2581
404     %%
405    
406     namespace LinuxSampler {
407    
408     void ParserContext::createScanner(std::istream* is) {
409     if (scanner) destroyScanner();
410     this->is = is;
411     yylex_init(&scanner);
412     yyset_extra(this, scanner);
413     }
414    
415     void ParserContext::destroyScanner() {
416     if (!scanner) return;
417     yylex_destroy(scanner);
418     scanner = NULL;
419     }
420    
421     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC