/****************************************************************************
ift, iftlen StrayNumToFt Stray [, iftno [, isep1 [, isep2]]]
Converts a string-array which just consists of numbers or simple math expressions to a function table

Puts all numbers in Stray (which must not contain non-numerical elements) in a function table and returns its variable ift (which is produced by iftno, default=0) and the length of the elements written iftlen. (An empty string as input writes a function table of size=1 to avoid an error but returns 0 as length of elements written.) Simple binary math expressions using +, -, *, /, ^ and % are allowed, with just one parenthesis in total (see the examples below). 
Elements are defined by two seperators as ASCII coded characters: isep1 defaults to 32 (= space), isep2 defaults to 9 (= tab). If just one seperator is used, isep2 equals isep1.
Requires csound 5.15 or higher, and the UDOs StrayLen, StrExpr1 and StrExpr2

Stray - a string as array
iftno - like in an ftgen statement: if 0 (which is also the default) an automatic number is generated by Csound; if any positive number, this is then the number of the function table
isep1 - the first seperator (default=32: space)
isep2 - the second seperator (default=9: tab) 
ift - the number of the function table which has been created
iftlen - the length of the elements written to the function table (usually this equals the length of the function table; just an empty string as input will create a function table of size=1 but with iftlen=0)
****************************************************************************/


<CsoundSynthesizer>
<CsOptions>
-n -m0
</CsOptions>
<CsInstruments>

  opcode StrayLen, i, Sjj
;returns the number of elements in Stray. elements are defined by two seperators as ASCII coded characters: isep1 defaults to 32 (= space), isep2 defaults to 9 (= tab). if just one seperator is used, isep2 equals isep1
Stray, isepA, isepB xin
;;DEFINE THE SEPERATORS
isep1     =         (isepA == -1 ? 32 : isepA)
isep2     =         (isepA == -1 && isepB == -1 ? 9 : (isepB == -1 ? isep1 : isepB))
Sep1      sprintf   "%c", isep1
Sep2      sprintf   "%c", isep2
;;INITIALIZE SOME PARAMETERS
ilen      strlen    Stray
icount    =         0; number of elements
iwarsep   =         1
indx      =         0
 if ilen == 0 igoto end ;don't go into the loop if String is empty
loop:
Snext     strsub    Stray, indx, indx+1; next sign
isep1p    strcmp    Snext, Sep1; returns 0 if Snext is sep1
isep2p    strcmp    Snext, Sep2; 0 if Snext is sep2
 if isep1p == 0 || isep2p == 0 then; if sep1 or sep2
iwarsep   =         1; tell the log so
 else 				; if not 
  if iwarsep == 1 then	; and has been sep1 or sep2 before
icount    =         icount + 1; increase counter
iwarsep   =         0; and tell you are ot sep1 nor sep2 
  endif 
 endif	
          loop_lt   indx, 1, ilen, loop 
end:      xout      icount
  endop 

  opcode StrExpr1, i, S
Str       xin
isum      strindex  Str, "+"; sum
idif      strindex  Str, "-"; difference
ipro      strindex  Str, "*"; product
irat      strindex  Str, "/"; ratio
ipow      strindex  Str, "^"; power
imod      strindex  Str, "%"; modulo
 if ipow > 0 then
ifirst    strindex  Str, "^"
S1        strsub    Str, 0, ifirst
S2        strsub    Str, ifirst+1
i1        strtod    S1
i2        strtod    S2
ires      =         i1 ^ i2
 elseif imod > 0 then
ifirst    strindex  Str, "%"
S1        strsub    Str, 0, ifirst
S2        strsub    Str, ifirst+1
i1        strtod    S1
i2        strtod    S2
ires      =         i1 % i2
 elseif ipro > 0 then
ifirst    strindex  Str, "*"
S1        strsub    Str, 0, ifirst
S2        strsub    Str, ifirst+1
i1        strtod    S1
i2        strtod    S2
ires      =         i1 * i2
 elseif irat > 0 then
ifirst    strindex  Str, "/"
S1        strsub    Str, 0, ifirst
S2        strsub    Str, ifirst+1
i1        strtod    S1
i2        strtod    S2
ires      =         i1 / i2
 elseif isum > 0 then 
ifirst    strindex  Str, "+"
S1        strsub    Str, 0, ifirst
S2        strsub    Str, ifirst+1
i1        strtod    S1
i2        strtod    S2
ires      =         i1 + i2
 elseif idif > -1 then
ifirst    strrindex Str, "-";(last occurrence: -3-4 is possible, but not 3--4)
S1        strsub    Str, 0, ifirst
S2        strsub    Str, ifirst+1
iS1len    strlen    S1
 if iS1len == 0 then ;just a negative number
inum      strtod    S2
ires      =         -inum
 else	 
ifirst    strtod    S1
isec      strtod    S2
ires      =         ifirst - isec
  endif
 else
ires      strtod    Str
 endif
          xout      ires
  endop 

  opcode StrExpr2, i, S
