#line 31 "xchunks.nw"
record chunk(lines, type)
  # lines is all the lines of the chunk
  # type is "code" or "docs" or "unknown"

procedure main(args)
  local merge, summary
  while args[1][1] == "-" do
    case args[1] of {
     "-merge"   : merge := get(args)
     "-summary" : { get(args); summary := " (summary)" }
     default    : break
   }
  *args > 0 | stop("xchunks needs an external database")

  
#line 136 "xchunks.nw"
db := "markup"
every db ||:= " " || !args
db := open(db, "rp") | stop("cannot pipe from ", image(db))

#line 47 "xchunks.nw"
  dbchunks := readchunks(db)
  defns := defnstable(dbchunks)
    # defns maps name to list of code chunks defining that name
  if \merge then
      every merge_chunks(!defns)
  if \summary then
      every add_defn_string(!defns, summary)

  thisdoc := readchunks(&input)
  every c := !thisdoc do
    if c.type ~== "code" | notEmpty(c) | /defns[chunkName(c)] then
      every write(!c.lines)
    else {
      write("@crc_prefix on")
      every write(!(!defns[chunkName(c)]).lines)
      write("@crc_prefix off")
    }
end
#line 69 "xchunks.nw"
procedure readchunks(infile)
  local chunks, c
  local file, lnum, lines, startline
  chunks := []
  lnum := 1
  while line := read(infile) do
    line ?
      if ="@file " then {
        file := tab(0)
        lnum := 1
      } else if ="@line " then {
        lnum := integer(tab(0))
      } else if ="@begin " then {
        lines := []
        put(lines, "@file " || \file)
        put(lines, "@line " || lnum)
        put(lines, line)
        startline := line
        c := chunk(lines, tab(upto(' ')|0))
        line := read(infile) | stop("unmatched ", startline)
        while line ? not ="@end " do {
          put(c.lines, line)
          line := read(infile) | stop("unmatched ", startline)
          line ?
            if ="@file " then { file := tab(0); lnum := 1 }
            else if ="@line " then { lnum := integer(tab(0)) }
            else if =("@nl"|"@index nl") & pos(0) then { lnum := lnum + 1 }
        }
        put(c.lines, line)
        put(chunks, c)
      } else {
        put(chunks, chunk([line], "unknown"))
        line ?
          if ="@file " then { file := tab(0); lnum := 1 }
          else if ="@line " then { lnum := integer(tab(0)) }
          else if =("@nl"|"@index nl") & pos(0) then { lnum := lnum + 1 }
      }
  return chunks
end
#line 111 "xchunks.nw"
procedure defnstable(chunks)
  t := table()
  every c := !chunks & c.type == "code" do {
    /t[chunkName(c)] := []
    put(t[chunkName(c)], c)
  }
  return t
end
#line 124 "xchunks.nw"
procedure chunkName(c)
  return (!c.lines ? (="@defn " & tab(0))) | stop("no @defn in code chunk")
end

procedure notEmpty(c)
  static nonwhite
  initial nonwhite := ~' \t'
  return !c.lines ? ="@use " | (="@text ", upto(nonwhite))
end
#line 142 "xchunks.nw"
procedure merge_chunks(chunks)
  while *chunks > 1 do {
    c := get(chunks)
    strip_code_tail(c)
    strip_code_head(chunks[1])
    every put(c.lines, !chunks[1].lines)
    chunks[1] := c
  }
  return chunks
end
#line 154 "xchunks.nw"
procedure strip_code_tail(c)
  if c.lines[-1] ? match("@end code") then
    pull(c.lines)
  else
    stop("Unexpected line")
  return c
end

procedure strip_code_head(c)
  local pfx
  pfx := []
  if match("@begin code", c.lines[1]) then
    get(c.lines)
  else
    stop("bad line " || c.lines[1])
  while not match("@defn ", c.lines[1]) do
    push(pfx, get(c.lines))
  get(c.lines) | stop("this can't happen")
  while c.lines[1] ~== "@nl" do
    push(pfx, get(c.lines))
  get(c.lines) | stop("this can't happen")
  every push(c.lines, !pfx)
  return c
end
#line 180 "xchunks.nw"
procedure add_defn_string(chunks, s)
  every c := !chunks & i := 1 to *c.lines do
    if match("@defn ", c.lines[i]) then
      c.lines[i] ||:= s
  return chunks
end