func (p *Parser) Parse(r io.Reader, name string) (*File, error) {
	p.reset()
	p.f = &File{Name: name}
	p.src = r
	p.rune()
	p.next()
	p.f.StmtList = p.stmtList()
	if p.err == nil {
		p.doHeredocs()
	}
	return p.f, p.err
}
Parse reads and parses a shell program with an optional name. It
returns the parsed program if no issues were encountered. Otherwise,
an error is returned. Reads from r are buffered.

Parse can be called more than once, but not concurrently. That is, a
Parser can be reused once it is done working.
package syntax
import (
	"bytes"
	"fmt"
	"io"
	"strconv"
	"strings"
	"unicode/utf8"
)
func KeepComments(p *Parser) { p.keepComments = true }
type LangVariant int
const (
	LangBash LangVariant = iota
	LangPOSIX
	LangMirBSDKorn
)
func Variant(l LangVariant) func(*Parser) {
	return func(p *Parser) { p.lang = l }
}
func (l LangVariant) String() string {
	switch l {
	case LangBash:
		return "bash"
	case LangPOSIX:
		return "posix"
	case LangMirBSDKorn:
		return "mksh"
	}
	return "unknown shell language variant"
}
func StopAt(word string) func(*Parser) {
	if len(word) > 4 {
		panic("stop word can't be over four bytes in size")
	}
	if strings.ContainsAny(word, " \t\n\r") {
		panic("stop word can't contain whitespace characters")
	}
	return func(p *Parser) { p.stopAt = []byte(word) }
}
func NewParser(options ...func(*Parser)) *Parser {
	p := &Parser{helperBuf: new(bytes.Buffer)}
	for _, opt := range options {
		opt(p)
	}
	return p
}