/*
Derived from Inferno's utils/iyacc/yacc.c
http://code.google.com/p/inferno-os/source/browse/utils/iyacc/yacc.c

This copyright NOTICE applies to all files in this directory and
subdirectories, unless another copyright notice appears in a given
file or subdirectory.  If you take substantial code from this software to use in
other programs, you must somehow include with it an appropriate
copyright notice that includes the copyright notice and the other
notices below.  It is fine (and often tidier) to do that in a separate
file such as NOTICE, LICENCE or COPYING.

	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
	Portions Copyright © 1997-1999 Vita Nuova Limited
	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
	Portions Copyright © 2004,2006 Bruce Ellis
	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
	Portions Copyright © 2009 The Go Authors. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package main

// yacc
// major difference is lack of stem ("y" variable)
//

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"math"
	"os"
	"slices"
	"strconv"
	"strings"
	"unicode"
)

// the following are adjustable
// according to memory size
const (
	ACTSIZE  = 240000
	NSTATES  = 16000
	TEMPSIZE = 16000

	SYMINC   = 50  // increase for non-term or term
	RULEINC  = 50  // increase for max rule length prodptr[i]
	PRODINC  = 100 // increase for productions     prodptr
	WSETINC  = 50  // increase for working sets    wsets
	STATEINC = 200 // increase for states          statemem

	PRIVATE = 0xE000 // unicode private use

	// relationships which must hold:
	//	TEMPSIZE >= NTERMS + NNONTERM + 1;
	//	TEMPSIZE >= NSTATES;
	//

	NTBASE     = 010000
	ERRCODE    = 8190
	ACCEPTCODE = 8191
	YYLEXUNK   = 3
	TOKSTART   = 4 //index of first defined token
)

// no, left, right, binary assoc.
const (
	NOASC = iota
	LASC
	RASC
	BASC
)

// flags for state generation
const (
	DONE = iota
	MUSTDO
	MUSTLOOKAHEAD
)

// flags for a rule having an action, and being reduced
const (
	ACTFLAG = 1 << (iota + 2)
	REDFLAG
)

// output parser flags
const yyFlag = -1000

// parse tokens
const (
	IDENTIFIER = PRIVATE + iota
	MARK
	TERM
	LEFT
	RIGHT
	BINARY
	PREC
	LCURLY
	IDENTCOLON
	NUMBER
	START
	TYPEDEF
	TYPENAME
	UNION
	ERROR
)

const ENDFILE = 0
const EMPTY = 1
const WHOKNOWS = 0
const OK = 1
const NOMORE = -1000

// macros for getting associativity and precedence levels
func ASSOC(i int) int { return i & 3 }

func PLEVEL(i int) int { return (i >> 4) & 077 }

func TYPE(i int) int { return (i >> 10) & 077 }

// macros for setting associativity and precedence levels
func SETASC(i, j int) int { return i | j }

func SETPLEV(i, j int) int { return i | (j << 4) }

func SETTYPE(i, j int) int { return i | (j << 10) }

// I/O descriptors
var finput *bufio.Reader // input file
var stderr *bufio.Writer
var ftable *bufio.Writer    // y.go file
var fcode = &bytes.Buffer{} // saved code
var foutput *bufio.Writer   // y.output file

var fmtImported bool // output file has recorded an import of "fmt"

var oflag string  // -o [y.go]		- y.go file
var vflag string  // -v [y.output]	- y.output file
var lflag bool    // -l			- disable line directives
var prefix string // name prefix for identifiers, default yy

func init() {
	flag.StringVar(&oflag, "o", "y.go", "parser output")
	flag.StringVar(&prefix, "p", "yy", "name prefix to use in generated code")
	flag.StringVar(&vflag, "v", "y.output", "create parsing tables")
	flag.BoolVar(&lflag, "l", false, "disable line directives")
}

var initialstacksize = 16

// communication variables between various I/O routines
var infile string  // input file name
var numbval int    // value of an input number
var tokname string // input token name, slop for runes and 0
var tokflag = false

// structure declarations
type Lkset []int

type Pitem struct {
	prod   []int
	off    int // offset within the production
	first  int // first term or non-term in item
	prodno int // production number for sorting
}

type Item struct {
	pitem Pitem
	look  Lkset
}

type Symb struct {
	name    string
	noconst bool
	value   int
}

type Wset struct {
	pitem Pitem
	flag  int
	ws    Lkset
}

// storage of types
var ntypes int                     // number of types defined
var typeset = make(map[int]string) // pointers to type tags

// token information

var ntokens = 0 // number of tokens
var tokset []Symb
var toklev []int // vector with the precedence of the terminals

// nonterminal information

var nnonter = -1 // the number of nonterminals
var nontrst []Symb
var start int // start symbol

// state information

var nstate = 0                      // number of states
var pstate = make([]int, NSTATES+2) // index into statemem to the descriptions of the states
var statemem []Item
var tystate = make([]int, NSTATES) // contains type information about the states
var tstates []int                  // states generated by terminal gotos
var ntstates []int                 // states generated by nonterminal gotos
var mstates = make([]int, NSTATES) // chain of overflows of term/nonterm generation lists
var lastred int                    // number of last reduction of a state
var defact = make([]int, NSTATES)  // default actions of states

// lookahead set information

var nolook = 0  // flag to turn off lookahead computations
var tbitset = 0 // size of lookahead sets
var clset Lkset // temporary storage for lookahead computations

// working set information

var wsets []Wset
var cwp int

// storage for action table

var amem []int                   // action table storage
var memp int                     // next free action table position
var indgo = make([]int, NSTATES) // index to the stored goto table

// temporary vector, indexable by states, terms, or ntokens

var temp1 = make([]int, TEMPSIZE) // temporary storage, indexed by terms + ntokens or states
var lineno = 1                    // current input line number
var fatfl = 1                     // if on, error is fatal
var nerrors = 0                   // number of errors

// assigned token type values

var extval = 0

// grammar rule information

var nprod = 1      // number of productions
var prdptr [][]int // pointers to descriptions of productions
var levprd []int   // precedence levels for the productions
var rlines []int   // line number for this rule

// statistics collection variables

var zzgoent = 0
var zzgobest = 0
var zzacent = 0
var zzexcp = 0
var zzclose = 0
var zzrrconf = 0
var zzsrconf = 0
var zzstate = 0

// optimizer arrays

var yypgo [][]int
var optst [][]int
var ggreed []int
var pgo []int

var maxspr int // maximum spread of any entry
var maxoff int // maximum offset into a array
var maxa int

// storage for information about the nonterminals

var pres [][][]int // vector of pointers to productions yielding each nonterminal
var pfirst []Lkset
var pempty []int // vector of nonterminals nontrivially deriving e

// random stuff picked out from between functions

var indebug = 0 // debugging flag for cpfir
var pidebug = 0 // debugging flag for putitem
var gsdebug = 0 // debugging flag for stagen
var cldebug = 0 // debugging flag for closure
var pkdebug = 0 // debugging flag for apack
var g2debug = 0 // debugging for go2gen
var adb = 0     // debugging for callopt

type Resrv struct {
	name  string
	value int
}

var resrv = []Resrv{
	{"binary", BINARY},
	{"left", LEFT},
	{"nonassoc", BINARY},
	{"prec", PREC},
	{"right", RIGHT},
	{"start", START},
	{"term", TERM},
	{"token", TERM},
	{"type", TYPEDEF},
	{"union", UNION},
	{"struct", UNION},
	{"error", ERROR},
}

type Error struct {
	lineno int
	tokens []string
	msg    string
}

var errors []Error

type Row struct {
	actions       []int
	defaultAction int
}

var stateTable []Row

var zznewstate = 0

const EOF = -1

func main() {

	setup() // initialize and read productions

	tbitset = (ntokens + 32) / 32
	cpres()  // make table of which productions yield a given nonterminal
	cempty() // make a table of which nonterminals can match the empty string
	cpfir()  // make a table of firsts of nonterminals

	stagen() // generate the states

	yypgo = make([][]int, nnonter+1)
	optst = make([][]int, nstate)
	output() // write the states and the tables
	go2out()

	hideprod()
	summary()

	callopt()

	others()

	exit(0)
}

func setup() {
	var j, ty int

	stderr = bufio.NewWriter(os.Stderr)
	foutput = nil

	flag.Parse()
	if flag.NArg() != 1 {
		usage()
	}
	if initialstacksize < 1 {
		// never set so cannot happen
		fmt.Fprintf(stderr, "yacc: stack size too small\n")
		usage()
	}
	yaccpar = strings.Replace(yaccpartext, "$$", prefix, -1)
	openup()

	fmt.Fprintf(ftable, "// Code generated by goyacc %s. DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))

	defin(0, "$end")
	extval = PRIVATE // tokens start in unicode 'private use'
	defin(0, "error")
	defin(1, "$accept")
	defin(0, "$unk")
	i := 0

	t := gettok()

outer:
	for {
		switch t {
		default:
			errorf("syntax error tok=%v", t-PRIVATE)

		case MARK, ENDFILE:
			break outer

		case ';':
			// Do nothing.

		case START:
			t = gettok()
			if t != IDENTIFIER {
				errorf("bad %%start construction")
			}
			start = chfind(1, tokname)

		case ERROR:
			lno := lineno
			var tokens []string
			for {
				t := gettok()
				if t == ':' {
					break
				}
				if t != IDENTIFIER && t != IDENTCOLON {
					errorf("bad syntax in %%error")
				}
				tokens = append(tokens, tokname)
				if t == IDENTCOLON {
					break
				}
			}
			if gettok() != IDENTIFIER {
				errorf("bad syntax in %%error")
			}
			errors = append(errors, Error{lno, tokens, tokname})

		case TYPEDEF:
			t = gettok()
			if t != TYPENAME {
				errorf("bad syntax in %%type")
			}
			ty = numbval
			for {
				t = gettok()
				switch t {
				case IDENTIFIER:
					t = chfind(1, tokname)
					if t < NTBASE {
						j = TYPE(toklev[t])
						if j != 0 && j != ty {
							errorf("type redeclaration of token %s",
								tokset[t].name)
						} else {
							toklev[t] = SETTYPE(toklev[t], ty)
						}
					} else {
						j = nontrst[t-NTBASE].value
						if j != 0 && j != ty {
							errorf("type redeclaration of nonterminal %v",
								nontrst[t-NTBASE].name)
						} else {
							nontrst[t-NTBASE].value = ty
						}
					}
					continue

				case ',':
					continue
				}
				break
			}
			continue

		case UNION:
			cpyunion()

		case LEFT, BINARY, RIGHT, TERM:
			// nonzero means new prec. and assoc.
			lev := t - TERM
			if lev != 0 {
				i++
			}
			ty = 0

			// get identifiers so defined
			t = gettok()

			// there is a type defined
			if t == TYPENAME {
				ty = numbval
				t = gettok()
			}
			for {
				switch t {
				case ',':
					t = gettok()
					continue

				case ';':
					// Do nothing.

				case IDENTIFIER:
					j = chfind(0, tokname)
					if j >= NTBASE {
						errorf("%v defined earlier as nonterminal", tokname)
					}
					if lev != 0 {
						if ASSOC(toklev[j]) != 0 {
							errorf("redeclaration of precedence of %v", tokname)
						}
						toklev[j] = SETASC(toklev[j], lev)
						toklev[j] = SETPLEV(toklev[j], i)
					}
					if ty != 0 {
						if TYPE(toklev[j]) != 0 {
							errorf("redeclaration of type of %v", tokname)
						}
						toklev[j] = SETTYPE(toklev[j], ty)
					}
					t = gettok()
					if t == NUMBER {
						tokset[j].value = numbval
						t = gettok()
					}

					continue
				}
				break
			}
			continue

		case LCURLY:
			cpycode()
		}
		t = gettok()
	}

	if t == ENDFILE {
		errorf("unexpected EOF before %%")
	}

	fmt.Fprintf(fcode, "switch %snt {\n", prefix)

	moreprod()
	prdptr[0] = []int{NTBASE, start, 1, 0}

	nprod = 1
	curprod := make([]int, RULEINC)
	t = gettok()
	if t != IDENTCOLON {
		errorf("bad syntax on first rule")
	}

	if start == 0 {
		prdptr[0][1] = chfind(1, tokname)
	}

	// read rules
	// put into prdptr array in the format
	// target
	// followed by id's of terminals and non-terminals
	// followed by -nprod

	for t != MARK && t != ENDFILE {
		mem := 0

		// process a rule
		rlines[nprod] = lineno
		ruleline := lineno
		if t == '|' {
			curprod[mem] = prdptr[nprod-1][0]
			mem++
		} else if t == IDENTCOLON {
			curprod[mem] = chfind(1, tokname)
			if curprod[mem] < NTBASE {
				lerrorf(ruleline, "token illegal on LHS of grammar rule")
			}
			mem++
		} else {
			lerrorf(ruleline, "illegal rule: missing semicolon or | ?")
		}

		// read rule body
		t = gettok()
		for {
			for t == IDENTIFIER {
				curprod[mem] = chfind(1, tokname)
				if curprod[mem] < NTBASE {
					levprd[nprod] = toklev[curprod[mem]]
				}
				mem++
				if mem >= len(curprod) {
					ncurprod := make([]int, mem+RULEINC)
					copy(ncurprod, curprod)
					curprod = ncurprod
				}
				t = gettok()
			}
			if t == PREC {
				if gettok() != IDENTIFIER {
					lerrorf(ruleline, "illegal %%prec syntax")
				}
				j = chfind(2, tokname)
				if j >= NTBASE {
					lerrorf(ruleline, "nonterminal %s illegal after %%prec", nontrst[j-NTBASE].name)
				}
				levprd[nprod] = toklev[j]
				t = gettok()
			}
			if t != '=' {
				break
			}
			levprd[nprod] |= ACTFLAG
			fmt.Fprintf(fcode, "\n\tcase %v:", nprod)
			fmt.Fprintf(fcode, "\n\t\t%sDollar = %sS[%spt-%v:%spt+1]", prefix, prefix, prefix, mem-1, prefix)
			cpyact(curprod, mem)

			// action within rule...
			t = gettok()
			if t == IDENTIFIER {
				// make it a nonterminal
				j = chfind(1, fmt.Sprintf("$$%v", nprod))

				//
				// the current rule will become rule number nprod+1
				// enter null production for action
				//
				prdptr[nprod] = make([]int, 2)
				prdptr[nprod][0] = j
				prdptr[nprod][1] = -nprod

				// update the production information
				nprod++
				moreprod()
				levprd[nprod] = levprd[nprod-1] & ^ACTFLAG
				levprd[nprod-1] = ACTFLAG
				rlines[nprod] = lineno

				// make the action appear in the original rule
				curprod[mem] = j
				mem++
				if mem >= len(curprod) {
					ncurprod := make([]int, mem+RULEINC)
					copy(ncurprod, curprod)
					curprod = ncurprod
				}
			}
		}

		for t == ';' {
			t = gettok()
		}
		curprod[mem] = -nprod
		mem++

		// check that default action is reasonable
		if ntypes != 0 && (levprd[nprod]&ACTFLAG) == 0 &&
			nontrst[curprod[0]-NTBASE].value != 0 {
			// no explicit action, LHS has value
			tempty := curprod[1]
			if tempty < 0 {
				lerrorf(ruleline, "must return a value, since LHS has a type")
			}
			if tempty >= NTBASE {
				tempty = nontrst[tempty-NTBASE].value
			} else {
				tempty = TYPE(toklev[tempty])
			}
			if tempty != nontrst[curprod[0]-NTBASE].value {
				lerrorf(ruleline, "default action causes potential type clash")
			}
		}
		moreprod()
		prdptr[nprod] = make([]int, mem)
		copy(prdptr[nprod], curprod)
		nprod++
		moreprod()
		levprd[nprod] = 0
	}

	if TEMPSIZE < ntokens+nnonter+1 {
		errorf("too many tokens (%d) or non-terminals (%d)", ntokens, nnonter)
	}

	//
	// end of all rules
	// dump out the prefix code
	//

	fmt.Fprintf(fcode, "\n\t}")

	// put out non-literal terminals
	for i := TOKSTART; i <= ntokens; i++ {
		// non-literals
		if !tokset[i].noconst {
			fmt.Fprintf(ftable, "const %v = %v\n", tokset[i].name, tokset[i].value)
		}
	}

	// put out names of tokens
	ftable.WriteRune('\n')
	fmt.Fprintf(ftable, "var %sToknames = [...]string{\n", prefix)
	for i := 1; i <= ntokens; i++ {
		fmt.Fprintf(ftable, "\t%q,\n", tokset[i].name)
	}
	fmt.Fprintf(ftable, "}\n")

	// put out names of states.
	// commented out to avoid a huge table just for debugging.
	// re-enable to have the names in the binary.
	ftable.WriteRune('\n')
	fmt.Fprintf(ftable, "var %sStatenames = [...]string{\n", prefix)
	//	for i:=TOKSTART; i<=ntokens; i++ {
	//		fmt.Fprintf(ftable, "\t%q,\n", tokset[i].name);
	//	}
	fmt.Fprintf(ftable, "}\n")

	ftable.WriteRune('\n')
	fmt.Fprintf(ftable, "const %sEofCode = 1\n", prefix)
	fmt.Fprintf(ftable, "const %sErrCode = 2\n", prefix)
	fmt.Fprintf(ftable, "const %sInitialStackSize = %v\n", prefix, initialstacksize)

	//
	// copy any postfix code
	//
	if t == MARK {
		if !lflag {
			fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
		}
		for {
			c := getrune(finput)
			if c == EOF {
				break
			}
			ftable.WriteRune(c)
		}
	}
}

// allocate enough room to hold another production
func moreprod() {
	n := len(prdptr)
	if nprod >= n {
		nn := n + PRODINC
		aprod := make([][]int, nn)
		alevprd := make([]int, nn)
		arlines := make([]int, nn)

		copy(aprod, prdptr)
		copy(alevprd, levprd)
		copy(arlines, rlines)

		prdptr = aprod
		levprd = alevprd
		rlines = arlines
	}
}

// define s to be a terminal if nt==0
// or a nonterminal if nt==1
func defin(nt int, s string) int {
	val := 0
	if nt != 0 {
		nnonter++
		if nnonter >= len(nontrst) {
			anontrst := make([]Symb, nnonter+SYMINC)
			copy(anontrst, nontrst)
			nontrst = anontrst
		}
		nontrst[nnonter] = Symb{name: s}
		return NTBASE + nnonter
	}

	// must be a token
	ntokens++
	if ntokens >= len(tokset) {
		nn := ntokens + SYMINC
		atokset := make([]Symb, nn)
		atoklev := make([]int, nn)

		copy(atoklev, toklev)
		copy(atokset, tokset)

		tokset = atokset
		toklev = atoklev
	}
	tokset[ntokens].name = s
	toklev[ntokens] = 0

	// establish value for token
	// single character literal
	if s[0] == '\'' || s[0] == '"' {
		q, err := strconv.Unquote(s)
		if err != nil {
			errorf("invalid token: %s", err)
		}
		rq := []rune(q)
		if len(rq) != 1 {
			errorf("character token too long: %s", s)
		}
		val = int(rq[0])
		if val == 0 {
			errorf("token value 0 is illegal")
		}
		tokset[ntokens].noconst = true
	} else {
		val = extval
		extval++
		if s[0] == '$' {
			tokset[ntokens].noconst = true
		}
	}

	tokset[ntokens].value = val
	return ntokens
}

var peekline = 0

func gettok() int {
	var i int
	var match, c rune

	tokname = ""
	for {
		lineno += peekline
		peekline = 0
		c = getrune(finput)
		for c == ' ' || c == '\n' || c == '\t' || c == '\v' || c == '\r' {
			if c == '\n' {
				lineno++
			}
			c = getrune(finput)
		}

		// skip comment -- fix
		if c != '/' {
			break
		}
		lineno += skipcom()
	}

	switch c {
	case EOF:
		if tokflag {
			fmt.Printf(">>> ENDFILE %v\n", lineno)
		}
		return ENDFILE

	case '{':
		ungetrune(finput, c)
		if tokflag {
			fmt.Printf(">>> ={ %v\n", lineno)
		}
		return '='

	case '<':
		// get, and look up, a type name (union member name)
		c = getrune(finput)
		for c != '>' && c != EOF && c != '\n' {
			tokname += string(c)
			c = getrune(finput)
		}

		if c != '>' {
			errorf("unterminated < ... > clause")
		}

		for i = 1; i <= ntypes; i++ {
			if typeset[i] == tokname {
				numbval = i
				if tokflag {
					fmt.Printf(">>> TYPENAME old <%v> %v\n", tokname, lineno)
				}
				return TYPENAME
			}
		}
		ntypes++
		numbval = ntypes
		typeset[numbval] = tokname
		if tokflag {
			fmt.Printf(">>> TYPENAME new <%v> %v\n", tokname, lineno)
		}
		return TYPENAME

	case '"', '\'':
		match = c
		tokname = string(c)
		for {
			c = getrune(finput)
			if c == '\n' || c == EOF {
				errorf("illegal or missing ' or \"")
			}
			if c == '\\' {
				tokname += string('\\')
				c = getrune(finput)
			} else if c == match {
				if tokflag {
					fmt.Printf(">>> IDENTIFIER \"%v\" %v\n", tokname, lineno)
				}
				tokname += string(c)
				return IDENTIFIER
			}
			tokname += string(c)
		}

	case '%':
		c = getrune(finput)
		switch c {
		case '%':
			if tokflag {
				fmt.Printf(">>> MARK %%%% %v\n", lineno)
			}
			return MARK
		case '=':
			if tokflag {
				fmt.Printf(">>> PREC %%= %v\n", lineno)
			}
			return PREC
		case '{':
			if tokflag {
				fmt.Printf(">>> LCURLY %%{ %v\n", lineno)
			}
			return LCURLY
		}

		getword(c)
		// find a reserved word
		for i := range resrv {
			if tokname == resrv[i].name {
				if tokflag {
					fmt.Printf(">>> %%%v %v %v\n", tokname,
						resrv[i].value-PRIVATE, lineno)
				}
				return resrv[i].value
			}
		}
		errorf("invalid escape, or illegal reserved word: %v", tokname)

	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		numbval = int(c - '0')
		for {
			c = getrune(finput)
			if !isdigit(c) {
				break
			}
			numbval = numbval*10 + int(c-'0')
		}
		ungetrune(finput, c)
		if tokflag {
			fmt.Printf(">>> NUMBER %v %v\n", numbval, lineno)
		}
		return NUMBER

	default:
		if isword(c) || c == '.' || c == '$' {
			getword(c)
			break
		}
		if tokflag {
			fmt.Printf(">>> OPERATOR %v %v\n", string(c), lineno)
		}
		return int(c)
	}

	// look ahead to distinguish IDENTIFIER from IDENTCOLON
	c = getrune(finput)
	for c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\r' || c == '/' {
		if c == '\n' {
			peekline++
		}
		// look for comments
		if c == '/' {
			peekline += skipcom()
		}
		c = getrune(finput)
	}
	if c == ':' {
		if tokflag {
			fmt.Printf(">>> IDENTCOLON %v: %v\n", tokname, lineno)
		}
		return IDENTCOLON
	}

	ungetrune(finput, c)
	if tokflag {
		fmt.Printf(">>> IDENTIFIER %v %v\n", tokname, lineno)
	}
	return IDENTIFIER
}

func getword(c rune) {
	tokname = ""
	for isword(c) || isdigit(c) || c == '.' || c == '$' {
		tokname += string(c)
		c = getrune(finput)
	}
	ungetrune(finput, c)
}

// determine the type of a symbol
func fdtype(t int) int {
	var v int
	var s string

	if t >= NTBASE {
		v = nontrst[t-NTBASE].value
		s = nontrst[t-NTBASE].name
	} else {
		v = TYPE(toklev[t])
		s = tokset[t].name
	}
	if v <= 0 {
		errorf("must specify type for %v", s)
	}
	return v
}

func chfind(t int, s string) int {
	if s[0] == '"' || s[0] == '\'' {
		t = 0
	}
	for i := 0; i <= ntokens; i++ {
		if s == tokset[i].name {
			return i
		}
	}
	for i := 0; i <= nnonter; i++ {
		if s == nontrst[i].name {
			return NTBASE + i
		}
	}

	// cannot find name
	if t > 1 {
		errorf("%v should have been defined earlier", s)
	}
	return defin(t, s)
}

// copy the union declaration to the output, and the define file if present
func cpyunion() {

	if !lflag {
		fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
	}
	fmt.Fprintf(ftable, "type %sSymType struct", prefix)

	level := 0

out:
	for {
		c := getrune(finput)
		if c == EOF {
			errorf("EOF encountered while processing %%union")
		}
		ftable.WriteRune(c)
		switch c {
		case '\n':
			lineno++
		case '{':
			if level == 0 {
				fmt.Fprintf(ftable, "\n\tyys int")
			}
			level++
		case '}':
			level--
			if level == 0 {
				break out
			}
		}
	}
	fmt.Fprintf(ftable, "\n\n")
}

// saves code between %{ and %}
// adds an import for __fmt__ the first time
func cpycode() {
	lno := lineno

	c := getrune(finput)
	if c == '\n' {
		c = getrune(finput)
		lineno++
	}
	if !lflag {
		fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno)
	}
	// accumulate until %}
	code := make([]rune, 0, 1024)
	for c != EOF {
		if c == '%' {
			c = getrune(finput)
			if c == '}' {
				emitcode(code, lno+1)
				return
			}
			code = append(code, '%')
		}
		code = append(code, c)
		if c == '\n' {
			lineno++
		}
		c = getrune(finput)
	}
	lineno = lno
	errorf("eof before %%}")
}

// emits code saved up from between %{ and %}
// called by cpycode
// adds an import for __yyfmt__ after the package clause
func emitcode(code []rune, lineno int) {
	for i, line := range lines(code) {
		writecode(line)
		if !fmtImported && isPackageClause(line) {
			fmt.Fprintln(ftable, `import __yyfmt__ "fmt"`)
			if !lflag {
				fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i)
			}
			fmtImported = true
		}
	}
}

// does this line look like a package clause?  not perfect: might be confused by early comments.
func isPackageClause(line []rune) bool {
	line = skipspace(line)

	// must be big enough.
	if len(line) < len("package X\n") {
		return false
	}

	// must start with "package"
	for i, r := range []rune("package") {
		if line[i] != r {
			return false
		}
	}
	line = skipspace(line[len("package"):])

	// must have another identifier.
	if len(line) == 0 || (!unicode.IsLetter(line[0]) && line[0] != '_') {
		return false
	}
	for len(line) > 0 {
		if !unicode.IsLetter(line[0]) && !unicode.IsDigit(line[0]) && line[0] != '_' {
			break
		}
		line = line[1:]
	}
	line = skipspace(line)

	// eol, newline, or comment must follow
	if len(line) == 0 {
		return true
	}
	if line[0] == '\r' || line[0] == '\n' {
		return true
	}
	if len(line) >= 2 {
		return line[0] == '/' && (line[1] == '/' || line[1] == '*')
	}
	return false
}

// skip initial spaces
func skipspace(line []rune) []rune {
	for len(line) > 0 {
		if line[0] != ' ' && line[0] != '\t' {
			break
		}
		line = line[1:]
	}
	return line
}

// break code into lines
func lines(code []rune) [][]rune {
	l := make([][]rune, 0, 100)
	for len(code) > 0 {
		// one line per loop
		var i int
		for i = range code {
			if code[i] == '\n' {
				break
			}
		}
		l = append(l, code[:i+1])
		code = code[i+1:]
	}
	return l
}

// writes code to ftable
func writecode(code []rune) {
	for _, r := range code {
		ftable.WriteRune(r)
	}
}

// skip over comments
// skipcom is called after reading a '/'
func skipcom() int {
	c := getrune(finput)
	if c == '/' {
		for c != EOF {
			if c == '\n' {
				return 1
			}
			c = getrune(finput)
		}
		errorf("EOF inside comment")
		return 0
	}
	if c != '*' {
		errorf("illegal comment")
	}

	nl := 0 // lines skipped
	c = getrune(finput)

l1:
	switch c {
	case '*':
		c = getrune(finput)
		if c == '/' {
			break
		}
		goto l1

	case '\n':
		nl++
		fallthrough

	default:
		c = getrune(finput)
		goto l1
	}
	return nl
}

// copy action to the next ; or closing }
func cpyact(curprod []int, max int) {

	if !lflag {
		fmt.Fprintf(fcode, "\n//line %v:%v", infile, lineno)
	}
	fmt.Fprint(fcode, "\n\t\t")

	lno := lineno
	brac := 0

loop:
	for {
		c := getrune(finput)

	swt:
		switch c {
		case ';':
			if brac == 0 {
				fcode.WriteRune(c)
				return
			}

		case '{':
			brac++

		case '$':
			s := 1
			tok := -1
			c = getrune(finput)

			// type description
			if c == '<' {
				ungetrune(finput, c)
				if gettok() != TYPENAME {
					errorf("bad syntax on $<ident> clause")
				}
				tok = numbval
				c = getrune(finput)
			}
			if c == '$' {
				fmt.Fprintf(fcode, "%sVAL", prefix)

				// put out the proper tag...
				if ntypes != 0 {
					if tok < 0 {
						tok = fdtype(curprod[0])
					}
					fmt.Fprintf(fcode, ".%v", typeset[tok])
				}
				continue loop
			}
			if c == '-' {
				s = -s
				c = getrune(finput)
			}
			j := 0
			if isdigit(c) {
				for isdigit(c) {
					j = j*10 + int(c-'0')
					c = getrune(finput)
				}
				ungetrune(finput, c)
				j = j * s
				if j >= max {
					errorf("Illegal use of $%v", j)
				}
			} else if isword(c) || c == '.' {
				// look for $name
				ungetrune(finput, c)
				if gettok() != IDENTIFIER {
					errorf("$ must be followed by an identifier")
				}
				tokn := chfind(2, tokname)
				fnd := -1
				c = getrune(finput)
				if c != '@' {
					ungetrune(finput, c)
				} else if gettok() != NUMBER {
					errorf("@ must be followed by number")
				} else {
					fnd = numbval
				}
				for j = 1; j < max; j++ {
					if tokn == curprod[j] {
						fnd--
						if fnd <= 0 {
							break
						}
					}
				}
				if j >= max {
					errorf("$name or $name@number not found")
				}
			} else {
				fcode.WriteRune('$')
				if s < 0 {
					fcode.WriteRune('-')
				}
				ungetrune(finput, c)
				continue loop
			}
			fmt.Fprintf(fcode, "%sDollar[%v]", prefix, j)

			// put out the proper tag
			if ntypes != 0 {
				if j <= 0 && tok < 0 {
					errorf("must specify type of $%v", j)
				}
				if tok < 0 {
					tok = fdtype(curprod[j])
				}
				fmt.Fprintf(fcode, ".%v", typeset[tok])
			}
			continue loop

		case '}':
			brac--
			if brac != 0 {
				break
			}
			fcode.WriteRune(c)
			return

		case '/':
			nc := getrune(finput)
			if nc != '/' && nc != '*' {
				ungetrune(finput, nc)
				break
			}
			// a comment
			fcode.WriteRune(c)
			fcode.WriteRune(nc)
			c = getrune(finput)
			for c != EOF {
				switch {
				case c == '\n':
					lineno++
					if nc == '/' { // end of // comment
						break swt
					}
				case c == '*' && nc == '*': // end of /* comment?
					nnc := getrune(finput)
					if nnc == '/' {
						fcode.WriteRune('*')
						fcode.WriteRune('/')
						continue loop
					}
					ungetrune(finput, nnc)
				}
				fcode.WriteRune(c)
				c = getrune(finput)
			}
			errorf("EOF inside comment")

		case '\'', '"':
			// character string or constant
			match := c
			fcode.WriteRune(c)
			c = getrune(finput)
			for c != EOF {
				if c == '\\' {
					fcode.WriteRune(c)
					c = getrune(finput)
					if c == '\n' {
						lineno++
					}
				} else if c == match {
					break swt
				}
				if c == '\n' {
					errorf("newline in string or char const")
				}
				fcode.WriteRune(c)
				c = getrune(finput)
			}
			errorf("EOF in string or character constant")

		case EOF:
			lineno = lno
			errorf("action does not terminate")

		case '\n':
			fmt.Fprint(fcode, "\n\t")
			lineno++
			continue loop
		}

		fcode.WriteRune(c)
	}
}

func openup() {
	infile = flag.Arg(0)
	finput = open(infile)
	if finput == nil {
		errorf("cannot open %v", infile)
	}

	foutput = nil
	if vflag != "" {
		foutput = create(vflag)
		if foutput == nil {
			errorf("can't create file %v", vflag)
		}
	}

	ftable = nil
	if oflag == "" {
		oflag = "y.go"
	}
	ftable = create(oflag)
	if ftable == nil {
		errorf("can't create file %v", oflag)
	}

}

// return a pointer to the name of symbol i
func symnam(i int) string {
	var s string

	if i >= NTBASE {
		s = nontrst[i-NTBASE].name
	} else {
		s = tokset[i].name
	}
	return s
}

// set elements 0 through n-1 to c
func aryfil(v []int, n, c int) {
	for i := range n {
		v[i] = c
	}
}

// compute an array with the beginnings of productions yielding given nonterminals
// The array pres points to these lists
// the array pyield has the lists: the total size is only NPROD+1
func cpres() {
	pres = make([][][]int, nnonter+1)
	curres := make([][]int, nprod)

	if false {
		for j := 0; j <= nnonter; j++ {
			fmt.Printf("nnonter[%v] = %v\n", j, nontrst[j].name)
		}
		for j := 0; j < nprod; j++ {
			fmt.Printf("prdptr[%v][0] = %v+NTBASE\n", j, prdptr[j][0]-NTBASE)
		}
	}

	fatfl = 0 // make undefined symbols nonfatal
	for i := 0; i <= nnonter; i++ {
		n := 0
		c := i + NTBASE
		for j := 0; j < nprod; j++ {
			if prdptr[j][0] == c {
				curres[n] = prdptr[j][1:]
				n++
			}
		}
		if n == 0 {
			errorf("nonterminal %v not defined", nontrst[i].name)
			continue
		}
		pres[i] = make([][]int, n)
		copy(pres[i], curres)
	}
	fatfl = 1
	if nerrors != 0 {
		summary()
		exit(1)
	}
}

// mark nonterminals which derive the empty string
// also, look for nonterminals which don't derive any token strings
func cempty() {
	var i, p, np int
	var prd []int

	pempty = make([]int, nnonter+1)

	// first, use the array pempty to detect productions that can never be reduced
	// set pempty to WHONOWS
	aryfil(pempty, nnonter+1, WHOKNOWS)

	// now, look at productions, marking nonterminals which derive something
more:
	for {
		for i = 0; i < nprod; i++ {
			prd = prdptr[i]
			if pempty[prd[0]-NTBASE] != 0 {
				continue
			}
			np = len(prd) - 1
			for p = 1; p < np; p++ {
				if prd[p] >= NTBASE && pempty[prd[p]-NTBASE] == WHOKNOWS {
					break
				}
			}
			// production can be derived
			if p == np {
				pempty[prd[0]-NTBASE] = OK
				continue more
			}
		}
		break
	}

	// now, look at the nonterminals, to see if they are all OK
	for i = 0; i <= nnonter; i++ {
		// the added production rises or falls as the start symbol ...
		if i == 0 {
			continue
		}
		if pempty[i] != OK {
			fatfl = 0
			errorf("nonterminal %s never derives any token string", nontrst[i].name)
		}
	}

	if nerrors != 0 {
		summary()
		exit(1)
	}

	// now, compute the pempty array, to see which nonterminals derive the empty string
	// set pempty to WHOKNOWS
	aryfil(pempty, nnonter+1, WHOKNOWS)

	// loop as long as we keep finding empty nonterminals

again:
	for {
	next:
		for i = 1; i < nprod; i++ {
			// not known to be empty
			prd = prdptr[i]
			if pempty[prd[0]-NTBASE] != WHOKNOWS {
				continue
			}
			np = len(prd) - 1
			for p = 1; p < np; p++ {
				if prd[p] < NTBASE || pempty[prd[p]-NTBASE] != EMPTY {
					continue next
				}
			}

			// we have a nontrivially empty nonterminal
			pempty[prd[0]-NTBASE] = EMPTY

			// got one ... try for another
			continue again
		}
		return
	}
}

// compute an array with the first of nonterminals
func cpfir() {
	var s, n, p, np, ch, i int
	var curres [][]int
	var prd []int

	wsets = make([]Wset, nnonter+WSETINC)
	pfirst = make([]Lkset, nnonter+1)
	for i = 0; i <= nnonter; i++ {
		wsets[i].ws = mkset()
		pfirst[i] = mkset()
		curres = pres[i]
		n = len(curres)

		// initially fill the sets
		for s = 0; s < n; s++ {
			prd = curres[s]
			np = len(prd) - 1
			for p = 0; p < np; p++ {
				ch = prd[p]
				if ch < NTBASE {
					setbit(pfirst[i], ch)
					break
				}
				if pempty[ch-NTBASE] == 0 {
					break
				}
			}
		}
	}

	// now, reflect transitivity
	changes := 1
	for changes != 0 {
		changes = 0
		for i = 0; i <= nnonter; i++ {
			curres = pres[i]
			n = len(curres)
			for s = 0; s < n; s++ {
				prd = curres[s]
				np = len(prd) - 1
				for p = 0; p < np; p++ {
					ch = prd[p] - NTBASE
					if ch < 0 {
						break
					}
					changes |= setunion(pfirst[i], pfirst[ch])
					if pempty[ch] == 0 {
						break
					}
				}
			}
		}
	}

	if indebug == 0 {
		return
	}
	if foutput != nil {
		for i = 0; i <= nnonter; i++ {
			fmt.Fprintf(foutput, "\n%v: %v %v\n",
				nontrst[i].name, pfirst[i], pempty[i])
		}
	}
}

// generate the states
func stagen() {
	// initialize
	nstate = 0
	tstates = make([]int, ntokens+1)  // states generated by terminal gotos
	ntstates = make([]int, nnonter+1) // states generated by nonterminal gotos
	amem = make([]int, ACTSIZE)
	memp = 0

	clset = mkset()
	pstate[0] = 0
	pstate[1] = 0
	aryfil(clset, tbitset, 0)
	putitem(Pitem{prdptr[0], 0, 0, 0}, clset)
	tystate[0] = MUSTDO
	nstate = 1
	pstate[2] = pstate[1]

	//
	// now, the main state generation loop
	// first pass generates all of the states
	// later passes fix up lookahead
	// could be sped up a lot by remembering
	// results of the first pass rather than recomputing
	//
	first := 1
	for more := 1; more != 0; first = 0 {
		more = 0
		for i := 0; i < nstate; i++ {
			if tystate[i] != MUSTDO {
				continue
			}

			tystate[i] = DONE
			aryfil(temp1, nnonter+1, 0)

			// take state i, close it, and do gotos
			closure(i)

			// generate goto's
			for p := 0; p < cwp; p++ {
				pi := wsets[p]
				if pi.flag != 0 {
					continue
				}
				wsets[p].flag = 1
				c := pi.pitem.first
				if c <= 1 {
					if pstate[i+1]-pstate[i] <= p {
						tystate[i] = MUSTLOOKAHEAD
					}
					continue
				}

				// do a goto on c
				putitem(wsets[p].pitem, wsets[p].ws)
				for q := p + 1; q < cwp; q++ {
					// this item contributes to the goto
					if c == wsets[q].pitem.first {
						putitem(wsets[q].pitem, wsets[q].ws)
						wsets[q].flag = 1
					}
				}

				if c < NTBASE {
					state(c) // register new state
				} else {
					temp1[c-NTBASE] = state(c)
				}
			}

			if gsdebug != 0 && foutput != nil {
				fmt.Fprintf(foutput, "%v: ", i)
				for j := 0; j <= nnonter; j++ {
					if temp1[j] != 0 {
						fmt.Fprintf(foutput, "%v %v,", nontrst[j].name, temp1[j])
					}
				}
				fmt.Fprintf(foutput, "\n")
			}

			if first != 0 {
				indgo[i] = apack(temp1[1:], nnonter-1) - 1
			}

			more++
		}
	}
}

// generate the closure of state i
func closure(i int) {
	zzclose++

	// first, copy kernel of state i to wsets
	cwp = 0
	q := pstate[i+1]
	for p := pstate[i]; p < q; p++ {
		wsets[cwp].pitem = statemem[p].pitem
		wsets[cwp].flag = 1 // this item must get closed
		copy(wsets[cwp].ws, statemem[p].look)
		cwp++
	}

	// now, go through the loop, closing each item
	work := 1
	for work != 0 {
		work = 0
		for u := 0; u < cwp; u++ {
			if wsets[u].flag == 0 {
				continue
			}

			// dot is before c
			c := wsets[u].pitem.first
			if c < NTBASE {
				wsets[u].flag = 0
				// only interesting case is where . is before nonterminal
				continue
			}

			// compute the lookahead
			aryfil(clset, tbitset, 0)

			// find items involving c
			for v := u; v < cwp; v++ {
				if wsets[v].flag != 1 || wsets[v].pitem.first != c {
					continue
				}
				pi := wsets[v].pitem.prod
				ipi := wsets[v].pitem.off + 1

				wsets[v].flag = 0
				if nolook != 0 {
					continue
				}

				ch := pi[ipi]
				ipi++
				for ch > 0 {
					// terminal symbol
					if ch < NTBASE {
						setbit(clset, ch)
						break
					}

					// nonterminal symbol
					setunion(clset, pfirst[ch-NTBASE])
					if pempty[ch-NTBASE] == 0 {
						break
					}
					ch = pi[ipi]
					ipi++
				}
				if ch <= 0 {
					setunion(clset, wsets[v].ws)
				}
			}

			//
			// now loop over productions derived from c
			//
			curres := pres[c-NTBASE]
			n := len(curres)

		nexts:
			// initially fill the sets
			for s := range n {
				prd := curres[s]

				//
				// put these items into the closure
				// is the item there
				//
				for v := 0; v < cwp; v++ {
					// yes, it is there
					if wsets[v].pitem.off == 0 &&
						aryeq(wsets[v].pitem.prod, prd) != 0 {
						if nolook == 0 &&
							setunion(wsets[v].ws, clset) != 0 {
							wsets[v].flag = 1
							work = 1
						}
						continue nexts
					}
				}

				//  not there; make a new entry
				if cwp >= len(wsets) {
					awsets := make([]Wset, cwp+WSETINC)
					copy(awsets, wsets)
					wsets = awsets
				}
				wsets[cwp].pitem = Pitem{prd, 0, prd[0], -prd[len(prd)-1]}
				wsets[cwp].flag = 1
				wsets[cwp].ws = mkset()
				if nolook == 0 {
					work = 1
					copy(wsets[cwp].ws, clset)
				}
				cwp++
			}
		}
	}

	// have computed closure; flags are reset; return
	if cldebug != 0 && foutput != nil {
		fmt.Fprintf(foutput, "\nState %v, nolook = %v\n", i, nolook)
		for u := 0; u < cwp; u++ {
			if wsets[u].flag != 0 {
				fmt.Fprintf(foutput, "flag set\n")
			}
			wsets[u].flag = 0
			fmt.Fprintf(foutput, "\t%v", writem(wsets[u].pitem))
			prlook(wsets[u].ws)
			fmt.Fprintf(foutput, "\n")
		}
	}
}

// sorts last state,and sees if it equals earlier ones. returns state number
func state(c int) int {
	zzstate++
	p1 := pstate[nstate]
	p2 := pstate[nstate+1]
	if p1 == p2 {
		return 0 // null state
	}

	// sort the items
	var k, l int
	for k = p1 + 1; k < p2; k++ { // make k the biggest
		for l = k; l > p1; l-- {
			if statemem[l].pitem.prodno < statemem[l-1].pitem.prodno ||
				statemem[l].pitem.prodno == statemem[l-1].pitem.prodno &&
					statemem[l].pitem.off < statemem[l-1].pitem.off {
				s := statemem[l]
				statemem[l] = statemem[l-1]
				statemem[l-1] = s
			} else {
				break
			}
		}
	}

	size1 := p2 - p1 // size of state

	var i int
	if c >= NTBASE {
		i = ntstates[c-NTBASE]
	} else {
		i = tstates[c]
	}

look:
	for ; i != 0; i = mstates[i] {
		// get ith state
		q1 := pstate[i]
		q2 := pstate[i+1]
		size2 := q2 - q1
		if size1 != size2 {
			continue
		}
		k = p1
		for l = q1; l < q2; l++ {
			if aryeq(statemem[l].pitem.prod, statemem[k].pitem.prod) == 0 ||
				statemem[l].pitem.off != statemem[k].pitem.off {
				continue look
			}
			k++
		}

		// found it
		pstate[nstate+1] = pstate[nstate] // delete last state

		// fix up lookaheads
		if nolook != 0 {
			return i
		}
		k = p1
		for l = q1; l < q2; l++ {
			if setunion(statemem[l].look, statemem[k].look) != 0 {
				tystate[i] = MUSTDO
			}
			k++
		}
		return i
	}

	// state is new
	zznewstate++
	if nolook != 0 {
		errorf("yacc state/nolook error")
	}
	pstate[nstate+2] = p2
	if nstate+1 >= NSTATES {
		errorf("too many states")
	}
	if c >= NTBASE {
		mstates[nstate] = ntstates[c-NTBASE]
		ntstates[c-NTBASE] = nstate
	} else {
		mstates[nstate] = tstates[c]
		tstates[c] = nstate
	}
	tystate[nstate] = MUSTDO
	nstate++
	return nstate - 1
}

func putitem(p Pitem, set Lkset) {
	p.off++
	p.first = p.prod[p.off]

	if pidebug != 0 && foutput != nil {
		fmt.Fprintf(foutput, "putitem(%v), state %v\n", writem(p), nstate)
	}
	j := pstate[nstate+1]
	if j >= len(statemem) {
		asm := make([]Item, j+STATEINC)
		copy(asm, statemem)
		statemem = asm
	}
	statemem[j].pitem = p
	if nolook == 0 {
		s := mkset()
		copy(s, set)
		statemem[j].look = s
	}
	j++
	pstate[nstate+1] = j
}

// creates output string for item pointed to by pp
func writem(pp Pitem) string {
	var i int

	p := pp.prod
	var q strings.Builder
	q.WriteString(chcopy(nontrst[prdptr[pp.prodno][0]-NTBASE].name))
	q.WriteString(": ")
	npi := pp.off

	pi := aryeq(p, prdptr[pp.prodno])

	for {
		c := ' '
		if pi == npi {
			c = '.'
		}
		q.WriteByte(byte(c))

		i = p[pi]
		pi++
		if i <= 0 {
			break
		}
		q.WriteString(chcopy(symnam(i)))
	}

	// an item calling for a reduction
	i = p[npi]
	if i < 0 {
		fmt.Fprintf(&q, "    (%v)", -i)
	}

	return q.String()
}

// pack state i from temp1 into amem
func apack(p []int, n int) int {
	//
	// we don't need to worry about checking because
	// we will only look at entries known to be there...
	// eliminate leading and trailing 0's
	//
	off := 0
	pp := 0
	for ; pp <= n && p[pp] == 0; pp++ {
		off--
	}

	// no actions
	if pp > n {
		return 0
	}
	for ; n > pp && p[n] == 0; n-- {
	}
	p = p[pp : n+1]

	// now, find a place for the elements from p to q, inclusive
	r := len(amem) - len(p)

nextk:
	for rr := 0; rr <= r; rr++ {
		qq := rr
		for pp = 0; pp < len(p); pp++ {
			if p[pp] != 0 {
				if p[pp] != amem[qq] && amem[qq] != 0 {
					continue nextk
				}
			}
			qq++
		}

		// we have found an acceptable k
		if pkdebug != 0 && foutput != nil {
			fmt.Fprintf(foutput, "off = %v, k = %v\n", off+rr, rr)
		}
		qq = rr
		for pp = 0; pp < len(p); pp++ {
			if p[pp] != 0 {
				if qq > memp {
					memp = qq
				}
				amem[qq] = p[pp]
			}
			qq++
		}
		if pkdebug != 0 && foutput != nil {
			for pp = 0; pp <= memp; pp += 10 {
				fmt.Fprintf(foutput, "\n")
				for qq = pp; qq <= pp+9; qq++ {
					fmt.Fprintf(foutput, "%v ", amem[qq])
				}
				fmt.Fprintf(foutput, "\n")
			}
		}
		return off + rr
	}
	errorf("no space in action table")
	return 0
}

// print the output for the states
func output() {
	var c, u, v int

	if !lflag {
		fmt.Fprintf(ftable, "\n//line yacctab:1")
	}
	var actions []int

	if len(errors) > 0 {
		stateTable = make([]Row, nstate)
	}

	noset := mkset()

	// output the stuff for state i
	for i := 0; i < nstate; i++ {
		nolook = 0
		if tystate[i] != MUSTLOOKAHEAD {
			nolook = 1
		}
		closure(i)

		// output actions
		nolook = 1
		aryfil(temp1, ntokens+nnonter+1, 0)
		for u = 0; u < cwp; u++ {
			c = wsets[u].pitem.first
			if c > 1 && c < NTBASE && temp1[c] == 0 {
				for v = u; v < cwp; v++ {
					if c == wsets[v].pitem.first {
						putitem(wsets[v].pitem, noset)
					}
				}
				temp1[c] = state(c)
			} else if c > NTBASE {
				c -= NTBASE
				if temp1[c+ntokens] == 0 {
					temp1[c+ntokens] = amem[indgo[i]+c]
				}
			}
		}
		if i == 1 {
			temp1[1] = ACCEPTCODE
		}

		// now, we have the shifts; look at the reductions
		lastred = 0
		for u = 0; u < cwp; u++ {
			c = wsets[u].pitem.first

			// reduction
			if c > 0 {
				continue
			}
			lastred = -c
			us := wsets[u].ws
			for k := 0; k <= ntokens; k++ {
				if bitset(us, k) == 0 {
					continue
				}
				if temp1[k] == 0 {
					temp1[k] = c
				} else if temp1[k] < 0 { // reduce/reduce conflict
					if foutput != nil {
						fmt.Fprintf(foutput,
							"\n %v: reduce/reduce conflict  (red'ns "+
								"%v and %v) on %v",
							i, -temp1[k], lastred, symnam(k))
					}
					if -temp1[k] > lastred {
						temp1[k] = -lastred
					}
					zzrrconf++
				} else {
					// potential shift/reduce conflict
					precftn(lastred, k, i)
				}
			}
		}
		actions = addActions(actions, i)
	}

	arrayOutColumns("Exca", actions, 2, false)
	fmt.Fprintf(ftable, "\n")
	ftable.WriteRune('\n')
	fmt.Fprintf(ftable, "const %sPrivate = %v\n", prefix, PRIVATE)
}

// decide a shift/reduce conflict by precedence.
// r is a rule number, t a token number
// the conflict is in state s
// temp1[t] is changed to reflect the action
func precftn(r, t, s int) {
	action := NOASC

	lp := levprd[r]
	lt := toklev[t]
	if PLEVEL(lt) == 0 || PLEVEL(lp) == 0 {
		// conflict
		if foutput != nil {
			fmt.Fprintf(foutput,
				"\n%v: shift/reduce conflict (shift %v(%v), red'n %v(%v)) on %v",
				s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t))
		}
		zzsrconf++
		return
	}
	if PLEVEL(lt) == PLEVEL(lp) {
		action = ASSOC(lt)
	} else if PLEVEL(lt) > PLEVEL(lp) {
		action = RASC // shift
	} else {
		action = LASC
	} // reduce
	switch action {
	case BASC: // error action
		temp1[t] = ERRCODE
	case LASC: // reduce
		temp1[t] = -r
	}
}

// output state i
// temp1 has the actions, lastred the default
func addActions(act []int, i int) []int {
	var p, p1 int

	// find the best choice for lastred
	lastred = 0
	ntimes := 0
	for j := 0; j <= ntokens; j++ {
		if temp1[j] >= 0 {
			continue
		}
		if temp1[j]+lastred == 0 {
			continue
		}
		// count the number of appearances of temp1[j]
		count := 0
		tred := -temp1[j]
		levprd[tred] |= REDFLAG
		for p = 0; p <= ntokens; p++ {
			if temp1[p]+tred == 0 {
				count++
			}
		}
		if count > ntimes {
			lastred = tred
			ntimes = count
		}
	}

	//
	// for error recovery, arrange that, if there is a shift on the
	// error recovery token, `error', that the default be the error action
	//
	if temp1[2] > 0 {
		lastred = 0
	}

	// clear out entries in temp1 which equal lastred
	// count entries in optst table
	n := 0
	for p = 0; p <= ntokens; p++ {
		p1 = temp1[p]
		if p1+lastred == 0 {
			temp1[p] = 0
			p1 = 0
		}
		if p1 > 0 && p1 != ACCEPTCODE && p1 != ERRCODE {
			n++
		}
	}

	wrstate(i)
	defact[i] = lastred
	flag := 0
	os := make([]int, n*2)
	n = 0
	for p = 0; p <= ntokens; p++ {
		p1 = temp1[p]
		if p1 != 0 {
			if p1 < 0 {
				p1 = -p1
			} else if p1 == ACCEPTCODE {
				p1 = -1
			} else if p1 == ERRCODE {
				p1 = 0
			} else {
				os[n] = p
				n++
				os[n] = p1
				n++
				zzacent++
				continue
			}
			if flag == 0 {
				act = append(act, -1, i)
			}
			flag++
			act = append(act, p, p1)
			zzexcp++
		}
	}
	if flag != 0 {
		defact[i] = -2
		act = append(act, -2, lastred)
	}
	optst[i] = os
	return act
}

// writes state i
func wrstate(i int) {
	var j0, j1, u int
	var pp, qq int

	if len(errors) > 0 {
		actions := slices.Clone(temp1)
		defaultAction := ERRCODE
		if lastred != 0 {
			defaultAction = -lastred
		}
		stateTable[i] = Row{actions, defaultAction}
	}

	if foutput == nil {
		return
	}
	fmt.Fprintf(foutput, "\nstate %v\n", i)
	qq = pstate[i+1]
	for pp = pstate[i]; pp < qq; pp++ {
		fmt.Fprintf(foutput, "\t%v\n", writem(statemem[pp].pitem))
	}
	if tystate[i] == MUSTLOOKAHEAD {
		// print out empty productions in closure
		for u = pstate[i+1] - pstate[i]; u < cwp; u++ {
			if wsets[u].pitem.first < 0 {
				fmt.Fprintf(foutput, "\t%v\n", writem(wsets[u].pitem))
			}
		}
	}

	// check for state equal to another
	for j0 = 0; j0 <= ntokens; j0++ {
		j1 = temp1[j0]
		if j1 != 0 {
			fmt.Fprintf(foutput, "\n\t%v  ", symnam(j0))

			// shift, error, or accept
			if j1 > 0 {
				if j1 == ACCEPTCODE {
					fmt.Fprintf(foutput, "accept")
				} else if j1 == ERRCODE {
					fmt.Fprintf(foutput, "error")
				} else {
					fmt.Fprintf(foutput, "shift %v", j1)
				}
			} else {
				fmt.Fprintf(foutput, "reduce %v (src line %v)", -j1, rlines[-j1])
			}
		}
	}

	// output the final production
	if lastred != 0 {
		fmt.Fprintf(foutput, "\n\t.  reduce %v (src line %v)\n\n",
			lastred, rlines[lastred])
	} else {
		fmt.Fprintf(foutput, "\n\t.  error\n\n")
	}

	// now, output nonterminal actions
	j1 = ntokens
	for j0 = 1; j0 <= nnonter; j0++ {
		j1++
		if temp1[j1] != 0 {
			fmt.Fprintf(foutput, "\t%v  goto %v\n", symnam(j0+NTBASE), temp1[j1])
		}
	}
}

// output the gotos for the nontermninals
func go2out() {
	for i := 1; i <= nnonter; i++ {
		go2gen(i)

		// find the best one to make default
		best := -1
		times := 0

		// is j the most frequent
		for j := 0; j < nstate; j++ {
			if tystate[j] == 0 {
				continue
			}
			if tystate[j] == best {
				continue
			}

			// is tystate[j] the most frequent
			count := 0
			cbest := tystate[j]
			for k := j; k < nstate; k++ {
				if tystate[k] == cbest {
					count++
				}
			}
			if count > times {
				best = cbest
				times = count
			}
		}

		// best is now the default entry
		zzgobest += times - 1
		n := 0
		for j := 0; j < nstate; j++ {
			if tystate[j] != 0 && tystate[j] != best {
				n++
			}
		}
		goent := make([]int, 2*n+1)
		n = 0
		for j := 0; j < nstate; j++ {
			if tystate[j] != 0 && tystate[j] != best {
				goent[n] = j
				n++
				goent[n] = tystate[j]
				n++
				zzgoent++
			}
		}

		// now, the default
		if best == -1 {
			best = 0
		}

		zzgoent++
		goent[n] = best
		yypgo[i] = goent
	}
}

// output the gotos for nonterminal c
func go2gen(c int) {
	var i, cc, p, q int

	// first, find nonterminals with gotos on c
	aryfil(temp1, nnonter+1, 0)
	temp1[c] = 1
	work := 1
	for work != 0 {
		work = 0
		for i = 0; i < nprod; i++ {
			// cc is a nonterminal with a goto on c
			cc = prdptr[i][1] - NTBASE
			if cc >= 0 && temp1[cc] != 0 {
				// thus, the left side of production i does too
				cc = prdptr[i][0] - NTBASE
				if temp1[cc] == 0 {
					work = 1
					temp1[cc] = 1
				}
			}
		}
	}

	// now, we have temp1[c] = 1 if a goto on c in closure of cc
	if g2debug != 0 && foutput != nil {
		fmt.Fprintf(foutput, "%v: gotos on ", nontrst[c].name)
		for i = 0; i <= nnonter; i++ {
			if temp1[i] != 0 {
				fmt.Fprintf(foutput, "%v ", nontrst[i].name)
			}
		}
		fmt.Fprintf(foutput, "\n")
	}

	// now, go through and put gotos into tystate
	aryfil(tystate, nstate, 0)
	for i = 0; i < nstate; i++ {
		q = pstate[i+1]
		for p = pstate[i]; p < q; p++ {
			cc = statemem[p].pitem.first
			if cc >= NTBASE {
				// goto on c is possible
				if temp1[cc-NTBASE] != 0 {
					tystate[i] = amem[indgo[i]+c]
					break
				}
			}
		}
	}
}

// in order to free up the mem and amem arrays for the optimizer,
// and still be able to output yyr1, etc., after the sizes of
// the action array is known, we hide the nonterminals
// derived by productions in levprd.
func hideprod() {
	nred := 0
	levprd[0] = 0
	for i := 1; i < nprod; i++ {
		if (levprd[i] & REDFLAG) == 0 {
			if foutput != nil {
				fmt.Fprintf(foutput, "Rule not reduced: %v\n",
					writem(Pitem{prdptr[i], 0, 0, i}))
			}
			fmt.Printf("rule %v never reduced\n", writem(Pitem{prdptr[i], 0, 0, i}))
			nred++
		}
		levprd[i] = prdptr[i][0] - NTBASE
	}
	if nred != 0 {
		fmt.Printf("%v rules never reduced\n", nred)
	}
}

func callopt() {
	var j, k, p, q, i int
	var v []int

	pgo = make([]int, nnonter+1)
	pgo[0] = 0
	maxoff = 0
	maxspr = 0
	for i = 0; i < nstate; i++ {
		k = 32000
		j = 0
		v = optst[i]
		q = len(v)
		for p = 0; p < q; p += 2 {
			if v[p] > j {
				j = v[p]
			}
			if v[p] < k {
				k = v[p]
			}
		}

		// nontrivial situation
		if k <= j {
			// j is now the range
			//			j -= k;			// call scj
			if k > maxoff {
				maxoff = k
			}
		}
		tystate[i] = q + 2*j
		if j > maxspr {
			maxspr = j
		}
	}

	// initialize ggreed table
	ggreed = make([]int, nnonter+1)
	for i = 1; i <= nnonter; i++ {
		ggreed[i] = 1
		j = 0

		// minimum entry index is always 0
		v = yypgo[i]
		q = len(v) - 1
		for p = 0; p < q; p += 2 {
			ggreed[i] += 2
			if v[p] > j {
				j = v[p]
			}
		}
		ggreed[i] = ggreed[i] + 2*j
		if j > maxoff {
			maxoff = j
		}
	}

	// now, prepare to put the shift actions into the amem array
	for i = 0; i < ACTSIZE; i++ {
		amem[i] = 0
	}
	maxa = 0
	for i = 0; i < nstate; i++ {
		if tystate[i] == 0 && adb > 1 {
			fmt.Fprintf(ftable, "State %v: null\n", i)
		}
		indgo[i] = yyFlag
	}

	i = nxti()
	for i != NOMORE {
		if i >= 0 {
			stin(i)
		} else {
			gin(-i)
		}
		i = nxti()
	}

	// print amem array
	if adb > 2 {
		for p = 0; p <= maxa; p += 10 {
			fmt.Fprintf(ftable, "%v  ", p)
			for i = range 10 {
				fmt.Fprintf(ftable, "%v  ", amem[p+i])
			}
			ftable.WriteRune('\n')
		}
	}

	aoutput()
	osummary()
}

// finds the next i
func nxti() int {
	max := 0
	maxi := 0
	for i := 1; i <= nnonter; i++ {
		if ggreed[i] >= max {
			max = ggreed[i]
			maxi = -i
		}
	}
	for i := 0; i < nstate; i++ {
		if tystate[i] >= max {
			max = tystate[i]
			maxi = i
		}
	}
	if max == 0 {
		return NOMORE
	}
	return maxi
}

func gin(i int) {
	var s int

	// enter gotos on nonterminal i into array amem
	ggreed[i] = 0

	q := yypgo[i]
	nq := len(q) - 1

	// now, find amem place for it
nextgp:
	for p := range ACTSIZE {
		if amem[p] != 0 {
			continue
		}
		for r := 0; r < nq; r += 2 {
			s = p + q[r] + 1
			if s > maxa {
				maxa = s
				if maxa >= ACTSIZE {
					errorf("a array overflow")
				}
			}
			if amem[s] != 0 {
				continue nextgp
			}
		}

		// we have found amem spot
		amem[p] = q[nq]
		if p > maxa {
			maxa = p
		}
		for r := 0; r < nq; r += 2 {
			s = p + q[r] + 1
			amem[s] = q[r+1]
		}
		pgo[i] = p
		if adb > 1 {
			fmt.Fprintf(ftable, "Nonterminal %v, entry at %v\n", i, pgo[i])
		}
		return
	}
	errorf("cannot place goto %v\n", i)
}

func stin(i int) {
	var s int

	tystate[i] = 0

	// enter state i into the amem array
	q := optst[i]
	nq := len(q)

nextn:
	// find an acceptable place
	for n := -maxoff; n < ACTSIZE; n++ {
		flag := 0
		for r := 0; r < nq; r += 2 {
			s = q[r] + n
			if s < 0 || s > ACTSIZE {
				continue nextn
			}
			if amem[s] == 0 {
				flag++
			} else if amem[s] != q[r+1] {
				continue nextn
			}
		}

		// check the position equals another only if the states are identical
		for j := 0; j < nstate; j++ {
			if indgo[j] == n {

				// we have some disagreement
				if flag != 0 {
					continue nextn
				}
				if nq == len(optst[j]) {

					// states are equal
					indgo[i] = n
					if adb > 1 {
						fmt.Fprintf(ftable, "State %v: entry at"+
							"%v equals state %v\n",
							i, n, j)
					}
					return
				}

				// we have some disagreement
				continue nextn
			}
		}

		for r := 0; r < nq; r += 2 {
			s = q[r] + n
			if s > maxa {
				maxa = s
			}
			if amem[s] != 0 && amem[s] != q[r+1] {
				errorf("clobber of a array, pos'n %v, by %v", s, q[r+1])
			}
			amem[s] = q[r+1]
		}
		indgo[i] = n
		if adb > 1 {
			fmt.Fprintf(ftable, "State %v: entry at %v\n", i, indgo[i])
		}
		return
	}
	errorf("Error; failure to place state %v", i)
}

// this version is for limbo
// write out the optimized parser
func aoutput() {
	ftable.WriteRune('\n')
	fmt.Fprintf(ftable, "const %sLast = %v\n", prefix, maxa+1)
	arout("Act", amem, maxa+1)
	arout("Pact", indgo, nstate)
	arout("Pgo", pgo, nnonter+1)
}

// put out other arrays, copy the parsers
func others() {
	var i, j int

	arout("R1", levprd, nprod)
	aryfil(temp1, nprod, 0)

	//
	//yyr2 is the number of rules for each production
	//
	for i = 1; i < nprod; i++ {
		temp1[i] = len(prdptr[i]) - 2
	}
	arout("R2", temp1, nprod)

	aryfil(temp1, nstate, -1000)
	for i = 0; i <= ntokens; i++ {
		for j := tstates[i]; j != 0; j = mstates[j] {
			temp1[j] = i
		}
	}
	for i = 0; i <= nnonter; i++ {
		for j = ntstates[i]; j != 0; j = mstates[j] {
			temp1[j] = -i
		}
	}
	arout("Chk", temp1, nstate)
	arrayOutColumns("Def", defact[:nstate], 10, false)

	// put out token translation tables
	// table 1 has 0-256
	aryfil(temp1, 256, 0)
	c := 0
	for i = 1; i <= ntokens; i++ {
		j = tokset[i].value
		if j >= 0 && j < 256 {
			if temp1[j] != 0 {
				fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n")
				fmt.Printf("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name)
				nerrors++
			}
			temp1[j] = i
			if j > c {
				c = j
			}
		}
	}
	for i = 0; i <= c; i++ {
		if temp1[i] == 0 {
			temp1[i] = YYLEXUNK
		}
	}
	arout("Tok1", temp1, c+1)

	// table 2 has PRIVATE-PRIVATE+256
	aryfil(temp1, 256, 0)
	c = 0
	for i = 1; i <= ntokens; i++ {
		j = tokset[i].value - PRIVATE
		if j >= 0 && j < 256 {
			if temp1[j] != 0 {
				fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n")
				fmt.Printf("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name)
				nerrors++
			}
			temp1[j] = i
			if j > c {
				c = j
			}
		}
	}
	arout("Tok2", temp1, c+1)

	// table 3 has everything else
	ftable.WriteRune('\n')
	var v []int
	for i = 1; i <= ntokens; i++ {
		j = tokset[i].value
		if j >= 0 && j < 256 {
			continue
		}
		if j >= PRIVATE && j < 256+PRIVATE {
			continue
		}

		v = append(v, j, i)
	}
	v = append(v, 0)
	arout("Tok3", v, len(v))
	fmt.Fprintf(ftable, "\n")

	// Custom error messages.
	fmt.Fprintf(ftable, "\n")
	fmt.Fprintf(ftable, "var %sErrorMessages = [...]struct {\n", prefix)
	fmt.Fprintf(ftable, "\tstate int\n")
	fmt.Fprintf(ftable, "\ttoken int\n")
	fmt.Fprintf(ftable, "\tmsg   string\n")
	fmt.Fprintf(ftable, "}{\n")
	for _, error := range errors {
		lineno = error.lineno
		state, token := runMachine(error.tokens)
		fmt.Fprintf(ftable, "\t{%v, %v, %s},\n", state, token, error.msg)
	}
	fmt.Fprintf(ftable, "}\n")

	// copy parser text
	ch := getrune(finput)
	for ch != EOF {
		ftable.WriteRune(ch)
		ch = getrune(finput)
	}

	// copy yaccpar
	if !lflag {
		fmt.Fprintf(ftable, "\n//line yaccpar:1\n")
	}

	parts := strings.SplitN(yaccpar, prefix+"run()", 2)
	fmt.Fprintf(ftable, "%v", parts[0])
	ftable.Write(fcode.Bytes())
	fmt.Fprintf(ftable, "%v", parts[1])
}

func runMachine(tokens []string) (state, token int) {
	var stack []int
	i := 0
	token = -1

Loop:
	if token < 0 {
		token = chfind(2, tokens[i])
		i++
	}

	row := stateTable[state]

	c := token
	if token >= NTBASE {
		c = token - NTBASE + ntokens
	}
	action := row.actions[c]
	if action == 0 {
		action = row.defaultAction
	}

	switch {
	case action == ACCEPTCODE:
		errorf("tokens are accepted")
		return
	case action == ERRCODE:
		if token >= NTBASE {
			errorf("error at non-terminal token %s", symnam(token))
		}
		return
	case action > 0:
		// Shift to state action.
		stack = append(stack, state)
		state = action
		token = -1
		goto Loop
	default:
		// Reduce by production -action.
		prod := prdptr[-action]
		if rhsLen := len(prod) - 2; rhsLen > 0 {
			n := len(stack) - rhsLen
			state = stack[n]
			stack = stack[:n]
		}
		if token >= 0 {
			i--
		}
		token = prod[0]
		goto Loop
	}
}

func minMax(v []int) (min, max int) {
	if len(v) == 0 {
		return
	}
	min = v[0]
	max = v[0]
	for _, i := range v {
		if i < min {
			min = i
		}
		if i > max {
			max = i
		}
	}
	return
}

// return the smaller integral base type to store the values in v
func minType(v []int, allowUnsigned bool) (typ string) {
	typ = "int"
	typeLen := 8
	min, max := minMax(v)
	checkType := func(name string, size, minType, maxType int) {
		if min >= minType && max <= maxType && typeLen > size {
			typ = name
			typeLen = size
		}
	}
	checkType("int32", 4, math.MinInt32, math.MaxInt32)
	checkType("int16", 2, math.MinInt16, math.MaxInt16)
	checkType("int8", 1, math.MinInt8, math.MaxInt8)
	if allowUnsigned {
		// Do not check for uint32, not worth and won't compile on 32 bit systems
		checkType("uint16", 2, 0, math.MaxUint16)
		checkType("uint8", 1, 0, math.MaxUint8)
	}
	return
}

func arrayOutColumns(s string, v []int, columns int, allowUnsigned bool) {
	s = prefix + s
	ftable.WriteRune('\n')
	minType := minType(v, allowUnsigned)
	fmt.Fprintf(ftable, "var %v = [...]%s{", s, minType)
	for i, val := range v {
		if i%columns == 0 {
			fmt.Fprintf(ftable, "\n\t")
		} else {
			ftable.WriteRune(' ')
		}
		fmt.Fprintf(ftable, "%d,", val)
	}
	fmt.Fprintf(ftable, "\n}\n")
}

func arout(s string, v []int, n int) {
	arrayOutColumns(s, v[:n], 10, true)
}

// output the summary on y.output
func summary() {
	if foutput != nil {
		fmt.Fprintf(foutput, "\n%v terminals, %v nonterminals\n", ntokens, nnonter+1)
		fmt.Fprintf(foutput, "%v grammar rules, %v/%v states\n", nprod, nstate, NSTATES)
		fmt.Fprintf(foutput, "%v shift/reduce, %v reduce/reduce conflicts reported\n", zzsrconf, zzrrconf)
		fmt.Fprintf(foutput, "%v working sets used\n", len(wsets))
		fmt.Fprintf(foutput, "memory: parser %v/%v\n", memp, ACTSIZE)
		fmt.Fprintf(foutput, "%v extra closures\n", zzclose-2*nstate)
		fmt.Fprintf(foutput, "%v shift entries, %v exceptions\n", zzacent, zzexcp)
		fmt.Fprintf(foutput, "%v goto entries\n", zzgoent)
		fmt.Fprintf(foutput, "%v entries saved by goto default\n", zzgobest)
	}
	if zzsrconf != 0 || zzrrconf != 0 {
		fmt.Printf("\nconflicts: ")
		if zzsrconf != 0 {
			fmt.Printf("%v shift/reduce", zzsrconf)
		}
		if zzsrconf != 0 && zzrrconf != 0 {
			fmt.Printf(", ")
		}
		if zzrrconf != 0 {
			fmt.Printf("%v reduce/reduce", zzrrconf)
		}
		fmt.Printf("\n")
	}
}

// write optimizer summary
func osummary() {
	if foutput == nil {
		return
	}
	i := 0
	for p := maxa; p >= 0; p-- {
		if amem[p] == 0 {
			i++
		}
	}

	fmt.Fprintf(foutput, "Optimizer space used: output %v/%v\n", maxa+1, ACTSIZE)
	fmt.Fprintf(foutput, "%v table entries, %v zero\n", maxa+1, i)
	fmt.Fprintf(foutput, "maximum spread: %v, maximum offset: %v\n", maxspr, maxoff)
}

// copies and protects "'s in q
func chcopy(q string) string {
	var s strings.Builder
	i := 0
	j := 0
	for i = 0; i < len(q); i++ {
		if q[i] == '"' {
			s.WriteString(q[j:i])
			s.WriteByte('\\')
			j = i
		}
	}
	s.WriteString(q[j:i])
	return s.String()
}

func usage() {
	fmt.Fprintf(stderr, "usage: yacc [-o output] [-v parsetable] input\n")
	exit(1)
}

func bitset(set Lkset, bit int) int { return set[bit>>5] & (1 << uint(bit&31)) }

func setbit(set Lkset, bit int) { set[bit>>5] |= (1 << uint(bit&31)) }

func mkset() Lkset { return make([]int, tbitset) }

// set a to the union of a and b
// return 1 if b is not a subset of a, 0 otherwise
func setunion(a, b []int) int {
	sub := 0
	for i := 0; i < tbitset; i++ {
		x := a[i]
		y := x | b[i]
		a[i] = y
		if y != x {
			sub = 1
		}
	}
	return sub
}

func prlook(p Lkset) {
	if p == nil {
		fmt.Fprintf(foutput, "\tNULL")
		return
	}
	fmt.Fprintf(foutput, " { ")
	for j := 0; j <= ntokens; j++ {
		if bitset(p, j) != 0 {
			fmt.Fprintf(foutput, "%v ", symnam(j))
		}
	}
	fmt.Fprintf(foutput, "}")
}

// utility routines
var peekrune rune

func isdigit(c rune) bool { return c >= '0' && c <= '9' }

func isword(c rune) bool {
	return c >= 0xa0 || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}

// return 1 if 2 arrays are equal
// return 0 if not equal
func aryeq(a []int, b []int) int {
	n := len(a)
	if len(b) != n {
		return 0
	}
	for ll := range n {
		if a[ll] != b[ll] {
			return 0
		}
	}
	return 1
}

func getrune(f *bufio.Reader) rune {
	var r rune

	if peekrune != 0 {
		if peekrune == EOF {
			return EOF
		}
		r = peekrune
		peekrune = 0
		return r
	}

	c, n, err := f.ReadRune()
	if n == 0 {
		return EOF
	}
	if err != nil {
		errorf("read error: %v", err)
	}
	//fmt.Printf("rune = %v n=%v\n", string(c), n);
	return c
}

func ungetrune(f *bufio.Reader, c rune) {
	if f != finput {
		panic("ungetc - not finput")
	}
	if peekrune != 0 {
		panic("ungetc - 2nd unget")
	}
	peekrune = c
}

func open(s string) *bufio.Reader {
	fi, err := os.Open(s)
	if err != nil {
		errorf("error opening %v: %v", s, err)
	}
	//fmt.Printf("open %v\n", s);
	return bufio.NewReader(fi)
}

func create(s string) *bufio.Writer {
	fo, err := os.Create(s)
	if err != nil {
		errorf("error creating %v: %v", s, err)
	}
	//fmt.Printf("create %v mode %v\n", s);
	return bufio.NewWriter(fo)
}

// write out error comment
func lerrorf(lineno int, s string, v ...any) {
	nerrors++
	fmt.Fprintf(stderr, s, v...)
	fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno)
	if fatfl != 0 {
		summary()
		exit(1)
	}
}

func errorf(s string, v ...any) {
	lerrorf(lineno, s, v...)
}

func exit(status int) {
	if ftable != nil {
		ftable.Flush()
		ftable = nil
		gofmt()
	}
	if foutput != nil {
		foutput.Flush()
		foutput = nil
	}
	if stderr != nil {
		stderr.Flush()
		stderr = nil
	}
	os.Exit(status)
}

func gofmt() {
	src, err := os.ReadFile(oflag)
	if err != nil {
		return
	}
	src, err = format.Source(src)
	if err != nil {
		return
	}
	os.WriteFile(oflag, src, 0666)
}

var yaccpar string // will be processed version of yaccpartext: s/$$/prefix/g
var yaccpartext = `
/*	parser for yacc output	*/

