1 #LyX 1.3 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: 2003/01/17 13:50:10 $"
247 __version__ = "$Revision: 1.3 $"
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."""
370 line = file.readline()
379 And now for the corresponding pushline function:
386 "push a line onto the pushback stack."
399 The main() entry point function is extremely simple.
400 Note that this version of
401 \begin_inset Quotes eld
405 \begin_inset Quotes erd
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.
419 """Entry point for listerrors.
424 Reads stdin and writes to stdout.
435 <<Check line against patterns and take action>>
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.
444 <<Check line against patterns and take action>>=
446 try_patterns_dispatch = [ noweb_try, gcc_try, xlc_try ]
448 for predicate in try_patterns_dispatch:
450 if predicate(line): break
455 Different Error Formats
458 The following sections handle the various error message formats that we
459 recognize in this program.
466 Noweb errors are output on a single line, so examining just the current
474 """see if line is a noweb error.
478 Returns 1 on success, 0 otherwise.
479 Outputs on stdout."""
483 <<Look for the unescaped angle-brackets in documentation>>
485 <<Look for anything with double angle brackets>>
487 <<Last ditch effort scan for specific strings>>
496 First, we look for the
497 \begin_inset Quotes eld
500 unescaped < < in documentation chunk
501 \begin_inset Quotes erd
505 This is the only message with an associated line number from noweb.
508 <<Look for the unescaped angle-brackets in documentation>>=
510 if string.find(line, ": unescaped << in documentation chunk") != -1:
512 line_parts = string.split(line, ':')
514 num_str = line_parts[1]
516 num_len = len(num_str)
520 while i < num_len and (num_str[i] in string.digits): i = i + 1
524 write_error(":" + line_parts[2], "noweb", int(num_str))
531 Some noweb messages are simply about undefined scraps.
532 These can be seen by looking for matching double-angle-brackets.
535 <<Look for anything with double angle brackets>>=
539 left = string.find(line, "<<")
541 if (left != -1) and ((left + 2) < len(line)) and
545 (string.find(line[left+2:], ">>") != -1):
547 write_error(line, "noweb");
554 Finally, here is an additional list of explicit strings to check for.
557 <<Last ditch effort scan for specific strings>>=
561 msgs_to_try = ("couldn't open file",
563 "couldn't open temporary file",
565 "error writing temporary file",
571 "Bad format sequence",
573 "Can't open output file",
575 "Can't open temporary file",
577 "Capacity exceeded:",
579 "Ignoring unknown option -",
581 "This can't happen:",
583 "non-numeric line number in")
585 for msg in msgs_to_try:
587 if string.find(line, msg) != -1:
589 write_error(line, "noweb")
601 The gcc errors can be multi-line, with the following format:
604 foo.c: In function `main':
606 foo.c:3: `bar' undeclared (first use in this function)
608 foo.c:3: (Each undeclared identifier is reported only once
610 foo.c:3: for each function it appears in.)
612 foo.c:3: parse error before `x'
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.
623 """See if line is a gcc error.
624 Read ahead to handle all the lines.
628 Returns 1 on success, 0 otherwise.
629 Outputs on stdout."""
633 <<Handle the gcc error message>>
642 The error message starts with a gcc header (as above) without an associated
646 <<Handle the gcc error message>>=
648 first_space = string.find(line, ' ')
650 if first_space > 1: # The smallest would be "X: "
652 if line[first_space - 1] == ':':
654 header_to_see = line[:first_space - 1]
656 next_line = getline()
658 if next_line and next_line[:first_space - 1] == header_to_see:
660 num_end = first_space
662 while next_line[num_end] in string.digits: num_end = num_end + 1
664 if num_end > first_space: # good!
666 <<Accumulate gcc error lines and print it>>
668 else: # oops! Not a gcc error.
674 pushline(next_line) # return this line to input stream
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:
683 <<Accumulate gcc error lines and print it>>=
685 num_str = next_line[first_space:num_end]
687 msgs = [line[first_space:]]
689 msgs.append(next_line[num_end + 1:])
691 header_to_see = next_line[:num_end]
693 next_line = getline()
695 while next_line and next_line[:num_end] == header_to_see:
697 msgs.append(next_line[num_end + 1:])
699 next_line = getline()
701 if next_line: pushline(next_line)
703 write_error(msgs, "gcc", int(num_str))
713 A xlc error message is easy to identify.
714 Every error message starts with a quoted string with no spaces, a comma,
716 \begin_inset Quotes eld
720 \begin_inset Quotes erd
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
730 \begin_inset Quotes erd
733 regexp module, but we don't really need the full regular expression engine
742 """see if line is an xlc error.
746 Returns 1 on success, 0 otherwise.
747 Outputs on stdout."""
751 if line[0] == '"': # This is the first character of all xlc errors
753 next_quote = string.find(line, '"', 1)
755 first_space = string.find(line, ' ')
757 if (next_quote != -1) and (first_space > next_quote): # no space inisde
760 if line[first_space - 1:first_space + 6] == ", line ":
762 num_start = num_end = first_space + 6
764 while line[num_end] in string.digits: num_end = num_end + 1
766 if num_end > num_start:
768 write_error(line, "xlc", int(line[num_start : num_end]))
782 This project can be tangled from LyX if you set your
783 \begin_inset Quotes eld
787 \begin_inset Quotes erd
790 convertor to call a generic script that always extracts a scrap named
795 Here is an example of such a generic script:
800 notangle -Rbuild-script $1 | env NOWEB_SOURCE=$1 sh
803 This section defines our build-script, which extracts the code.
810 if [ -z "$NOWEB_SOURCE" ]; then NOWEB_SOURCE=listerrors.nw; fi
812 notangle -Rlisterrors ${NOWEB_SOURCE} > listerrors
822 This section provides cross-references into the rest of the program.