code: func (p *Parser) Interactive(r io.Reader, fn func([]*Stmt) bool) error {
  	w := wrappedReader{Parser: p, Reader: r, fn: fn}
  	return p.Stmts(&w, func(stmt *Stmt) bool {
  		w.accumulated = append(w.accumulated, stmt)
  		// We finished parsing a statement and we're at a newline token,
  		// so we finished fully parsing a number of statements. Call
  		// back to run the statements and print "$ ".
  		if p.tok == _Newl {
  			if !fn(w.accumulated) {
  				return false
  			}
  			w.accumulated = w.accumulated[:0]
  			// The callback above would already print "$ ", so we
  			// don't want the subsequent wrappedReader.Read to cause
  			// another "$ " print thinking that nothing was parsed.
  			w.lastLine = w.npos.line + 1
  		}
  		return true
  	})
  }
docs: // Interactive implements what is necessary to parse statements in an
  // interactive shell. The parser will call the given function under two
  // circumstances outlined below.
  //
  // If a line containing any number of statements is parsed, the function will be
  // called with said statements.
  //
  // If a line ending in an incomplete statement is parsed, the function will be
  // called with any fully parsed statents, and Parser.Incomplete will return
  // true.
  //
  // One can imagine a simple interactive shell implementation as follows:
  //
  //         fmt.Fprintf(os.Stdout, "$ ")
  //         parser.Interactive(os.Stdin, func(stmts []*syntax.Stmt) bool {
  //                 if parser.Incomplete() {
  //                         fmt.Fprintf(os.Stdout, "> ")
  //                         return true
  //                 }
  //                 run(stmts)
  //                 fmt.Fprintf(os.Stdout, "$ ")
  //                 return true
  //         }
  //
  // If the callback function returns false, parsing is stopped and the function
  // is not called again.