var (
	$$Debug        = 0
	$$ErrorVerbose = false
)

type $$Lexer interface {
	Lex(lval *$$SymType) int
	Error(s string)
}

type $$Parser interface {
	Parse($$Lexer) int
	Lookahead() int
}

type $$ParserImpl struct {
	lval  $$SymType
	stack [$$InitialStackSize]$$SymType
	char  int
}

func (p *$$ParserImpl) Lookahead() int {
	return p.char
}

func $$NewParser() $$Parser {
	return &$$ParserImpl{}
}

const $$Flag = -1000

func $$Tokname(c int) string {
	if c >= 1 && c-1 < len($$Toknames) {
		if $$Toknames[c-1] != "" {
			return $$Toknames[c-1]
		}
	}
	return __yyfmt__.Sprintf("tok-%v", c)
}

func $$Statname(s int) string {
	if s >= 0 && s < len($$Statenames) {
		if $$Statenames[s] != "" {
			return $$Statenames[s]
		}
	}
	return __yyfmt__.Sprintf("state-%v", s)
}

func $$ErrorMessage(state, lookAhead int) string {
	const TOKSTART = 4

	if !$$ErrorVerbose {
		return "syntax error"
	}

	for _, e := range $$ErrorMessages {
		if e.state == state && e.token == lookAhead {
			return "syntax error: " + e.msg
		}
	}

	res := "syntax error: unexpected " + $$Tokname(lookAhead)

	// To match Bison, suggest at most four expected tokens.
	expected := make([]int, 0, 4)

	// Look for shiftable tokens.
	base := int($$Pact[state])
	for tok := TOKSTART; tok-1 < len($$Toknames); tok++ {
		if n := base + tok; n >= 0 && n < $$Last && int($$Chk[int($$Act[n])]) == tok {
			if len(expected) == cap(expected) {
				return res
			}
			expected = append(expected, tok)
		}
	}

	if $$Def[state] == -2 {
		i := 0
		for $$Exca[i] != -1 || int($$Exca[i+1]) != state {
			i += 2
		}

		// Look for tokens that we accept or reduce.
		for i += 2; $$Exca[i] >= 0; i += 2 {
			tok := int($$Exca[i])
			if tok < TOKSTART || $$Exca[i+1] == 0 {
				continue
			}
			if len(expected) == cap(expected) {
				return res
			}
			expected = append(expected, tok)
		}

		// If the default action is to accept or reduce, give up.
		if $$Exca[i+1] != 0 {
			return res
		}
	}

	for i, tok := range expected {
		if i == 0 {
			res += ", expecting "
		} else {
			res += " or "
		}
		res += $$Tokname(tok)
	}
	return res
}

