]> git.lyx.org Git - lyx.git/blob - lib/examples/listerrors.lyx
fix typo that put too many include paths for most people
[lyx.git] / lib / examples / listerrors.lyx
1 #LyX 1.2 created this file. For more info see http://www.lyx.org/
2 \lyxformat 220
3 \textclass literate-article
4 \begin_preamble
5 %
6 % ps2pdf stuff
7 %
8 \usepackage[ps2pdf,pdftitle={LyX listerrors re-implemented},urlcolor=blue,linktocpage,letterpaper,colorlinks=true]{hyperref}
9 \@savsf=1% This is to get around a hyperref+noweb interaction problem
10 \hyphenpenalty 10000
11
12 %
13 % This (from the noweb FAQ) relaxes the constraint that chunks are never broken across pages.
14 %
15 \def\nwendcode{\endtrivlist \endgroup \vfil\penalty10\vfilneg}
16 \let\nwdocspar=\smallbreak
17 \end_preamble
18 \language english
19 \inputencoding auto
20 \fontscheme pslatex
21 \graphics default
22 \paperfontsize default
23 \spacing single 
24 \papersize Default
25 \paperpackage a4
26 \use_geometry 0
27 \use_amsmath 0
28 \use_natbib 0
29 \use_numerical_citations 0
30 \paperorientation portrait
31 \secnumdepth 3
32 \tocdepth 3
33 \paragraph_separation indent
34 \defskip medskip
35 \quotes_language english
36 \quotes_times 2
37 \papercolumns 1
38 \papersides 1
39 \paperpagestyle default
40
41 \layout Title
42
43 LyX listerrors:
44 \newline 
45 rewritten in Python
46 \layout Author
47
48 Kayvan A.
49  Sylvan
50 \newline 
51
52 \begin_inset LatexCommand \url{mailto:kayvan@sylvan.com}
53
54 \end_inset 
55
56
57 \layout Date
58
59 3/15/2002
60 \layout Abstract
61
62 The listerrors program used to be compiled as a C program and installed
63  as 
64 \emph on 
65 BINDIR/listerrors
66 \emph default 
67  along with LyX in order to perform some simple re-formatting of noweb and
68  GCC error messages.
69  This document describes and implements the Python version of the same program.
70 \layout Standard
71
72
73 \begin_inset LatexCommand \tableofcontents{}
74
75 \end_inset 
76
77
78 \layout Section
79
80 Introduction
81 \layout Standard
82
83 The motivation for this program was Bugzilla bug 190
84 \begin_inset Foot
85 collapsed true
86
87 \layout Standard
88
89 Visit 
90 \begin_inset LatexCommand \url{http://bugzilla.lyx.org/show_bug.cgi?id=190}
91
92 \end_inset 
93
94  for the details.
95 \end_inset 
96
97  dealing with the 
98 \begin_inset Quotes eld
99 \end_inset 
100
101 listerrors
102 \begin_inset Quotes erd
103 \end_inset 
104
105  executable.
106 \layout Standard
107
108 What is 
109 \begin_inset Quotes eld
110 \end_inset 
111
112 listerrors
113 \begin_inset Quotes erd
114 \end_inset 
115
116 ? Usually, LyX has great support for parsing of error messages.
117  For each error in the log file, LyX pops up an error box at that location
118  in the LyX window.
119  The error scanning routines expect these errors to be in a certain format
120  (similar to LaTeX errors).
121  When dealing with Literate Programs, you have 
122 \begin_inset Quotes eld
123 \end_inset 
124
125 noweb
126 \begin_inset Foot
127 collapsed true
128
129 \layout Standard
130
131 See 
132 \begin_inset LatexCommand \url{http://www.eecs.harvard.edu/~nr/noweb}
133
134 \end_inset 
135
136  for more information about noweb.
137 \end_inset 
138
139
140 \begin_inset Quotes erd
141 \end_inset 
142
143  as well as gcc error messages (and potentially others).
144  The listerrors program attempts to standardize these error messages to
145  a format that LyX can parse and react to.
146 \layout Standard
147
148 In a nutshell, the problems with the old implementation of listerrors that
149  bug 190 refers to were::
150 \layout Enumerate
151
152 It was a C program and it was installed in the user path in the same directory
153  as LyX.
154  Having such a generically named binary in, for example, 
155 \emph on 
156 /usr/bin
157 \emph default 
158 , was potentially confusing.
159 \layout Enumerate
160
161 It required that noweb be installed on the compiling machine (the source
162  was extracted by noweb from 
163 \emph on 
164 SRCDIR/examples/Literate.lyx
165 \emph default 
166 , compiled and installed by 
167 \begin_inset Quotes eld
168 \end_inset 
169
170 make install
171 \begin_inset Quotes erd
172 \end_inset 
173
174 ).
175 \layout Standard
176
177 The new version deals with these problems in the following fashion:
178 \layout Enumerate
179
180 Both the example file (this document) and the program are to be added to
181  the LyX CVS repository.
182 \layout Enumerate
183
184 The program itself will be installed in 
185 \emph on 
186 SHAREDIR/lyx/scripts
187 \emph default 
188 , along with other LyX-specific helper scripts.
189 \layout Standard
190
191 In the design and implementation of this new 
192 \begin_inset Quotes eld
193 \end_inset 
194
195 listerrors
196 \begin_inset Quotes erd
197 \end_inset 
198
199 , the Python
200 \begin_inset Foot
201 collapsed true
202
203 \layout Standard
204
205 See the Python home page (
206 \begin_inset LatexCommand \url{http://www.python.org}
207
208 \end_inset 
209
210 ) for more information.
211 \end_inset 
212
213  language was chosen since it is fully multi-platform and provides a very
214  uniform and easy to read syntax.
215  This re-write also simplifies the code for 
216 \begin_inset Quotes eld
217 \end_inset 
218
219 listerrors
220 \begin_inset Quotes erd
221 \end_inset 
222
223  greatly.
224  Python is installed by default on all modern Linux systems and is freely
225  available for all other platforms.
226 \layout Scrap
227
228 <<listerrors>>=
229 \newline 
230 #!/usr/bin/python
231 \newline 
232 """reformat noweb and compiler errors for LyX.
233 \newline 
234
235 \newline 
236 Expects to read from stdin and output to stdout.
237 \newline 
238 """
239 \newline 
240
241 \newline 
242 __author__ = "Kayvan A.
243  Sylvan <kayvan@sylvan.com>"
244 \newline 
245 __date__ = "$Date: 2002/03/25 23:29:30 $"
246 \newline 
247 __version__ = "$Revision: 1.2 $"
248 \newline 
249 __credits__ = """Edmar Wienskoski Jr.
250  <edmar-w-jr@technologist.com>
251 \newline 
252     original Literate support for LyX.
253 \newline 
254 Bernard Michael Hurley <berhardh@westherts.ac.uk>
255 \newline 
256     modifications to original listerrors."""
257 \newline 
258 __copyright__ = "Copyright 2002 - The LyX team."
259 \newline 
260
261 \newline 
262 import sys, string
263 \newline 
264
265 \newline 
266 <<Function Bodies>>
267 \newline 
268
269 \newline 
270 if __name__ == "__main__":
271 \newline 
272   main()
273 \newline 
274 @
275 \layout Section
276
277 LaTeX style error message
278 \layout Standard
279
280 The following function mimics the TeX error message format.
281 \layout Scrap
282
283 <<Function Bodies>>=
284 \newline 
285 def write_error(msg, tool = "noweb", line_number = 1):
286 \newline 
287   """Write out the given message in TeX error style.
288 \newline 
289
290 \newline 
291   called like: write_error(msg, tool, line_number)."""
292 \newline 
293   print "! Build Error: ==> %s ==>
294 \backslash 
295 n" % (tool),
296 \newline 
297   print " ...
298 \backslash 
299 n
300 \backslash 
301 nl.%d ...
302 \backslash 
303 n" % (line_number),
304 \newline 
305   if type(msg) == type("str"): # simple string
306 \newline 
307     print msg
308 \newline 
309   else: # some kind of list (sequence or tuple)
310 \newline 
311     for m in msg:
312 \newline 
313         if m != "": print m,
314 \newline 
315     print
316 \newline 
317
318 \newline 
319 @ %def write_error
320 \layout Section
321
322 Filtering errors
323 \layout Standard
324
325 The only complication in our filtering code is that some parsers might need
326  to push back lines that are read in to be read again later.
327  We solve this problem by implementing a 
328 \begin_inset Quotes eld
329 \end_inset 
330
331 getline
332 \begin_inset Quotes erd
333 \end_inset 
334
335  and 
336 \begin_inset Quotes eld
337 \end_inset 
338
339 pushline
340 \begin_inset Quotes erd
341 \end_inset 
342
343  set of functions:
344 \layout Scrap
345
346 <<Function Bodies>>=
347 \newline 
348 __lines = [] # lines pushed back
349 \newline 
350
351 \newline 
352 def getline(file = sys.stdin):
353 \newline 
354   """read a line from internal stack or from file.
355 \newline 
356
357 \newline 
358   optional file argument defaults to sys.stdin."""
359 \newline 
360   global __lines
361 \newline 
362   lines = __lines
363 \newline 
364   if lines:
365 \newline 
366     line = lines.pop()
367 \newline 
368   else:
369 \newline 
370     line = file.readline()
371 \newline 
372   return line
373 \newline 
374
375 \newline 
376 @ %def getline
377 \layout Standard
378
379 And now for the corresponding pushline function:
380 \layout Scrap
381
382 <<Function Bodies>>=
383 \newline 
384 def pushline(line):
385 \newline 
386   "push a line onto the pushback stack."
387 \newline 
388   global __lines
389 \newline 
390   lines = __lines
391 \newline 
392   lines.append(line)
393 \newline 
394
395 \newline 
396 @ %def pushline
397 \layout Standard
398
399 The main() entry point function is extremely simple.
400  Note that this version of 
401 \begin_inset Quotes eld
402 \end_inset 
403
404 listerrors
405 \begin_inset Quotes erd
406 \end_inset 
407
408  takes no options and simply filters, attempting simply to match against
409  the known error message patterns.
410  The listerrors C program handled a single-character command-line argument
411  that the current code no longer needs.
412  
413 \layout Scrap
414
415 <<Function Bodies>>=
416 \newline 
417 def main():
418 \newline 
419   """Entry point for listerrors.
420  Takes no options.
421 \newline 
422
423 \newline 
424   Reads stdin and writes to stdout.
425  Filter errors"""
426 \newline 
427
428 \newline 
429   while 1:
430 \newline 
431     line = getline()
432 \newline 
433     if line == "": break
434 \newline 
435     <<Check line against patterns and take action>>
436 \newline 
437 @ %def main
438 \layout Standard
439
440 For each line read in, we need to find out if it matches any of our tools
441  (noweb, gcc, etc.) and act accordingly.
442 \layout Scrap
443
444 <<Check line against patterns and take action>>=
445 \newline 
446 try_patterns_dispatch = [ noweb_try, gcc_try, xlc_try ]
447 \newline 
448 for predicate in try_patterns_dispatch:
449 \newline 
450   if predicate(line): break
451 \newline 
452 @
453 \layout Section
454
455 Different Error Formats
456 \layout Standard
457
458 The following sections handle the various error message formats that we
459  recognize in this program.
460  
461 \layout Subsection
462
463 noweb errors
464 \layout Standard
465
466 Noweb errors are output on a single line, so examining just the current
467  line is enough.
468 \layout Scrap
469
470 <<Function Bodies>>=
471 \newline 
472 def noweb_try(line):
473 \newline 
474   """see if line is a noweb error.
475 \newline 
476
477 \newline 
478   Returns 1 on success, 0 otherwise.
479  Outputs on stdout."""
480 \newline 
481   retval = 0
482 \newline 
483   <<Look for the unescaped angle-brackets in documentation>>
484 \newline 
485   <<Look for anything with double angle brackets>>
486 \newline 
487   <<Last ditch effort scan for specific strings>>
488 \newline 
489   return retval
490 \newline 
491
492 \newline 
493 @ %def noweb_try
494 \layout Standard
495
496 First, we look for the 
497 \begin_inset Quotes eld
498 \end_inset 
499
500 unescaped < < in documentation chunk
501 \begin_inset Quotes erd
502 \end_inset 
503
504  message.
505  This is the only message with an associated line number from noweb.
506 \layout Scrap
507
508 <<Look for the unescaped angle-brackets in documentation>>=
509 \newline 
510 if string.find(line, ": unescaped << in documentation chunk") != -1:
511 \newline 
512   line_parts = string.split(line, ':')
513 \newline 
514   num_str = line_parts[1]
515 \newline 
516   num_len = len(num_str)
517 \newline 
518   i = 0
519 \newline 
520   while i < num_len and (num_str[i] in string.digits): i = i + 1
521 \newline 
522   if i == num_len:
523 \newline 
524     write_error(":" + line_parts[2], "noweb", int(num_str))
525 \newline 
526     retval = 1
527 \newline 
528 @
529 \layout Standard
530
531 Some noweb messages are simply about undefined scraps.
532  These can be seen by looking for matching double-angle-brackets.
533 \layout Scrap
534
535 <<Look for anything with double angle brackets>>=
536 \newline 
537 if (not retval):
538 \newline 
539   left = string.find(line, "<<")
540 \newline 
541   if (left != -1) and ((left + 2) < len(line)) and 
542 \backslash 
543
544 \newline 
545      (string.find(line[left+2:], ">>") != -1):
546 \newline 
547     write_error(line, "noweb");
548 \newline 
549     retval = 1;
550 \newline 
551 @
552 \layout Standard
553
554 Finally, here is an additional list of explicit strings to check for.
555 \layout Scrap
556
557 <<Last ditch effort scan for specific strings>>=
558 \newline 
559 if (not retval):
560 \newline 
561   msgs_to_try = ("couldn't open file",
562 \newline 
563     "couldn't open temporary file",
564 \newline 
565     "error writing temporary file",
566 \newline 
567     "ill-formed option",
568 \newline 
569     "unknown option",
570 \newline 
571     "Bad format sequence",
572 \newline 
573     "Can't open output file",
574 \newline 
575     "Can't open temporary file",
576 \newline 
577     "Capacity exceeded:",
578 \newline 
579     "Ignoring unknown option -",
580 \newline 
581     "This can't happen:",
582 \newline 
583     "non-numeric line number in")
584 \newline 
585   for msg in msgs_to_try:
586 \newline 
587     if string.find(line, msg) != -1:
588 \newline 
589       write_error(line, "noweb")
590 \newline 
591       retval = 1
592 \newline 
593       break
594 \newline 
595 @
596 \layout Subsection
597
598 gcc errors
599 \layout Standard
600
601 The gcc errors can be multi-line, with the following format:
602 \layout LyX-Code
603
604 foo.c: In function `main': 
605 \newline 
606 foo.c:3: `bar' undeclared (first use in this function) 
607 \newline 
608 foo.c:3: (Each undeclared identifier is reported only once 
609 \newline 
610 foo.c:3: for each function it appears in.) 
611 \newline 
612 foo.c:3: parse error before `x'
613 \layout Standard
614
615 In order to parse this, the gcc error handler has to look ahead and return
616  any and all lines that do not match the expected pattern.
617 \layout Scrap
618
619 <<Function Bodies>>=
620 \newline 
621 def gcc_try(line):
622 \newline 
623   """See if line is a gcc error.
624  Read ahead to handle all the lines.
625 \newline 
626
627 \newline 
628   Returns 1 on success, 0 otherwise.
629  Outputs on stdout."""
630 \newline 
631   retval = 0
632 \newline 
633   <<Handle the gcc error message>>
634 \newline 
635   return retval
636 \newline 
637
638 \newline 
639 @ %def gcc_try
640 \layout Standard
641
642 The error message starts with a gcc header (as above) without an associated
643  line number.
644 \layout Scrap
645
646 <<Handle the gcc error message>>= 
647 \newline 
648 first_space = string.find(line, ' ')
649 \newline 
650 if first_space > 1: # The smallest would be "X: "
651 \newline 
652   if line[first_space - 1] == ':':
653 \newline 
654     header_to_see = line[:first_space - 1]
655 \newline 
656     next_line = getline()
657 \newline 
658     if next_line and next_line[:first_space - 1] == header_to_see:
659 \newline 
660       num_end = first_space
661 \newline 
662       while next_line[num_end] in string.digits: num_end = num_end + 1
663 \newline 
664       if num_end > first_space: # good!
665 \newline 
666         <<Accumulate gcc error lines and print it>>
667 \newline 
668       else: # oops! Not a gcc error.
669 \newline 
670         pushline(next_line)
671 \newline 
672     elif next_line:
673 \newline 
674       pushline(next_line) # return this line to input stream
675 \newline 
676 @
677 \layout Standard
678
679 At the point in the code that we know that we are in the middle of an error
680  message, we do the following:
681 \layout Scrap
682
683 <<Accumulate gcc error lines and print it>>=
684 \newline 
685 num_str = next_line[first_space:num_end]
686 \newline 
687 msgs = [line[first_space:]]
688 \newline 
689 msgs.append(next_line[num_end + 1:])
690 \newline 
691 header_to_see = next_line[:num_end]
692 \newline 
693 next_line = getline()
694 \newline 
695 while next_line and next_line[:num_end] == header_to_see:
696 \newline 
697   msgs.append(next_line[num_end + 1:])
698 \newline 
699   next_line = getline()
700 \newline 
701 if next_line: pushline(next_line)
702 \newline 
703 write_error(msgs, "gcc", int(num_str))
704 \newline 
705 retval = 1
706 \newline 
707 @
708 \layout Subsection
709
710 xlc errors
711 \layout Standard
712
713 A xlc error message is easy to identify.
714  Every error message starts with a quoted string with no spaces, a comma,
715  a space, the word 
716 \begin_inset Quotes eld
717 \end_inset 
718
719 line
720 \begin_inset Quotes erd
721 \end_inset 
722
723 , a space, and some variable text.
724  The following routine tests if a given buffer line matches this criteria
725  (this code would probably be simpler if I used the 
726 \begin_inset Quotes eld
727 \end_inset 
728
729 re
730 \begin_inset Quotes erd
731 \end_inset 
732
733  regexp module, but we don't really need the full regular expression engine
734  here).
735  
736 \layout Scrap
737
738 <<Function Bodies>>=
739 \newline 
740 def xlc_try(line):
741 \newline 
742   """see if line is an xlc error.
743 \newline 
744
745 \newline 
746   Returns 1 on success, 0 otherwise.
747  Outputs on stdout."""
748 \newline 
749   retval = 0
750 \newline 
751   if line[0] == '"': # This is the first character of all xlc errors
752 \newline 
753     next_quote = string.find(line, '"', 1)
754 \newline 
755     first_space = string.find(line, ' ')
756 \newline 
757     if (next_quote != -1) and (first_space > next_quote): # no space inisde
758  quotes
759 \newline 
760       if line[first_space - 1:first_space + 6] == ", line ":
761 \newline 
762         num_start = num_end = first_space + 6
763 \newline 
764         while line[num_end] in string.digits: num_end = num_end + 1
765 \newline 
766         if num_end > num_start:
767 \newline 
768           write_error(line, "xlc", int(line[num_start : num_end]))
769 \newline 
770           retval = 1
771 \newline 
772   return retval
773 \newline 
774   
775 \newline 
776 @ %def xlc_try
777 \layout Section
778
779 Extracting the code
780 \layout Standard
781
782 This project can be tangled from LyX if you set your 
783 \begin_inset Quotes eld
784 \end_inset 
785
786 Program
787 \begin_inset Quotes erd
788 \end_inset 
789
790  convertor to call a generic script that always extracts a scrap named 
791 \family typewriter 
792 build-script
793 \family default 
794  and executes it.
795  Here is an example of such a generic script:
796 \layout LyX-Code
797
798 #!/bin/sh
799 \newline 
800 notangle -Rbuild-script $1 | env NOWEB_SOURCE=$1 sh
801 \layout Standard
802
803 This section defines our build-script, which extracts the code.
804 \layout Scrap
805
806 <<build-script>>=
807 \newline 
808 #!/bin/sh
809 \newline 
810 if [ -z "$NOWEB_SOURCE" ]; then NOWEB_SOURCE=listerrors.nw; fi
811 \newline 
812 notangle -Rlisterrors ${NOWEB_SOURCE} > listerrors
813 \newline 
814 chmod +x listerrors
815 \newline 
816 @
817 \layout Section
818
819 Indices
820 \layout Standard
821
822 This section provides cross-references into the rest of the program.
823 \layout Subsection
824
825 Macros
826 \layout Standard
827
828
829 \begin_inset ERT
830 status Collapsed
831
832 \layout Standard
833
834 \backslash 
835 nowebchunks
836 \end_inset 
837
838
839 \layout Subsection
840
841 Identifiers
842 \layout Standard
843
844
845 \begin_inset ERT
846 status Collapsed
847
848 \layout Standard
849
850 \backslash 
851 nowebindex
852 \end_inset 
853
854
855 \the_end