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