func $$lex1(lex $$Lexer, lval *$$SymType) (char, token int) {
	token = 0
	char = lex.Lex(lval)
	if char <= 0 {
		token = int($$Tok1[0])
		goto out
	}
	if char < len($$Tok1) {
		token = int($$Tok1[char])
		goto out
	}
	if char >= $$Private {
		if char < $$Private+len($$Tok2) {
			token = int($$Tok2[char-$$Private])
			goto out
		}
	}
	for i := 0; i < len($$Tok3); i += 2 {
		token = int($$Tok3[i+0])
		if token == char {
			token = int($$Tok3[i+1])
			goto out
		}
	}

out:
	if token == 0 {
		token = int($$Tok2[1]) /* unknown char */
	}
	if $$Debug >= 3 {
		__yyfmt__.Printf("lex %s(%d)\n", $$Tokname(token), uint(char))
	}
	return char, token
}

func $$Parse($$lex $$Lexer) int {
	return $$NewParser().Parse($$lex)
}

func ($$rcvr *$$ParserImpl) Parse($$lex $$Lexer) int {
	var $$n int
	var $$VAL $$SymType
	var $$Dollar []$$SymType
	_ = $$Dollar // silence set and not used
	$$S := $$rcvr.stack[:]

	Nerrs := 0   /* number of errors */
	Errflag := 0 /* error recovery flag */
	$$state := 0
	$$rcvr.char = -1
	$$token := -1 // $$rcvr.char translated into internal numbering
	defer func() {
		// Make sure we report no lookahead when not parsing.
		$$state = -1
		$$rcvr.char = -1
		$$token = -1
	}()
	$$p := -1
	goto $$stack

ret0:
	return 0

ret1:
	return 1

$$stack:
	/* put a state and value onto the stack */
	if $$Debug >= 4 {
		__yyfmt__.Printf("char %v in %v\n", $$Tokname($$token), $$Statname($$state))
	}

	$$p++
	if $$p >= len($$S) {
		nyys := make([]$$SymType, len($$S)*2)
		copy(nyys, $$S)
		$$S = nyys
	}
	$$S[$$p] = $$VAL
	$$S[$$p].yys = $$state

$$newstate:
	$$n = int($$Pact[$$state])
	if $$n <= $$Flag {
		goto $$default /* simple state */
	}
	if $$rcvr.char < 0 {
		$$rcvr.char, $$token = $$lex1($$lex, &$$rcvr.lval)
	}
	$$n += $$token
	if $$n < 0 || $$n >= $$Last {
		goto $$default
	}
	$$n = int($$Act[$$n])
	if int($$Chk[$$n]) == $$token { /* valid shift */
		$$rcvr.char = -1
		$$token = -1
		$$VAL = $$rcvr.lval
		$$state = $$n
		if Errflag > 0 {
			Errflag--
		}
		goto $$stack
	}

$$default:
	/* default state action */
	$$n = int($$Def[$$state])
	if $$n == -2 {
		if $$rcvr.char < 0 {
			$$rcvr.char, $$token = $$lex1($$lex, &$$rcvr.lval)
		}

		/* look through exception table */
		xi := 0
		for {
			if $$Exca[xi+0] == -1 && int($$Exca[xi+1]) == $$state {
				break
			}
			xi += 2
		}
		for xi += 2; ; xi += 2 {
			$$n = int($$Exca[xi+0])
			if $$n < 0 || $$n == $$token {
				break
			}
		}
		$$n = int($$Exca[xi+1])
		if $$n < 0 {
			goto ret0
		}
	}
	if $$n == 0 {
		/* error ... attempt to resume parsing */
		switch Errflag {
		case 0: /* brand new error */
			$$lex.Error($$ErrorMessage($$state, $$token))
			Nerrs++
			if $$Debug >= 1 {
				__yyfmt__.Printf("%s", $$Statname($$state))
				__yyfmt__.Printf(" saw %s\n", $$Tokname($$token))
			}
			fallthrough

		case 1, 2: /* incompletely recovered error ... try again */
			Errflag = 3

			/* find a state where "error" is a legal shift action */
			for $$p >= 0 {
				$$n = int($$Pact[$$S[$$p].yys]) + $$ErrCode
				if $$n >= 0 && $$n < $$Last {
					$$state = int($$Act[$$n]) /* simulate a shift of "error" */
					if int($$Chk[$$state]) == $$ErrCode {
						goto $$stack
					}
				}

				/* the current p has no shift on "error", pop stack */
				if $$Debug >= 2 {
					__yyfmt__.Printf("error recovery pops state %d\n", $$S[$$p].yys)
				}
				$$p--
			}
			/* there is no state on the stack with an error shift ... abort */
			goto ret1

		case 3: /* no shift yet; clobber input char */
			if $$Debug >= 2 {
				__yyfmt__.Printf("error recovery discards %s\n", $$Tokname($$token))
			}
			if $$token == $$EofCode {
				goto ret1
			}
			$$rcvr.char = -1
			$$token = -1
			goto $$newstate /* try again in the same state */
		}
	}

	/* reduction by production $$n */
	if $$Debug >= 2 {
		__yyfmt__.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state))
	}

	$$nt := $$n
	$$pt := $$p
	_ = $$pt // guard against "declared and not used"

	$$p -= int($$R2[$$n])
	// $$p is now the index of $0. Perform the default action. Iff the
	// reduced production is ε, $1 is possibly out of range.
	if $$p+1 >= len($$S) {
		nyys := make([]$$SymType, len($$S)*2)
		copy(nyys, $$S)
		$$S = nyys
	}
	$$VAL = $$S[$$p+1]

	/* consult goto table to find next state */
	$$n = int($$R1[$$n])
	$$g := int($$Pgo[$$n])
	$$j := $$g + $$S[$$p].yys + 1

	if $$j >= $$Last {
		$$state = int($$Act[$$g])
	} else {
		$$state = int($$Act[$$j])
		if int($$Chk[$$state]) != -$$n {
			$$state = int($$Act[$$g])
		}
	}
	// dummy call; replaced with literal code
	$$run()
	goto $$stack /* stack new state and value */
}
`
