/[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 3561 - (hide annotations) (download)
Fri Aug 23 11:44:00 2019 UTC (4 years, 8 months ago) by schoenebeck
File size: 10579 byte(s)
NKSP: Added standard units support for numbers and final "!" operator:

* NKSP strictness: Variable names, function names and preprocessor condition
  names must start with a regular character (a-z or A-Z); starting them with
  a digit or underscore is no longer allowed.

* NKSP parser fix: equal comparison operator "=" and not equal comparison
  operator "#" must only accept integer operands.

* NKSP language: Implemented support for standard units like Hertz, seconds,
  Bel including support for metric unit prefixes; so one can now e.g.
  conveniently use numbers in scripts like "5us" meaning "5 microseconds",
  or e.g. "12kHz" meaning "12 kilo Hertz", or e.g. "-14mdB" meaning
  "minus 14 Millidecibel", or e.g. "28c" meaning "28 cents" (for tuning).

* NKSP language: Introduced "final" operator "!" which is specifically
  intended for synthesis parameter values to denote that the synthesis
  parameter value is intended to be the "final" value for that synthesis
  parameter that should explicitly be used by the engine and thus causing
  the sampler engine to ignore all other modulation sources for the same
  synthesis parameter (like e.g. LFO, EG); by simply prefixing a value,
  variable or formula with this new "!" operator the expression is marked as
  being "final".

* Bumped version (2.1.1.svn4).

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 3561 {DIGIT}+({METRIC}{1,2}|({METRIC}{0,2}{UNIT}?)) {
132     int pos = 0;
133    
134     // parse number portion
135     vmint value = 0;
136     for (; yytext[pos] >= '0' && yytext[pos] <= '9'; ++pos) {
137     value *= 10;
138     value += yytext[pos] - '0';
139     }
140     yylval->iUnitValue.iValue = value;
141    
142     // parse metric prefix portion
143     for (int i = 0; i < 2; ++i, ++pos) {
144     switch (yytext[pos]) {
145     case 'k': yylval->iUnitValue.prefix[i] = VM_KILO; continue;
146     case 'h': yylval->iUnitValue.prefix[i] = VM_HECTO; continue;
147     case 'c': yylval->iUnitValue.prefix[i] = VM_CENTI; continue;
148     case 'm': yylval->iUnitValue.prefix[i] = VM_MILLI; continue;
149     case 'u': yylval->iUnitValue.prefix[i] = VM_MICRO; continue;
150     case 'd':
151     if (yytext[pos+1] == 'a') {
152     yylval->iUnitValue.prefix[i] = VM_DECA;
153     ++pos;
154     } else {
155     yylval->iUnitValue.prefix[i] = VM_DECI;
156     }
157     continue;
158     default:
159     yylval->iUnitValue.prefix[i] = VM_NO_PREFIX;
160     goto parseStdUnit;
161     }
162     }
163    
164     parseStdUnit:
165    
166     // parse standard measurement unit
167     switch (yytext[pos]) {
168     case 's': yylval->iUnitValue.unit = VM_SECOND; break;
169     case 'H': yylval->iUnitValue.unit = VM_HERTZ; break;
170     case 'B': yylval->iUnitValue.unit = VM_BEL; break;
171     default: yylval->iUnitValue.unit = VM_NO_UNIT; break;
172     }
173    
174     return INTEGER_UNIT;
175     }
176    
177 schoenebeck 2935 /* there is currently no support for floating point numbers in NKSP yet */
178     /*{DIGIT}+"."{DIGIT}* {
179 schoenebeck 2581 printf("A float: %s (%g)\n", yytext, atof(yytext));
180 schoenebeck 2935 }*/
181 schoenebeck 2581
182    
183     /* Preprocessor statement: SET_CONDITION(name) */
184    
185     <*>"SET_CONDITION"[ \t]*"(" {
186 schoenebeck 2588 //printf("SET_CONDITION\n");
187 schoenebeck 2581 yy_push_state(PREPROC_SET_COND, yyscanner);
188     }
189     <PREPROC_SET_COND>{ID} {
190 schoenebeck 2588 //printf("preproc set condition '%s'\n", yytext);
191 schoenebeck 2581 bool success = yyextra->setPreprocessorCondition(yytext);
192     if (!success) {
193     SCANNER_WRN((String("Preprocessor: Condition '") +
194     yytext + "' is already set.").c_str());
195     }
196     }
197     <PREPROC_SET_COND>[ \t]*")" {
198 schoenebeck 2588 //printf("End of PREPROC_SET_COND\n");
199 schoenebeck 2581 yy_pop_state(yyscanner);
200     }
201    
202    
203     /* Preprocessor statement: RESET_CONDITION(name) */
204    
205     <*>"RESET_CONDITION"[ \t]*"(" {
206 schoenebeck 2588 //printf("RESET_CONDITION\n");
207 schoenebeck 2581 yy_push_state(PREPROC_RESET_COND, yyscanner);
208     }
209     <PREPROC_RESET_COND>{ID} {
210 schoenebeck 2588 //printf("preproc reset condition '%s'\n", yytext);
211 schoenebeck 2581 bool success = yyextra->resetPreprocessorCondition(yytext);
212     if (!success) {
213     SCANNER_ERR((String("Preprocessor: could not reset condition '") +
214     yytext + "' (either not set or a built-in condition).").c_str());
215     }
216     }
217     <PREPROC_RESET_COND>[ \t]*")" {
218 schoenebeck 2588 //printf("End of RESET_CONDITION\n");
219 schoenebeck 2581 yy_pop_state(yyscanner);
220     }
221    
222    
223     /* Preprocessor conditional statements:
224    
225     USE_CODE_IF(name)
226     ...
227     END_USE_CODE
228    
229     and:
230    
231     USE_CODE_IF_NOT(name)
232     ...
233     END_USE_CODE
234     */
235    
236     <*>"USE_CODE_IF"[ \t]*"(" {
237 schoenebeck 3308 //printf("{%s}\n", yytext);
238 schoenebeck 2581 yy_push_state(PREPROC_IF, yyscanner);
239     }
240 schoenebeck 3308 <PREPROC_BODY_EAT>"USE_CODE_IF"[ \t]*"("{ID}")" {
241     //printf("[EAT{%s}\n", yytext);
242     yy_push_state(PREPROC_BODY_EAT, yyscanner);
243     }
244 schoenebeck 2581 <*>"USE_CODE_IF_NOT"[ \t]*"(" {
245 schoenebeck 2588 //printf("USE_CODE_IF_NOT\n");
246 schoenebeck 2581 yy_push_state(PREPROC_IF_NOT, yyscanner);
247     }
248 schoenebeck 3308 <PREPROC_BODY_EAT>"USE_CODE_IF_NOT"[ \t]*"("{ID}")" {
249     //printf("[EAT{%s}\n", yytext);
250     yy_push_state(PREPROC_BODY_EAT, yyscanner);
251     }
252 schoenebeck 2581 <PREPROC_IF>{ID} {
253 schoenebeck 2588 //printf("preproc use code if '%s'\n", yytext);
254 schoenebeck 2581 yy_pop_state(yyscanner);
255     if (yyextra->isPreprocessorConditionSet(yytext))
256     yy_push_state(PREPROC_PRE_BODY_USE, yyscanner);
257     else
258     yy_push_state(PREPROC_PRE_BODY_EAT, yyscanner);
259     }
260     <PREPROC_IF_NOT>{ID} {
261 schoenebeck 2588 //printf("preproc use code if not '%s'\n", yytext);
262 schoenebeck 2581 yy_pop_state(yyscanner);
263     if (!yyextra->isPreprocessorConditionSet(yytext))
264     yy_push_state(PREPROC_PRE_BODY_USE, yyscanner);
265     else
266     yy_push_state(PREPROC_PRE_BODY_EAT, yyscanner);
267     }
268     <PREPROC_PRE_BODY_USE>[ \t]*")" {
269     yy_pop_state(yyscanner);
270     yy_push_state(PREPROC_BODY_USE, yyscanner);
271     }
272     <PREPROC_PRE_BODY_EAT>[ \t]*")" {
273 schoenebeck 3308 //printf("PREPROCESSOR EAT : {%s}\n", yytext);
274 schoenebeck 2581 yy_pop_state(yyscanner);
275     yy_push_state(PREPROC_BODY_EAT, yyscanner);
276     }
277 schoenebeck 3308 <PREPROC_BODY_EAT,PREPROC_BODY_USE>"END_USE_CODE" {
278 schoenebeck 2588 //printf("-->END_USE_CODE\n");
279 schoenebeck 2581 yy_pop_state(yyscanner);
280     }
281 schoenebeck 2888 <PREPROC_BODY_EAT>[ \t\r\n]* { /* eat up code block filtered out by preprocessor */
282 schoenebeck 3308 //printf("PREPROCESSOR EAT2 : {%s}\n", yytext);
283 schoenebeck 2888 processLocation();
284     }
285     <PREPROC_BODY_EAT>.* { /* eat up code block filtered out by preprocessor */
286 schoenebeck 3308 //printf("PREPROCESSOR EAT3 : {%s}\n", yytext);
287 schoenebeck 3285 yyextra->addPreprocessorComment(yylloc->first_line, yylloc->last_line,
288     yylloc->first_column+1, yylloc->last_column+1);
289 schoenebeck 2888 }
290 schoenebeck 2581
291    
292     /* Language keywords */
293    
294     "on" return ON;
295     "end" return END;
296     "note" return NOTE;
297     "init" return INIT;
298     "declare" return DECLARE;
299     "while" return WHILE;
300     "if" return IF;
301 schoenebeck 2935 ".or." return BITWISE_OR;
302 schoenebeck 2581 "or" return OR;
303     "release" return RELEASE;
304 schoenebeck 2935 ".and." return BITWISE_AND;
305 schoenebeck 2581 "and" return AND;
306 schoenebeck 2935 ".not." return BITWISE_NOT;
307 schoenebeck 2581 "not" return NOT;
308     "else" return ELSE;
309     "controller" return CONTROLLER;
310     "case" return CASE;
311     "select" return SELECT;
312     "to" return TO;
313     "<=" return LE;
314     ">=" return GE;
315 schoenebeck 2585 "const" return CONST_; // note: "CONST" is already defined for C/C++ compilers on Windows by default
316 schoenebeck 2581 "polyphonic" return POLYPHONIC;
317     "mod" return MOD;
318 schoenebeck 2951 "function" return FUNCTION;
319     "call" return CALL;
320 schoenebeck 3260 "synchronized" return SYNCHRONIZED;
321 schoenebeck 2581
322 schoenebeck 3561 [&,!()[\]<>=*+#\/-] {
323 schoenebeck 2935 return *yytext;
324 schoenebeck 2581 }
325    
326 schoenebeck 2935 ("$"|"@"|"%"){ID} {
327 schoenebeck 2581 yylval->sValue = strdup(yytext);
328     return VARIABLE;
329     }
330    
331     {ID} {
332     yylval->sValue = strdup(yytext);
333     return IDENTIFIER;
334     }
335    
336     ":=" return ASSIGNMENT;
337    
338     \n+ {
339 schoenebeck 2888 yylineno += countNewLineChars(yytext);
340     yycolumn = 0;
341 schoenebeck 2581 //printf("lex: new line %d\n", yylineno, yytext);
342     //return LF;
343     }
344    
345 schoenebeck 2888 "{"[^}]*"}" { /* eat up comments */
346     processLocation();
347     }
348 schoenebeck 2581
349     [ \t\r]+ /* eat up whitespace */
350    
351 schoenebeck 3259 . {
352     printf( "Unrecognized character: '%s' (line %d, column %d)\n", yytext, yylineno, yycolumn);
353     return UNKNOWN_CHAR;
354     }
355 schoenebeck 2581
356     %%
357    
358     namespace LinuxSampler {
359    
360     void ParserContext::createScanner(std::istream* is) {
361     if (scanner) destroyScanner();
362     this->is = is;
363     yylex_init(&scanner);
364     yyset_extra(this, scanner);
365     }
366    
367     void ParserContext::destroyScanner() {
368     if (!scanner) return;
369     yylex_destroy(scanner);
370     scanner = NULL;
371     }
372    
373     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC