[m2sci] kernel code reorganization: clarify dependencies & ease code browsing
[scilab.git] / scilab / modules / m2sci / macros / kernel / m2sci_syntax.sci
1 // Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
2 // Copyright (C) ???? - INRIA - Scilab
3 // Copyright (C) 2002-2004 - INRIA - Vincent COUVERT
4 // Copyright (C) 2012 - 2016 - Scilab Enterprises
5 // Copyright 2018 - Samuel GOUGEON
6 //
7 // This file is hereby licensed under the terms of the GNU GPL v2.0,
8 // pursuant to article 5.3.4 of the CeCILL v.2.1.
9 // This file was originally licensed under the terms of the CeCILL v2.1,
10 // and continues to be available under such terms.
11 // For more information, see the COPYING file which you should have received
12 // along with this program.
13
14 function [helppart,txt,batch] = m2sci_syntax(txt)
15     // Make minor changes on M-file data syntax to have it readable by Scilab
16     // Input arguments:
17     //  - txt: the contents of an M-file
18     // Output:
19     //  - helppart: Matlab help contained in M-file
20     //  - txt: input txt modified (If M-file contains only comments returned txt is[])
21     //  - batch: boolean flag indicates if it is a batch file
22
23     // m2sci kernel functions called :
24     //  - isacomment
25     //  - isinstring
26     //  - replace_brackets
27     //  - replace_end_dollar
28
29     // Pre-process block-comments:
30     // --------------------------
31     // Converts block-comments tags
32     k = grep(txt, "/^\%\{\s*$/", "r"); if k <> [] then, txt(k) = "/*", end
33     k = grep(txt, "/^\%\}\s*$/", "r"); if k <> [] then, txt(k) = "*/", end
34     // Comments the block-comments
35     // (otherwise their content is cleared by the compiler)
36     ks = find(txt=="/*")
37     ke = find(txt=="*/")
38     for k = matrix(ks,1,-1)
39         i = find(ke>k,1)
40         if i==[]
41             j = size(txt,1)
42         else
43             j = ke(i)
44             ke(i) = []
45         end
46         txt(k:j) = ["///*" ; "// " + txt(k+1:j-1) ; "//*/"]
47     end
48     //
49
50     sciparam();
51     quote = "''"
52     dquote = """"
53     ctm = "." + "." + "." // Continuation mark
54     batch = %t
55
56     k=0
57     first_ncl=[]
58     while k<size(txt,"r")
59         k=k+1
60         tk=txt(k)
61         if part(stripblanks(tk),1:9) == "function " | part(stripblanks(tk),1:9) == "function[" then
62             eolind=strindex(tk,";")
63             if eolind<>[] then
64                 kc=isacomment(tk)
65                 if kc<>0 then // Current line has or is a comment
66                     // If function prototype immediately followed by a comment on same line
67                     if stripblanks(part(tk,eolind(1):kc))<>"" then
68                         txt=[txt(1:k-1);part(tk,1:eolind(1)-1);part(tk,eolind(1)+1:length(tk));txt(k+1:size(txt,"*"))]
69                         tk=part(tk,1:eolind(1)-1)
70                     end
71                 elseif stripblanks(part(tk,eolind(1)+1:length(tk)))<>"" then
72                     txt=[txt(1:k-1);part(tk,1:eolind(1)-1);part(tk,eolind(1)+1:length(tk));txt(k+1:size(txt,"*"))]
73                     tk=part(tk,1:eolind(1)-1)
74                 end
75             end
76         end
77     end
78
79     // Number of lines in txt (i.e. in M-file)
80     n=size(txt,"r")
81     eoltoinsert=0
82     firstctm=[]
83     k=0
84     while k<size(txt,"r")
85         k=k+1
86         kc=strindex(txt(k),ctm)
87         isacontline=%f
88
89         for kck=kc,
90             kc1=isacomment(txt(k))
91             if ~isinstring(txt(k),kck) then
92                 if kc1<>0 then // Line has a comment
93                     if kc1<kck then // Continuation mark in a comment
94                     else // Comment follow continuation mark
95                         com=part(txt(k),kc1(1):length(txt(k)))
96                         txt(k)=part(txt(k),1:kck-1)+txt(k+1)+com
97                         txt(k+1)=[]
98                         k=k-1
99                         break
100                     end
101                 else // Not a comment line
102                     if isempty(firstctm) then firstctm=k;end
103                     txt(k)=part(txt(k),1:kck-1)+txt(k+1)
104                     txt(k+1)=[]
105                     eoltoinsert=eoltoinsert+1
106                     if isempty(strindex(txt(k),ctm)) then // If no ctm in txt(k), insert all necessary EOL to keep nblines unchanged
107                         for l=0:eoltoinsert-1
108                             txt=[txt(1:firstctm-1+l);"";txt(firstctm-1+l+1:$)]
109                         end
110                         eoltoinsert=0
111                         firstctm=[]
112                     end
113                     k=k-1
114                     break
115                 end
116             end
117         end
118         if k<>0 then
119             if part(stripblanks(txt(k)),1)=="}" then
120                 txt(k-1)=txt(k-1)+txt(k);
121                 txt(k)="";
122             end
123         end
124     end
125
126     // Change comments and get help part in input txt
127     n=size(txt,"r")
128     first=%t
129     helppart=[],endofhelp=%f
130
131     for k=1:n
132         tk=txt(k)
133
134         // ifthenelse expression like if (x==1)t=2 becomes if (x==1) t=2
135         // Add a blank between parenthesize expression and the first instruction
136         kif=strindex(tk,"if")
137         if kif<>[] then
138             kcom=isacomment(tk)
139             for i=1:size(kif,"*")
140                 if kif(i)>kcom & kcom<>0 then
141                     break
142                 else
143                     if (kif(i)>1 & or(stripblanks(part(tk,kif(i)-1:kif(i)+2))==["if(","if"])) | (kif(i)==1 & or(stripblanks(part(tk,kif(i):kif(i)+2))==["if(","if"]))
144                         m=min(strindex(tk,"("))
145                         if m<>[] then
146                             for l=1:size(m,"*")
147                                 if m(l)>=kif(i)+2
148                                     if stripblanks(part(tk,kif(i)+2:m(l)))=="(" then
149                                         openpar=1
150                                         mtemp=m(l)+1
151                                         while openpar<>0
152                                             if or(part(tk,mtemp)=="(") then
153                                                 openpar=openpar+1
154                                             elseif or(part(tk,mtemp)==")") then
155                                                 openpar=openpar-1
156                                             end
157                                             mtemp=mtemp+1
158                                         end
159                                         tk=part(tk,1:mtemp-1)+" "+part(tk,mtemp:length(tk))
160                                         break
161                                     end
162                                 end
163                             end
164                         end
165                     end
166                 end
167             end
168         end
169
170         // Modify struct like x.(fieldname) which become x(fieldname)
171         tk=strsubst(tk,".(","(")
172
173         // Parenthesize calls to pause when pause on or pause off
174         kpause=strindex(tk,"pause")
175         kpsav=length(tk) // kpsave is kp value for l-1 index
176         for l=size(kpause,"*"):-1:1
177             kp=kpause(l)
178             kon=strindex(tk,"on")
179             kon=kon(find((kon>kp)&(kon<kpsav)))
180             if kon<>[] then
181                 for l=kp+5:kon-1
182                     if part(tk,l)<>" " then
183                         break
184                     end
185                 end
186                 tk=part(tk,1:kp+4)+"(''on'')"+part(tk,kon+2:length(tk))
187             end
188             koff=strindex(tk,"off")
189             koff=koff(find((koff>kp)&(koff<kpsav)))
190             if koff<>[] then
191                 for l=kp+5:koff-1
192                     if part(tk,l)<>" " then
193                         break
194                     end
195                 end
196                 tk=part(tk,1:kp+4)+"(''off'')"+part(tk,koff+3:length(tk))
197             end
198             kpsav=kp
199         end
200
201         // Convert @fhandle to 'fhandle' (cf feval)
202         symbs=[" ",",",";","=",")","]"]
203         kpunct=strindex(tk,"@")
204         kcom=isacomment(tk)
205         if kcom<>0 then
206             kpunct=kpunct(kpunct<kcom)
207         end
208         if kpunct<>[] then
209             for l=size(kpunct,"*"):-1:1
210                 if ~isinstring(tk,kpunct(l)) then
211                     kk=gsort(strindex(tk,symbs),"r","i")
212                     kk=kk(find(kk>kpunct(l)))
213                     if kk==[] then
214                         kk=length(tk)
215                         tk=part(tk,1:kpunct(l)-1)+quote+part(tk,kpunct(l)+1:kk)+quote
216                     else
217                         kk=kk(1)
218                         tk=part(tk,1:kpunct(l)-1)+quote+part(tk,kpunct(l)+1:kk-1)+quote+part(tk,kk:length(tk))
219                     end
220                 end
221             end
222         end
223
224         // Looking for comments
225         kc=isacomment(tk)
226         if kc<>0 then // Current line has or is a comment
227             // If function prototype immediately followed by a comment on same line
228             protoline = or(part(stripblanks(tk),1:9) == ["function " "function["]);
229             if protoline then
230                 first_ncl=k
231             end
232             com = part(tk,kc+1:length(tk))
233             if stripblanks(part(tk,1:kc-1))<>"" & ~protoline
234                 endofhelp = %t
235             end
236             if ~endofhelp & part(tk,1:9) ~= "function " then
237                 helppart = [helppart;com];
238             end // Get help part placed at the beginning of the file
239             if length(com)==0 then com=" ",end
240             com = strsubst(com, quote, quote+quote)
241             com = strsubst(com, dquote, dquote+dquote)
242             if part(com,1:12)=="m2sciassume " | part(com,1:13)=="m2scideclare " then
243                 // User has given a clue to help translation
244                 if part(com,1:12)=="m2sciassume " then
245                     warning(gettext("m2sciassume is obsolete, used m2scideclare instead."));
246                 end
247                 com = "m2scideclare("+quote+part(com,13:$)+quote+")"
248                 if kc>1
249                     com = ";" + com
250                 end
251             else
252                 if protoline
253                     com = ";//" + com
254                 else
255                     com = " //" + com
256                 end
257             end
258             tkbeg=part(tk,1:kc-1)
259
260             // Short circuiting operators
261             if ~isempty(strindex(tkbeg,"||")) then
262                 orexpr=tokens(tkbeg,"|")
263                 boolendsymbol=%f
264                 orexprtemp=orexpr($)
265                 indendsymbol=strindex(orexpr($),[";",","])
266                 if indendsymbol<>[] then
267                     if stripblanks(part(orexpr($),indendsymbol($)+1:length(orexpr($))))=="" then
268                         boolendsymbol=%t
269                         endsymbol=part(orexprtemp,indendsymbol($))
270                         indendsymbol=indendsymbol($)
271                         orexpr($)=part(orexpr($),1:indendsymbol($)-1)
272                     end
273                 end
274                 for i=2:size(orexpr,"*")
275                     notsymbol=strindex(stripblanks(orexpr(i)),"~")
276                     if notsymbol<>[]
277                         if notsymbol(1)==1 then
278                             orexpr(i)="("+  stripblanks(orexpr(i)) + ")"
279                         end
280                     end
281                 end
282                 for kk=2:2:size(orexpr,"*")
283                     orexpr=[orexpr(1:kk);"%shortcircuit";orexpr(kk+1:size(orexpr,"*"))]
284                 end
285                 tkbeg=strcat(orexpr,"|")
286                 if boolendsymbol then
287                     tkbeg=tkbeg+endsymbol
288                 end
289             end
290             if ~isempty(strindex(tkbeg,"&&")) then
291                 andexpr=tokens(tkbeg,"&")
292                 boolendsymbol=%f
293                 andexprtemp=andexpr($)
294                 indendsymbol=strindex(andexpr($),[";",","])
295                 if indendsymbol<>[] then
296                     if stripblanks(part(andexpr($),indendsymbol($)+1:length(andexpr($))))=="" then
297                         boolendsymbol=%t
298                         endsymbol=part(andexprtemp,indendsymbol($))
299                         indendsymbol=indendsymbol($)
300                         andexpr($)=part(andexpr($),1:indendsymbol($)-1)
301                     end
302                 end
303                 for i=2:size(andexpr,"*")
304                     notsymbol=strindex(stripblanks(andexpr(i)),"~")
305                     if notsymbol<>[]
306                         if notsymbol(1)==1 then
307                             andexpr(i)="("+  stripblanks(andexpr(i)) + ")"
308                         end
309                     end
310                 end
311                 for kk=2:2:size(andexpr,"*")
312                     andexpr=[andexpr(1:kk);"%shortcircuit";andexpr(kk+1:size(andexpr,"*"))]
313                 end
314                 tkbeg=strcat(andexpr,"&")
315                 if boolendsymbol then
316                     tkbeg=tkbeg+endsymbol
317                 end
318             end
319
320             // varargout replaced by %varargout so that code can be compiled with varargout considered as a Cell
321             if isempty(strindex(tkbeg,"function")) then
322                 tkbeg=strsubst(tkbeg,"varargout","%varargout")
323             end
324
325             txt(k)=tkbeg+com
326         else // Current line has not and is not a comment line
327             if first then // Function keyword not yet found
328                 tk=stripblanks(tk)
329                 if tk<>"" then // Current line is not a blank line
330                     if ~(part(tk,1:9) == "function "| part(tk,1:9) == "function[") then
331                         endofhelp=%t;
332                         txt(k)=tk; // VC 01/04/2003
333                     else
334                         first_ncl=k
335                         first=%f
336                     end
337                 else
338                     if ~endofhelp then helppart=[helppart;" "],end
339                     txt(k)="// "
340                 end
341             else // Current line is a line after function keyword
342                 endofhelp=%t
343                 txt(k)=tk
344             end
345
346             // Short circuiting operators
347             if ~isempty(strindex(tk,"||")) then
348                 orexpr=tokens(tk,"|")
349                 indendsymbol=strindex(orexpr($),[";",","])
350                 boolendsymbol=%f
351                 if indendsymbol<>[] then
352                     if stripblanks(part(orexpr($),indendsymbol($)+1:length(orexpr($))))=="" then
353                         boolendsymbol=%t
354                         indendsymbol=indendsymbol($)
355                         endsymbol=part(orexpr($),indendsymbol($))
356                         orexpr($)=part(orexpr($),1:indendsymbol($)-1)
357                     end
358                 end
359                 for i=2:size(orexpr,"*")
360                     notsymbol=strindex(stripblanks(orexpr(i)),"~")
361                     if notsymbol<>[]
362                         if notsymbol(1)==1 then
363                             orexpr(i)="("+  stripblanks(orexpr(i)) + ")"
364                         end
365                     end
366                 end
367                 for kk=2:2:size(orexpr,"*")
368                     orexpr=[orexpr(1:kk);"%shortcircuit";orexpr(kk+1:size(orexpr,"*"))]
369                 end
370                 tk=strcat(orexpr,"|")
371                 if boolendsymbol then
372                     tk=tk+endsymbol
373                 end
374             end
375             if ~isempty(strindex(tk,"&&")) then
376                 andexpr=tokens(tk,"&")
377                 boolendsymbol=%f
378                 andexprtemp=andexpr($)
379                 indendsymbol=strindex(andexpr($),[";",","])
380                 if indendsymbol<>[] then
381                     if stripblanks(part(andexpr($),indendsymbol($)+1:length(andexpr($))))=="" then
382                         boolendsymbol=%t
383                         endsymbol=part(andexprtemp,indendsymbol($))
384                         indendsymbol=indendsymbol($)
385                         andexpr($)=part(andexpr($),1:indendsymbol($)-1)
386                     end
387                 end
388                 for i=2:size(andexpr,"*")
389                     notsymbol=strindex(stripblanks(andexpr(i)),"~")
390                     if notsymbol<>[]
391                         if notsymbol(1)==1 then
392                             andexpr(i)="("+  stripblanks(andexpr(i)) + ")"
393                         end
394                     end
395                 end
396                 for kk=2:2:size(andexpr,"*")
397                     andexpr=[andexpr(1:kk);"%shortcircuit";andexpr(kk+1:size(andexpr,"*"))]
398                 end
399                 tk=strcat(andexpr,"&")
400                 if boolendsymbol then
401                     tk=tk+endsymbol
402                 end
403             end
404
405             // varargout replaced by %varargout so that code can be compiled with varargout considered as a Cell
406             if isempty(strindex(tk,"function")) then
407                 tk=strsubst(tk,"varargout","%varargout")
408             end
409
410             txt(k)=tk
411         end
412     end
413
414     // When there is just help line in txt
415     if ~endofhelp then
416         txt=[]
417         return
418     end
419
420     // ===================
421     // Syntax modification
422     // ===================
423
424     // Complex variable
425     txt=i_notation(txt)
426
427     // Replace double quotes
428     txt=strsubst(txt,dquote,dquote+dquote)
429
430     // Replace switch by select
431     txt=strsubst(txt,"switch ","select ")
432     txt=strsubst(txt,"switch(","select (")
433
434     // Replace otherwise by else
435     txt=strsubst(txt,"otherwise","else")
436
437     // Replace {..} by (..) or [..] : useful for cells translation
438     txt=replace_brackets(txt)
439
440     // Replace end with $ where it is relevant
441     txt = replace_end_dollar(txt)
442
443     // Place function definition line at first line
444     kc=strindex(txt(first_ncl),"function")
445     if kc==[] then
446         // Batch file
447         txt=["function []="+fnam+"()";txt] // fnam is defined in mfile2sci()
448         batch=%t
449     else
450         kc=kc(1)
451         batch=%f
452         if first_ncl<>1 then
453             while strindex(txt(first_ncl($)+1),ctm)<>[] then
454                 first_ncl=[first_ncl,first_ncl($)+1]
455             end
456             txt=[txt(first_ncl);txt(1:first_ncl(1)-1);txt(first_ncl($)+1:$)]
457         end
458         // Beginning of BUG 2341 fix: function prototype with no comma between output parameters names
459         begb=strindex(txt(1),"[");
460         endb=strindex(txt(1),"]");
461         if ~isempty(begb) & ~isempty(endb)
462             outputparams = stripblanks(part(txt(1),(begb+1):(endb-1)))+"   ";
463             k=0;
464             while k<length(outputparams)
465                 k=k+1;
466                 while (and(part(outputparams,k)<>[","," "])) & (k<length(outputparams)) // skip identifier
467                     k=k+1;
468                 end
469                 while (part(outputparams,k)==" ") & (k<length(outputparams)) // skip spaces before comma (or next identifier)
470                     k=k+1;
471                 end
472                 if (part(outputparams,k)<>",") & (k<length(outputparams))
473                     outputparams=part(outputparams,1:(k-1))+","+part(outputparams,k:length(outputparams));
474                     k=k+1;
475                 else
476                     k=k+1;
477                     while (part(outputparams,k)==" ") & (k<length(outputparams)) // skip spaces after comma
478                         k=k+1;
479                     end
480                 end
481             end
482             txt(1)=stripblanks(part(txt(1),1:begb)+outputparams+part(txt(1),endb:length(txt(1))));
483         end
484         // END of BUG 2341 fix: function prototype with no comma between output parameters names
485     end
486 endfunction
487
488 // ---------------------------------------------------------------------------
489
490 function txt=replace_brackets(txt)
491
492     symbs=[",",";","=",")","]","("]
493     // This file will be use to deal with cells...
494     for k=1:size(txt,"r")
495
496         // select-case
497         if strindex(txt(k),"case")<>[] then
498             txt(k)=strsubst(strsubst(txt(k),"{","makecell("),"}",")")
499         else
500             tk=strsubst(txt(k)," ","")
501
502             ko=strindex(tk,"{")
503             if ko<>[] then
504                 teq=strindex(tk,"=")
505
506                 ///
507                 if ko(1)==1 then
508                     txt(k)=strsubst(txt(k),"{}","makecell()")
509                     txt(k)=strsubst(strsubst(txt(k),"{","(makecell([cell(),"),"}","]))")
510                 elseif or(part(tk,ko(1)-1)==symbs) then
511                     txt(k)=strsubst(txt(k),"{}","makecell()")
512                     txt(k)=strsubst(strsubst(txt(k),"{","(makecell([cell(),"),"}","]))")
513                 else // Cell index
514                     txt(k)=strsubst(strsubst(txt(k),"{","("),"}",").entries")
515                 end
516
517                 ////
518
519                 for kk=2:size(ko,"*")
520                     if or(part(tk,ko(kk)-1)==symbs) then // Cell creation
521                         txt(k)=strsubst(txt(k),"{}","makecell()")
522                         txt(k)=strsubst(strsubst(txt(k),"{","(makecell([cell(),"),"}","]))")
523                     else // Cell index
524                         txt(k)=strsubst(strsubst(txt(k),"{","("),"}",").entries")
525                     end
526                 end
527             elseif ~isempty(strindex(txt(k),"}")) then
528                 txt(k)=strsubst(txt(k),"}","]))")
529             end
530         end
531     end
532 endfunction
533
534 // ---------------------------------------------------------------------------
535
536 function txt = replace_end_dollar(txt)
537
538     patterns = ["/(?:\(|\-|\+|\*|\:|\,)\s*end\s*(\)|(\-|\+|\*|\/|\:|\,).*?\))/"
539                 "/(?:\{|\-|\+|\*|\:|\,)\s*end\s*(\}|(\-|\+|\*|\/|\:|\,).*?\})/"
540                ]'
541     for pattern = patterns
542         rows = grep(txt, pattern, "r");
543         for i = rows
544             t = txt(i);
545             [d, f, M] = regexp(t, pattern);
546             Mr = strsubst(M, "end", "$");
547             for j = 1:size(M,1)
548                 t = strsubst(t, M(j), Mr(j));
549             end
550             txt(i) = t;
551         end
552     end
553 endfunction
554
555 // ---------------------------------------------------------------------------
556
557 function txt = i_notation(txt)
558     // This function changes 1i ,... by 1*i,...
559
560     // M2SCI kernel functions called :
561     //  - isinstring
562
563     // To succeed in this work, we successively suppress occurences which can be proved not to be complex notation
564     // Until we are 'sure' to have a complex notation
565
566     n=size(txt,"r")
567
568     I="i";J="j"
569     matches=[string(0:9)+I(ones(1,10)),".i",string(0:9)+J(ones(1,10)),".j"]
570     symbs=["+","-","*","/","\","(","["," ","^"," ",",",";","=","{"]
571     s1=["+","-","*","/","\",",",";"," ","^",".","&","|","''","]",")","}"]
572     s2=[string(0:9),"d","e","D","E","."]
573
574     for k=1:n
575         // Isolate a possible appended comment
576         st=strindex(txt(k),[";//","//"])
577         endComment = "";
578         tk = txt(k) + " "
579         if st<> [] then
580             for stk=1:size(st,"*")
581                 if ~isinstring(txt(k),st(stk)) then
582                     endComment = part(txt(k), st(stk):$);
583                     tk = part(txt(k), 1:st(stk)-1)
584                     break
585                 end
586             end
587         end
588
589         // Find possible occurence of complex notation
590         kc=strindex(tk,matches)
591
592         // Kill indexes which point to non complex values (e.g. : a1item...)
593         for kk=size(kc,"*"):-1:1
594             km=kc(kk)+2
595             if find(part(tk,km)==s1)==[] then kc(kk)=[],end
596         end
597
598         kc=[0 kc]
599
600         for kk=size(kc,"*"):-1:2
601             km=kc(kk)
602             num=%T
603             // Reads numeric value leading complex variable
604             while or(part(tk,km)==s2)
605                 km=km-1
606                 if km<=kc(kk-1)+1 then
607                     km=kc(kk-1);
608                     num=%F;
609                     break
610                 end
611             end
612
613             tokill=%F
614             num=part(tk,km+1:kc(kk)-1)
615             ke=strindex(convstr(num),["e","d"])
616             kd=strindex(convstr(num),".")
617
618             // Searching for invalid numeric values (more than one dot...)
619             if size(ke,2)>1|size(kd,2)>1 then
620                 tokill=%T
621             elseif size(ke,2)==1&size(kd,2)==1 then
622                 if ke<kd then tokill=%T,end
623             end
624
625             if ~tokill then
626                 // If char which follows supposed complex notation is not an operation symbol
627                 if km<>kc(kk-1) then
628                     if and(part(tk,km)<>symbs) then tokill=%T,end
629                 end
630             end
631
632             if ~tokill then
633                 km=kc(kk)
634                 // If supposed complex notation is not in a string
635                 if ~isinstring(tk,km) then
636                     tk=part(tk,1:km)+"*%"+part(tk,km+1:length(tk))
637                 end
638             end
639         end
640         txt(k) = tk + endComment
641     end
642 endfunction