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