home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / OPENSTEP / Languages / Python / python-14-src / Doc / partparse.py < prev    next >
Encoding:
Python Source  |  1997-01-17  |  59.0 KB  |  2,121 lines

  1. #
  2. # partparse.py: parse a by-Guido-written-and-by-Jan-Hein-edited LaTeX file,
  3. #     and generate texinfo source.
  4. #
  5. # This is *not* a good example of good programming practices. In fact, this
  6. #     file could use a complete rewrite, in order to become faster, more
  7. #     easily extensible and maintainable.
  8. #
  9. # However, I added some comments on a few places for the pityful person who
  10. #     would ever need to take a look into this file.
  11. #
  12. # Have I been clear enough??
  13. #
  14. # -jh
  15. #
  16. # Yup.  I made some performance improvements and hope this lasts a while;
  17. #     I don't want to be the schmuck who ends up re-writting it!
  18. #
  19. # -fld
  20.  
  21. import sys, string, regex, getopt, os
  22.  
  23. from types import IntType, ListType, StringType, TupleType
  24.  
  25. # Different parse modes for phase 1
  26. MODE_REGULAR = 0
  27. MODE_VERBATIM = 1
  28. MODE_CS_SCAN = 2
  29. MODE_COMMENT = 3
  30. MODE_MATH = 4
  31. MODE_DMATH = 5
  32. MODE_GOBBLEWHITE = 6
  33.  
  34. the_modes = (MODE_REGULAR, MODE_VERBATIM, MODE_CS_SCAN, MODE_COMMENT,
  35.          MODE_MATH, MODE_DMATH, MODE_GOBBLEWHITE)
  36.  
  37. # Show the neighbourhood of the scanned buffer
  38. def epsilon(buf, where):
  39.     wmt, wpt = where - 10, where + 10
  40.     if wmt < 0:
  41.     wmt = 0
  42.     if wpt > len(buf):
  43.     wpt = len(buf)
  44.     return ' Context ' + `buf[wmt:where]` + '.' + `buf[where:wpt]` + '.'
  45.  
  46. # Should return the line number. never worked
  47. def lin():
  48.     global lineno
  49.     return ' Line ' + `lineno` + '.'
  50.  
  51. # Displays the recursion level.
  52. def lv(lvl):
  53.     return ' Level ' + `lvl` + '.'
  54.  
  55. # Combine the three previous functions. Used often.
  56. def lle(lvl, buf, where):
  57.     return lv(lvl) + lin() + epsilon(buf, where)
  58.  
  59.  
  60. # This class is only needed for _symbolic_ representation of the parse mode.
  61. class Mode:
  62.     def __init__(self, arg):
  63.     if arg not in the_modes:
  64.         raise ValueError, 'mode not in the_modes'
  65.     self.mode = arg
  66.  
  67.     def __cmp__(self, other):
  68.     if type(self) != type(other):
  69.         other = mode[other]
  70.     return cmp(self.mode, other.mode)
  71.  
  72.     def __repr__(self):
  73.     if self.mode == MODE_REGULAR:
  74.         return 'MODE_REGULAR'
  75.     elif self.mode == MODE_VERBATIM:
  76.         return 'MODE_VERBATIM'
  77.     elif self.mode == MODE_CS_SCAN:
  78.         return 'MODE_CS_SCAN'
  79.     elif self.mode == MODE_COMMENT:
  80.         return 'MODE_COMMENT'
  81.     elif self.mode == MODE_MATH:
  82.         return 'MODE_MATH'
  83.     elif self.mode == MODE_DMATH:
  84.         return 'MODE_DMATH'
  85.     elif self.mode == MODE_GOBBLEWHITE:
  86.         return 'MODE_GOBBLEWHITE'
  87.     else:
  88.         raise ValueError, 'mode not in the_modes'
  89.  
  90. # just a wrapper around a class initialisation
  91. mode = {}
  92. for t in the_modes:
  93.     mode[t] = Mode(t)
  94.  
  95.  
  96. # After phase 1, the text consists of chunks, with a certain type
  97. # this type will be assigned to the chtype member of the chunk
  98. # the where-field contains the file position where this is found
  99. # and the data field contains (1): a tuple describing start- end end
  100. # positions of the substring (can be used as slice for the buf-variable),
  101. # (2) just a string, mostly generated by the changeit routine,
  102. # or (3) a list, describing a (recursive) subgroup of chunks
  103. PLAIN = 0            # ASSUME PLAINTEXT, data = the text
  104. GROUP = 1            # GROUP ({}), data = [chunk, chunk,..]
  105. CSNAME = 2            # CONTROL SEQ TOKEN, data = the command
  106. COMMENT = 3            # data is the actual comment
  107. DMATH = 4            # DISPLAYMATH, data = [chunk, chunk,..]
  108. MATH = 5            # MATH, see DISPLAYMATH
  109. OTHER = 6            # CHAR WITH CATCODE OTHER, data = char
  110. ACTIVE = 7            # ACTIVE CHAR
  111. GOBBLEDWHITE = 8        # Gobbled LWSP, after CSNAME
  112. ENDLINE = 9            # END-OF-LINE, data = '\n'
  113. DENDLINE = 10            # DOUBLE EOL, data='\n', indicates \par
  114. ENV = 11            # LaTeX-environment
  115.                 # data =(envname,[ch,ch,ch,.])
  116. CSLINE = 12            # for texi: next chunk will be one group
  117.                 # of args. Will be set all on 1 line
  118. IGNORE = 13            # IGNORE this data
  119. ENDENV = 14            # TEMP END OF GROUP INDICATOR
  120. IF = 15                # IF-directive
  121.                 # data = (flag,negate,[ch, ch, ch,...])
  122.  
  123. the_types = (PLAIN, GROUP, CSNAME, COMMENT, DMATH, MATH, OTHER, ACTIVE,
  124.          GOBBLEDWHITE, ENDLINE, DENDLINE, ENV, CSLINE, IGNORE, ENDENV, IF)
  125.  
  126. # class, just to display symbolic name
  127. class ChunkType:
  128.     def __init__(self, chunk_type):
  129.     if chunk_type not in the_types:
  130.         raise ValueError, 'chunk_type not in the_types'
  131.     self.chunk_type = chunk_type
  132.  
  133.     def __cmp__(self, other):
  134.     if type(self) != type(other):
  135.         other = chunk_type[other]
  136.     return cmp(self.chunk_type, other.chunk_type)
  137.  
  138.     def __repr__(self):
  139.     if self.chunk_type == PLAIN:
  140.         return 'PLAIN'
  141.     elif self.chunk_type == GROUP:
  142.         return 'GROUP'
  143.     elif self.chunk_type == CSNAME:
  144.         return 'CSNAME'
  145.     elif self.chunk_type == COMMENT:
  146.         return 'COMMENT'
  147.     elif self.chunk_type == DMATH:
  148.         return 'DMATH'
  149.     elif self.chunk_type == MATH:
  150.         return 'MATH'
  151.     elif self.chunk_type == OTHER:
  152.         return 'OTHER'
  153.     elif self.chunk_type == ACTIVE:
  154.         return 'ACTIVE'
  155.     elif self.chunk_type == GOBBLEDWHITE:
  156.         return 'GOBBLEDWHITE'
  157.     elif self.chunk_type == DENDLINE:
  158.         return 'DENDLINE'
  159.     elif self.chunk_type == ENDLINE:
  160.         return 'ENDLINE'
  161.     elif self.chunk_type == ENV:
  162.         return 'ENV'
  163.     elif self.chunk_type == CSLINE:
  164.         return 'CSLINE'
  165.     elif self.chunk_type == IGNORE:
  166.         return 'IGNORE'
  167.     elif self.chunk_type == ENDENV:
  168.         return 'ENDENV'
  169.     elif self.chunk_type == IF:
  170.         return 'IF'
  171.     else:
  172.         raise ValueError, 'chunk_type not in the_types'
  173.  
  174. # ...and the wrapper
  175. chunk_type = {}
  176. for t in the_types:
  177.     chunk_type[t] = ChunkType(t)
  178.  
  179. # store a type object of the ChunkType-class-instance...
  180. chunk_type_type = type(chunk_type[PLAIN])
  181.  
  182. # this class contains a part of the parsed buffer
  183. class Chunk:
  184.     def __init__(self, chtype, where, data):
  185.     if type(chtype) != chunk_type_type:
  186.         chtype = chunk_type[chtype]
  187.     self.chtype = chtype
  188.     self.where = where
  189.     self.data = data
  190.  
  191.     def __repr__(self):
  192.     return 'chunk' + `self.chtype, self.where, self.data`
  193.  
  194. # and the wrapper
  195. chunk = Chunk
  196.  
  197.  
  198. error = 'partparse.error'
  199.  
  200. #
  201. # TeX's catcodes...
  202. #
  203. CC_ESCAPE = 0
  204. CC_LBRACE = 1
  205. CC_RBRACE = 2
  206. CC_MATHSHIFT = 3
  207. CC_ALIGNMENT = 4
  208. CC_ENDLINE = 5
  209. CC_PARAMETER = 6
  210. CC_SUPERSCRIPT = 7
  211. CC_SUBSCRIPT = 8
  212. CC_IGNORE = 9
  213. CC_WHITE = 10
  214. CC_LETTER = 11
  215. CC_OTHER = 12
  216. CC_ACTIVE = 13
  217. CC_COMMENT = 14
  218. CC_INVALID = 15
  219.  
  220. # and the names
  221. cc_names = [
  222.       'CC_ESCAPE',
  223.       'CC_LBRACE',
  224.       'CC_RBRACE',
  225.       'CC_MATHSHIFT',
  226.       'CC_ALIGNMENT',
  227.       'CC_ENDLINE',
  228.       'CC_PARAMETER',
  229.       'CC_SUPERSCRIPT',
  230.       'CC_SUBSCRIPT',
  231.       'CC_IGNORE',
  232.       'CC_WHITE',
  233.       'CC_LETTER',
  234.       'CC_OTHER',
  235.       'CC_ACTIVE',
  236.       'CC_COMMENT',
  237.       'CC_INVALID',
  238.       ]
  239.  
  240. # Show a list of catcode-name-symbols
  241. def pcl(codelist):
  242.     result = ''
  243.     for i in codelist:
  244.     result = result + cc_names[i] + ', '
  245.     return '[' + result[:-2] + ']'
  246.  
  247. # the name of the catcode (ACTIVE, OTHER, etc.)
  248. def pc(code):
  249.     return cc_names[code]
  250.  
  251.  
  252. # Which catcodes make the parser stop parsing regular plaintext
  253. regular_stopcodes = [CC_ESCAPE, CC_LBRACE, CC_RBRACE, CC_MATHSHIFT,
  254.       CC_ALIGNMENT, CC_PARAMETER, CC_SUPERSCRIPT, CC_SUBSCRIPT,
  255.       CC_IGNORE, CC_ACTIVE, CC_COMMENT, CC_INVALID, CC_ENDLINE]
  256.  
  257. # same for scanning a control sequence name
  258. csname_scancodes = [CC_LETTER]
  259.  
  260. # same for gobbling LWSP
  261. white_scancodes = [CC_WHITE]
  262. ##white_scancodes = [CC_WHITE, CC_ENDLINE]
  263.  
  264. # make a list of all catcode id's, except for catcode ``other''
  265. all_but_other_codes = range(16)
  266. del all_but_other_codes[CC_OTHER]
  267. ##print all_but_other_codes
  268.  
  269. # when does a comment end
  270. comment_stopcodes = [CC_ENDLINE]
  271.  
  272. # gather all characters together, specified by a list of catcodes
  273. def code2string(cc, codelist):
  274.     ##print 'code2string: codelist = ' + pcl(codelist),
  275.     result = ''
  276.     for category in codelist:
  277.     if cc[category]:
  278.         result = result + cc[category]
  279.     ##print 'result = ' + `result`
  280.     return result
  281.  
  282. # automatically generate all characters of catcode other, being the
  283. # complement set in the ASCII range (128 characters)
  284. def make_other_codes(cc):
  285.     otherchars = range(256)        # could be made 256, no problem
  286.     for category in all_but_other_codes:
  287.     if cc[category]:
  288.         for c in cc[category]:
  289.         otherchars[ord(c)] = None
  290.     result = ''
  291.     for i in otherchars:
  292.     if i != None:
  293.         result = result + chr(i)
  294.     return result
  295.  
  296. # catcode dump (which characters have which catcodes).
  297. def dump_cc(name, cc):
  298.     ##print '\t' + name
  299.     ##print '=' * (8+len(name))
  300.     if len(cc) != 16:
  301.     raise TypeError, 'cc not good cat class'
  302. ##    for i in range(16):
  303. ##        print pc(i) + '\t' + `cc[i]`
  304.  
  305.  
  306. # In the beginning,....
  307. epoch_cc = [None] * 16
  308. ##dump_cc('epoch_cc', epoch_cc)
  309.  
  310.  
  311. # INITEX
  312. initex_cc = epoch_cc[:]
  313. initex_cc[CC_ESCAPE] = '\\'
  314. initex_cc[CC_ENDLINE], initex_cc[CC_IGNORE], initex_cc[CC_WHITE] = \
  315.       '\n', '\0', ' '
  316. initex_cc[CC_LETTER] = string.uppercase + string.lowercase
  317. initex_cc[CC_COMMENT], initex_cc[CC_INVALID] = '%', '\x7F'
  318. #initex_cc[CC_OTHER] = make_other_codes(initex_cc) I don't need them, anyway
  319. ##dump_cc('initex_cc', initex_cc)
  320.  
  321.  
  322. # LPLAIN: LaTeX catcode setting (see lplain.tex)
  323. lplain_cc = initex_cc[:]
  324. lplain_cc[CC_LBRACE], lplain_cc[CC_RBRACE] = '{', '}'
  325. lplain_cc[CC_MATHSHIFT] = '$'
  326. lplain_cc[CC_ALIGNMENT] = '&'
  327. lplain_cc[CC_PARAMETER] = '#'
  328. lplain_cc[CC_SUPERSCRIPT] = '^\x0B'    # '^' and C-k
  329. lplain_cc[CC_SUBSCRIPT] = '_\x01'    # '_' and C-a
  330. lplain_cc[CC_WHITE] = lplain_cc[CC_WHITE] + '\t'
  331. lplain_cc[CC_ACTIVE] = '~\x0C'        # '~' and C-l
  332. lplain_cc[CC_OTHER] = make_other_codes(lplain_cc)
  333. ##dump_cc('lplain_cc', lplain_cc)
  334.  
  335.  
  336. # Guido's LaTeX environment catcoded '_' as ``other''
  337. # my own purpose catlist
  338. my_cc = lplain_cc[:]
  339. my_cc[CC_SUBSCRIPT] = my_cc[CC_SUBSCRIPT][1:] # remove '_' here
  340. my_cc[CC_OTHER] = my_cc[CC_OTHER] + '_'          # add it to OTHER list
  341. dump_cc('my_cc', my_cc)
  342.  
  343.  
  344.  
  345. # needed for un_re, my equivalent for regexp-quote in Emacs
  346. re_meaning = '\\[]^$'
  347.  
  348. def un_re(str):
  349.     result = ''
  350.     for i in str:
  351.     if i in re_meaning:
  352.         result = result + '\\'
  353.     result = result + i
  354.     return result
  355.  
  356. # NOTE the negate ('^') operator in *some* of the regexps below
  357. def make_rc_regular(cc):
  358.     # problems here if '[]' are included!!
  359.     return regex.compile('[' + code2string(cc, regular_stopcodes) + ']')
  360.  
  361. def make_rc_cs_scan(cc):
  362.     return regex.compile('[^' + code2string(cc, csname_scancodes) + ']')
  363.  
  364. def make_rc_comment(cc):
  365.     return regex.compile('[' + code2string(cc, comment_stopcodes) + ']')
  366.  
  367. def make_rc_endwhite(cc):
  368.     return regex.compile('[^' + code2string(cc, white_scancodes) + ']')
  369.  
  370.  
  371.  
  372. # regular: normal mode: 
  373. rc_regular = make_rc_regular(my_cc)
  374.  
  375. # scan: scan a command sequence e.g. `newlength' or `mbox' or `;', `,' or `$'
  376. rc_cs_scan = make_rc_cs_scan(my_cc)
  377. rc_comment = make_rc_comment(my_cc)
  378. rc_endwhite = make_rc_endwhite(my_cc)
  379.  
  380.  
  381. # parseit (BUF, PARSEMODE=mode[MODE_REGULAR], START=0, RECURSION-LEVEL=0)
  382. #     RECURSION-LEVEL will is incremented on entry.
  383. #     result contains the list of chunks returned
  384. #     together with this list, the buffer position is returned
  385.  
  386. #     RECURSION-LEVEL will be set to zero *again*, when recursively a
  387. #     {,D}MATH-mode scan has been enetered.
  388. #     This has been done in order to better check for environment-mismatches
  389.  
  390. def parseit(buf, parsemode=mode[MODE_REGULAR], start=0, lvl=0):
  391.     global lineno
  392.  
  393.     result = []
  394.     end = len(buf)
  395.     if lvl == 0 and parsemode == mode[MODE_REGULAR]:
  396.     lineno = 1
  397.     lvl = lvl + 1
  398.  
  399.     ##print 'parseit(' + epsilon(buf, start) + ', ' + `parsemode` + ', ' + `start` + ', ' + `lvl` + ')'
  400.  
  401.     #
  402.     # some of the more regular modes...
  403.     #
  404.  
  405.     if parsemode in (mode[MODE_REGULAR], mode[MODE_DMATH], mode[MODE_MATH]):
  406.     cstate = []
  407.     newpos = start
  408.     curpmode = parsemode
  409.     while 1:
  410.         where = newpos
  411.         #print '\tnew round: ' + epsilon(buf, where)
  412.         if where == end:
  413.         if lvl > 1 or curpmode != mode[MODE_REGULAR]:
  414.             # not the way we started...
  415.             raise EOFError, 'premature end of file.' + lle(lvl, buf, where)
  416.         # the real ending of lvl-1 parse
  417.         return end, result
  418.  
  419.         pos = rc_regular.search(buf, where)
  420.  
  421.         if pos < 0:
  422.         pos = end
  423.  
  424.         if pos != where:
  425.         newpos, c = pos, chunk(PLAIN, where, (where, pos))
  426.         result.append(c)
  427.         continue
  428.  
  429.  
  430.         #
  431.         # ok, pos == where and pos != end
  432.         #
  433.         foundchar = buf[where]
  434.         if foundchar in my_cc[CC_LBRACE]:
  435.         # recursive subgroup parse...
  436.         newpos, data = parseit(buf, curpmode, where+1, lvl)
  437.         result.append(chunk(GROUP, where, data))
  438.  
  439.         elif foundchar in my_cc[CC_RBRACE]:
  440.         if lvl <= 1:
  441.             raise error, 'ENDGROUP while in base level.' + lle(lvl, buf, where)
  442.         if  lvl == 1 and mode != mode[MODE_REGULAR]:
  443.             raise error, 'endgroup while in math mode. +lin() + epsilon(buf, where)'
  444.         return where + 1, result
  445.  
  446.         elif foundchar in my_cc[CC_ESCAPE]:
  447.         #
  448.         # call the routine that actually deals with
  449.         #     this problem. If do_ret is None, than
  450.         #     return the value of do_ret
  451.         #
  452.         # Note that handle_cs might call this routine
  453.         #     recursively again...
  454.         #
  455.         do_ret, newpos = handlecs(buf, where,
  456.               curpmode, lvl, result, end)
  457.         if do_ret != None:
  458.             return do_ret
  459.  
  460.         elif foundchar in my_cc[CC_COMMENT]:
  461.         newpos, data = parseit(buf,
  462.               mode[MODE_COMMENT], where+1, lvl)
  463.         result.append(chunk(COMMENT, where, data))
  464.  
  465.         elif foundchar in my_cc[CC_MATHSHIFT]:
  466.         # note that recursive calls to math-mode
  467.         # scanning are called with recursion-level 0
  468.         # again, in order to check for bad mathend
  469.         #
  470.         if where + 1 != end and buf[where + 1] in my_cc[CC_MATHSHIFT]:
  471.             #
  472.             # double mathshift, e.g. '$$'
  473.             #
  474.             if curpmode == mode[MODE_REGULAR]:
  475.             newpos, data = parseit(buf, mode[MODE_DMATH],
  476.                            where + 2, 0)
  477.             result.append(chunk(DMATH, where, data))
  478.             elif curpmode == mode[MODE_MATH]:
  479.             raise error, 'wrong math delimiiter' + lin() + epsilon(buf, where)
  480.             elif lvl != 1:
  481.             raise error, 'bad mathend.' + lle(lvl, buf, where)
  482.             else:
  483.             return where + 2, result
  484.         else:
  485.             #
  486.             # single math shift, e.g. '$'
  487.             #
  488.             if curpmode == mode[MODE_REGULAR]:
  489.             newpos, data = parseit(buf, mode[MODE_MATH],
  490.                            where + 1, 0)
  491.             result.append(chunk(MATH, where, data))
  492.             elif curpmode == mode[MODE_DMATH]:
  493.             raise error, 'wrong math delimiiter' + lin() + epsilon(buf, where)
  494.             elif lvl != 1:
  495.             raise error, 'bad mathend.' + lv(lvl, buf, where)
  496.             else:
  497.             return where + 1, result
  498.  
  499.         elif foundchar in my_cc[CC_IGNORE]:
  500.         print 'warning: ignored char', `foundchar`
  501.         newpos = where + 1
  502.  
  503.         elif foundchar in my_cc[CC_ACTIVE]:
  504.         result.append(chunk(ACTIVE, where, foundchar))
  505.         newpos = where + 1
  506.  
  507.         elif foundchar in my_cc[CC_INVALID]:
  508.         raise error, 'invalid char ' + `foundchar`
  509.         newpos = where + 1
  510.  
  511.         elif foundchar in my_cc[CC_ENDLINE]:
  512.         #
  513.         # after an end of line, eat the rest of
  514.         # whitespace on the beginning of the next line
  515.         # this is what LaTeX more or less does
  516.         #
  517.         # also, try to indicate double newlines (\par)
  518.         #
  519.         lineno = lineno + 1
  520.         savedwhere = where
  521.         newpos, dummy = parseit(buf, mode[MODE_GOBBLEWHITE], where + 1, lvl)
  522.         if newpos != end and buf[newpos] in my_cc[CC_ENDLINE]:
  523.             result.append(chunk(DENDLINE, savedwhere, foundchar))
  524.         else:
  525.             result.append(chunk(ENDLINE, savedwhere, foundchar))
  526.         else:
  527.         result.append(chunk(OTHER, where, foundchar))
  528.         newpos = where + 1
  529.  
  530.     elif parsemode == mode[MODE_CS_SCAN]:
  531.     #
  532.     # scan for a control sequence token. `\ape', `\nut' or `\%'
  533.     #
  534.     if start == end:
  535.         raise EOFError, 'can\'t find end of csname'
  536.     pos = rc_cs_scan.search(buf, start)
  537.     if pos < 0:
  538.         pos = end
  539.     if pos == start:
  540.         # first non-letter right where we started the search
  541.         # ---> the control sequence name consists of one single
  542.         # character. Also: don't eat white space...
  543.         if buf[pos] in my_cc[CC_ENDLINE]:
  544.         lineno = lineno + 1
  545.         pos = pos + 1
  546.         return pos, (start, pos)
  547.     else:
  548.         spos = pos
  549.         if buf[pos] == '\n':
  550.         lineno = lineno + 1
  551.         spos = pos + 1
  552.         pos2, dummy = parseit(buf, mode[MODE_GOBBLEWHITE], spos, lvl)
  553.         return pos2, (start, pos)
  554.  
  555.     elif parsemode == mode[MODE_GOBBLEWHITE]:
  556.     if start == end:
  557.         return start, ''
  558.     pos = rc_endwhite.search(buf, start)
  559.     if pos < 0:
  560.         pos = start
  561.     return pos, (start, pos)
  562.  
  563.     elif parsemode == mode[MODE_COMMENT]:
  564.     pos = rc_comment.search(buf, start)
  565.     lineno = lineno + 1
  566.     if pos < 0:
  567.         print 'no newline perhaps?'
  568.         raise EOFError, 'can\'t find end of comment'
  569.     pos = pos + 1
  570.     pos2, dummy = parseit(buf, mode[MODE_GOBBLEWHITE], pos, lvl)
  571.     return pos2, (start, pos)
  572.  
  573.     else:
  574.     raise error, 'Unknown mode (' + `parsemode` + ')'
  575.  
  576.  
  577. #moreresult = cswitch(buf[x1:x2], buf, newpos, parsemode, lvl)
  578.  
  579. #boxcommands = 'mbox', 'fbox'
  580. #defcommands = 'def', 'newcommand'
  581.  
  582. endverbstr = '\\end{verbatim}'
  583.  
  584. re_endverb = regex.compile(un_re(endverbstr))
  585.  
  586. #
  587. # handlecs: helper function for parseit, for the special thing we might
  588. #     wanna do after certain command control sequences
  589. # returns: None or return_data, newpos
  590. #
  591. # in the latter case, the calling function is instructed to immediately
  592. # return with the data in return_data
  593. #
  594. def handlecs(buf, where, curpmode, lvl, result, end):
  595.     global lineno
  596.  
  597.     # get the control sequence name...
  598.     newpos, data = parseit(buf, mode[MODE_CS_SCAN], where+1, lvl)
  599.     saveddata = data
  600.     s_buf_data = s(buf, data)
  601.  
  602.     if s_buf_data in ('begin', 'end'):
  603.     # skip the expected '{' and get the LaTeX-envname '}'
  604.     newpos, data = parseit(buf, mode[MODE_REGULAR], newpos+1, lvl)
  605.     if len(data) != 1:
  606.         raise error, 'expected 1 chunk of data.' + lle(lvl, buf, where)
  607.  
  608.     # yucky, we've got an environment
  609.     envname = s(buf, data[0].data)
  610.     s_buf_saveddata = s(buf, saveddata)
  611.     ##print 'FOUND ' + s(buf, saveddata) + '. Name ' + `envname` + '.' + lv(lvl)
  612.     if s_buf_saveddata == 'begin' and envname == 'verbatim':
  613.         # verbatim deserves special treatment
  614.         pos = re_endverb.search(buf, newpos)
  615.         if pos < 0:
  616.         raise error, "%s not found.%s" \
  617.               % (`endverbstr`, lle(lvl, buf, where))
  618.         result.append(chunk(ENV, where, (envname, [chunk(PLAIN, newpos, (newpos, pos))])))
  619.         newpos = pos + len(endverbstr)
  620.  
  621.     elif s_buf_saveddata == 'begin':
  622.         # start parsing recursively... If that parse returns
  623.         # from an '\end{...}', then should the last item of
  624.         # the returned data be a string containing the ended
  625.         # environment
  626.         newpos, data = parseit(buf, curpmode, newpos, lvl)
  627.         if not data or type(data[-1]) is not StringType:
  628.         raise error, "missing 'end'" + lle(lvl, buf, where) \
  629.               + epsilon(buf, newpos)
  630.         retenv = data[-1]
  631.         del data[-1]
  632.         if retenv != envname:
  633.         #[`retenv`, `envname`]
  634.         raise error, 'environments do not match.%s%s' \
  635.               % (lle(lvl, buf, where), epsilon(buf, newpos))
  636.         result.append(chunk(ENV, where, (retenv, data)))
  637.     else:
  638.         # 'end'... append the environment name, as just
  639.         # pointed out, and order parsit to return...
  640.         result.append(envname)
  641.         ##print 'POINT of return: ' + epsilon(buf, newpos)
  642.         # the tuple will be returned by parseit
  643.         return (newpos, result), newpos
  644.  
  645.     # end of \begin ... \end handling
  646.  
  647.     elif s_buf_data[0:2] == 'if':
  648.     # another scary monster: the 'if' directive
  649.     flag = s_buf_data[2:]
  650.  
  651.     # recursively call parseit, just like environment above..
  652.     # the last item of data should contain the if-termination
  653.     # e.g., 'else' of 'fi'
  654.     newpos, data = parseit(buf, curpmode, newpos, lvl)
  655.     if not data or data[-1] not in ('else', 'fi'):
  656.         raise error, 'wrong if... termination' + \
  657.               lle(lvl, buf, where) + epsilon(buf, newpos)
  658.  
  659.     ifterm = data[-1]
  660.     del data[-1]
  661.     # 0 means dont_negate flag
  662.     result.append(chunk(IF, where, (flag, 0, data)))
  663.     if ifterm == 'else':
  664.         # do the whole thing again, there is only one way
  665.         # to end this one, by 'fi'
  666.         newpos, data = parseit(buf, curpmode, newpos, lvl)
  667.         if not data or data[-1] not in ('fi', ):
  668.         raise error, 'wrong if...else... termination' \
  669.               + lle(lvl, buf, where) \
  670.               + epsilon(buf, newpos)
  671.  
  672.         ifterm = data[-1]
  673.         del data[-1]
  674.         result.append(chunk(IF, where, (flag, 1, data)))
  675.     #done implicitely: return None, newpos
  676.  
  677.     elif s_buf_data in ('else', 'fi'):
  678.     result.append(s(buf, data))
  679.     # order calling party to return tuple
  680.     return (newpos, result), newpos
  681.  
  682.     # end of \if, \else, ... \fi handling
  683.  
  684.     elif s(buf, saveddata) == 'verb':
  685.     x2 = saveddata[1]
  686.     result.append(chunk(CSNAME, where, data))
  687.     if x2 == end:
  688.         raise error, 'premature end of command.' + lle(lvl, buf, where)
  689.     delimchar = buf[x2]
  690.     ##print 'VERB: delimchar ' + `delimchar`
  691.     pos = regex.compile(un_re(delimchar)).search(buf, x2 + 1)
  692.     if pos < 0:
  693.         raise error, 'end of \'verb\' argument (' + \
  694.           `delimchar` + ') not found.' + \
  695.           lle(lvl, buf, where)
  696.     result.append(chunk(GROUP, x2, [chunk(PLAIN, x2+1, (x2+1, pos))]))
  697.     newpos = pos + 1
  698.     else:
  699.     result.append(chunk(CSNAME, where, data))
  700.     return None, newpos
  701.  
  702. # this is just a function to get the string value if the possible data-tuple
  703. def s(buf, data):
  704.     if type(data) is StringType:
  705.     return data
  706.     if len(data) != 2 or not (type(data[0]) is type(data[1]) is IntType):
  707.     raise TypeError, 'expected tuple of 2 integers'
  708.     x1, x2 = data
  709.     return buf[x1:x2]
  710.  
  711.  
  712. ##length, data1, i = getnextarg(length, buf, pp, i + 1)
  713.  
  714. # make a deep-copy of some chunks
  715. def crcopy(r):
  716.     return map(chunkcopy, r)
  717.  
  718.  
  719. # copy a chunk, would better be a method of class Chunk...
  720. def chunkcopy(ch):
  721.     if ch.chtype == chunk_type[GROUP]:
  722.     return chunk(GROUP, ch.where, map(chunkcopy, ch.data))
  723.     else:
  724.     return chunk(ch.chtype, ch.where, ch.data)
  725.  
  726.  
  727. # get next argument for TeX-macro, flatten a group (insert between)
  728. # or return Command Sequence token, or give back one character
  729. def getnextarg(length, buf, pp, item):
  730.  
  731.     ##wobj = Wobj()
  732.     ##dumpit(buf, wobj.write, pp[item:min(length, item + 5)])
  733.     ##print 'GETNEXTARG, (len, item) =', `length, item` + ' ---> ' + wobj.data + ' <---'
  734.  
  735.     while item < length and pp[item].chtype == chunk_type[ENDLINE]:
  736.     del pp[item]
  737.     length = length - 1
  738.     if item >= length:
  739.     raise error, 'no next arg.' + epsilon(buf, pp[-1].where)
  740.     if pp[item].chtype == chunk_type[GROUP]:
  741.     newpp = pp[item].data
  742.     del pp[item]
  743.     length = length - 1
  744.     changeit(buf, newpp)
  745.     length = length + len(newpp)
  746.     pp[item:item] = newpp
  747.     item = item + len(newpp)
  748.     if len(newpp) < 10:
  749.         wobj = Wobj()
  750.         dumpit(buf, wobj.write, newpp)
  751.         ##print 'GETNEXTARG: inserted ' + `wobj.data`
  752.     return length, item
  753.     elif pp[item].chtype == chunk_type[PLAIN]:
  754.     #grab one char
  755.     print 'WARNING: grabbing one char'
  756.     if len(s(buf, pp[item].data)) > 1:
  757.         pp.insert(item, chunk(PLAIN, pp[item].where, s(buf, pp[item].data)[:1]))
  758.         item, length = item+1, length+1
  759.         pp[item].data = s(buf, pp[item].data)[1:]
  760.     else:
  761.         item = item+1
  762.     return length, item
  763.     else:
  764.     ch = pp[item]
  765.     try:
  766.         str = `s(buf, ch.data)`
  767.     except TypeError:
  768.         str = `ch.data`
  769.         if len(str) > 400:
  770.         str = str[:400] + '...'
  771.     print 'GETNEXTARG:', ch.chtype, 'not handled, data ' + str
  772.     return length, item
  773.  
  774.  
  775. # this one is needed to find the end of LaTeX's optional argument, like
  776. # item[...]
  777. re_endopt = regex.compile(']')
  778.  
  779. # get a LaTeX-optional argument, you know, the square braces '[' and ']'
  780. def getoptarg(length, buf, pp, item):
  781.  
  782.     wobj = Wobj()
  783.     dumpit(buf, wobj.write, pp[item:min(length, item + 5)])
  784.     ##print 'GETOPTARG, (len, item) =', `length, item` + ' ---> ' + wobj.data + ' <---'
  785.  
  786.     if item >= length or \
  787.           pp[item].chtype != chunk_type[PLAIN] or \
  788.           s(buf, pp[item].data)[0] != '[':
  789.     return length, item
  790.  
  791.     pp[item].data = s(buf, pp[item].data)[1:]
  792.     if len(pp[item].data) == 0:
  793.     del pp[item]
  794.     length = length-1
  795.  
  796.     while 1:
  797.     if item == length:
  798.         raise error, 'No end of optional arg found'
  799.     if pp[item].chtype == chunk_type[PLAIN]:
  800.         text = s(buf, pp[item].data)
  801.         pos = re_endopt.search(text)
  802.         if pos >= 0:
  803.         pp[item].data = text[:pos]
  804.         if pos == 0:
  805.             del pp[item]
  806.             length = length-1
  807.         else:
  808.             item=item+1
  809.         text = text[pos+1:]
  810.  
  811.         while text and text[0] in ' \t':
  812.             text = text[1:]
  813.  
  814.         if text:
  815.             pp.insert(item, chunk(PLAIN, 0, text))
  816.             length = length + 1
  817.         return length, item
  818.  
  819.     item = item+1
  820.  
  821.  
  822. # Wobj just add write-requests to the ``data'' attribute
  823. class Wobj:
  824.     data = ''
  825.  
  826.     def write(self, data):
  827.     self.data = self.data + data
  828.  
  829. # ignore these commands
  830. ignoredcommands = ('bcode', 'ecode')
  831. # map commands like these to themselves as plaintext
  832. wordsselves = ('UNIX', 'ABC', 'C', 'ASCII', 'EOF', 'LaTeX')
  833. # \{ --> {,  \} --> }, etc
  834. themselves = ('{', '}', ',', '.', '@', ' ', '\n') + wordsselves
  835. # these ones also themselves (see argargs macro in myformat.sty)
  836. inargsselves = (',', '[', ']', '(', ')')
  837. # this is how *I* would show the difference between emph and strong
  838. #  code 1 means: fold to uppercase
  839. markcmds = {'code': ('', ''), 'var': 1, 'emph': ('_', '_'),
  840.       'strong': ('*', '*')}
  841.  
  842. # recognise patter {\FONTCHANGE-CMD TEXT} to \MAPPED-FC-CMD{TEXT}
  843. fontchanges = {'rm': 'r', 'it': 'i', 'em': 'emph', 'bf': 'b', 'tt': 't'}
  844.  
  845. # transparent for these commands
  846. for_texi = ('emph', 'var', 'strong', 'code', 'kbd', 'key', 'dfn', 'samp',
  847.         'file', 'r', 'i', 't')
  848.  
  849.  
  850. # try to remove macros and return flat text
  851. def flattext(buf, pp):
  852.     pp = crcopy(pp)
  853.     ##print '---> FLATTEXT ' + `pp`
  854.     wobj = Wobj()
  855.  
  856.     i, length = 0, len(pp)
  857.     while 1:
  858.     if len(pp) != length:
  859.         raise 'FATAL', 'inconsistent length'
  860.     if i >= length:
  861.         break
  862.     ch = pp[i]
  863.     i = i+1
  864.     if ch.chtype == chunk_type[PLAIN]:
  865.         pass
  866.     elif ch.chtype == chunk_type[CSNAME]:
  867.         s_buf_data = s(buf, ch.data)
  868.         if s_buf_data in themselves or hist.inargs and s_buf_data in inargsselves:
  869.         ch.chtype = chunk_type[PLAIN]
  870.         elif s_buf_data == 'e':
  871.         ch.chtype = chunk_type[PLAIN]
  872.         ch.data = '\\'
  873.         elif len(s_buf_data) == 1 \
  874.               and s_buf_data in onlylatexspecial:
  875.         ch.chtype = chunk_type[PLAIN]
  876.         # if it is followed by an empty group,
  877.         # remove that group, it was needed for
  878.         # a true space
  879.         if i < length \
  880.               and pp[i].chtype==chunk_type[GROUP] \
  881.               and len(pp[i].data) == 0:
  882.             del pp[i]
  883.             length = length-1
  884.  
  885.         elif s_buf_data in markcmds.keys():
  886.         length, newi = getnextarg(length, buf, pp, i)
  887.         str = flattext(buf, pp[i:newi])
  888.         del pp[i:newi]
  889.         length = length - (newi - i)
  890.         ch.chtype = chunk_type[PLAIN]
  891.         markcmd = s_buf_data
  892.         x = markcmds[markcmd]
  893.         if type(x) == TupleType:
  894.             pre, after = x
  895.             str = pre+str+after
  896.         elif x == 1:
  897.             str = string.upper(str)
  898.         else:
  899.             raise 'FATAL', 'corrupt markcmds'
  900.         ch.data = str
  901.         else:
  902.         if s_buf_data not in ignoredcommands:
  903.             print 'WARNING: deleting command ' + s_buf_data
  904.             print 'PP' + `pp[i-1]`
  905.         del pp[i-1]
  906.         i, length = i-1, length-1
  907.     elif ch.chtype == chunk_type[GROUP]:
  908.         length, newi = getnextarg(length, buf, pp, i-1)
  909.         i = i-1
  910. ##            str = flattext(buf, crcopy(pp[i-1:newi]))
  911. ##            del pp[i:newi]
  912. ##            length = length - (newi - i)
  913. ##            ch.chtype = chunk_type[PLAIN]
  914. ##            ch.data = str
  915.     else:
  916.         pass
  917.  
  918.     dumpit(buf, wobj.write, pp)
  919.     ##print 'FLATTEXT: RETURNING ' + `wobj.data`
  920.     return wobj.data
  921.  
  922. # try to generate node names (a bit shorter than the chapter title)
  923. # note that the \nodename command (see elsewhere) overules these efforts
  924. def invent_node_names(text):
  925.     words = string.split(text)
  926.  
  927.     ##print 'WORDS ' + `words`
  928.  
  929.     if len(words) == 2 \
  930.        and string.lower(words[0]) == 'built-in' \
  931.        and string.lower(words[1]) not in ('modules', 'functions'):
  932.     return words[1]
  933.     if len(words) == 3 and string.lower(words[1]) == 'module':
  934.     return words[2]
  935.     if len(words) == 3 and string.lower(words[1]) == 'object':
  936.     return string.join(words[0:2])
  937.     if len(words) > 4 \
  938.        and (string.lower(string.join(words[-4:])) \
  939.         == 'methods and data attributes'):
  940.     return string.join(words[:2])
  941.     return text
  942.  
  943. re_commas_etc = regex.compile('[,`\'@{}]')
  944.  
  945. re_whitespace = regex.compile('[ \t]*')
  946.  
  947.  
  948. ##nodenamecmd = next_command_p(length, buf, pp, newi, 'nodename')
  949.  
  950. # look if the next non-white stuff is also a command, resulting in skipping
  951. # double endlines (DENDLINE) too, and thus omitting \par's
  952. # Sometimes this is too much, maybe consider DENDLINE's as stop
  953. def next_command_p(length, buf, pp, i, cmdname):
  954.  
  955.     while 1:
  956.     if i >= len(pp):
  957.         break
  958.     ch = pp[i]
  959.     i = i+1
  960.     if ch.chtype == chunk_type[ENDLINE]:
  961.         continue
  962.     if ch.chtype == chunk_type[DENDLINE]:
  963.         continue
  964.     if ch.chtype == chunk_type[PLAIN]:
  965.         if re_whitespace.search(s(buf, ch.data)) == 0 and \
  966.               re_whitespace.match(s(buf, ch.data)) == len(s(buf, ch.data)):
  967.         continue
  968.         return -1
  969.     if ch.chtype == chunk_type[CSNAME]:
  970.         if s(buf, ch.data) == cmdname:
  971.         return i # _after_ the command
  972.         return -1
  973.     return -1
  974.  
  975.  
  976. # things that are special to LaTeX, but not to texi..
  977. onlylatexspecial = '_~^$#&%'
  978.  
  979. class Struct: pass
  980.  
  981. hist = Struct()
  982. out = Struct()
  983.  
  984. def startchange():
  985.     global hist, out
  986.  
  987.     hist.inenv = []
  988.     hist.nodenames = []
  989.     hist.cindex = []
  990.     hist.inargs = 0
  991.     hist.enumeratenesting, hist.itemizenesting = 0, 0
  992.  
  993.     out.doublenodes = []
  994.     out.doublecindeces = []
  995.  
  996.  
  997. spacech = [chunk(PLAIN, 0, ' ')]
  998. commach = [chunk(PLAIN, 0, ', ')]
  999. cindexch = [chunk(CSLINE, 0, 'cindex')]
  1000.  
  1001. # the standard variation in symbols for itemize
  1002. itemizesymbols = ['bullet', 'minus', 'dots']
  1003.  
  1004. # same for enumerate
  1005. enumeratesymbols = ['1', 'A', 'a']
  1006.  
  1007. ##
  1008. ## \begin{ {func,data,exc}desc }{name}...
  1009. ##   the resulting texi-code is dependent on the contents of indexsubitem
  1010. ##
  1011.  
  1012. # indexsubitem: `['XXX', 'function']
  1013. # funcdesc:
  1014. #     deffn {`idxsi`} NAME (FUNCARGS)
  1015.  
  1016. # indexsubitem: `['XXX', 'method']`
  1017. # funcdesc:
  1018. #     defmethod {`idxsi[0]`} NAME (FUNCARGS)
  1019.  
  1020. # indexsubitem: `['in', 'module', 'MODNAME']'
  1021. # datadesc:
  1022. #     defcv data {`idxsi[1:]`} NAME
  1023. # excdesc:
  1024. #     defcv exception {`idxsi[1:]`} NAME
  1025. # funcdesc:
  1026. #     deffn {function of `idxsi[1:]`} NAME (FUNCARGS)
  1027.  
  1028. # indexsubitem: `['OBJECT', 'attribute']'
  1029. # datadesc
  1030. #     defcv attribute {`OBJECT`} NAME
  1031.  
  1032.  
  1033. ## this routine will be called on \begin{funcdesc}{NAME}{ARGS}
  1034. ##   or \funcline{NAME}{ARGS}
  1035. ##
  1036. def do_funcdesc(length, buf, pp, i):
  1037.     startpoint = i-1
  1038.     ch = pp[startpoint]
  1039.     wh = ch.where
  1040.     length, newi = getnextarg(length, buf, pp, i)
  1041.     funcname = chunk(GROUP, wh, pp[i:newi])
  1042.     del pp[i:newi]
  1043.     length = length - (newi-i)
  1044.     save = hist.inargs
  1045.     hist.inargs = 1
  1046.     length, newi = getnextarg(length, buf, pp, i)
  1047.     hist.inargs = save
  1048.     del save
  1049.     the_args = [chunk(PLAIN, wh, '()'[0])] + pp[i:newi] + \
  1050.            [chunk(PLAIN, wh, '()'[1])]
  1051.     del pp[i:newi]
  1052.     length = length - (newi-i)
  1053.  
  1054.     idxsi = hist.indexsubitem    # words
  1055.     command = ''
  1056.     cat_class = ''
  1057.     if idxsi and idxsi[-1] in ('method', 'protocol', 'attribute'):
  1058.     command = 'defmethod'
  1059.     cat_class = string.join(idxsi[:-1])
  1060.     elif len(idxsi) == 2 and idxsi[1] == 'function':
  1061.     command = 'deffn'
  1062.     cat_class = string.join(idxsi)
  1063.     elif len(idxsi) == 3 and idxsi[:2] == ['in', 'module']:
  1064.     command = 'deffn'
  1065.     cat_class = 'function of ' + string.join(idxsi[1:])
  1066.  
  1067.     if not command:
  1068.     raise error, 'don\'t know what to do with indexsubitem ' + `idxsi`
  1069.  
  1070.     ch.chtype = chunk_type[CSLINE]
  1071.     ch.data = command
  1072.  
  1073.     cslinearg = [chunk(GROUP, wh, [chunk(PLAIN, wh, cat_class)])]
  1074.     cslinearg.append(chunk(PLAIN, wh, ' '))
  1075.     cslinearg.append(funcname)
  1076.     cslinearg.append(chunk(PLAIN, wh, ' '))
  1077.     l = len(cslinearg)
  1078.     cslinearg[l:l] = the_args
  1079.  
  1080.     pp.insert(i, chunk(GROUP, wh, cslinearg))
  1081.     i, length = i+1, length+1
  1082.     hist.command = command
  1083.     return length, i
  1084.  
  1085.  
  1086. ## this routine will be called on \begin{excdesc}{NAME}
  1087. ## or \excline{NAME}
  1088. ##    
  1089. def do_excdesc(length, buf, pp, i):
  1090.     startpoint = i-1
  1091.     ch = pp[startpoint]
  1092.     wh = ch.where
  1093.     length, newi = getnextarg(length, buf, pp, i)
  1094.     excname = chunk(GROUP, wh, pp[i:newi])
  1095.     del pp[i:newi]
  1096.     length = length - (newi-i)
  1097.  
  1098.     idxsi = hist.indexsubitem    # words
  1099.     command = ''
  1100.     cat_class = ''
  1101.     class_class = ''
  1102.     if len(idxsi) == 2 and idxsi[1] == 'exception':
  1103.     command = 'defvr'
  1104.     cat_class = string.join(idxsi)
  1105.     elif len(idxsi) == 3 and idxsi[:2] == ['in', 'module']:
  1106.     command = 'defcv'
  1107.     cat_class = 'exception'
  1108.     class_class = string.join(idxsi[1:])
  1109.     elif len(idxsi) == 4 and idxsi[:3] == ['exception', 'in', 'module']:
  1110.     command = 'defcv'
  1111.     cat_class = 'exception'
  1112.     class_class = string.join(idxsi[2:])
  1113.  
  1114.  
  1115.     if not command:
  1116.     raise error, 'don\'t know what to do with indexsubitem ' + `idxsi`
  1117.  
  1118.     ch.chtype = chunk_type[CSLINE]
  1119.     ch.data = command
  1120.  
  1121.     cslinearg = [chunk(GROUP, wh, [chunk(PLAIN, wh, cat_class)])]
  1122.     cslinearg.append(chunk(PLAIN, wh, ' '))
  1123.     if class_class:
  1124.     cslinearg.append(chunk(GROUP, wh, [chunk(PLAIN, wh, class_class)]))
  1125.     cslinearg.append(chunk(PLAIN, wh, ' '))
  1126.     cslinearg.append(excname)
  1127.  
  1128.     pp.insert(i, chunk(GROUP, wh, cslinearg))
  1129.     i, length = i+1, length+1
  1130.     hist.command = command
  1131.     return length, i
  1132.  
  1133. ## same for datadesc or dataline...
  1134. def do_datadesc(length, buf, pp, i):
  1135.     startpoint = i-1
  1136.     ch = pp[startpoint]
  1137.     wh = ch.where
  1138.     length, newi = getnextarg(length, buf, pp, i)
  1139.     dataname = chunk(GROUP, wh, pp[i:newi])
  1140.     del pp[i:newi]
  1141.     length = length - (newi-i)
  1142.  
  1143.     idxsi = hist.indexsubitem    # words
  1144.     command = ''
  1145.     cat_class = ''
  1146.     class_class = ''
  1147.     if idxsi[-1] in ('attribute', 'option'):
  1148.     command = 'defcv'
  1149.     cat_class = idxsi[-1]
  1150.     class_class = string.join(idxsi[:-1])
  1151.     elif len(idxsi) == 3 and idxsi[:2] == ['in', 'module']:
  1152.     command = 'defcv'
  1153.     cat_class = 'data'
  1154.     class_class = string.join(idxsi[1:])
  1155.     elif len(idxsi) == 4 and idxsi[:3] == ['data', 'in', 'module']:
  1156.     command = 'defcv'
  1157.     cat_class = 'data'
  1158.     class_class = string.join(idxsi[2:])
  1159.     else:
  1160.     command = 'defcv'
  1161.     cat_class = 'data'
  1162.     class_class = string.join(idxsi)
  1163.  
  1164.     ch.chtype = chunk_type[CSLINE]
  1165.     ch.data = command
  1166.  
  1167.     cslinearg = [chunk(GROUP, wh, [chunk(PLAIN, wh, cat_class)])]
  1168.     cslinearg.append(chunk(PLAIN, wh, ' '))
  1169.     if class_class:
  1170.     cslinearg.append(chunk(GROUP, wh, [chunk(PLAIN, wh, class_class)]))
  1171.     cslinearg.append(chunk(PLAIN, wh, ' '))
  1172.     cslinearg.append(dataname)
  1173.  
  1174.     pp.insert(i, chunk(GROUP, wh, cslinearg))
  1175.     i, length = i+1, length+1
  1176.     hist.command = command
  1177.     return length, i
  1178.  
  1179.  
  1180. # regular indices: those that are not set in tt font by default....
  1181. regindices = ('cindex', )
  1182.  
  1183. # remove illegal characters from node names
  1184. def rm_commas_etc(text):
  1185.     result = ''
  1186.     changed = 0
  1187.     while 1:
  1188.     pos = re_commas_etc.search(text)
  1189.     if pos >= 0:
  1190.         changed = 1
  1191.         result = result + text[:pos]
  1192.         text = text[pos+1:]
  1193.     else:
  1194.         result = result + text
  1195.         break
  1196.     if changed:
  1197.     print 'Warning: nodename changhed to ' + `result`
  1198.  
  1199.     return result
  1200.  
  1201. # boolean flags
  1202. flags = {'texi': 1}
  1203.  
  1204.  
  1205. ##
  1206. ## changeit: the actual routine, that changes the contents of the parsed
  1207. ##           chunks
  1208. ##
  1209.  
  1210. def changeit(buf, pp):
  1211.     global onlylatexspecial, hist, out
  1212.  
  1213.     i, length = 0, len(pp)
  1214.     while 1:
  1215.     # sanity check: length should always equal len(pp)
  1216.     if len(pp) != length:
  1217.         raise 'FATAL', 'inconsistent length. thought ' + `length` + ', but should really be ' + `len(pp)`
  1218.     if i >= length:
  1219.         break
  1220.     ch = pp[i]
  1221.     i = i + 1
  1222.  
  1223.     if type(ch) is StringType:
  1224.         #normally, only chunks are present in pp,
  1225.         # but in some cases, some extra info
  1226.         # has been inserted, e.g., the \end{...} clauses
  1227.         raise 'FATAL', 'got string, probably too many ' + `end`
  1228.  
  1229.     if ch.chtype == chunk_type[GROUP]:
  1230.         # check for {\em ...} constructs
  1231.         if ch.data and \
  1232.            ch.data[0].chtype == chunk_type[CSNAME] and \
  1233.            s(buf, ch.data[0].data) in fontchanges.keys():
  1234.         k = s(buf, ch.data[0].data)
  1235.         del ch.data[0]
  1236.         pp.insert(i-1, chunk(CSNAME, ch.where, fontchanges[k]))
  1237.         length, i = length+1, i+1
  1238.  
  1239.         # recursively parse the contents of the group
  1240.         changeit(buf, ch.data)
  1241.  
  1242.     elif ch.chtype == chunk_type[IF]:
  1243.         # \if...
  1244.         flag, negate, data = ch.data
  1245.         ##print 'IF: flag, negate = ' + `flag, negate`
  1246.         if flag not in flags.keys():
  1247.         raise error, 'unknown flag ' + `flag`
  1248.  
  1249.         value = flags[flag]
  1250.         if negate:
  1251.         value = (not value)
  1252.         del pp[i-1]
  1253.         length, i = length-1, i-1
  1254.         if value:
  1255.         pp[i:i] = data
  1256.         length = length + len(data)
  1257.  
  1258.  
  1259.     elif ch.chtype == chunk_type[ENV]:
  1260.         # \begin{...} ....
  1261.         envname, data = ch.data
  1262.  
  1263.         #push this environment name on stack
  1264.         hist.inenv.insert(0, envname)
  1265.  
  1266.         #append an endenv chunk after grouped data
  1267.         data.append(chunk(ENDENV, ch.where, envname))
  1268.         ##[`data`]
  1269.  
  1270.         #delete this object
  1271.         del pp[i-1]
  1272.         i, length = i-1, length-1
  1273.  
  1274.         #insert found data
  1275.         pp[i:i] = data
  1276.         length = length + len(data)
  1277.  
  1278.         if envname == 'verbatim':
  1279.         pp[i:i] = [chunk(CSLINE, ch.where, 'example'),
  1280.               chunk(GROUP, ch.where, [])]
  1281.         length, i = length+2, i+2
  1282.  
  1283.         elif envname == 'itemize':
  1284.         if hist.itemizenesting > len(itemizesymbols):
  1285.             raise error, 'too deep itemize nesting'
  1286.         ingroupch = [chunk(CSNAME, ch.where,
  1287.               itemizesymbols[hist.itemizenesting])]
  1288.         hist.itemizenesting = hist.itemizenesting + 1
  1289.         pp[i:i] = [chunk(CSLINE, ch.where, 'itemize'),
  1290.               chunk(GROUP, ch.where, ingroupch)]
  1291.         length, i = length+2, i+2
  1292.  
  1293.         elif envname == 'enumerate':
  1294.         if hist.enumeratenesting > len(enumeratesymbols):
  1295.             raise error, 'too deep enumerate nesting'
  1296.         ingroupch = [chunk(PLAIN, ch.where,
  1297.               enumeratesymbols[hist.enumeratenesting])]
  1298.         hist.enumeratenesting = hist.enumeratenesting + 1
  1299.         pp[i:i] = [chunk(CSLINE, ch.where, 'enumerate'),
  1300.               chunk(GROUP, ch.where, ingroupch)]
  1301.         length, i = length+2, i+2
  1302.  
  1303.         elif envname == 'description':
  1304.         ingroupch = [chunk(CSNAME, ch.where, 'b')]
  1305.         pp[i:i] = [chunk(CSLINE, ch.where, 'table'),
  1306.               chunk(GROUP, ch.where, ingroupch)]
  1307.         length, i = length+2, i+2
  1308.  
  1309.         elif (envname == 'tableiii') or (envname == 'tableii'):
  1310.         if (envname == 'tableii'):
  1311.             ltable = 2
  1312.         else:
  1313.             ltable = 3
  1314.         wh = ch.where
  1315.         newcode = []
  1316.  
  1317.         #delete tabular format description
  1318.         # e.g., {|l|c|l|}
  1319.         length, newi = getnextarg(length, buf, pp, i)
  1320.         del pp[i:newi]
  1321.         length = length - (newi-i)
  1322.  
  1323.         newcode.append(chunk(CSLINE, wh, 'table'))
  1324.         ingroupch = [chunk(CSNAME, wh, 'asis')]
  1325.         newcode.append(chunk(GROUP, wh, ingroupch))
  1326.         newcode.append(chunk(CSLINE, wh, 'item'))
  1327.  
  1328.         #get the name of macro for @item
  1329.         # e.g., {code}
  1330.         length, newi = getnextarg(length, buf, pp, i)
  1331.  
  1332.         if newi-i != 1:
  1333.             raise error, 'Sorry, expected 1 chunk argument'
  1334.         if pp[i].chtype != chunk_type[PLAIN]:
  1335.             raise error, 'Sorry, expected plain text argument'
  1336.         hist.itemargmacro = s(buf, pp[i].data)
  1337.         del pp[i:newi]
  1338.         length = length - (newi-i)
  1339.  
  1340.         itembody = []
  1341.         for count in range(ltable):
  1342.             length, newi = getnextarg(length, buf, pp, i)
  1343.             emphgroup = [
  1344.                   chunk(CSNAME, wh, 'emph'),
  1345.                   chunk(GROUP, 0, pp[i:newi])]
  1346.             del pp[i:newi]
  1347.             length = length - (newi-i)
  1348.             if count == 0:
  1349.             itemarg = emphgroup
  1350.             elif count == ltable-1:
  1351.             itembody = itembody + \
  1352.                   [chunk(PLAIN, wh, '  ---  ')] + emphgroup
  1353.             else:
  1354.             itembody = emphgroup
  1355.         newcode.append(chunk(GROUP, wh, itemarg))
  1356.         newcode = newcode + itembody + [chunk(DENDLINE, wh, '\n')]
  1357.         pp[i:i] = newcode
  1358.         l = len(newcode)
  1359.         length, i = length+l, i+l
  1360.         del newcode, l
  1361.  
  1362.         if length != len(pp):
  1363.             raise 'STILL, SOMETHING wrong', `i`
  1364.  
  1365.  
  1366.         elif envname == 'funcdesc':
  1367.         pp.insert(i, chunk(PLAIN, ch.where, ''))
  1368.         i, length = i+1, length+1
  1369.         length, i = do_funcdesc(length, buf, pp, i)
  1370.  
  1371.         elif envname == 'excdesc':
  1372.         pp.insert(i, chunk(PLAIN, ch.where, ''))
  1373.         i, length = i+1, length+1
  1374.         length, i = do_excdesc(length, buf, pp, i)
  1375.  
  1376.         elif envname == 'datadesc':
  1377.         pp.insert(i, chunk(PLAIN, ch.where, ''))
  1378.         i, length = i+1, length+1
  1379.         length, i = do_datadesc(length, buf, pp, i)
  1380.  
  1381.         else:
  1382.         print 'WARNING: don\'t know what to do with env ' + `envname`
  1383.  
  1384.     elif ch.chtype == chunk_type[ENDENV]:
  1385.         envname = ch.data
  1386.         if envname != hist.inenv[0]:
  1387.         raise error, '\'end\' does not match. Name ' + `envname` + ', expected ' + `hist.inenv[0]`
  1388.         del hist.inenv[0]
  1389.         del pp[i-1]
  1390.         i, length = i-1, length-1
  1391.  
  1392.         if envname == 'verbatim':
  1393.         pp[i:i] = [
  1394.               chunk(CSLINE, ch.where, 'end'),
  1395.               chunk(GROUP, ch.where, [
  1396.               chunk(PLAIN, ch.where, 'example')])]
  1397.         i, length = i+2, length+2
  1398.         elif envname == 'itemize':
  1399.         hist.itemizenesting = hist.itemizenesting - 1
  1400.         pp[i:i] = [
  1401.               chunk(CSLINE, ch.where, 'end'),
  1402.               chunk(GROUP, ch.where, [
  1403.               chunk(PLAIN, ch.where, 'itemize')])]
  1404.         i, length = i+2, length+2
  1405.         elif envname == 'enumerate':
  1406.         hist.enumeratenesting = hist.enumeratenesting-1
  1407.         pp[i:i] = [
  1408.               chunk(CSLINE, ch.where, 'end'),
  1409.               chunk(GROUP, ch.where, [
  1410.               chunk(PLAIN, ch.where, 'enumerate')])]
  1411.         i, length = i+2, length+2
  1412.         elif envname == 'description':
  1413.         pp[i:i] = [
  1414.               chunk(CSLINE, ch.where, 'end'),
  1415.               chunk(GROUP, ch.where, [
  1416.               chunk(PLAIN, ch.where, 'table')])]
  1417.         i, length = i+2, length+2
  1418.         elif (envname == 'tableiii') or (envname == 'tableii'):
  1419.         pp[i:i] = [
  1420.               chunk(CSLINE, ch.where, 'end'),
  1421.               chunk(GROUP, ch.where, [
  1422.               chunk(PLAIN, ch.where, 'table')])]
  1423.         i, length = i+2, length + 2
  1424.         pp.insert(i, chunk(DENDLINE, ch.where, '\n'))
  1425.         i, length = i+1, length+1
  1426.  
  1427.         elif envname in ('funcdesc', 'excdesc', 'datadesc'):
  1428.         pp[i:i] = [
  1429.               chunk(CSLINE, ch.where, 'end'),
  1430.               chunk(GROUP, ch.where, [
  1431.               chunk(PLAIN, ch.where, hist.command)])]
  1432.         i, length = i+2, length+2
  1433.         else:
  1434.         print 'WARNING: ending env ' + `envname` + 'has no actions'
  1435.  
  1436.     elif ch.chtype == chunk_type[CSNAME]:
  1437.         # control name transformations
  1438.         s_buf_data = s(buf, ch.data)
  1439.         if s_buf_data == 'optional':
  1440.         pp[i-1].chtype = chunk_type[PLAIN]
  1441.         pp[i-1].data = '['
  1442.         if (i < length) and \
  1443.            (pp[i].chtype == chunk_type[GROUP]):
  1444.             cp=pp[i].data
  1445.             pp[i:i+1]=cp + [
  1446.             chunk(PLAIN, ch.where, ']')]
  1447.             length = length+len(cp)
  1448.         elif s_buf_data in ignoredcommands:
  1449.         del pp[i-1]
  1450.         i, length = i-1, length-1
  1451.         elif s_buf_data == '@' and \
  1452.               i != length and \
  1453.               pp[i].chtype == chunk_type[PLAIN] and \
  1454.               s(buf, pp[i].data)[0] == '.':
  1455.         # \@. --> \. --> @.
  1456.         ch.data = '.'
  1457.         del pp[i]
  1458.         length = length-1
  1459.         elif s_buf_data == '\\':
  1460.         # \\ --> \* --> @*
  1461.         ch.data = '*'
  1462.         elif len(s_buf_data) == 1 and \
  1463.               s_buf_data in onlylatexspecial:
  1464.         ch.chtype = chunk_type[PLAIN]
  1465.         # check if such a command is followed by
  1466.         # an empty group: e.g., `\%{}'.  If so, remove
  1467.         # this empty group too
  1468.         if i < length and \
  1469.               pp[i].chtype == chunk_type[GROUP] \
  1470.               and len(pp[i].data) == 0:
  1471.             del pp[i]
  1472.             length = length-1
  1473.  
  1474.         elif hist.inargs and s_buf_data in inargsselves:
  1475.         # This is the special processing of the
  1476.         # arguments of the \begin{funcdesc}... or
  1477.         # \funcline... arguments
  1478.         # \, --> , \[ --> [, \] --> ]
  1479.         ch.chtype = chunk_type[PLAIN]
  1480.  
  1481.         elif s_buf_data == 'renewcommand':
  1482.         # \renewcommand{\indexsubitem}....
  1483.         i, length = i-1, length-1
  1484.         del pp[i]
  1485.         length, newi = getnextarg(length, buf, pp, i)
  1486.         if newi-i == 1 \
  1487.               and i < length \
  1488.               and pp[i].chtype == chunk_type[CSNAME] \
  1489.               and s(buf, pp[i].data) == 'indexsubitem':
  1490.             del pp[i:newi]
  1491.             length = length - (newi-i)
  1492.             length, newi = getnextarg(length, buf, pp, i)
  1493.             text = flattext(buf, pp[i:newi])
  1494.             if text[:1] != '(' or text[-1:] != ')':
  1495.             raise error, \
  1496.                   'expected indexsubitem enclosed in parenteses'
  1497.             words = string.split(text[1:-1])
  1498.             hist.indexsubitem = words
  1499. ##             print 'set hist.indexsubitem =', words
  1500.             del text, words
  1501.         else:
  1502.             print 'WARNING: renewcommand with unsupported arg removed'
  1503.         del pp[i:newi]
  1504.         length = length - (newi-i)
  1505.  
  1506.         elif s_buf_data == 'item':
  1507.         ch.chtype = chunk_type[CSLINE]
  1508.         length, newi = getoptarg(length, buf, pp, i)
  1509.         ingroupch = pp[i:newi]
  1510.         del pp[i:newi]
  1511.         length = length - (newi-i)
  1512.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1513.         i, length = i+1, length+1
  1514.  
  1515.         elif s_buf_data == 'ttindex':
  1516.         idxsi = hist.indexsubitem
  1517.  
  1518.         cat_class = ''
  1519.         if len(idxsi) >= 2 and idxsi[1] in \
  1520.               ('method', 'function', 'protocol'):
  1521.             command = 'findex'
  1522.         elif len(idxsi) >= 2 and idxsi[1] in \
  1523.               ('exception', 'object'):
  1524.             command = 'vindex'
  1525.         elif len(idxsi) == 3 and idxsi[:2] == ['in', 'module']:
  1526.             command = 'cindex'
  1527.         else:
  1528.             print 'WARNING: can\'t categorize ' + `idxsi` \
  1529.               + ' for \'ttindex\' command'
  1530.             command = 'cindex'
  1531.  
  1532.         if not cat_class:
  1533.             cat_class = '('+string.join(idxsi)+')'
  1534.  
  1535.         ch.chtype = chunk_type[CSLINE]
  1536.         ch.data = command
  1537.  
  1538.         length, newi = getnextarg(length, buf, pp, i)
  1539.         arg = pp[i:newi]
  1540.         del pp[i:newi]
  1541.         length = length - (newi-i)
  1542.  
  1543.         cat_arg = [chunk(PLAIN, ch.where, cat_class)]
  1544.  
  1545.         # determine what should be set in roman, and
  1546.         # what in tt-font
  1547.         if command in regindices:
  1548.  
  1549.             arg = [chunk(CSNAME, ch.where, 't'),
  1550.                   chunk(GROUP, ch.where, arg)]
  1551.         else:
  1552.             cat_arg = [chunk(CSNAME, ch.where, 'r'),
  1553.                   chunk(GROUP, ch.where, cat_arg)]
  1554.  
  1555.         ingroupch = arg + \
  1556.               [chunk(PLAIN, ch.where, ' ')] + \
  1557.               cat_arg
  1558.  
  1559.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1560.         length, i = length+1, i+1
  1561.  
  1562.         elif s_buf_data == 'ldots':
  1563.         # \ldots --> \dots{} --> @dots{}
  1564.         ch.data = 'dots'
  1565.         if i == length \
  1566.               or pp[i].chtype != chunk_type[GROUP] \
  1567.               or pp[i].data != []:
  1568.             pp.insert(i, chunk(GROUP, ch.where, []))
  1569.             i, length = i+1, length+1
  1570.         elif s_buf_data in themselves:
  1571.         # \UNIX --> UNIX
  1572.         ch.chtype = chunk_type[PLAIN]
  1573.         if i != length \
  1574.               and pp[i].chtype == chunk_type[GROUP] \
  1575.               and pp[i].data == []:
  1576.             del pp[i]
  1577.             length = length-1
  1578.         elif s_buf_data in for_texi:
  1579.         pass
  1580.  
  1581.         elif s_buf_data == 'e':
  1582.         # "\e" --> "\"
  1583.         ch.data = '\\'
  1584.         ch.chtype = chunk_type[PLAIN]
  1585.         elif s_buf_data in ('lineiii', 'lineii'):
  1586.         # This is the most tricky one
  1587.         # \lineiii{a1}{a2}[{a3}] -->
  1588.         # @item @<cts. of itemargmacro>{a1}
  1589.         #  a2 [ -- a3]
  1590.         #
  1591.         ##print 'LINEIIIIII!!!!!!!'
  1592. ##                wobj = Wobj()
  1593. ##                dumpit(buf, wobj.write, pp[i-1:i+5])
  1594. ##                print '--->' + wobj.data + '<----'
  1595.         if not hist.inenv:
  1596.             raise error, 'no environment for lineiii'
  1597.         if (hist.inenv[0] != 'tableiii') and \
  1598.            (hist.inenv[0] != 'tableii'):
  1599.             raise error, \
  1600.               'wrong command (%s) in wrong environment (%s)' \
  1601.               % (s_buf_data, `hist.inenv[0]`)
  1602.         ch.chtype = chunk_type[CSLINE]
  1603.         ch.data = 'item'
  1604.         length, newi = getnextarg(length, buf, pp, i)
  1605.         ingroupch = [chunk(CSNAME, 0, hist.itemargmacro),
  1606.                  chunk(GROUP, 0, pp[i:newi])]
  1607.         del pp[i:newi]
  1608.         length = length - (newi-i)
  1609. ##                print 'ITEM ARG: --->',
  1610. ##                wobj = Wobj()
  1611. ##                dumpit(buf, wobj.write, ingroupch)
  1612. ##                print wobj.data, '<---'
  1613.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1614.         grouppos = i
  1615.         i, length = i+1, length+1
  1616.         length, i = getnextarg(length, buf, pp, i)
  1617.         length, newi = getnextarg(length, buf, pp, i)
  1618.         if newi > i:
  1619.             # we have a 3rd arg
  1620.             pp.insert(i, chunk(PLAIN, ch.where, '  ---  '))
  1621.             i = newi + 1
  1622.             length = length + 1
  1623. ##                    pp[grouppos].data = pp[grouppos].data \
  1624. ##                          + [chunk(PLAIN, ch.where, '  ')] \
  1625. ##                          + pp[i:newi]
  1626. ##                    del pp[i:newi]
  1627. ##                    length = length - (newi-i)
  1628.         if length != len(pp):
  1629.             raise 'IN LINEIII IS THE ERR', `i`
  1630.  
  1631.         elif s_buf_data in ('chapter', 'section', 'subsection', 'subsubsection'):
  1632.         #\xxxsection{A} ---->
  1633.         # @node A, , ,
  1634.         # @xxxsection A
  1635.         ## also: remove commas and quotes
  1636.         ch.chtype = chunk_type[CSLINE]
  1637.         length, newi = getnextarg(length, buf, pp, i)
  1638.         afternodenamecmd = next_command_p(length, buf, pp, newi, 'nodename')
  1639.         if afternodenamecmd < 0:
  1640.             cp1 = crcopy(pp[i:newi])
  1641.             pp[i:newi] = [chunk(GROUP, ch.where, pp[i:newi])]
  1642.             length, newi = length - (newi-i) + 1, i+1
  1643.             text = flattext(buf, cp1)
  1644.             text = invent_node_names(text)
  1645.         else:
  1646.             length, endarg = getnextarg(length, buf, pp, afternodenamecmd)
  1647.             cp1 = crcopy(pp[afternodenamecmd:endarg])
  1648.             del pp[newi:endarg]
  1649.             length = length - (endarg-newi)
  1650.  
  1651.             pp[i:newi] = [chunk(GROUP, ch.where, pp[i:newi])]
  1652.             length, newi = length - (newi-i) + 1, i + 1
  1653.             text = flattext(buf, cp1)
  1654.         if text[-1] == '.':
  1655.             text = text[:-1]
  1656. ##                print 'FLATTEXT:', `text`
  1657.         if text in hist.nodenames:
  1658.             print 'WARNING: node name ' + `text` + ' already used'
  1659.             out.doublenodes.append(text)
  1660.         else:
  1661.             hist.nodenames.append(text)
  1662.         text = rm_commas_etc(text)
  1663.         pp[i-1:i-1] = [chunk(CSLINE, ch.where, 'node'),
  1664.                    chunk(GROUP, ch.where, [
  1665.                    chunk(PLAIN, ch.where, text+', , ,')
  1666.                    ])]
  1667.         i, length = newi+2, length+2
  1668.  
  1669.         elif s_buf_data == 'funcline':
  1670.         # fold it to a very short environment
  1671.         pp[i-1:i-1] = [chunk(CSLINE, ch.where, 'end'),
  1672.                    chunk(GROUP, ch.where, [
  1673.                    chunk(PLAIN, ch.where, hist.command)])]
  1674.         i, length = i+2, length+2
  1675.         length, i = do_funcdesc(length, buf, pp, i)
  1676.  
  1677.         elif s_buf_data == 'dataline':
  1678.         pp[i-1:i-1] = [chunk(CSLINE, ch.where, 'end'),
  1679.                    chunk(GROUP, ch.where, [
  1680.                    chunk(PLAIN, ch.where, hist.command)])]
  1681.         i, length = i+2, length+2
  1682.         length, i = do_datadesc(length, buf, pp, i)
  1683.  
  1684.         elif s_buf_data == 'excline':
  1685.         pp[i-1:i-1] = [chunk(CSLINE, ch.where, 'end'),
  1686.                    chunk(GROUP, ch.where, [
  1687.                    chunk(PLAIN, ch.where, hist.command)])]
  1688.         i, length = i+2, length+2
  1689.         length, i = do_excdesc(length, buf, pp, i)
  1690.  
  1691.         elif s_buf_data == 'index':
  1692.         #\index{A} --->
  1693.         # @cindex A
  1694.         ch.chtype = chunk_type[CSLINE]
  1695.         ch.data = 'cindex'
  1696.         length, newi = getnextarg(length, buf, pp, i)
  1697.  
  1698.         ingroupch = pp[i:newi]
  1699.         del pp[i:newi]
  1700.         length = length - (newi-i)
  1701.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1702.         length, i = length+1, i+1
  1703.  
  1704.         elif s_buf_data == 'bifuncindex':
  1705.         ch.chtype = chunk_type[CSLINE]
  1706.         ch.data = 'findex'
  1707.         length, newi = getnextarg(length, buf, pp, i)
  1708.         ingroupch = pp[i:newi]
  1709.         del pp[i:newi]
  1710.         length = length - (newi-i)
  1711.  
  1712.         ingroupch.append(chunk(PLAIN, ch.where, ' '))
  1713.         ingroupch.append(chunk(CSNAME, ch.where, 'r'))
  1714.         ingroupch.append(chunk(GROUP, ch.where, [
  1715.               chunk(PLAIN, ch.where,
  1716.               '(built-in function)')]))
  1717.  
  1718.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1719.         length, i = length+1, i+1
  1720.  
  1721.         elif s_buf_data == 'obindex':
  1722.         ch.chtype = chunk_type[CSLINE]
  1723.         ch.data = 'findex'
  1724.         length, newi = getnextarg(length, buf, pp, i)
  1725.         ingroupch = pp[i:newi]
  1726.         del pp[i:newi]
  1727.         length = length - (newi-i)
  1728.  
  1729.         ingroupch.append(chunk(PLAIN, ch.where, ' '))
  1730.         ingroupch.append(chunk(CSNAME, ch.where, 'r'))
  1731.         ingroupch.append(chunk(GROUP, ch.where, [
  1732.               chunk(PLAIN, ch.where,
  1733.               '(object)')]))
  1734.  
  1735.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1736.         length, i = length+1, i+1
  1737.  
  1738.         elif s_buf_data == 'opindex':
  1739.         ch.chtype = chunk_type[CSLINE]
  1740.         ch.data = 'findex'
  1741.         length, newi = getnextarg(length, buf, pp, i)
  1742.         ingroupch = pp[i:newi]
  1743.         del pp[i:newi]
  1744.         length = length - (newi-i)
  1745.  
  1746.         ingroupch.append(chunk(PLAIN, ch.where, ' '))
  1747.         ingroupch.append(chunk(CSNAME, ch.where, 'r'))
  1748.         ingroupch.append(chunk(GROUP, ch.where, [
  1749.               chunk(PLAIN, ch.where,
  1750.               '(operator)')]))
  1751.  
  1752.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1753.         length, i = length+1, i+1
  1754.  
  1755.         elif s_buf_data == 'bimodindex':
  1756.         ch.chtype = chunk_type[CSLINE]
  1757.         ch.data = 'pindex'
  1758.         length, newi = getnextarg(length, buf, pp, i)
  1759.         ingroupch = pp[i:newi]
  1760.         del pp[i:newi]
  1761.         length = length - (newi-i)
  1762.  
  1763.         ingroupch.append(chunk(PLAIN, ch.where, ' '))
  1764.         ingroupch.append(chunk(CSNAME, ch.where, 'r'))
  1765.         ingroupch.append(chunk(GROUP, ch.where, [
  1766.               chunk(PLAIN, ch.where,
  1767.               '(built-in)')]))
  1768.  
  1769.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1770.         length, i = length+1, i+1
  1771.  
  1772.         elif s_buf_data == 'sectcode':
  1773.         ch.data = 'code'
  1774.  
  1775.         elif s_buf_data == 'stmodindex':
  1776.         ch.chtype = chunk_type[CSLINE]
  1777.         # use the program index as module index
  1778.         ch.data = 'pindex'
  1779.         length, newi = getnextarg(length, buf, pp, i)
  1780.         ingroupch = pp[i:newi]
  1781.         del pp[i:newi]
  1782.         length = length - (newi-i)
  1783.  
  1784.         ingroupch.append(chunk(PLAIN, ch.where, ' '))
  1785.         ingroupch.append(chunk(CSNAME, ch.where, 'r'))
  1786.         ingroupch.append(chunk(GROUP, ch.where, [
  1787.               chunk(PLAIN, ch.where,
  1788.               '(standard)')]))
  1789.  
  1790.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1791.         length, i = length+1, i+1
  1792.  
  1793.         elif s_buf_data == 'stindex':
  1794.         # XXX must actually go to newindex st
  1795.         wh = ch.where
  1796.         ch.chtype = chunk_type[CSLINE]
  1797.         ch.data = 'cindex'
  1798.         length, newi = getnextarg(length, buf, pp, i)
  1799.         ingroupch = [chunk(CSNAME, wh, 'code'),
  1800.               chunk(GROUP, wh, pp[i:newi])]
  1801.  
  1802.         del pp[i:newi]
  1803.         length = length - (newi-i)
  1804.  
  1805.         t = ingroupch[:]
  1806.         t.append(chunk(PLAIN, wh, ' statement'))
  1807.  
  1808.         pp.insert(i, chunk(GROUP, wh, t))
  1809.         i, length = i+1, length+1
  1810.  
  1811.         pp.insert(i, chunk(CSLINE, wh, 'cindex'))
  1812.         i, length = i+1, length+1
  1813.  
  1814.         t = ingroupch[:]
  1815.         t.insert(0, chunk(PLAIN, wh, 'statement, '))
  1816.  
  1817.         pp.insert(i, chunk(GROUP, wh, t))
  1818.         i, length = i+1, length+1
  1819.  
  1820.         elif s_buf_data == 'indexii':
  1821.         #\indexii{A}{B} --->
  1822.         # @cindex A B
  1823.         # @cindex B, A
  1824.         length, newi = getnextarg(length, buf, pp, i)
  1825.         cp11 = pp[i:newi]
  1826.         cp21 = crcopy(pp[i:newi])
  1827.         del pp[i:newi]
  1828.         length = length - (newi-i)
  1829.         length, newi = getnextarg(length, buf, pp, i)
  1830.         cp12 = pp[i:newi]
  1831.         cp22 = crcopy(pp[i:newi])
  1832.         del pp[i:newi]
  1833.         length = length - (newi-i)
  1834.  
  1835.         ch.chtype = chunk_type[CSLINE]
  1836.         ch.data = 'cindex'
  1837.         pp.insert(i, chunk(GROUP, ch.where, cp11 + [
  1838.               chunk(PLAIN, ch.where, ' ')] + cp12))
  1839.         i, length = i+1, length+1
  1840.         pp[i:i] = [chunk(CSLINE, ch.where, 'cindex'),
  1841.               chunk(GROUP, ch.where, cp22 + [
  1842.               chunk(PLAIN, ch.where, ', ')]+ cp21)]
  1843.         i, length = i+2, length+2
  1844.  
  1845.         elif s_buf_data == 'indexiii':
  1846.         length, newi = getnextarg(length, buf, pp, i)
  1847.         cp11 = pp[i:newi]
  1848.         cp21 = crcopy(pp[i:newi])
  1849.         cp31 = crcopy(pp[i:newi])
  1850.         del pp[i:newi]
  1851.         length = length - (newi-i)
  1852.         length, newi = getnextarg(length, buf, pp, i)
  1853.         cp12 = pp[i:newi]
  1854.         cp22 = crcopy(pp[i:newi])
  1855.         cp32 = crcopy(pp[i:newi])
  1856.         del pp[i:newi]
  1857.         length = length - (newi-i)
  1858.         length, newi = getnextarg(length, buf, pp, i)
  1859.         cp13 = pp[i:newi]
  1860.         cp23 = crcopy(pp[i:newi])
  1861.         cp33 = crcopy(pp[i:newi])
  1862.         del pp[i:newi]
  1863.         length = length - (newi-i)
  1864.  
  1865.         ch.chtype = chunk_type[CSLINE]
  1866.         ch.data = 'cindex'
  1867.         pp.insert(i, chunk(GROUP, ch.where, cp11 + [
  1868.               chunk(PLAIN, ch.where, ' ')] + cp12
  1869.               + [chunk(PLAIN, ch.where, ' ')]
  1870.               + cp13))
  1871.         i, length = i+1, length+1
  1872.         pp[i:i] = [chunk(CSLINE, ch.where, 'cindex'),
  1873.               chunk(GROUP, ch.where, cp22 + [
  1874.               chunk(PLAIN, ch.where, ' ')]+ cp23
  1875.               + [chunk(PLAIN, ch.where, ', ')] +
  1876.               cp21)]
  1877.         i, length = i+2, length+2
  1878.         pp[i:i] = [chunk(CSLINE, ch.where, 'cindex'),
  1879.               chunk(GROUP, ch.where, cp33 + [
  1880.               chunk(PLAIN, ch.where, ', ')]+ cp31
  1881.               + [chunk(PLAIN, ch.where, ' ')] +
  1882.               cp32)]
  1883.         i, length = i+2, length+2
  1884.  
  1885.         elif s_buf_data == 'indexiv':
  1886.         length, newi = getnextarg(length, buf, pp, i)
  1887.         cp11 = pp[i:newi]
  1888.         cp21 = crcopy(pp[i:newi])
  1889.         cp31 = crcopy(pp[i:newi])
  1890.         cp41 = crcopy(pp[i:newi])
  1891.         del pp[i:newi]
  1892.         length = length - (newi-i)
  1893.         length, newi = getnextarg(length, buf, pp, i)
  1894.         cp12 = pp[i:newi]
  1895.         cp22 = crcopy(pp[i:newi])
  1896.         cp32 = crcopy(pp[i:newi])
  1897.         cp42 = crcopy(pp[i:newi])
  1898.         del pp[i:newi]
  1899.         length = length - (newi-i)
  1900.         length, newi = getnextarg(length, buf, pp, i)
  1901.         cp13 = pp[i:newi]
  1902.         cp23 = crcopy(pp[i:newi])
  1903.         cp33 = crcopy(pp[i:newi])
  1904.         cp43 = crcopy(pp[i:newi])
  1905.         del pp[i:newi]
  1906.         length = length - (newi-i)
  1907.         length, newi = getnextarg(length, buf, pp, i)
  1908.         cp14 = pp[i:newi]
  1909.         cp24 = crcopy(pp[i:newi])
  1910.         cp34 = crcopy(pp[i:newi])
  1911.         cp44 = crcopy(pp[i:newi])
  1912.         del pp[i:newi]
  1913.         length = length - (newi-i)
  1914.  
  1915.         ch.chtype = chunk_type[CSLINE]
  1916.         ch.data = 'cindex'
  1917.         ingroupch = cp11 + \
  1918.               spacech + cp12 + \
  1919.               spacech + cp13 + \
  1920.               spacech + cp14
  1921.         pp.insert(i, chunk(GROUP, ch.where, ingroupch))
  1922.         i, length = i+1, length+1
  1923.         ingroupch = cp22 + \
  1924.               spacech + cp23 + \
  1925.               spacech + cp24 + \
  1926.               commach + cp21
  1927.         pp[i:i] = cindexch + [
  1928.               chunk(GROUP, ch.where, ingroupch)]
  1929.         i, length = i+2, length+2
  1930.         ingroupch = cp33 + \
  1931.               spacech + cp34 + \
  1932.               commach + cp31 + \
  1933.               spacech + cp32
  1934.         pp[i:i] = cindexch + [
  1935.               chunk(GROUP, ch.where, ingroupch)]
  1936.         i, length = i+2, length+2
  1937.         ingroupch = cp44 + \
  1938.               commach + cp41 + \
  1939.               spacech + cp42 + \
  1940.               spacech + cp43
  1941.         pp[i:i] = cindexch + [
  1942.               chunk(GROUP, ch.where, ingroupch)]
  1943.         i, length = i+2, length+2
  1944.  
  1945. ##         elif s_buf_data == 'indexsubitem':
  1946. ##         ch.data = flattext(buf, [ch])
  1947. ##         ch.chtype = chunk_type[PLAIN]
  1948.  
  1949.         elif s_buf_data in ('noindent', 'indexsubitem'):
  1950.         pass
  1951.  
  1952.         else:
  1953.         print "don't know what to do with keyword " + s_buf_data
  1954.  
  1955.  
  1956. re_atsign = regex.compile('[@{}]')
  1957. re_newline = regex.compile('\n')
  1958.  
  1959. def dumpit(buf, wm, pp):
  1960.  
  1961.     global out
  1962.  
  1963.     i, length = 0, len(pp)
  1964.  
  1965.     addspace = 0
  1966.  
  1967.     while 1:
  1968.     if len(pp) != length:
  1969.         raise 'FATAL', 'inconsistent length'
  1970.     if i == length:
  1971.         break
  1972.     ch = pp[i]
  1973.     i = i + 1
  1974.  
  1975.     dospace = addspace
  1976.     addspace = 0
  1977.  
  1978.     if ch.chtype == chunk_type[CSNAME]:
  1979.         s_buf_data = s(buf, ch.data)
  1980.         wm('@' + s_buf_data)
  1981.         if s_buf_data == 'node' and \
  1982.               pp[i].chtype == chunk_type[PLAIN] and \
  1983.               s(buf, pp[i].data) in out.doublenodes:
  1984.         ##XXX doesnt work yet??
  1985.         wm(' ZZZ-' + zfill(`i`, 4))
  1986.         if s_buf_data[0] in string.letters:
  1987.         addspace = 1
  1988.     elif ch.chtype == chunk_type[PLAIN]:
  1989.         if dospace and s(buf, ch.data) not in (' ', '\t'):
  1990.         wm(' ')
  1991.         text = s(buf, ch.data)
  1992.         while 1:
  1993.         pos = re_atsign.search(text)
  1994.         if pos < 0:
  1995.             break
  1996.         wm(text[:pos] + '@' + text[pos])
  1997.         text = text[pos+1:]
  1998.         wm(text)
  1999.     elif ch.chtype == chunk_type[GROUP]:
  2000.         wm('{')
  2001.         dumpit(buf, wm, ch.data)
  2002.         wm('}')
  2003.     elif ch.chtype == chunk_type[DENDLINE]:
  2004.         wm('\n\n')
  2005.         while i != length and pp[i].chtype in \
  2006.               (chunk_type[DENDLINE], chunk_type[ENDLINE]):
  2007.         i = i + 1
  2008.     elif ch.chtype == chunk_type[OTHER]:
  2009.         wm(s(buf, ch.data))
  2010.     elif ch.chtype == chunk_type[ACTIVE]:
  2011.         wm(s(buf, ch.data))
  2012.     elif ch.chtype == chunk_type[ENDLINE]:
  2013.         wm('\n')
  2014.     elif ch.chtype == chunk_type[CSLINE]:
  2015.         if i >= 2 and pp[i-2].chtype not in \
  2016.               (chunk_type[ENDLINE], chunk_type[DENDLINE]) \
  2017.               and (pp[i-2].chtype != chunk_type[PLAIN]
  2018.               or s(buf, pp[i-2].data)[-1] != '\n'):
  2019.  
  2020.         wm('\n')
  2021.         wm('@' + s(buf, ch.data))
  2022.         if i == length:
  2023.         raise error, 'CSLINE expected another chunk'
  2024.         if pp[i].chtype != chunk_type[GROUP]:
  2025.         raise error, 'CSLINE expected GROUP'
  2026.         if type(pp[i].data) != ListType:
  2027.         raise error, 'GROUP chould contain []-data'
  2028.  
  2029.         wobj = Wobj()
  2030.         dumpit(buf, wobj.write, pp[i].data)
  2031.         i = i + 1
  2032.         text = wobj.data
  2033.         del wobj
  2034.         if text:
  2035.         wm(' ')
  2036.         while 1:
  2037.             pos = re_newline.search(text)
  2038.             if pos < 0:
  2039.             break
  2040.             print 'WARNING: found newline in csline arg'
  2041.             wm(text[:pos] + ' ')
  2042.             text = text[pos+1:]
  2043.         wm(text)
  2044.         if i >= length or \
  2045.               pp[i].chtype not in (chunk_type[CSLINE],
  2046.               chunk_type[ENDLINE], chunk_type[DENDLINE]) \
  2047.               and (pp[i].chtype != chunk_type[PLAIN]
  2048.               or s(buf, pp[i].data)[0] != '\n'):
  2049.         wm('\n')
  2050.  
  2051.     elif ch.chtype == chunk_type[COMMENT]:
  2052. ##         print 'COMMENT: previous chunk =', pp[i-2]
  2053. ##         if pp[i-2].chtype == chunk_type[PLAIN]:
  2054. ##         print 'PLAINTEXT =', `s(buf, pp[i-2].data)`
  2055.         if s(buf, ch.data) and \
  2056.               regex.match('^[ \t]*$', s(buf, ch.data)) < 0:
  2057.         if i >= 2 \
  2058.            and pp[i-2].chtype not in (chunk_type[ENDLINE], chunk_type[DENDLINE]) \
  2059.            and not (pp[i-2].chtype == chunk_type[PLAIN]
  2060.                 and regex.match('\\(.\\|\n\\)*[ \t]*\n$', s(buf, pp[i-2].data)) >= 0):
  2061.             wm('\n')
  2062.         wm('@c ' + s(buf, ch.data))
  2063.     elif ch.chtype == chunk_type[IGNORE]:
  2064.         pass
  2065.     else:
  2066.         try:
  2067.         str = `s(buf, ch.data)`
  2068.         except TypeError:
  2069.         str = `ch.data`
  2070.         if len(str) > 400:
  2071.         str = str[:400] + '...'
  2072.         print 'warning:', ch.chtype, 'not handled, data ' + str
  2073.  
  2074.  
  2075.  
  2076. def main():
  2077.     outfile = None
  2078.     headerfile = 'texipre.dat'
  2079.     trailerfile = 'texipost.dat'
  2080.  
  2081.     try:
  2082.     opts, args = getopt.getopt(sys.argv[1:], 'o:h:t:')
  2083.     except getopt.error:
  2084.     args = []
  2085.  
  2086.     if not args:
  2087.     print 'usage: partparse [-o outfile] [-h headerfile]',
  2088.     print '[-t trailerfile] file ...'
  2089.     sys.exit(2)
  2090.  
  2091.     for opt, arg in opts:
  2092.     if opt == '-o': outfile = arg
  2093.     if opt == '-h': headerfile = arg
  2094.     if opt == '-t': trailerfile = arg
  2095.  
  2096.     if not outfile:
  2097.     root, ext = os.path.splitext(args[0])
  2098.     outfile = root + '.texi'
  2099.  
  2100.     if outfile in args:
  2101.     print 'will not overwrite input file', outfile
  2102.     sys.exit(2)
  2103.  
  2104.     outf = open(outfile, 'w')
  2105.     outf.write(open(headerfile, 'r').read())
  2106.  
  2107.     for file in args:
  2108.     if len(args) > 1: print '='*20, file, '='*20
  2109.     buf = open(file, 'r').read()
  2110.     w, pp = parseit(buf)
  2111.     startchange()
  2112.     changeit(buf, pp)
  2113.     dumpit(buf, outf.write, pp)
  2114.  
  2115.     outf.write(open(trailerfile, 'r').read())
  2116.  
  2117.     outf.close()
  2118.  
  2119. if __name__ == "__main__":
  2120.     main()
  2121.