00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082 ::CLASS rexxprogram PUBLIC
00083
00084 ::ATTRIBUTE src
00085 ::ATTRIBUTE cleansrc
00086
00087
00088 ::ATTRIBUTE comments
00089 ::ATTRIBUTE blockcomments
00090 ::ATTRIBUTE linecomments
00091 ::ATTRIBUTE quotes
00092 ::ATTRIBUTE clauses
00093 ::ATTRIBUTE labels
00094 ::ATTRIBUTE procedures
00095 ::ATTRIBUTE requires
00096 ::ATTRIBUTE classes
00097 ::ATTRIBUTE methods
00098 ::ATTRIBUTE routines
00099 ::ATTRIBUTE roptions
00100 ::ATTRIBUTE rconstants
00101 ::ATTRIBUTE features
00102 ::ATTRIBUTE codefeatures
00103 ::ATTRIBUTE mixedclauses
00104
00105 ::ATTRIBUTE procedural
00106 ::ATTRIBUTE hashbang
00107
00108 ::ATTRIBUTE quotemap
00109 ::ATTRIBUTE clausemap
00110 ::ATTRIBUTE commentmap
00111 ::ATTRIBUTE codefeaturemap
00112
00113 ::ATTRIBUTE user
00114
00115
00116
00117 ::METHOD init
00118 use arg programname
00119 s = .stream~new(programname)
00120 if s~query('EXISTS') = ''
00121 then do
00122 s~close
00123 raise SYNTAX 93 ARRAY(programname 'does not exist')
00124 end
00125
00126 self~src = s~arrayin
00127 s~close
00128
00129
00130 self~cleansrc = .queue~new
00131 self~comments = .queue~new
00132 self~quotes = .queue~new
00133
00134 self~blockcomments = .queue~new
00135 self~linecomments = .queue~new
00136 self~clauses = .queue~new
00137
00138 self~features = .queue~new
00139 self~codefeatures = .queue~new
00140 self~mixedclauses = .queue~new
00141
00142 self~labels = .queue~new
00143 self~procedures = .queue~new
00144 self~requires = .queue~new
00145 self~classes = .queue~new
00146 self~methods = .queue~new
00147 self~routines = .queue~new
00148 self~roptions = .queue~new
00149 self~rconstants = .queue~new
00150
00151 self~codefeaturemap = .relation~new
00152
00153
00154 self~procedural = .true
00155 self~hashbang = ''
00156
00157
00158 self~parsesource
00159
00160
00161 self~sanity(self~comments)
00162 self~sanity(self~quotes)
00163
00164
00165 self~mapquotes
00166
00167
00168 self~delimitclauses
00169
00170
00171 if self~clauses~items = 0
00172 then return
00173
00174
00175
00176 self~getcodefeatures
00177
00178
00179
00180 self~sortcomments
00181
00182
00183
00184 self~mergefeatures
00185
00186
00187 do i over self~classes
00188 meths = self~getcontained(i)
00189 do m over meths
00190 if m~objectname = 'METH' | m~objectname = 'ATTR' | m~objectname = 'CONS'
00191 then m~ownerclass = i
00192 end
00193 end
00194 return
00195
00196
00197
00198
00199
00200 ::METHOD sanity PRIVATE
00201 use arg chunks
00202 do i over chunks
00203 if i~startline = 0
00204 then i~startline = 1
00205 if i~startcol = 0
00206 then i~startcol = 1
00207 if i~endline = 0
00208 then i~endline = self~src~items
00209 if i~endcol = 0
00210 then i~endcol = self~src[self~src~items]~length~max(1)
00211 end
00212
00213
00214
00215 ::METHOD parsesource PRIVATE
00216 expose inblock inquote inline quotechar qblock
00217
00218 inblock = .false
00219 blockentry = .false
00220 blockexit = .false
00221 blockdepth = 0
00222
00223 inline = .false
00224 lineentry = .false
00225
00226 inquote = .false
00227 quotechar = ''
00228
00229 lineno = 0
00230 colno = 0
00231 self~commentmap = .relation~new
00232
00233 do line over self~src
00234
00235 lineno = lineno + 1
00236
00237
00238
00239 colno = 1
00240
00241 inline = .false
00242
00243
00244 parse var line 1 c 2 line
00245 if inblock
00246 then cleanline = ' '
00247 else do
00248 cleanline = c
00249 self~quotestate(c, lineno, colno)
00250 end
00251
00252
00253 chars = line~length
00254 do chars
00255 prev = c
00256 parse var line 1 c 2 line
00257 colno = colno + 1
00258
00259
00260
00261
00262 self~quotestate(c, lineno, colno)
00263
00264
00265 select
00266
00267 when \inline & \inquote & inblock & prev||c = '*/'
00268 then do
00269 blockdepth = blockdepth - 1
00270 if blockdepth = 0
00271 then do
00272 blockexit = .true
00273 bcomment~endline = lineno
00274 bcomment~endcol = colno
00275 end
00276 end
00277
00278 when \inline & \inquote & prev||c = '/*'
00279
00280 then do
00281 blockdepth = blockdepth + 1
00282 if blockdepth = 1
00283 then blockentry = .true
00284 end
00285
00286 when \inline & \inquote & \inblock & prev||c = '--'
00287 then do
00288 lineentry = .true
00289 end
00290
00291 otherwise nop
00292 end
00293
00294
00295 select
00296 when blockentry
00297 then do
00298
00299 cleanline = cleanline~left(cleanline~length-1)||' '
00300 blockentry = .false
00301 inblock = .true
00302 bcomment = .comment~new(lineno, colno-1, 0, 0)
00303 end
00304 when blockexit
00305 then do
00306 blockexit = .false
00307 inblock = .false
00308 bcomment~endline = lineno
00309 bcomment~endcol = colno
00310 self~comments~queue(bcomment)
00311 self~commentmap[bcomment~startline] = bcomment
00312 c = ' '
00313 end
00314 when lineentry
00315 then do
00316
00317 cleanline = cleanline~left(cleanline~length-1)||' '
00318 lineentry = .false
00319 inline = .true
00320 lcomment = .comment~new(lineno, colno-1, lineno, chars+1)
00321 self~comments~queue(lcomment)
00322 self~commentmap[lcomment~startline] = lcomment
00323 end
00324 otherwise nop
00325 end
00326
00327 if inblock | inline
00328 then cleanline = cleanline||' '
00329 else cleanline = cleanline||c
00330
00331 end
00332
00333 self~cleansrc~queue(cleanline)
00334
00335 end
00336
00337
00338 ::METHOD quotestate PRIVATE
00339 expose inblock inquote inline quotechar qblock
00340 use arg c, lineno, colno
00341
00342 select
00343 when \inblock & inquote & \inline & c = quotechar
00344 then do
00345 inquote = .false
00346 quotechar = ''
00347 qblock~endline = lineno
00348 qblock~endcol = colno
00349 end
00350 when \inblock & \inquote & \inline & c = '"'
00351 then do
00352 inquote = .true
00353 quotechar = '"'
00354 qblock = .quoted~new(lineno, colno, 0, 0)
00355 self~quotes~queue(qblock)
00356 end
00357 when \inblock & \inquote & \inline & c = "'"
00358 then do
00359 inquote = .true
00360 quotechar = "'"
00361 qblock = .quoted~new(lineno, colno, 0, 0)
00362 self~quotes~queue(qblock)
00363 end
00364
00365 otherwise nop
00366 end
00367 return
00368
00369
00370
00371 ::METHOD mapquotes PRIVATE
00372 self~quotemap = .relation~new
00373 do i over self~quotes
00374 sl = i~startline
00375 el = i~endline
00376 if el > sl
00377 then do j = sl to el
00378 self~quotemap~put(i, j)
00379 end
00380 else self~quotemap~put(i, sl)
00381 end
00382
00383
00384
00385
00386
00387 ::METHOD delimitclauses PRIVATE
00388
00389 self~clausemap = .relation~new
00390 self~clauses = .queue~new
00391
00392 lines = self~cleansrc~items
00393 continue = .false
00394
00395
00396
00397
00398
00399
00400 roughclauses = .queue~new
00401
00402 do lineno = 1 to lines
00403 line = self~cleansrc[lineno]
00404 if line~words = 0
00405 then iterate
00406
00407 if lineno = 1 & line~word(1)~left(2) = '#!'
00408 then do
00409 self~hashbang = line
00410 iterate
00411 end
00412
00413 if \continue
00414 then do
00415 pfword = line~wordindex(1)
00416 cl = .clause~new(lineno, pfword, 0, 0)
00417 roughclauses~queue(cl)
00418 end
00419
00420
00421 tpos = line~pos('~', 1)
00422 do while tpos > 0
00423 if \self~isinquote(lineno, tpos)
00424 then do
00425 self~procedural = .false
00426 leave
00427 end
00428 tpos = line~pos('~', tpos+1)
00429 end
00430
00431
00432 spos = line~pos(';',1)
00433 do while spos > 0
00434 if \self~isinquote(lineno, spos)
00435 then do
00436 cl~endline = lineno
00437 cl~endcol = spos-1
00438 if spos < line~length
00439 then do
00440 cl = .clause~new(lineno, spos+1, 0, 0)
00441 roughclauses~queue(cl)
00442 end
00443 end
00444 spos = line~pos(';', spos+1)
00445 end
00446
00447 if line~word(line~words)~right(1) = ','
00448 then do
00449 continue = .true
00450 end
00451 else do
00452
00453 continue = .false
00454 cl~endline = lineno
00455 plastword = line~wordindex(line~words)
00456 cl~endcol = plastword + line~word(line~words)~length - 1
00457 end
00458 end
00459
00460
00461
00462 do c over roughclauses
00463 sour = self~unsugar(c)
00464 do s over sour
00465 self~clauses~queue(s)
00466 self~clausemap~put(s, s~startline)
00467 end
00468 end
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482 ::METHOD unsugar PRIVATE
00483 use arg c
00484 ret = .queue~new
00485
00486 src = self~getnoppedsource(c)
00487
00488
00489 parse upper value src[1] with w1 .
00490 if w1 <> 'IF',
00491 & w1 <> 'WHEN',
00492 & w1 <> 'ELSE',
00493 & w1 <> 'OTHERWISE',
00494 & w1 <> 'THEN'
00495 then do
00496
00497 ret~queue(c)
00498 return ret
00499 end
00500
00501
00502 if src~items = 1 & src[1]~words < 2
00503 then do
00504
00505 ret~queue(c)
00506 return ret
00507 end
00508
00509
00510 fc = .flatclause~new(c, src)
00511 totwords = fc~flat~words
00512
00513 upcase = fc~flat~translate
00514
00515
00516 tword = upcase~wordpos('THEN')
00517 dword = upcase~wordpos('DO')
00518 oword = upcase~wordpos('OTHERWISE')
00519
00520
00521 if tword = 0 & dword = 0 & oword = 0
00522 then do
00523
00524 ret~queue(c)
00525 return ret
00526 end
00527
00528
00529
00530
00531
00532 if tword > 0
00533 then tpos = upcase~wordindex(tword)
00534 else tpos = 0
00535
00536 if dword > 0
00537 then dpos = upcase~wordindex(dword)
00538 else dpos = 0
00539
00540 if oword > 0
00541 then opos = upcase~wordindex(oword)
00542 else opos = 0
00543
00544
00545
00546
00547
00548
00549 select
00550 when tpos > 0 & dpos > 0 & totwords = 2
00551 then do
00552
00553 ret = self~splitclause(c, fc, dpos)
00554 end
00555 when opos > 0 & dpos > 0 & totwords = 2
00556 then do
00557
00558 ret = self~splitclause(c, fc, dpos)
00559 end
00560 when tpos > 0 & dpos > 0
00561 then do
00562
00563 ret = self~splitclause(c, fc, tpos dpos)
00564 end
00565 when opos > 0 & dpos > 0
00566 then do
00567
00568 ret = self~splitclause(c, fc, opos dpos)
00569 end
00570 when dpos > 0
00571 then do
00572
00573 ret = self~splitclause(c, fc, dpos)
00574 end
00575 when tpos > 0
00576 then do
00577
00578 select
00579 when tword = totwords
00580 then do
00581
00582 ret = self~splitclause(c, fc, tpos)
00583 end
00584 when tword = 1
00585 then do
00586
00587 ret = self~splitclause(c, fc, tpos+'THEN'~length+1)
00588 end
00589 otherwise do
00590
00591 ret = self~splitclause(c, fc, tpos tpos+'THEN'~length+1)
00592 end
00593 end
00594 end
00595 when opos > 0
00596 then do
00597
00598 select
00599 when oword = totwords
00600 then do
00601
00602 ret = self~splitclause(c, fc, opos)
00603 end
00604 when oword = 1
00605 then do
00606
00607 ret = self~splitclause(c, fc, opos+'OTHERWISE'~length+1)
00608 end
00609 otherwise do
00610
00611 ret = self~splitclause(c, fc, opos tpos+'OTHERWISE'~length+1)
00612 end
00613 end
00614 end
00615 otherwise do
00616 ret~queue(c)
00617 end
00618
00619 end
00620 return ret
00621
00622
00623 ::METHOD splitclause PRIVATE
00624 use arg c, fc, splitpoints
00625 ret = .queue~new
00626
00627
00628 do j = 1 to splitpoints~words + 1
00629 ret~queue(c~clone)
00630 end
00631
00632 do j = 1 to splitpoints~words
00633 split = splitpoints~word(j)
00634 k = j + 1
00635 parse value fc~index2vector(split) with endline endcol
00636 ret[j]~endline = endline
00637 ret[j]~endcol = endcol - 1
00638 ret[k]~startline = endline
00639 ret[k]~startcol = endcol
00640 end
00641
00642 return ret
00643
00644
00645
00646 ::METHOD getcodefeatures PRIVATE
00647
00648 clause = self~clauses[1]
00649 uclause = self~getwholeclause(clause)~translate
00650 parse var uclause w1 w2 .
00651 if w1~translate \= '::REQUIRES',
00652 & w1~translate \= '::CLASS',
00653 & w1~translate \= '::ROUTINE',
00654 & w1~translate \= '::METHOD'
00655 then do
00656
00657
00658 if w2~translate <> 'PROCEDURE'
00659 then do
00660
00661 p = .proc~new('--entry_proc',clause~startline, clause~startcol, 0, 0, 1, self)
00662 self~procedures~queue(p)
00663 self~codefeatures~queue(p)
00664 self~mixedclauses~queue(p)
00665 end
00666 end
00667
00668 do clauseix = 1 to self~clauses~items
00669 clause = self~clauses[clauseix]
00670
00671 mclause = self~getwholeclause(clause)
00672 uclause = mclause~translate
00673 parse var uclause w1 w2 .
00674 select
00675 when w1~right(1) = ':' | w1~right(2) = ':;'
00676 then do
00677 parse var mclause labname ':'
00678
00679 if w2 = 'PROCEDURE'
00680 then do
00681 p = .proc~new(labname, clause~startline, clause~startcol, 0, 0, clauseix, self)
00682 self~procedures~queue(p)
00683 self~codefeatures~queue(p)
00684 self~mixedclauses~queue(p)
00685 end
00686 else do
00687 l = .label~new(labname, clause~startline, clause~startcol, clause~endline, clause~endcol, clauseix, self)
00688 self~labels~queue(l)
00689 self~codefeatures~queue(l)
00690 self~mixedclauses~queue(l)
00691 end
00692 end
00693 when w1 = '::REQUIRES'
00694 then do
00695 parse var mclause . req
00696 r = .requires~new(req~strip, clause~startline, clause~startcol, 0, 0, clauseix, self)
00697 self~requires~queue(r)
00698 self~codefeatures~queue(r)
00699 self~mixedclauses~queue(r)
00700 self~procedural = .false
00701 end
00702 when w1 = '::CLASS'
00703 then do
00704 parse var mclause . classname .
00705 if classname~words = 0
00706 then iterate
00707 c = .rclass~new(classname, clause~startline, clause~startcol, 0, 0, clauseix, self)
00708 self~classes~queue(c)
00709 self~codefeatures~queue(c)
00710 self~mixedclauses~queue(c)
00711 self~procedural = .false
00712 end
00713 when w1 = '::METHOD'
00714 then do
00715 parse var mclause . methodname .
00716 if methodname~words = 0
00717 then iterate
00718 if uclause~wordpos('ATTRIBUTE') > 0
00719 then m = .rattribute~new(methodname, clause~startline, clause~startcol, 0, 0, clauseix, self)
00720 else m = .rmethod~new(methodname, clause~startline, clause~startcol, 0, 0, clauseix, self)
00721 self~methods~queue(m)
00722 self~codefeatures~queue(m)
00723 self~mixedclauses~queue(m)
00724 self~procedural = .false
00725 end
00726 when w1 = '::ATTRIBUTE'
00727 then do
00728 parse var mclause . methodname .
00729 if methodname~words = 0
00730 then iterate
00731 m = .rattribute~new(methodname, clause~startline, clause~startcol, 0, 0, clauseix, self)
00732 self~methods~queue(m)
00733 self~codefeatures~queue(m)
00734 self~mixedclauses~queue(m)
00735 self~procedural = .false
00736 end
00737 when w1 = '::CONSTANT'
00738 then do
00739 parse var mclause . varname varvalue
00740 if varname~words = 0
00741 then iterate
00742 m = .rconstant~new(varname, varvalue, clause~startline, clause~startcol, 0, 0, clauseix, self)
00743 self~rconstants~queue(m)
00744 self~codefeatures~queue(m)
00745 self~mixedclauses~queue(m)
00746 self~procedural = .false
00747 end
00748 when w1 = '::ROUTINE'
00749 then do
00750 parse var mclause . routinename .
00751 if routinename~words = 0
00752 then iterate
00753 t = .routine~new(routinename, clause~startline, clause~startcol, 0, 0, clauseix, self)
00754 self~routines~queue(t)
00755 self~codefeatures~queue(t)
00756 self~mixedclauses~queue(t)
00757 self~procedural = .false
00758 end
00759 when w1 = '::OPTIONS'
00760 then do
00761 parse var mclause . optionlist.
00762 t = .roptions~new(optionlist~strip, clause~startline, clause~startcol, 0, 0, clauseix, self)
00763 self~roptions~queue(t)
00764 self~codefeatures~queue(t)
00765 self~mixedclauses~queue(t)
00766 self~procedural = .false
00767 end
00768 otherwise self~mixedclauses~queue(clause)
00769 end
00770 end
00771
00772
00773
00774 do j = 1 to self~codefeatures~items
00775 curfeat = self~codefeatures[j]
00776
00777 select
00778
00779 when curfeat~objectname = 'LABL'
00780 then do
00781
00782 endclause = self~clauses[curfeat~startclause]
00783 end
00784
00785 when curfeat~objectname = 'CLAS'
00786 then do
00787
00788 nf = self~nextfeature(curfeat, 'CLAS ROUT')
00789 if nf = .nil
00790 then endclause = self~clauses[self~clauses~last]
00791 else endclause = self~clauses[nf~startclause-1]
00792 end
00793
00794 otherwise do
00795 nf = self~nextfeature(curfeat, 'CLAS ROUT METH ATTR PROC REQU')
00796 if nf = .nil
00797 then endclause = self~clauses[self~clauses~last]
00798 else endclause = self~clauses[nf~startclause-1]
00799
00800 end
00801 end
00802
00803
00804 curfeat~endline = endclause~endline
00805 curfeat~endcol = endclause~endcol
00806 curfeat~endclause = self~clauses~index(endclause)
00807
00808 end
00809
00810
00811
00812 do i over self~procedures
00813 self~procsetup(i)
00814 end
00815 do i over self~requires
00816 self~requiressetup(i)
00817 end
00818 do i over self~classes
00819 self~classsetup(i)
00820 end
00821 do i over self~methods
00822 self~methodsetup(i)
00823 end
00824 do i over self~routines
00825 self~routinesetup(i)
00826 end
00827
00828
00829 do i over self~codefeatures
00830 self~codefeaturemap~put(i, i~startline)
00831 end
00832
00833
00834
00835
00836
00837
00838 ::METHOD nextfeature
00839 use arg current, goodlist
00840
00841 j = self~codefeatures~index(current) + 1
00842
00843 do j = j to self~codefeatures~items
00844 t = self~codefeatures[j]
00845 if goodlist~wordpos(t~objectname) > 0
00846 then return t
00847 end
00848 return .nil
00849
00850
00851
00852
00853 ::METHOD procsetup PRIVATE
00854 use arg fc
00855
00856
00857 proc = self~getclausecode(fc)
00858
00859 if fc~rname <> '--entry_proc'
00860 then do
00861 fc~exposelist = self~getexposelist(proc)
00862 end
00863 fc~arglist = self~getarglist(proc)
00864
00865
00866
00867
00868
00869 ::METHOD requiressetup PRIVATE
00870 use arg fc
00871
00872 return
00873
00874
00875
00876 ::METHOD classsetup PRIVATE
00877 use arg fc
00878
00879
00880 clauses = self~getfeature(fc)
00881
00882
00883 definer = self~getcode(clauses[1])[1]
00884 udefiner = definer~translate
00885
00886 ix = udefiner~wordpos('METACLASS')
00887 if ix > 0
00888 then fc~metaclass = definer~word(ix+1)
00889
00890 ix = udefiner~wordpos('MIXINCLASS')
00891 if ix > 0
00892 then fc~mixinclass = definer~word(ix+1)
00893
00894 ix = udefiner~wordpos('SUBCLASS')
00895 if ix > 0
00896 then fc~subclass = definer~word(ix+1)
00897
00898 if udefiner~wordpos('PUBLIC') > 0
00899 then fc~public = .true
00900
00901 ix = udefiner~wordpos('INHERIT')
00902 if ix > 0
00903 then fc~inherit = definer~subword(ix+1)
00904
00905
00906
00907 ::METHOD methodsetup PRIVATE
00908 use arg fc
00909
00910
00911 meth = self~getclausecode(fc)
00912
00913 if fc~objectname = 'METH'
00914 then do
00915
00916 fc~exposelist = self~getexposelist(meth)
00917 fc~arglist = self~getarglist(meth)
00918 end
00919
00920
00921 definer = meth[1]
00922 udefiner = definer~translate
00923
00924 if udefiner~wordpos('CLASS') > 0
00925 then fc~cclass = .true
00926
00927 if udefiner~wordpos('PRIVATE') > 0
00928 then fc~public = .false
00929
00930 if udefiner~wordpos('ABSTRACT') > 0
00931 then fc~abstract = .true
00932
00933 if udefiner~wordpos('PROTECTED') > 0
00934 then fc~protected = .true
00935
00936 if udefiner~wordpos('UNGUARDED') > 0
00937 then fc~guarded = .false
00938
00939
00940
00941 ::METHOD routinesetup PRIVATE
00942 use arg fc
00943
00944
00945 rout = self~getclausecode(fc)
00946
00947
00948 fc~exposelist = self~getexposelist(rout)
00949 fc~arglist = self~getarglist(rout)
00950
00951
00952 definer = rout[1]
00953 udefiner = definer~translate
00954
00955 if udefiner~wordpos('PUBLIC') > 0
00956 then fc~public = .true
00957
00958
00959
00960
00961
00962
00963 ::METHOD getexposelist
00964 use arg clauses
00965
00966 clause = clauses[1]
00967 uclause = clause~translate
00968
00969 if uclause~word(2) = 'PROCEDURE',
00970 & uclause~word(3) = 'EXPOSE'
00971 then do
00972 return clause~subword(4)
00973 end
00974
00975 if clauses~items > 1
00976 then do
00977 clause = clauses[2]
00978 uclause = clause~translate
00979 if uclause~word(1) = 'EXPOSE'
00980 then do
00981 return clause~subword(2)
00982 end
00983 end
00984
00985 return ''
00986
00987
00988
00989
00990
00991 ::METHOD getarglist
00992 use arg clauses
00993
00994 do clause over clauses
00995 uclause = clause~translate
00996 parse var uclause w1 w2 w3 w4 .
00997 select
00998 when w1 = 'ARG'
00999 then return clause~subword(2)~translate(' ',',')~space
01000 when w1 = 'PARSE' & w2 = 'ARG'
01001 then return clause~subword(3)~translate(' ',',')~space
01002 when w1 = 'PARSE' & w3 = 'ARG'
01003 then return clause~subword(4)~translate(' ',',')~space
01004 when w1 = 'PARSE' & w4 = 'ARG'
01005 then return clause~subword(5)~translate(' ',',')~space
01006 when w1 = 'USE' & w2 = 'ARG'
01007 then do
01008 self~procedural = .false
01009 return clause~subword(3)~translate(' ',',')~space
01010 end
01011 when w1 = 'USE' & w3 = 'ARG'
01012 then do
01013 self~procedural = .false
01014 return clause~subword(4)~translate(' ',',')~space
01015 end
01016 otherwise nop
01017 end
01018 end
01019
01020 return ''
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030 ::METHOD sortcomments PRIVATE
01031 do j = 1 to self~comments~items
01032 com = self~comments[j]
01033
01034
01035
01036 aclause = self~safeallat(self~clausemap, com~startline)
01037 if aclause~items > 0
01038 then self~linecomments~queue(com)
01039 else do
01040
01041 endcom = com
01042 do k = j + 1 to self~comments~items
01043 next = self~comments[k]
01044 if next~startline <> endcom~endline+1,
01045 & next~startline <> endcom~endline
01046 then leave
01047
01048 zclause = self~safeallat(self~clausemap, next~startline)
01049 if zclause~items > 0
01050 then leave
01051 endcom = next
01052 end
01053 nc = .comment~new(com~startline, com~startcol, endcom~endline, endcom~endcol)
01054 self~blockcomments~queue(nc)
01055 j = k - 1
01056 end
01057
01058 end
01059
01060
01061
01062
01063
01064 ::METHOD safeallat
01065 use arg r, index
01066 if r~hasindex(index)
01067 then return r~allat(index)
01068 return .array~new
01069
01070
01071
01072 ::METHOD mergefeatures PRIVATE
01073
01074 if self~codefeatures~items = 0
01075 then do
01076
01077 do j = 1 to self~blockcomments~items
01078 self~features~queue(self~blockcomments[j])
01079 end
01080 return
01081 end
01082
01083 if self~blockcomments~items = 0
01084 then do
01085
01086 do j = 1 to self~codefeatures~items
01087 self~features~queue(self~codefeatures[j])
01088 end
01089 return
01090 end
01091
01092
01093 cfix = 1
01094 bcix = 1
01095
01096 code = self~codefeatures[cfix]
01097 com = self~blockcomments[bcix]
01098
01099 do forever
01100
01101 select
01102 when code~startline < com~startline
01103 then do
01104 self~features~queue(code)
01105 cfix = cfix + 1
01106 if cfix > self~codefeatures~items
01107 then do
01108
01109 do j = bcix to self~blockcomments~items
01110 self~features~queue(self~blockcomments[j])
01111 end
01112 leave
01113 end
01114 code = self~codefeatures[cfix]
01115 end
01116 when code~startline > com~startline
01117 then do
01118 self~features~queue(com)
01119 bcix = bcix + 1
01120 if bcix > self~blockcomments~items
01121 then do
01122
01123 do j = cfix to self~codefeatures~items
01124 self~features~queue(self~codefeatures[j])
01125 end
01126 leave
01127 end
01128 com = self~blockcomments[bcix]
01129 end
01130 otherwise do
01131 say 'logic error! code and blockcomment coincide!'
01132 say 'code:' code~getpos 'comment' com~saypos
01133 exit
01134 end
01135 end
01136 end
01137
01138
01139
01140
01141
01142 ::METHOD getclausesat
01143 use strict arg lineno
01144
01145 clauses = self~safeallat(self~clausemap, lineno)
01146
01147 sorted = .queue~new
01148
01149
01150
01151 do while clauses~items > 0
01152 s = clauses~supplier
01153
01154 min = s~item
01155
01156 do while s~available
01157 i = s~item
01158 if i~startcol < min~startcol
01159 then do
01160 min = i
01161 end
01162 s~next
01163 end
01164 sorted~queue(min)
01165 gigo = clauses~removeitem(min)
01166 end
01167 return sorted
01168
01169
01170
01171
01172 ::METHOD getfeature
01173 use arg feature
01174
01175 clauses = .queue~new
01176
01177
01178 do j = feature~startclause to feature~endclause
01179 clauses~queue(self~clauses[j])
01180 end
01181 return clauses
01182
01183
01184
01185
01186 ::METHOD getlinecomment
01187 use arg filechunk
01188
01189 return self~commentmap[filechunk~startline]
01190
01191
01192
01193
01194
01195 ::METHOD getprevcomment
01196 use arg feature
01197
01198 j = self~features~index(feature)
01199 if j = 1
01200 then return .nil
01201
01202 j = j - 1
01203 if self~features[j]~objectname <> 'COMT'
01204 then return .nil
01205
01206 prevblock = self~features[j]
01207 if j = 1
01208 then return prevblock
01209
01210 prevnonblock = .nil
01211 j = j - 1
01212 do j = j to 1
01213 if self~features[j]~objectname <> 'COMT'
01214 then do
01215 prevnonblock = self~features[j]
01216 leave
01217 end
01218 end
01219 if prevnonblock = .nil
01220 then return prevblock
01221 if prevnonblock~endline > prevblock~startline
01222 then return .nil
01223
01224 return prevblock
01225
01226
01227
01228
01229
01230
01231
01232 ::METHOD getwholeclause
01233 use arg fc
01234 stack = self~getcode(fc)
01235 if stack~items = 1
01236 then return stack[1]~strip
01237
01238 whole = ''
01239 do j = 1 to stack~items
01240 cont = stack[j]~strip
01241 if j <> stack~items & cont~length > 1
01242 then whole = whole cont~left(cont~length-1)
01243 else whole = whole cont
01244 end
01245 return whole
01246
01247
01248
01249
01250
01251 ::METHOD getcode
01252 use arg fc
01253 code = .queue~new
01254
01255 do j = fc~startline to fc~endline
01256 line = self~cleansrc[j]
01257 select
01258 when j = fc~startline & j = fc~endline
01259 then clause = line~substr(fc~startcol, fc~endcol-fc~startcol+1)
01260 when j = fc~startline
01261 then clause = line~substr(fc~startcol)
01262 when j = fc~endline
01263 then clause = line~substr(1, fc~endcol)~strip
01264 otherwise clause = line~strip
01265 end
01266 code~queue(clause)
01267 end
01268 return code
01269
01270
01271
01272
01273
01274 ::METHOD getclausecode
01275 use arg fc
01276
01277 clausecode = .queue~new
01278 clauses = self~getfeature(fc)
01279 do clause over clauses
01280 clausecode~queue(self~getcode(clause)[1])
01281 end
01282
01283 return clausecode
01284
01285
01286
01287
01288
01289 ::METHOD getsource
01290 use arg fc
01291 clauses = .queue~new
01292
01293 do j = fc~startline to fc~endline
01294 line = self~src[j]
01295 select
01296 when j = fc~startline & j = fc~endline
01297 then clause = line~substr(fc~startcol, fc~endcol-fc~startcol+1)
01298 when j = fc~startline
01299 then clause = line~substr(fc~startcol)
01300 when j = fc~endline
01301 then clause = line~substr(1, fc~endcol)
01302 otherwise clause = line
01303 end
01304 clauses~queue(clause)
01305 end
01306 return clauses
01307
01308
01309
01310
01311
01312 ::METHOD getnoppedsource
01313 use arg fc
01314 clauses = .queue~new
01315
01316 do j = fc~startline to fc~endline
01317 line = self~src[j]
01318
01319
01320 quoted = self~safeallat(self~quotemap, j)
01321
01322 do quote over quoted
01323 litlen = quote~endcol - quote~startcol - 1
01324 line = line~overlay('o'~copies(litlen), quote~startcol+1, litlen)
01325 end
01326
01327 select
01328 when j = fc~startline & j = fc~endline
01329 then clause = line~substr(fc~startcol, fc~endcol-fc~startcol+1)
01330 when j = fc~startline
01331 then clause = line~substr(fc~startcol)
01332 when j = fc~endline
01333 then clause = line~substr(1, fc~endcol)
01334 otherwise clause = line
01335 end
01336 clauses~queue(clause)
01337 end
01338 return clauses
01339
01340
01341
01342
01343
01344
01345
01346 ::METHOD isinquote PRIVATE
01347 use arg line, col
01348 if \self~quotemap~hasindex(line)
01349 then return .false
01350
01351
01352 quoted = self~safeallat(self~quotemap, line)
01353
01354 do i over quoted
01355 if col < i~endcol & col > i~startcol
01356 then return .true
01357 end
01358 return .false
01359
01360 ::METHOD iscomment PRIVATE
01361 use arg line, col
01362
01363 commented = self~safeallat(self~commentemap, line)
01364
01365 do i over commented
01366 if col < i~endcol & col > i~startcol
01367 then return .true
01368 end
01369 return .false
01370
01371
01372
01373
01374 ::METHOD getcontained
01375 use arg feature
01376
01377 j = self~features~index(feature)
01378 if j = .nil
01379 then raise SYNTAX 93 ARRAY('getcontained for unknown feature')
01380
01381 contained = .queue~new
01382
01383 do j = j+1 to self~features~items
01384 cfeat = self~features[j]
01385 if cfeat~startline <= feature~endline
01386 then do
01387 if cfeat~startline = feature~startline,
01388 & cfeat~endcol < feature~startcol
01389 then iterate
01390
01391 if cfeat~startline = feature~endline,
01392 & cfeat~startcol > feature~endcol
01393 then iterate
01394
01395 contained~queue(cfeat)
01396 iterate
01397 end
01398 leave
01399 end
01400 return contained
01401
01402
01403
01404
01405
01406
01407
01408
01409 ::CLASS filechunk
01410 ::METHOD startline ATTRIBUTE
01411 ::METHOD startcol ATTRIBUTE
01412 ::METHOD endline ATTRIBUTE
01413 ::METHOD endcol ATTRIBUTE
01414 ::ATTRIBUTE user
01415 ::METHOD init
01416 use arg startline, startcol, endline, endcol
01417 self~startline = startline
01418 self~startcol = startcol
01419 self~endline = endline
01420 self~endcol = endcol
01421 self~init:super()
01422
01423
01424 ::METHOD getpos
01425 return self~startline':'self~startcol','self~endline':'self~endcol
01426
01427
01428 ::METHOD makestring
01429 return self~objectname self~getpos
01430
01431
01432
01433
01434
01435 ::METHOD contains
01436 use arg contained
01437
01438 if contained~startline >= self~startline,
01439 & contained~endline <= self~endline
01440 then do
01441 if contained~startline = self~startline,
01442 & contained~endcol < self~startcol
01443 then return .false
01444
01445 if contained~startline = self~endline,
01446 & contained~startcol > self~endcol
01447 then return .false
01448
01449 return .true
01450 end
01451
01452 return .false
01453
01454
01455
01456
01457
01458
01459
01460
01461 ::CLASS comment SUBCLASS filechunk
01462 ::METHOD init
01463 use arg startline, startcol, endline, endcol
01464 self~init:super(startline, startcol, endline, endcol)
01465 self~objectname = 'COMT'
01466
01467
01468
01469
01470 ::CLASS clause PUBLIC SUBCLASS filechunk
01471 ::METHOD init
01472 use arg startline, startcol, endline, endcol
01473 self~init:super(startline, startcol, endline, endcol)
01474 self~objectname = 'CLAU'
01475
01476 ::METHOD clone
01477 return .clause~new(self~startline, self~startcol, self~endline, self~endcol)
01478
01479
01480
01481
01482 ::CLASS quoted SUBCLASS filechunk
01483 ::METHOD init
01484 use arg startline, startcol, endline, endcol
01485 self~init:super(startline, startcol, endline, endcol)
01486 self~objectname = 'QUOT'
01487
01488
01489
01490
01491
01492
01493
01494
01495
01496 ::CLASS feature SUBCLASS filechunk inherit Comparable
01497 ::ATTRIBUTE rname
01498 ::ATTRIBUTE startclause
01499 ::ATTRIBUTE endclause
01500 ::ATTRIBUTE owner
01501
01502 ::METHOD init
01503 use strict arg startline, startcol, endline, endcol, owner
01504 self~init:super(startline, startcol, endline, endcol)
01505 self~startclause = 0
01506 self~endclause = 0
01507 self~rname = ''
01508 self~owner = owner
01509
01510
01511 ::METHOD compareTo
01512 use arg other
01513 if other~rname > self~rname
01514 then return -1
01515 if other~rname < self~rname
01516 then return 1
01517 return 0
01518
01519
01520 ::METHOD makestring
01521 return self~makestring:super() self~rname self~startclause self~endclause
01522
01523
01524
01525
01526
01527
01528
01529
01530 ::CLASS label SUBCLASS feature INHERIT callable
01531 ::METHOD init
01532 use strict arg name, startline, startcol, endline, endcol, startclause, owner
01533 self~init:super(startline, startcol, endline, endcol, owner)
01534 self~rname = name
01535 self~objectname = 'LABL'
01536 self~startclause = startclause
01537
01538
01539
01540
01541 ::CLASS callable MIXINCLASS object
01542 ::ATTRIBUTE exposelist
01543 ::ATTRIBUTE arglist
01544 ::METHOD init
01545 self~exposelist = ''
01546 self~arglist = ''
01547 forward class(super)
01548
01549
01550
01551
01552 ::CLASS proc SUBCLASS feature INHERIT callable
01553
01554 ::METHOD init
01555 use strict arg name, startline, startcol, endline, endcol, startclause, owner
01556 self~init:super(startline, startcol, endline, endcol, owner)
01557 self~rname = name
01558 self~objectname = 'PROC'
01559 self~startclause = startclause
01560
01561
01562
01563
01564 ::CLASS rclass SUBCLASS feature
01565 ::ATTRIBUTE public
01566 ::ATTRIBUTE metaclass
01567 ::ATTRIBUTE mixinclass
01568 ::ATTRIBUTE subclass
01569 ::ATTRIBUTE inherit
01570
01571 ::METHOD init
01572 use strict arg name, startline, startcol, endline, endcol, startclause, owner
01573 self~init:super(startline, startcol, endline, endcol, owner)
01574 self~rname = name
01575 self~objectname = 'CLAS'
01576 self~startclause = startclause
01577 self~public = .false
01578 self~metaclass = ''
01579 self~mixinclass = ''
01580 self~subclass = ''
01581 self~inherit = ''
01582
01583
01584
01585 ::CLASS methodic MIXINCLASS object
01586
01587 ::ATTRIBUTE cclass
01588 ::ATTRIBUTE public
01589 ::ATTRIBUTE guarded
01590 ::ATTRIBUTE protected
01591 ::ATTRIBUTE abstract
01592 ::ATTRIBUTE ownerclass
01593 ::METHOD init
01594 self~cclass = .false
01595 self~public = .true
01596 self~guarded = .true
01597 self~protected = .false
01598 self~abstract = .false
01599 self~ownerclass = .nil
01600 forward class(super)
01601
01602
01603
01604
01605 ::CLASS rmethod SUBCLASS feature inherit callable methodic
01606
01607 ::METHOD init
01608 use strict arg name, startline, startcol, endline, endcol, startclause, owner
01609 self~init:super(startline, startcol, endline, endcol, owner)
01610 self~rname = name
01611 self~objectname = 'METH'
01612 self~startclause = startclause
01613
01614
01615
01616
01617 ::CLASS rattribute SUBCLASS feature INHERIT methodic
01618
01619 ::ATTRIBUTE get
01620 ::ATTRIBUTE set
01621
01622 ::METHOD init
01623 use strict arg name, startline, startcol, endline, endcol, startclause, owner
01624 self~init:super(startline, startcol, endline, endcol, owner)
01625 self~rname = name
01626 self~objectname = 'ATTR'
01627 self~startclause = startclause
01628 self~get = .false
01629 self~set = .false
01630
01631
01632
01633
01634 ::CLASS routine SUBCLASS feature INHERIT callable
01635 ::ATTRIBUTE public
01636 ::METHOD init
01637 use strict arg name, startline, startcol, endline, endcol, startclause, owner
01638 self~init:super(startline, startcol, endline, endcol, owner)
01639 self~rname = name
01640 self~objectname = 'ROUT'
01641 self~startclause = startclause
01642 self~public = .false
01643
01644
01645 ::CLASS requires SUBCLASS feature
01646 ::METHOD init
01647 use strict arg name, startline, startcol, endline, endcol, startclause, owner
01648 self~init:super(startline, startcol, endline, endcol, owner)
01649 self~rname = name
01650 self~objectname = 'REQU'
01651 self~startclause = startclause
01652
01653
01654 ::CLASS roptions SUBCLASS feature
01655 ::METHOD init
01656 use strict arg optionlist, startline, startcol, endline, endcol, startclause, owner
01657 self~init:super(startline, startcol, endline, endcol, owner)
01658 self~rname = 'options'
01659 self~objectname = 'OPTS'
01660 self~startclause = startclause
01661
01662
01663 ::CLASS rconstant SUBCLASS feature inherit callable methodic
01664 ::ATTRIBUTE varvalue
01665 ::METHOD init
01666 use strict arg varname, varvalue, startline, startcol, endline, endcol, startclause, owner
01667 self~init:super(startline, startcol, endline, endcol, owner)
01668 self~rname = varname
01669 self~varvalue = varvalue
01670 self~objectname = 'CONS'
01671 self~startclause = startclause
01672
01673
01674 ::CLASS flatclause PUBLIC
01675 ::ATTRIBUTE clause
01676 ::ATTRIBUTE linelengths
01677 ::ATTRIBUTE flat
01678
01679
01680
01681 ::METHOD init
01682 use arg clause, src
01683 self~clause = clause
01684 self~linelengths = .queue~new
01685 self~flat = ''
01686
01687 do i over src
01688 self~linelengths~queue(i~length)
01689 self~flat = self~flat||i
01690
01691 if self~flat~right(1) = ','
01692 then do
01693 self~flat = self~flat~left(self~flat~length-1)' '
01694 end
01695 end
01696
01697
01698
01699
01700
01701 ::METHOD index2vector
01702 use arg index
01703 reloff = 0
01704
01705
01706
01707 if index > self~flat~length
01708 then raise SYNTAX 93 ARRAY('index is' index 'line length is' self~flat~length)
01709
01710
01711 if index <= self~linelengths[1]
01712 then do
01713 return self~clause~startline index + self~clause~startcol -1
01714 end
01715
01716 line = 0
01717 col = -1
01718 do i over self~linelengths
01719
01720 if reloff + i >= index
01721 then do
01722 col = index - reloff
01723 leave
01724 end
01725 reloff = reloff + i
01726 line = line + 1
01727 end
01728 return line + self~clause~startline col
01729