1 #LyX 1.2 created this file. For more info see http://www.lyx.org/
3 \textclass literate-article
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
13 % This (from the noweb FAQ) relaxes the constraint that chunks are never broken across pages.
15 \def\nwendcode{\endtrivlist \endgroup \vfil\penalty10\vfilneg}
16 \let\nwdocspar=\smallbreak
22 \paperfontsize default
29 \use_numerical_citations 0
30 \paperorientation portrait
33 \paragraph_separation indent
35 \quotes_language english
39 \paperpagestyle default
52 \begin_inset LatexCommand \url{mailto:kayvan@sylvan.com}
62 The listerrors program used to be compiled as a C program and installed
67 along with LyX in order to perform some simple re-formatting of noweb and
69 This document describes and implements the Python version of the same program.
73 \begin_inset LatexCommand \tableofcontents{}
83 The motivation for this program was Bugzilla bug 190
90 \begin_inset LatexCommand \url{http://bugzilla.lyx.org/show_bug.cgi?id=190}
98 \begin_inset Quotes eld
102 \begin_inset Quotes erd
109 \begin_inset Quotes eld
113 \begin_inset Quotes erd
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
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
132 \begin_inset LatexCommand \url{http://www.eecs.harvard.edu/~nr/noweb}
136 for more information about noweb.
140 \begin_inset Quotes erd
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.
148 In a nutshell, the problems with the old implementation of listerrors that
149 bug 190 refers to were::
152 It was a C program and it was installed in the user path in the same directory
154 Having such a generically named binary in, for example,
158 , was potentially confusing.
161 It required that noweb be installed on the compiling machine (the source
162 was extracted by noweb from
164 SRCDIR/examples/Literate.lyx
166 , compiled and installed by
167 \begin_inset Quotes eld
171 \begin_inset Quotes erd
177 The new version deals with these problems in the following fashion:
180 Both the example file (this document) and the program are to be added to
181 the LyX CVS repository.
184 The program itself will be installed in
188 , along with other LyX-specific helper scripts.
191 In the design and implementation of this new
192 \begin_inset Quotes eld
196 \begin_inset Quotes erd
205 See the Python home page (
206 \begin_inset LatexCommand \url{http://www.python.org}
210 ) for more information.
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
220 \begin_inset Quotes erd
224 Python is installed by default on all modern Linux systems and is freely
225 available for all other platforms.
232 """reformat noweb and compiler errors for LyX.
236 Expects to read from stdin and output to stdout.
242 __author__ = "Kayvan A.
243 Sylvan <kayvan@sylvan.com>"
245 __date__ = "$Date: 2002/03/19 21:42:48 $"
247 __version__ = "$Revision: 1.1 $"
249 __credits__ = """Edmar Wienskoski Jr.
250 <edmar-w-jr@technologist.com>
252 original Literate support for LyX.
254 Bernard Michael Hurley <berhardh@westherts.ac.uk>
256 modifications to original listerrors."""
258 __copyright__ = "Copyright 2002 - The LyX team."
270 if __name__ == "__main__":
277 LaTeX style error message
280 The following function mimics the TeX error message format.
285 def write_error(msg, tool = "noweb", line_number = 1):
287 """Write out the given message in TeX error style.
291 called like: write_error(msg, tool, line_number)."""
293 print "! Build Error: ==> %s ==>
305 if type(msg) == type("str"): # simple string
309 else: # some kind of list (sequence or tuple)
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
332 \begin_inset Quotes erd
336 \begin_inset Quotes eld
340 \begin_inset Quotes erd
348 __lines = [] # lines pushed back
352 def getline(file = sys.stdin):
354 """read a line from internal stack or from file.
358 optional file argument defaults to sys.stdin."""
372 line = file.readline()
381 And now for the corresponding pushline function:
388 "push a line onto the pushback stack."
394 lines += (line,) # push a list onto the stack, not individual letters
401 The main() entry point function is extremely simple.
402 Note that this version of
403 \begin_inset Quotes eld
407 \begin_inset Quotes erd
410 takes no options and simply filters, attempting simply to match against
411 the known error message patterns.
412 The listerrors C program handled a single-character command-line argument
413 that the current code no longer needs.
421 """Entry point for listerrors.
426 Reads stdin and writes to stdout.
437 <<Check line against patterns and take action>>
442 For each line read in, we need to find out if it matches any of our tools
443 (noweb, gcc, etc.) and act accordingly.
446 <<Check line against patterns and take action>>=
448 try_patterns_dispatch = [ noweb_try, gcc_try, xlc_try ]
450 for predicate in try_patterns_dispatch:
452 if predicate(line): break
457 Different Error Formats
460 The following sections handle the various error message formats that we
461 recognize in this program.
468 Noweb errors are output on a single line, so examining just the current
476 """see if line is a noweb error.
480 Returns 1 on success, 0 otherwise.
481 Outputs on stdout."""
485 <<Look for the unescaped angle-brackets in documentation>>
487 <<Look for anything with double angle brackets>>
489 <<Last ditch effort scan for specific strings>>
498 First, we look for the
499 \begin_inset Quotes eld
502 unescaped < < in documentation chunk
503 \begin_inset Quotes erd
507 This is the only message with an associated line number from noweb.
510 <<Look for the unescaped angle-brackets in documentation>>=
512 if line.find(": unescaped << in documentation chunk") != -1:
514 line_parts = line.split(':')
516 num_str = line_parts[1]
518 num_len = len(num_str)
522 while i < num_len and num_str[i].isdigit(): i += 1
526 write_error(":" + line_parts[2], "noweb", int(num_str))
533 Some noweb messages are simply about undefined scraps.
534 These can be seen by looking for matching double-angle-brackets.
537 <<Look for anything with double angle brackets>>=
541 left = line.find("<<")
543 if (left != -1) and ((left + 2) < len(line)) and
547 (line[left+2:].find(">>") != -1):
549 write_error(line, "noweb");
556 Finally, here is an additional list of explicit strings to check for.
559 <<Last ditch effort scan for specific strings>>=
563 msgs_to_try = ("couldn't open file",
565 "couldn't open temporary file",
567 "error writing temporary file",
573 "Bad format sequence",
575 "Can't open output file",
577 "Can't open temporary file",
579 "Capacity exceeded:",
581 "Ignoring unknown option -",
583 "This can't happen:",
585 "non-numeric line number in")
587 for msg in msgs_to_try:
589 if line.find(msg) != -1:
591 write_error(line, "noweb")
603 The gcc errors can be multi-line, with the following format:
606 foo.c: In function `main':
608 foo.c:3: `bar' undeclared (first use in this function)
610 foo.c:3: (Each undeclared identifier is reported only once
612 foo.c:3: for each function it appears in.)
614 foo.c:3: parse error before `x'
617 In order to parse this, the gcc error handler has to look ahead and return
618 any and all lines that do not match the expected pattern.
625 """See if line is a gcc error.
626 Read ahead to handle all the lines.
630 Returns 1 on success, 0 otherwise.
631 Outputs on stdout."""
635 <<Handle the gcc error message>>
644 The error message starts with a gcc header (as above) without an associated
648 <<Handle the gcc error message>>=
650 first_space = line.find(' ')
652 if first_space > 1: # The smallest would be "X: "
654 if line[first_space - 1] == ':':
656 header_to_see = line[:first_space - 1]
658 next_line = getline()
660 if next_line and next_line[:first_space - 1] == header_to_see:
662 num_end = first_space
664 while next_line[num_end].isdigit(): num_end += 1
666 if num_end > first_space: # good!
668 <<Accumulate gcc error lines and print it>>
670 else: # oops! Not a gcc error.
676 pushline(next_line) # return this line to input stream
681 At the point in the code that we know that we are in the middle of an error
682 message, we do the following:
685 <<Accumulate gcc error lines and print it>>=
687 num_str = next_line[first_space:num_end]
691 msgs += (line[first_space:],)
693 msgs += (next_line[num_end + 1:],)
695 header_to_see = next_line[:num_end]
697 next_line = getline()
699 while next_line and next_line[:num_end] == header_to_see:
701 msgs += (next_line[num_end + 1:],)
703 next_line = getline()
705 if next_line: pushline(next_line)
707 write_error(msgs, "gcc", int(num_str))
717 A xlc error message is easy to identify.
718 Every error message starts with a quoted string with no spaces, a comma,
720 \begin_inset Quotes eld
724 \begin_inset Quotes erd
727 , a space, and some variable text.
728 The following routine tests if a given buffer line matches this criteria
729 (this code would probably be simpler if I used the
730 \begin_inset Quotes eld
734 \begin_inset Quotes erd
737 regexp module, but we don't really need the full regular expression engine
746 """see if line is an xlc error.
750 Returns 1 on success, 0 otherwise.
751 Outputs on stdout."""
755 if line[0] == '"': # This is the first character of all xlc errors
757 next_quote = line.find('"', 1)
759 first_space = line.find(' ')
761 if (next_quote != -1) and (first_space > next_quote): # no space inisde
764 if line[first_space - 1:first_space + 6] == ", line ":
766 num_start = num_end = first_space + 6
768 while line[num_end].isdigit(): num_end += 1
770 if num_end > num_start:
772 write_error(line, "xlc", int(line[num_start : num_end]))
786 This project can be tangled from LyX if you set your
787 \begin_inset Quotes eld
791 \begin_inset Quotes erd
794 convertor to call a generic script that always extracts a scrap named
799 Here is an example of such a generic script:
804 notangle -Rbuild-script $1 | env NOWEB_SOURCE=$1 sh
807 This section defines our build-script, which extracts the code.
814 if [ -z "$NOWEB_SOURCE" ]; then NOWEB_SOURCE=listerrors.nw; fi
816 notangle -Rlisterrors ${NOWEB_SOURCE} > listerrors
826 This section provides cross-references into the rest of the program.