[Prev][Next][Index][Thread]

Re: Critique a first prog?



Hello again, and thanks to everyone for their valuable input. I've 
attached the new, transmogrified version. In addition to the changes 
others suggested, I've converted it more to a stream-filter approach, 
and I've had to also make it a bit more involved since the natural 
directory listing wasn't actually putting the pieces in the correct 
order as I'd thought originally. I've reprinted the original below the 
current version for comparison.



Module:      docbuilder
Synopsis:    Convert chunked up html docs to one html file.

// Read lines from a stream until accept-line? succeeds, return that line.
define function find-line(stream :: <stream>, accept-line? :: <function>)
  let line = #f;
  block(break)
    while (line := read-line(stream, on-end-of-stream: #f))
      if (accept-line?(line))
	break(line);
      end;
      #f;
    end;
  end;
end;

//Read lines from a stream, if they pass accept-line?, write them to out-stream.
define function filter-stream ( in-stream :: <stream>, out-stream :: <stream>, accept-line? :: <function> )
  let line = #f;
  while (line := find-line(in-stream, accept-line?))
    write-line(out-stream, line);
  end;
end;

//Find all files in pathname that meet criterion?. Sort-func takes the pathname and sequence
//and returns a sorted sequence.
define function find-files(pathname :: <string>, criterion? :: <function>, sort-func :: <function>)
  let files = make(<stretchy-vector>);
  do-directory(compose(curry(add!, files), second, list), pathname);
  sort-func(pathname, choose(criterion?, files));
end;


//Return the first region of a sequence between the regions which match start-pattern
//and end-pattern.
define method copy-match(seq :: <sequence>, start-pattern :: <sequence>, end-pattern :: <sequence>)
  let start = subsequence-position(seq, start-pattern);
  start := start + start-pattern.size + 1;
  let remainder = copy-sequence(seq, start: start);
  let endpos = subsequence-position(remainder, end-pattern);
  copy-sequence(remainder, end: endpos);
end;

//These files have <LINK REL=NEXT tags in them, sort into this order.
define function order-by-next(pathname :: <string>, files :: <collection>)
  let topkey = "<LINK REL=TOP HREF=";
  let nextkey = "<LINK REL=NEXT HREF=";
  let find-next = rcurry(find-line, rcurry(subsequence-position, nextkey));
  let find-top = rcurry(find-line, rcurry(subsequence-position, topkey));
  let line = #f;
  let top = #f;
  with-open-file(fis = concatenate(pathname, files[0]))
    line := find-top(fis);
    top := copy-match( line, topkey, "\"");
  end;
  let nexts = make(<string-table>);
  for (file in files)
    with-open-file(fis = concatenate(pathname, file))
      line := find-next(fis);
    end;
    if (line)
      nexts[file] := copy-match(line, nextkey,"\"");
    else
      nexts[file] := #f;
    end;
  end;
  let sorted = make(<stretchy-vector>);
  let next = #f;
  while (top)
    add!(sorted, top);
    next := nexts[top];
    top := next;
  end;
  sorted;
end;


define function docbuilder-top-level ()
  let arguments = application-arguments();
  if (empty?(arguments))
    format-out("Usage: %s pathname\n", application-name());
  else
    let $bad_stuff = #["SRC=next.gif", "<HTML>", "<BODY",
		       "<!DOCTYPE", "HEAD>", "<TITLE>", "<ADDRESS>", "</HTML>",
		       "</BODY", "<LINK", "<META", "<DIV", "</DIV"];
    local
      method good-line?(line :: <string>)
	~any?(curry(subsequence-position, line), $bad_stuff);
      end,
      method html-file?(file)
	(subsequence-position(file, ".htm") = (file.size - 4)) & ~(file = "Build.htm");
      end;
    let pathname = arguments[0];
    format-out("Checking path: %s\n", pathname);
    let files = find-files(pathname, html-file?, order-by-next);
    let filename = concatenate(pathname, "Build.htm");
    format-out("Output filename: %s\n", filename);
    with-open-file(out-stream = filename, direction: #"output", if-does-not-exist: #"create")
      for (file in files)
	format-out("%s\n", file);
	with-open-file(in-stream = concatenate(pathname, file))
	  filter-stream(in-stream, out-stream, good-line?);
	end;
      end;
    end;
  end
end function docbuilder-top-level;

docbuilder-top-level();





> 
> 
> Module:      docbuilder
> Synopsis:    Convert chunked up docs to one printable page.
> 
> 
> define method read-lines(stream :: <stream>)
>   let v = make(<stretchy-vector>);
>   block(exit)
>     let handler <end-of-stream-error>
>       = method(condition, next)
>      exit();
>  end;
>     while (#t)
>       add!(v, read-line(stream));
>     end;
>   end;
>   v;
> end;
> 
> 
> define function good-line?(line)
>   let bad = vector("SRC=next.gif", "<HTML>", "<BODY",
>      "<!DOCTYPE", "HEAD>", "<TITLE>", "<ADDRESS>", "</HTML>",
>      "</BODY", "<LINK", "<META", "<DIV", "</DIV");
>   block(break)
>     for (thing in bad)
>       if (subsequence-position(line, thing))
>         break(#f);
>       end;
>     end;
>     #t;
>   end;
> end;
> 
> define function process-file(filename, writer)
>   let fis = make(<file-stream>, locator: filename);
>   let lines = read-lines(fis);
>   close(fis);
>   lines := choose(good-line?, lines);
>   do(writer, lines);
> end;
> 
> define function find-files(pathname :: <string>, criterion? ::
> <function>)
>   let files = make(<stretchy-vector>);
>   do-directory(compose(curry(add!, files), second, list), pathname);
>   choose(criterion?, files);
> end;
> 
> define function html-file?(file)
>   (subsequence-position(file, ".htm") = (file.size - 4));
> end;
> 
> define function docbuilder-top-level ()
>   let arguments = application-arguments();
>   if (arguments.size == 0)
>     format-out("Usage: %s pathname\n", application-name());
>   else
>     format-out("Checking path: %s\n", arguments[0]);
>     let files = find-files(arguments[0], html-file?);
>     let fos = make(<file-stream>, locator: concatenate(arguments[0],
> "Build.htm"),
>      direction: #"output", if-does-not-exist: #"create" );
>     let write-fos = curry(write-line, fos);
>     for (file in files)
>       process-file(concatenate(arguments[0], file), write-fos);
>     end;
>     close(fos);
>   end
> end function docbuilder-top-level;
> 
> docbuilder-top-level();





Follow-Ups: References: