0

Using guile 1.8 or guile 2, the following code reads past EOF, seemingly for a few extra lines, then stops. The effect this has in the larger program of which this is an extract is to seemingly corrupt the previously read data. Am I using read-line or testing for eof-object incorrectly?

(use-modules (ice-9 rdelim))

(define f
  (lambda (p)
    (let loop ((line (read-line p)))
      (format #t "line: ~a\n" line)
      (if (not (eof-object? (peek-char p)))
      (begin
        (let ((m (string-match "^[ \t]*#" line)))
          (if m
          (begin
              (format #t "comment: ~a\n" (match:string m))
              (loop (read-line p))
            )))
        (format #t "read next line\n")
        (loop (read-line p)))))))

(define main
  (lambda ()
    (let ((h (open-input-file "test")))
      (f h))))

Here is a minimal sample dummy input file:

1
2
3
# comment line
4
5
1
2
3
# comment line
4
5
1
2
3
# comment line
4
5

It needs to be more than a few lines long for the problem to manifest. Apologies for the length of the code example, but the issue only arises when the code gets to this amount of complexity (albeit small).

andro
  • 901
  • 9
  • 20
  • The main problem seems to be the fact that you're reading _two lines_ per iteration when you find a comment, see my answer for a different way to structure the solution. – Óscar López May 21 '17 at 16:01

1 Answers1

4

I suggest a rewrite of the procedure, it doesn't seem the correct way to read a file and loop over its lines. Please try this one:

(define (f)
  (let loop ((line (read-line)))
    (if (not (eof-object? line))
        (begin
          (format #t "line: ~a\n" line)
          (let ((m (string-match "^[ \t]*#" line)))
            (if m (format #t "comment: ~a\n" line)))
          (format #t "read next line\n")
          (loop (read-line))))))

(define (main)
  (with-input-from-file "test" f))

With your sample input, calling (main) prints on the console the following output, which hopefully is what you expected:

line: 1
read next line
line: 2
read next line
line: 3
read next line
line: # comment line
comment: # comment line
read next line
line: 4
read next line
line: 5
read next line
line: 1
read next line
line: 2
read next line
line: 3
read next line
line: # comment line
comment: # comment line
read next line
line: 4
read next line
line: 5
read next line
line: 1
read next line
line: 2
read next line
line: 3
read next line
line: # comment line
comment: # comment line
read next line
line: 4
read next line
line: 5
read next line
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • Guile 1.8 does not have when and unless, surprisingly. One can of course write them, or use alternate logic. – andro May 21 '17 at 23:40
  • @andro I didn't know that. There, its fixed. Other than that, did this work for you? – Óscar López May 21 '17 at 23:43
  • That fragment works fine. But the issue appears to be more subtle. What I am doing is writing a small parser that checks lines for pattern matches, does some computation on a match, and then I want to get the next line, which I attempt to do by calling the loop again, with the loop value provided by reading input with read-line. In other words, a sort of 'next' control form. When I add many such statements, scheme behaves in an unpredictable way. Is it that one cannot break out of a loop and return to the next iteration in this way? – andro May 22 '17 at 00:00
  • It's hard to guess what you _really_ need. There's no "next" in Scheme, but you make it sound as if an if/else would be enough. If there's a match process it, else do nothing - and either way, loop to the next line. – Óscar López May 22 '17 at 00:04
  • I'm pretty sure that what you need can be managed using standard control flow operations and recursion, but without more context it's impossible to tell. For the time being, I think your original question was answered. If you have further difficulties, please consider posting a new question. IMHO my answer is already acceptable ;) – Óscar López May 22 '17 at 00:07