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