1 /*
2 symbtab.c:
3
4 Copyright (C) 2006
5 John ffitch, Steven Yi
6
7 This file is part of Csound.
8
9 The Csound Library is free software; you can redistribute it
10 and/or modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 Csound 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 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with Csound; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 02110-1301 USA
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "csoundCore.h"
29 #include "tok.h"
30 #include "csound_orc.h"
31 #include "insert.h"
32 #include "namedins.h"
33 #include "interlocks.h"
34 #include "csound_orc_semantics.h"
35 #include "csound_standard_types.h"
36
37 #ifndef PARSER_DEBUG
38 #define PARSER_DEBUG (0)
39 #endif
40
41
42 // FIXME - this is global...
43 // VL moved to csound struct
44 //CS_HASH_TABLE* symbtab;
45
46 #define udoflag csound->parserUdoflag
47 #define namedInstrFlag csound->parserNamedInstrFlag
48
49 ORCTOKEN *add_token(CSOUND *csound, char *s, int type);
50 //static ORCTOKEN *add_token_p(CSOUND *csound, char *s, int type, int val);
51 extern int csound_orcget_lineno(void*);
52
53 /* from csound_orc_compile.c */
54 extern char** splitArgs(CSOUND* csound, char* argString);
55
get_opcode_type(OENTRY * ep)56 int get_opcode_type(OENTRY *ep)
57 {
58 int retVal = 0;
59
60 if (ep->outypes == NULL || strlen(ep->outypes) == 0) {
61 retVal = T_OPCODE0;
62 }
63 else {
64 retVal = T_OPCODE;
65 }
66 return retVal;
67 }
68
init_symbtab(CSOUND * csound)69 void init_symbtab(CSOUND *csound)
70 {
71 OENTRY *ep;
72 CONS_CELL *top, *head, *items;
73
74 char *shortName;
75
76
77 if (csound->symbtab == NULL) {
78 /* VL 27 02 2015 -- if symbtab exists, do not create it again
79 to avoid memory consumption.
80 */
81 //printf("init symbtab\n");
82 csound->symbtab = cs_hash_table_create(csound);
83 /* Now we need to populate with basic words */
84 /* Add token types for opcodes to symbtab. If a polymorphic opcode
85 * definition is found (dsblksiz >= 0xfffb), look for implementations
86 * of that opcode to correctly mark the type of opcode it is (T_OPCODE,
87 * T_OPCODE0, or T_OPCODE00)
88 */
89
90 top = head = cs_hash_table_values(csound, csound->opcodes);
91
92 while (head != NULL) {
93 items = head->value;
94 while (items != NULL) {
95 ep = items->value;
96
97 if (ep->dsblksiz < 0xfffb) {
98 shortName = get_opcode_short_name(csound, ep->opname);
99
100 add_token(csound, shortName, get_opcode_type(ep));
101
102 if (shortName != ep->opname) {
103 csound->Free(csound, shortName);
104 }
105 }
106 items = items->next;
107 }
108 head = head->next;
109 }
110 csound->Free(csound, top);
111 }
112 }
113
add_token(CSOUND * csound,char * s,int type)114 ORCTOKEN *add_token(CSOUND *csound, char *s, int type)
115 {
116 //printf("Hash value for %s: %i\n", s, h);
117
118 ORCTOKEN *a = cs_hash_table_get(csound, csound->symbtab, s);
119
120 ORCTOKEN *ans;
121 if (a!=NULL) {
122 if (type == a->type) return a;
123 if (UNLIKELY((type!=T_FUNCTION || a->type!=T_OPCODE)))
124 csound->Warning(csound,
125 Str("Type confusion for %s (%d,%d), replacing\n"),
126 s, type, a->type);
127 a->type = type;
128 return a;
129 }
130 ans = new_token(csound, T_IDENT);
131 ans->lexeme = (char*)csound->Malloc(csound, 1+strlen(s));
132 strcpy(ans->lexeme, s);
133 ans->type = type;
134
135 cs_hash_table_put(csound, csound->symbtab, s, ans);
136
137 return ans;
138 }
139
140 /* static ORCTOKEN *add_token_p(CSOUND *csound, char *s, int type, int val) */
141 /* { */
142 /* ORCTOKEN *ans = add_token(csound, s, type); */
143 /* ans->value = val; */
144 /* return ans; */
145 /* } */
146
isUDOArgList(char * s)147 int isUDOArgList(char *s)
148 {
149 int len = strlen(s) - 1;
150
151 while (len >= 0) {
152 if (UNLIKELY(strchr("aijkftKOJVPopS[]0", s[len]) == NULL)) {
153 /* printf("Invalid char '%c' in '%s'", *p, s); */
154 return 0;
155 }
156 len--;
157 }
158 return 1;
159 }
160
isUDOAnsList(char * s)161 int isUDOAnsList(char *s)
162 {
163 int len = strlen(s) - 1;
164
165 while (len >= 0) {
166 if (UNLIKELY(strchr("aikftSK[]0", s[len]) == NULL)) {
167 return 0;
168 }
169 len--;
170 }
171 return 1;
172 }
173
lookup_token(CSOUND * csound,char * s,void * yyscanner)174 ORCTOKEN *lookup_token(CSOUND *csound, char *s, void *yyscanner)
175 {
176 IGN(yyscanner);
177 int type = T_IDENT;
178 ORCTOKEN *a;
179 ORCTOKEN *ans;
180
181 if (UNLIKELY(PARSER_DEBUG))
182 csound->Message(csound, "Looking up token for: %s\n", s);
183
184 if (udoflag == 0) {
185 if (isUDOAnsList(s)) {
186 ans = new_token(csound, UDO_ANS_TOKEN);
187 ans->lexeme = (char*)csound->Malloc(csound, 1+strlen(s));
188 strcpy(ans->lexeme, s);
189 return ans;
190 }
191 }
192
193 if (udoflag == 1) {
194 if (UNLIKELY(csound->oparms->odebug)) printf("Found UDO Arg List\n");
195 if (isUDOArgList(s)) {
196 ans = new_token(csound, UDO_ARGS_TOKEN);
197 ans->lexeme = (char*)csound->Malloc(csound, 1+strlen(s));
198 strcpy(ans->lexeme, s);
199 return ans;
200 }
201 }
202
203 a = cs_hash_table_get(csound, csound->symbtab, s);
204
205 if (a != NULL) {
206 ans = (ORCTOKEN*)csound->Malloc(csound, sizeof(ORCTOKEN));
207 memcpy(ans, a, sizeof(ORCTOKEN));
208 ans->next = NULL;
209 ans->lexeme = (char *)csound->Malloc(csound, strlen(a->lexeme) + 1);
210 strcpy(ans->lexeme, a->lexeme);
211 return ans;
212 }
213
214 ans = new_token(csound, T_IDENT);
215 ans->lexeme = (char*)csound->Malloc(csound, 1+strlen(s));
216 strcpy(ans->lexeme, s);
217
218 if (udoflag == -2 || namedInstrFlag == 1) {
219 return ans;
220 }
221
222 ans->type = type;
223
224 return ans;
225 }
226
227
228 //static int is_optional_udo_in_arg(char* argtype) {
229 // return strchr("jOPVop", *argtype) != NULL;
230 //}
231
map_udo_in_arg_type(char in)232 static char map_udo_in_arg_type(char in) {
233 if (strchr("ijop", in) != NULL) {
234 return 'i';
235 } else if (strchr("kKOJPV", in) != NULL) {
236 return 'k';
237 }
238 return in;
239 }
240
map_udo_out_arg_type(char in)241 static char map_udo_out_arg_type(char in) {
242 if (in == 'K') {
243 return 'k';
244 }
245 return in;
246 }
247
map_args(char * args)248 static void map_args(char* args) {
249 while (*args != '\0') {
250 *args = map_udo_out_arg_type(*args);
251 args++;
252 }
253 }
254
255 /**
256 *
257 This function takes in the arguments from useropinfo in OENTRY and
258 parses them, filling the OENTRY input and output types and creating
259 the argument lists for xinset/xouset in insert.c argument
260 pointerlists, stored in useropinfo->in_ndx_list and
261 useropinfo->out_ndx_list.
262
263 The argument lists are terminated by a -1 and are set in the
264 following order:
265 i-var args (i-time vars)
266 S-var args (strings)
267 i-arrays
268 a-vars
269 k-vars
270 f-sigs
271 arrays (a,k,f)
272
273 This order is fixed and followed up in xinset/xoutset and
274 useropcd1, useropcd2.
275
276 Original code - IV Oct 12 2002
277 modified by VL for Csound 6
278
279 */
280
parse_opcode_args(CSOUND * csound,OENTRY * opc)281 static int parse_opcode_args(CSOUND *csound, OENTRY *opc)
282 {
283 OPCODINFO *inm = (OPCODINFO*) opc->useropinfo;
284 char** in_args;
285 char** out_args;
286 char intypes[256];
287 char typeSpecifier[2];
288 char tempName[20];
289 int i = 0, err = 0;
290 int n=0;
291
292 ARRAY_VAR_INIT varInit;
293
294 typeSpecifier[1] = '\0';
295
296 // The following handles adding of extra 'o' type for optional
297 // ksmps arg for all UDO's
298 if (*inm->intypes == '0') {
299 intypes[0] = 'o';
300 intypes[1] = '\0';
301 } else {
302 snprintf(intypes, 256, "%so", inm->intypes);
303 }
304 in_args = splitArgs(csound, intypes);
305 out_args = splitArgs(csound, inm->outtypes);
306
307 if (UNLIKELY(in_args == NULL)) {
308 synterr(csound,
309 Str("invalid input argument types found for opcode %s: %s\n"),
310 inm->name, intypes);
311 err++;
312 }
313 if (UNLIKELY(out_args == NULL)) {
314 synterr(csound,
315 Str("invalid output argument types found for opcode %s: %s\n"),
316 inm->name, inm->outtypes);
317 err++;
318 }
319
320 if (UNLIKELY(err > 0)) goto early_exit;
321
322 if (*in_args[0] != '0') {
323 while (in_args[i] != NULL) {
324 char* in_arg = in_args[i];
325 snprintf(tempName, 20, "in%d", i);
326
327 if (*in_arg == '[') {
328 int dimensions = 0;
329 while (*in_arg == '[') {
330 dimensions += 1;
331 in_arg += 1;
332 }
333 typeSpecifier[0] = *in_arg;
334 // printf("Dimensions: %d SubArgType: %s\n", dimensions, typeSpecifier);
335 CS_TYPE* type =
336 csoundGetTypeWithVarTypeName(csound->typePool, typeSpecifier);
337
338 if (UNLIKELY(type == NULL)) {
339 synterr(csound, Str("invalid input type for opcode %s\n"), in_arg);
340 err++;
341 continue;
342 }
343
344 varInit.dimensions = dimensions;
345 varInit.type = type;
346 CS_VARIABLE* var = csoundCreateVariable(csound, csound->typePool,
347 (CS_TYPE*)&CS_VAR_TYPE_ARRAY,
348 tempName, &varInit);
349 var->dimensions = dimensions;
350 csoundAddVariable(csound, inm->in_arg_pool, var);
351 } else {
352 char c = map_udo_in_arg_type(*in_arg);
353 // printf("found arg type %s -> %c\n", in_arg, c);
354
355 typeSpecifier[0] = c;
356 CS_TYPE* type =
357 csoundGetTypeWithVarTypeName(csound->typePool, typeSpecifier);
358
359 if (UNLIKELY(type == NULL)) {
360 synterr(csound, Str("invalid input type for opcode %s\n"), in_arg);
361 err++;
362 continue;
363 }
364
365 CS_VARIABLE* var = csoundCreateVariable(csound, csound->typePool,
366 type, tempName, NULL);
367 csoundAddVariable(csound, inm->in_arg_pool, var);
368 }
369 i++;
370 }
371 }
372 // inm->inchns = i + 1; /* Add one for optional local ksmps */
373 inm->inchns = i - 1;
374
375 i = 0;
376 if (*out_args[0] != '0') {
377 while(out_args[i] != NULL) {
378 char* out_arg = out_args[i];
379 snprintf(tempName, 20, "out%d", i);
380
381 if (*out_arg == '[') {
382 int dimensions = 0;
383 while (*out_arg == '[') {
384 dimensions += 1;
385 out_arg += 1;
386 }
387 typeSpecifier[0] = *out_arg;
388 //printf("Dimensions: %d SubArgType: %s\n", dimensions, typeSpecifier);
389 CS_TYPE* type =
390 csoundGetTypeWithVarTypeName(csound->typePool, typeSpecifier);
391
392 if (UNLIKELY(type == NULL)) {
393 synterr(csound, Str("invalid output type for opcode %s"), out_arg);
394 err++;
395 continue;
396 }
397
398 varInit.dimensions = dimensions;
399 varInit.type = type;
400 CS_VARIABLE* var = csoundCreateVariable(csound, csound->typePool,
401 (CS_TYPE*)&CS_VAR_TYPE_ARRAY,
402 tempName, &varInit);
403 var->dimensions = dimensions;
404 csoundAddVariable(csound, inm->out_arg_pool, var);
405 } else {
406 char c = map_udo_out_arg_type(*out_arg);
407 // printf("found arg type %s -> %c\n", out_arg, c);
408 typeSpecifier[0] = c;
409 CS_TYPE* type =
410 csoundGetTypeWithVarTypeName(csound->typePool, typeSpecifier);
411
412 if (UNLIKELY(type == NULL)) {
413 synterr(csound, Str("invalid output type for opcode %s"), out_arg);
414 err++;
415 continue;
416 }
417
418 CS_VARIABLE* var = csoundCreateVariable(csound, csound->typePool, type,
419 tempName, NULL);
420 csoundAddVariable(csound, inm->out_arg_pool, var);
421 }
422 i++;
423 }
424 }
425
426 inm->outchns = i;
427
428 opc->dsblksiz = (uint16) (sizeof(UOPCODE) +
429 sizeof(MYFLT*) * (inm->inchns + inm->outchns));
430 opc->dsblksiz = ((opc->dsblksiz + (uint16) 15)
431 & (~((uint16) 15))); /* align (needed ?) */
432
433
434 opc->intypes = cs_strdup(csound, intypes);
435 opc->outypes = cs_strdup(csound, (inm->outtypes[0] == '0') ? "" :
436 inm->outtypes);
437
438 map_args(opc->intypes);
439 map_args(opc->outypes);
440 // /* count the number of arguments, and check types */
441 // default:
442 // synterr(csound, Str("invalid input type for opcode %s"), inm->name);
443 // err++; i--;
444 // }
445 // i++; types++;
446 // if (UNLIKELY(i > OPCODENUMOUTS_MAX)) {
447 // synterr(csound, Str("too many input args for opcode %s"), inm->name);
448 // csound->LongJmp(csound, 1);
449 // }
450 // }
451 // default:
452 // synterr(csound, Str("invalid output type for opcode %s"), inm->name);
453 // err++; i--;
454 // }
455 // i++; types++;
456 // }
457 //
458
459 early_exit:
460 if(in_args != NULL) {
461 while(in_args[n] != NULL) {
462 // printf("delete %p\n", argsFound[n]);
463 csound->Free(csound, in_args[n]);
464 n++;
465 }
466 csound->Free(csound, in_args);
467 }
468 if (out_args != NULL) {
469 n = 0;
470 while(out_args[n] != NULL) {
471 // printf("delete %p\n", argsFound[n]);
472 csound->Free(csound, out_args[n]);
473 n++;
474 }
475 csound->Free(csound, out_args);
476 }
477 return err;
478 }
479
480
csound_find_internal_oentry(CSOUND * csound,OENTRY * oentry)481 OENTRY* csound_find_internal_oentry(CSOUND* csound, OENTRY* oentry) {
482 CONS_CELL *items;
483 char *shortName;
484 OENTRY *ep, *retVal = NULL;
485
486 if (oentry == NULL) {
487 return NULL;
488 }
489 shortName = get_opcode_short_name(csound, oentry->opname);
490
491 items = cs_hash_table_get(csound, csound->opcodes, shortName);
492
493 while (items != NULL) {
494 ep = items->value;
495 if (oentry->iopadr == ep->iopadr &&
496 oentry->kopadr == ep->kopadr &&
497 oentry->aopadr == ep->aopadr &&
498 strcmp(oentry->opname, ep->opname) == 0 &&
499 strcmp(oentry->outypes, ep->outypes) == 0 &&
500 strcmp(oentry->intypes, ep->intypes) == 0) {
501 retVal = ep;
502 break;
503 }
504 items = items->next;
505 }
506
507 if (shortName != oentry->opname) {
508 csound->Free(csound, shortName);
509 }
510
511 return retVal;
512 }
513
514
515 /** Adds a UDO definition as an T_OPCODE or T_OPCODE0 type to the symbol table
516 * used at parse time. An OENTRY is also added at this time so that at
517 * verification time the opcode can be looked up to get its signature.
518 */
add_udo_definition(CSOUND * csound,char * opname,char * outtypes,char * intypes)519 int add_udo_definition(CSOUND *csound, char *opname,
520 char *outtypes, char *intypes) {
521
522 OENTRY tmpEntry, *opc, *newopc;
523 OPCODINFO *inm;
524 int len;
525
526 if (UNLIKELY(!check_instr_name(opname))) {
527 synterr(csound, Str("invalid name for opcode"));
528 return -1;
529 }
530
531 len = strlen(intypes);
532 if (len == 1 && *intypes == '0') {
533 opc = find_opcode_exact(csound, opname, outtypes, "o");
534 } else {
535 char* adjusted_intypes = csound->Malloc(csound, sizeof(char) * (len + 2));
536 sprintf(adjusted_intypes, "%so", intypes);
537 opc = find_opcode_exact(csound, opname, outtypes, adjusted_intypes);
538 csound->Free(csound, adjusted_intypes);
539 }
540
541 /* check if opcode is already defined */
542 if (UNLIKELY(opc != NULL)) {
543 /* IV - Oct 31 2002: redefine old opcode if possible */
544 if (UNLIKELY(!strcmp(opname, "instr") ||
545 !strcmp(opname, "endin") ||
546 !strcmp(opname, "opcode") ||
547 !strcmp(opname, "endop") ||
548 !strcmp(opname, "$label") ||
549 !strcmp(opname, "pset") ||
550 !strcmp(opname, "xin") ||
551 !strcmp(opname, "xout") ||
552 !strcmp(opname, "subinstr"))) {
553 synterr(csound, Str("cannot redefine %s"), opname);
554 return -2;
555 }
556 csound->Message(csound,
557 Str("WARNING: redefined opcode: %s\n"), opname);
558 }
559 /* IV - Oct 31 2002 */
560 /* store the name in a linked list (note: must use csound->Calloc) */
561 inm = (OPCODINFO *) csound->Calloc(csound, sizeof(OPCODINFO));
562 inm->name = cs_strdup(csound, opname);
563 inm->intypes = intypes;
564 inm->outtypes = outtypes;
565 inm->in_arg_pool = csoundCreateVarPool(csound);
566 inm->out_arg_pool = csoundCreateVarPool(csound);
567
568 inm->prv = csound->opcodeInfo;
569 csound->opcodeInfo = inm;
570
571 if (opc != NULL) {
572 /* printf("**** Redefining case: %s %s %s\n", */
573 /* inm->name, inm->outtypes, inm->intypes); */
574 opc->useropinfo = inm;
575 newopc = opc;
576 } else {
577 //printf("****New UDO: %s %s %s\n", inm->name, inm->outtypes, inm->intypes);
578 /* IV - Oct 31 2002: */
579 /* create a fake opcode so we can call it as such */
580 opc = find_opcode(csound, "##userOpcode");
581 memcpy(&tmpEntry, opc, sizeof(OENTRY));
582 tmpEntry.opname = cs_strdup(csound, opname);
583
584 csound->AppendOpcodes(csound, &tmpEntry, 1);
585 newopc = csound_find_internal_oentry(csound, &tmpEntry);
586
587 newopc->useropinfo = (void*) inm; /* ptr to opcode parameters */
588
589 /* check in/out types and copy to the opcode's */
590 /* IV - Sep 8 2002: opcodes have an optional arg for ksmps */
591 newopc->outypes = csound->Malloc(csound, strlen(outtypes) + 1
592 + strlen(intypes) + 2);
593 newopc->intypes = &(newopc->outypes[strlen(outtypes) + 1]);
594
595 if (strcmp(outtypes, "0")==0) {
596 add_token(csound, opname, T_OPCODE0);
597 } else {
598 add_token(csound, opname, T_OPCODE);
599 }
600
601 }
602
603 //printf("****Calling parse_opcode_args\n");
604 if (UNLIKELY(parse_opcode_args(csound, newopc) != 0))
605 return -3;
606
607 return 0;
608 }
609
synterr(CSOUND * csound,const char * s,...)610 void synterr(CSOUND *csound, const char *s, ...)
611 {
612 va_list args;
613
614 csound->MessageS(csound, CSOUNDMSG_ERROR, Str("error: "));
615 va_start(args, s);
616 csound->MessageV(csound, CSOUNDMSG_ERROR, s, args);
617 va_end(args);
618
619 /* FIXME - Removed temporarily for debugging
620 * This function may not be necessary at all in the end if some of this is
621 * done in the parser
622 */
623 csound->synterrcnt++;
624 }
625