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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3573 - (show annotations) (download)
Tue Aug 27 21:36:53 2019 UTC (4 years, 7 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 /*
2 * Copyright (c) 2014-2017 Christian Schoenebeck and Andreas Persson
3 *
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 /* Token scanner for NKSP real-time instrument script language. */
11
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 yylloc->first_column = yycolumn; \
24 yycolumn += yyleng; \
25 yylloc->last_column = yycolumn - 1; \
26 /*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 context->addErr(locp->first_line, locp->last_line, locp->first_column, locp->last_column, err);
42 }
43
44 static void scanner_warning(YYLTYPE* locp, LinuxSampler::ParserContext* context, const char* txt) {
45 context->addWrn(locp->first_line, locp->last_line, locp->first_column, locp->last_column, txt);
46 }
47
48 #define SCANNER_ERR(txt) scanner_error(yylloc, yyextra, txt)
49 #define SCANNER_WRN(txt) scanner_warning(yylloc, yyextra, txt)
50
51 // 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 using namespace LinuxSampler;
57
58 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 const int n = (int)strlen(txt);
67 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 // 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 %}
87
88 /* 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 /* 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 ID [a-zA-Z][a-zA-Z0-9_]*
112 METRIC (k|h|(da)|d|c|m|u)
113 UNIT (s|(Hz)|B)
114
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 if (sizeof(vmint) < 8)
125 yylval->iValue = atoi(yytext);
126 else
127 yylval->iValue = atoll(yytext);
128 return INTEGER;
129 }
130
131 {DIGIT}+"."{DIGIT}+ {
132 yylval->fValue = atof(yytext);
133 return REAL;
134 }
135
136 {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 goto parseIntStdUnit;
166 }
167 }
168
169 parseIntStdUnit:
170
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 {DIGIT}+"."{DIGIT}+({METRIC}{1,2}|({METRIC}{0,2}{UNIT}?)) {
183 int pos = 0;
184
185 // 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
195 // 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 /* Preprocessor statement: SET_CONDITION(name) */
232
233 <*>"SET_CONDITION"[ \t]*"(" {
234 //printf("SET_CONDITION\n");
235 yy_push_state(PREPROC_SET_COND, yyscanner);
236 }
237 <PREPROC_SET_COND>{ID} {
238 //printf("preproc set condition '%s'\n", yytext);
239 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 //printf("End of PREPROC_SET_COND\n");
247 yy_pop_state(yyscanner);
248 }
249
250
251 /* Preprocessor statement: RESET_CONDITION(name) */
252
253 <*>"RESET_CONDITION"[ \t]*"(" {
254 //printf("RESET_CONDITION\n");
255 yy_push_state(PREPROC_RESET_COND, yyscanner);
256 }
257 <PREPROC_RESET_COND>{ID} {
258 //printf("preproc reset condition '%s'\n", yytext);
259 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 //printf("End of RESET_CONDITION\n");
267 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 //printf("{%s}\n", yytext);
286 yy_push_state(PREPROC_IF, yyscanner);
287 }
288 <PREPROC_BODY_EAT>"USE_CODE_IF"[ \t]*"("{ID}")" {
289 //printf("[EAT{%s}\n", yytext);
290 yy_push_state(PREPROC_BODY_EAT, yyscanner);
291 }
292 <*>"USE_CODE_IF_NOT"[ \t]*"(" {
293 //printf("USE_CODE_IF_NOT\n");
294 yy_push_state(PREPROC_IF_NOT, yyscanner);
295 }
296 <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 <PREPROC_IF>{ID} {
301 //printf("preproc use code if '%s'\n", yytext);
302 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 //printf("preproc use code if not '%s'\n", yytext);
310 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 //printf("PREPROCESSOR EAT : {%s}\n", yytext);
322 yy_pop_state(yyscanner);
323 yy_push_state(PREPROC_BODY_EAT, yyscanner);
324 }
325 <PREPROC_BODY_EAT,PREPROC_BODY_USE>"END_USE_CODE" {
326 //printf("-->END_USE_CODE\n");
327 yy_pop_state(yyscanner);
328 }
329 <PREPROC_BODY_EAT>[ \t\r\n]* { /* eat up code block filtered out by preprocessor */
330 //printf("PREPROCESSOR EAT2 : {%s}\n", yytext);
331 processLocation();
332 }
333 <PREPROC_BODY_EAT>.* { /* eat up code block filtered out by preprocessor */
334 //printf("PREPROCESSOR EAT3 : {%s}\n", yytext);
335 yyextra->addPreprocessorComment(yylloc->first_line, yylloc->last_line,
336 yylloc->first_column+1, yylloc->last_column+1);
337 }
338
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 ".or." return BITWISE_OR;
350 "or" return OR;
351 "release" return RELEASE;
352 ".and." return BITWISE_AND;
353 "and" return AND;
354 ".not." return BITWISE_NOT;
355 "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 "const" return CONST_; // note: "CONST" is already defined for C/C++ compilers on Windows by default
364 "polyphonic" return POLYPHONIC;
365 "mod" return MOD;
366 "function" return FUNCTION;
367 "call" return CALL;
368 "synchronized" return SYNCHRONIZED;
369
370 [&,!()[\]<>=*+#\/-] {
371 return *yytext;
372 }
373
374 ("$"|"@"|"%"|"~"|"?"){ID} {
375 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 yylineno += countNewLineChars(yytext);
388 yycolumn = 0;
389 //printf("lex: new line %d\n", yylineno, yytext);
390 //return LF;
391 }
392
393 "{"[^}]*"}" { /* eat up comments */
394 processLocation();
395 }
396
397 [ \t\r]+ /* eat up whitespace */
398
399 . {
400 printf( "Unrecognized character: '%s' (line %d, column %d)\n", yytext, yylineno, yycolumn);
401 return UNKNOWN_CHAR;
402 }
403
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