/[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 3561 - (show 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 /*
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}+({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 /* there is currently no support for floating point numbers in NKSP yet */
178 /*{DIGIT}+"."{DIGIT}* {
179 printf("A float: %s (%g)\n", yytext, atof(yytext));
180 }*/
181
182
183 /* Preprocessor statement: SET_CONDITION(name) */
184
185 <*>"SET_CONDITION"[ \t]*"(" {
186 //printf("SET_CONDITION\n");
187 yy_push_state(PREPROC_SET_COND, yyscanner);
188 }
189 <PREPROC_SET_COND>{ID} {
190 //printf("preproc set condition '%s'\n", yytext);
191 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 //printf("End of PREPROC_SET_COND\n");
199 yy_pop_state(yyscanner);
200 }
201
202
203 /* Preprocessor statement: RESET_CONDITION(name) */
204
205 <*>"RESET_CONDITION"[ \t]*"(" {
206 //printf("RESET_CONDITION\n");
207 yy_push_state(PREPROC_RESET_COND, yyscanner);
208 }
209 <PREPROC_RESET_COND>{ID} {
210 //printf("preproc reset condition '%s'\n", yytext);
211 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 //printf("End of RESET_CONDITION\n");
219 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 //printf("{%s}\n", yytext);
238 yy_push_state(PREPROC_IF, yyscanner);
239 }
240 <PREPROC_BODY_EAT>"USE_CODE_IF"[ \t]*"("{ID}")" {
241 //printf("[EAT{%s}\n", yytext);
242 yy_push_state(PREPROC_BODY_EAT, yyscanner);
243 }
244 <*>"USE_CODE_IF_NOT"[ \t]*"(" {
245 //printf("USE_CODE_IF_NOT\n");
246 yy_push_state(PREPROC_IF_NOT, yyscanner);
247 }
248 <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 <PREPROC_IF>{ID} {
253 //printf("preproc use code if '%s'\n", yytext);
254 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 //printf("preproc use code if not '%s'\n", yytext);
262 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 //printf("PREPROCESSOR EAT : {%s}\n", yytext);
274 yy_pop_state(yyscanner);
275 yy_push_state(PREPROC_BODY_EAT, yyscanner);
276 }
277 <PREPROC_BODY_EAT,PREPROC_BODY_USE>"END_USE_CODE" {
278 //printf("-->END_USE_CODE\n");
279 yy_pop_state(yyscanner);
280 }
281 <PREPROC_BODY_EAT>[ \t\r\n]* { /* eat up code block filtered out by preprocessor */
282 //printf("PREPROCESSOR EAT2 : {%s}\n", yytext);
283 processLocation();
284 }
285 <PREPROC_BODY_EAT>.* { /* eat up code block filtered out by preprocessor */
286 //printf("PREPROCESSOR EAT3 : {%s}\n", yytext);
287 yyextra->addPreprocessorComment(yylloc->first_line, yylloc->last_line,
288 yylloc->first_column+1, yylloc->last_column+1);
289 }
290
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 ".or." return BITWISE_OR;
302 "or" return OR;
303 "release" return RELEASE;
304 ".and." return BITWISE_AND;
305 "and" return AND;
306 ".not." return BITWISE_NOT;
307 "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 "const" return CONST_; // note: "CONST" is already defined for C/C++ compilers on Windows by default
316 "polyphonic" return POLYPHONIC;
317 "mod" return MOD;
318 "function" return FUNCTION;
319 "call" return CALL;
320 "synchronized" return SYNCHRONIZED;
321
322 [&,!()[\]<>=*+#\/-] {
323 return *yytext;
324 }
325
326 ("$"|"@"|"%"){ID} {
327 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 yylineno += countNewLineChars(yytext);
340 yycolumn = 0;
341 //printf("lex: new line %d\n", yylineno, yytext);
342 //return LF;
343 }
344
345 "{"[^}]*"}" { /* eat up comments */
346 processLocation();
347 }
348
349 [ \t\r]+ /* eat up whitespace */
350
351 . {
352 printf( "Unrecognized character: '%s' (line %d, column %d)\n", yytext, yylineno, yycolumn);
353 return UNKNOWN_CHAR;
354 }
355
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