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