Sin       xin
ilen      strlen    Sin
;if a parenthesis can be found
iparenth  strindex  Sin, "("
if iparenth > -1 then
  ;if in first half and no '-' preceeds
  if iparenth == 0 then
    ;then first element ends in ")"
iprend    strindex   Sin, ")"
S1        strsub     Sin, 1, iprend
    ;convert this element into a number
i1        StrExpr1   S1
    ;append the rest and convert again
S2        strsub     Sin, iprend+2 
Sep       strsub     Sin, iprend+1, iprend+2
Scoll     sprintf    "%f%s%s", i1, Sep, S2
ires      StrExpr1   Scoll
  ;if in first half and '-' preceeds
  elseif iparenth == 1 then
    ;then first element ends in ")"
iprend    strindex   Sin, ")"
S1        strsub     Sin, 2, iprend
    ;convert this element into a number
i1        StrExpr1   S1
i1        =          -i1
    ;append the rest and convert again
S2        strsub     Sin, iprend+2 
Sep       strsub     Sin, iprend+1, iprend+2
Scoll     sprintf    "%f%s%s", i1, Sep, S2
ires      StrExpr1   Scoll
  ;if the parenthesis is in the second half
  else
    ;isolate first element and the conjunction
S1        strsub     Sin, 0, iparenth-1
Sep       strsub     Sin, iparenth-1, iparenth
    ;convert the second element
S2        strsub     Sin, iparenth+1, ilen-1
i2        StrExpr1   S2
      ;if subtraction and i2 negative, convert to addition
isepminus strcmp     Sep, "-"
      if i2 < 0 && isepminus == 0 then
i2        =          -i2
Sep       =          "+"
      endif
    ;convert the whole
Scoll     sprintf    "%s%s%f", S1, Sep, i2
ires      StrExpr1   Scoll
  endif
;if no parenthesis, simply convert
else	
ires      StrExpr1   Sin
endif
          xout       ires
  endop


  opcode StrayNumToFt, ii, Sojj
  ;requires the UDOs StrayLen, StrExpr1 and StrExpr2
Stray, iftno, isepA, isepB xin
;;DEFINE THE SEPERATORS
isep1     =         (isepA == -1 ? 32 : isepA)
isep2     =         (isepA == -1 && isepB == -1 ? 9 : (isepB == -1 ? isep1 : isepB))
Sep1      sprintf   "%c", isep1
Sep2      sprintf   "%c", isep2
;;CREATE A FUNCTION TABLE
iftlen    StrayLen  Stray, isep1, isep2
 if iftlen == 0 then
          prints    "WARNING! StrayNumToFt got empty string as input. Function table with length=1 created, but iftlen=0 returned.\n"
iftl      =         1
 else
iftl      =         iftlen
 endif
ift       ftgen     iftno, 0, -iftl, -2, 0 
;;INITIALIZE SOME PARAMETERS
ilen      strlen    Stray
istartsel =         -1; startindex for searched element
iel       =         -1; number of element in Stray and ift
iwarleer  =         1; is this the start of a new element
indx      =         0 ;character index
inewel    =         0 ;new element to find
;;LOOP
 if ilen == 0 igoto end ;don't go into the loop if Stray is empty
loop:
Schar     strsub    Stray, indx, indx+1; this character
isep1p    strcmp    Schar, Sep1; returns 0 if Schar is sep1
isep2p    strcmp    Schar, Sep2; 0 if Schar is sep2
is_sep    =         (isep1p == 0 || isep2p == 0 ? 1 : 0) ;1 if Schar is a seperator
 ;END OF STRING AND NO SEPARATORS BEFORE?
 if indx == ilen && iwarleer == 0 then
Sel       strsub    Stray, istartsel, -1
inewel    =         1
 ;FIRST CHARACTER OF AN ELEMENT?
 elseif is_sep == 0 && iwarleer == 1 then
istartsel =         indx ;if so, set startindex
iwarleer  =         0 ;reset info about previous separator 
iel       =         iel+1 ;increment element count
 ;FIRST SEPERATOR AFTER AN ELEMENT?
 elseif iwarleer == 0 && is_sep == 1 then
Sel       strsub    Stray, istartsel, indx ;get element
inewel    =         1 ;tell about
iwarleer  =         1 ;reset info about previous separator
 endif
 ;WRITE THE ELEMENT TO THE TABLE
 if inewel == 1 then
inum      StrExpr2  Sel ;convert expression to number
          tabw_i    inum, iel, ift ;write to ift
 endif
inewel    =         0
          loop_le   indx, 1, ilen, loop 
end:
          xout      ift, iftlen
  endop 
  
  opcode TbDmpSmpS, 0, iiSo
;prints the content of a table in a simple way, with an additional string as 'introduction'
ifn, iprec, String, ippr xin; function table, float precision while printing, String, parameters per row (maximum = 32)
ippr      =         (ippr == 0 ? 10 : ippr)
iend      =         ftlen(ifn)
indx      =         0
Sformat   sprintf   "%%.%df, ", iprec
Sdump     sprintf   "%s[", String
loop:
ival      tab_i     indx, ifn
Snew      sprintf   Sformat, ival
Sdump     strcat    Sdump, Snew
imod      =         (indx+1) % ippr
 if imod == 0 && indx != iend-1 then
          puts      Sdump, 1
Sdump     =         ""
 endif
          loop_lt   indx, 1, iend, loop
ilen      strlen    Sdump
Slast     strsub    Sdump, 0, ilen-2
		printf_i	"%s]\n", 1, Slast
  endop

instr 1
Stray     strget    p4
          printf_i  "\nStray = '%s'\n", 1, Stray
ipcnt     pcount
if ipcnt == 4 then
ift, ilen StrayNumToFt Stray
elseif ipcnt == 5 then
ift, ilen StrayNumToFt Stray, p5
elseif ipcnt == 6 then
ift, ilen StrayNumToFt Stray, p5, p6
elseif ipcnt == 7 then
ift, ilen StrayNumToFt Stray, p5, p6, p7
endif		
          prints    "ift# = %d, iftlen = %d%n", ift, ilen
          TbDmpSmpS ift, 5, "table content = "
endin 


</CsInstruments>
<CsScore>
i 1 0 .01 "1 -2 3 4 5 6 7"
i . + . "1.2  	2+3.45  6.7/-8.09 "
i . + . "  	15%12  -5-3   "
i . + . "	 2^0.6	 2^(1/12) 2^(-1/12)  	16 32"
i . + . "123*-4.56	 "
i . + . "123*(-4.56)	 "
i . + . "	 "
i . + . ""
i . + . "-(1+2.7)*-3 2^-(-1/-12) -(-3.10*-65)/-3.4567 27%5"
i . + . "1+2.7 2^(1/12) 3.10*65 27%5"
i . + . "1+2.7 2^(1/12) 3.10*65 27%6" 13 ;write table number 13
i . + . "1+2.7,2^(1/12),3.10*65,27%7" 0 44 ;just commas are seperators
i . + . "1+2.7, 2^(1/12), 3.10*65, 27%8" 0 44 ;no problem to write spaces though
i . + . "1+2.7,2^(1/12)|3.10*65,27%9" 0 44 124 ;commas and '|' are seperators
e
</CsScore>
</CsoundSynthesizer>

returns:
    
Stray = '1 -2 3 4 5 6 7'
ift# = 101, iftlen = 7
table content = [1.00000, -2.00000, 3.00000, 4.00000, 5.00000, 6.00000, 7.00000]

Stray = '1.2  	2+3.45  6.7/-8.09 '
ift# = 102, iftlen = 3
table content = [1.20000, 5.45000, -0.82818]

Stray = '  	15%12  -5-3   '
ift# = 103, iftlen = 2
table content = [3.00000, -8.00000]

Stray = '	 2^0.6	 2^(1/12) 2^(-1/12)  	16 32'
ift# = 104, iftlen = 5
table content = [1.51572, 1.05946, 0.94387, 16.00000, 32.00000]

Stray = '123*-4.56	 '
ift# = 105, iftlen = 1
table content = [-560.88000]

Stray = '123*(-4.56)	 '
ift# = 106, iftlen = 1
table content = [-560.88000]

Stray = '	 '
WARNING! StrayNumToFt got empty string as input. Function table with length=1 created, but iftlen=0 returned.
ift# = 107, iftlen = 0
table content = [0.00000]

Stray = ''
WARNING! StrayNumToFt got empty string as input. Function table with length=1 created, but iftlen=0 returned.
ift# = 108, iftlen = 0
table content = [0.00000]

Stray = '-(1+2.7)*-3 2^-(-1/-12) -(-3.10*-65)/-3.4567 27%5'
ift# = 109, iftlen = 4
table content = [11.10000, 0.94387, 58.29259, 2.00000]

Stray = '1+2.7 2^(1/12) 3.10*65 27%5'
ift# = 110, iftlen = 4
table content = [3.70000, 1.05946, 201.50000, 2.00000]

Stray = '1+2.7 2^(1/12) 3.10*65 27%6'
ift# = 13, iftlen = 4
table content = [3.70000, 1.05946, 201.50000, 3.00000]

Stray = '1+2.7,2^(1/12),3.10*65,27%7'
ift# = 111, iftlen = 4
table content = [3.70000, 1.05946, 201.50000, 6.00000]

Stray = '1+2.7, 2^(1/12), 3.10*65, 27%8'
ift# = 112, iftlen = 4
table content = [3.70000, 1.05946, 201.50000, 3.00000]

Stray = '1+2.7,2^(1/12)|3.10*65,27%9'
ift# = 113, iftlen = 4
table content = [3.70000, 1.05946, 201.50000, 0.00000]
<bsbPanel>
 <label>Widgets</label>
 <objectName/>
 <x>100</x>
 <y>100</y>
 <width>320</width>
 <height>240</height>
 <visible>true</visible>
 <uuid/>
 <bgcolor mode="nobackground">
  <r>255</r>
  <g>255</g>
  <b>255</b>
 </bgcolor>
</bsbPanel>
<bsbPresets>
</bsbPresets>
