Francisco J Ballesterosclive fixesbbd019010 years agoHistory
867 lines · 16.8 KB · Go
RawDownload
package main import (	"clive/cmd"	"clive/cmd/wr/refs"	"clive/dbg"	"fmt"	"os/exec"	"strconv"	"strings"	"unicode") func (s *scan) get() string {	if s.saved {		s.saved = false		s.nb++		return s.last	}	s.nb++	ln, ok := <-s.lnc	if len(ln) > 0 && ln[len(ln)-1] == '\n' {		ln = ln[:len(ln)-1]	}	if len(ln) > 0 && ln[0] == '#' {		return s.get()	}	s.last = ln	s.eof = s.eof || !ok	return ln} func (s *scan) unget() {	s.saved = true	s.nb--} func (s *scan) skipBlanks() bool {	some := false	for !s.eof {		ln := s.get()		if len(ln) == 0 || ln[0] == '#' {			continue		}		if strings.TrimSpace(ln) != "" {			s.unget()			return some		}		some = true	}	return some} func (e *Elem) Warn(fmts string, args ...face{}) {	if e != nil && e.fname != "" {		fmts = "%s:%d: " + fmts		args = append([]face{}{e.fname, e.lno}, args...)	}	cmd.Warn(fmts, args...)} // return indent lvl, kind of paragraph and data from the first line if any// Knone is returned for empty or blank lines.func lookLine(ln string) (int, Kind, string) {	if strings.TrimSpace(ln) == "" {		return 0, Kpar, ""	}	nt := 0	for ; nt < len(ln) && ln[nt] == '\t'; nt++ {	}	ln = ln[nt:]	if ln == "" {		return 0, Knone, ""	}	if ln == "-" {		return nt, Kbr, ""	}	if ln == "_" {		return nt, Kit, ""	}	if ln == "*" {		return nt, Kbf, ""	}	if ln == "|" {		return nt, Ktt, ""	}	for m, k := range marks {		if strings.HasPrefix(ln, m) {			dat := strings.TrimPrefix(ln, m)			return nt, k, dat		}	}	if len(ln) > 1 && (ln[0] == '+' || ln[0] == '-') &&		len(strings.TrimLeft(ln[1:], "0123456789")) == 0 {		return nt, Kfont, ln	}	return nt, Ktext, ln} func (t *Text) contdTitle(el *Elem) *Elem {	for !t.eof {		_, k, ln := lookLine(t.get())		if k != Ktext {			t.unget()			break		}		el.Data += " " + strings.TrimSpace(ln)	}	return el} func Parse() (chan<- string, <-chan *Text) {	lnc := make(chan string)	tc := make(chan *Text, 1)	go func() {		t := &Text{			scan:    &scan{lnc: lnc, fname: uname},			pprintf: dbg.FlagPrintf(&debugPars),			sprintf: dbg.FlagPrintf(&debugSplit),			iprintf: dbg.FlagPrintf(&debugIndent),			refsdir: refsdir,		}		t.parse()		tc <- t		close(tc)	}()	return lnc, tc} func (t *Text) parse() {	if t == nil {		return	}	for p := t.parsePar(); p != nil; p = t.parsePar() {		t.splitMarks(p)		t.pprintf("PAR %s\n", p)		t.Elems = append(t.Elems, p)		if p.Kind == Ktitle || p.Kind == Kchap ||			p.Kind == Khdr1 || p.Kind == Khdr2 || p.Kind == Khdr3 {			t.skipBlanks()		}	}	t.fixRefs()	t.indentPars()	t.splitLists()} func sh(dflt string) string {	if dflt != "" {		return dflt	}	// NB: Don't use SHELL, to leave unix alone.	if c := cmd.GetEnv("shell"); c != "" {		return c	}	return "rc"} // executes shell command and replaces data with command outputfunc (e *Elem) sh() {	xcmd := exec.Command(sh(e.Tag))	if e := cmd.OSEnv(); e != nil {		xcmd.Env = e	}	stdin, err := xcmd.StdinPipe()	if err != nil {		e.Warn("pipe to rc: %s", err)		return	}	in := e.Data	go func() {		fmt.Fprint(stdin, in)		stdin.Close()	}()	out, err := xcmd.CombinedOutput()	if err != nil && len(out) == 0 {		e.Warn("command: %s", err)		return	}	outs := string(out)	e.Data = outs} func (t *Text) parsePar() (el *Elem) {	if t.eof {		t.pprintf("EOF\n")		return nil	}	nt, k, ln := lookLine(t.get())	fname := t.fname	lno := t.nb	defer func() {		if el != nil {			el.fname = fname			el.lno = lno		}	}()	// t.pprintf("\t%v %v %s\n", nt, k, dbg.Text(ln))	switch k {	case Kpar:		t.skipBlanks()		return &Elem{Kind: k}	case Kbr:		return &Elem{Kind: k}	case Ktitle, Kfoot:		el := &Elem{Kind: k, Data: strings.TrimSpace(ln)}		el = t.contdTitle(el)		if k == Kfoot {			t.addRef(el, Kfoot)		}		return el	case Kchap, Khdr1, Khdr2, Khdr3:		el := &Elem{Kind: k, Data: strings.TrimSpace(ln)}		if strings.ToLower(ln) != "abstract" {			t.addRef(el, k)		}		return el	case Knone:		return nil	case Kit:		if t.itset {			k = Kitend		}		t.itset = !t.itset		return &Elem{Kind: k, indent: nt}	case Kbf:		if t.bfset {			k = Kbfend		}		t.bfset = !t.bfset		return &Elem{Kind: k, indent: nt}	case Ktt:		if t.ttset {			k = Kttend		}		t.ttset = !t.ttset		return &Elem{Kind: k, indent: nt}	case Kverb, Ksh, Kfig, Ktbl, Keqn, Kpic, Kgrap, Kcode:		// could consume ln here to select labels, captions from data.		el := &Elem{Kind: k, Tag: strings.TrimSpace(ln), indent: nt}		el = t.contdRaw(el)		switch k {		case Ktbl:			el.parseTbl()			fallthrough		case Kfig, Keqn, Kcode, Kpic, Kgrap:			rk := k			if k == Kfig || k == Kpic || k == Kgrap {				rk = Kfig			}			t.addRef(el, rk)		case Ksh:			if strings.HasPrefix(ln, QlMark) {				el.Tag = "ql"			} else if strings.HasPrefix(ln, RcMark) {				el.Tag = "rc"			}			el.sh()		} 		return el	}	el = &Elem{Kind: k, Data: ln, indent: nt}	if k == Kfont {		el.Data = strings.TrimSpace(el.Data)		return el	}	// must consume Ktext lines while the indent level remains the same	for !t.eof {		nt, k, ln = lookLine(t.get())		if nt != el.indent || k != Ktext {			t.unget()			break		}		el.Data += " " + ln	}	return el} // called for verb, fig, tbl, eqn, code to consume all lines until the end// of the corresponding element and strip caption linesfunc (t *Text) contdRaw(el *Elem) *Elem {	end := strings.Repeat("\t", el.indent) + "]"	first := true	nt := 0	incap := false	cap := ""	for !t.eof {		ln := t.get()		if ln == "" {			continue		}		if ln == end {			break		}		lnt := ntabs(ln)		if first {			nt = lnt			first = false		}		if el.Kind != Kverb && el.Kind != Ksh && strings.TrimSpace(ln) != "" && lnt <= el.indent {			incap = true		}		ln = rmtabs(ln, nt)		if incap {			cap += ln + "\n"		} else {			el.Data += ln + "\n"		}	}	if cap != "" {		el.Caption = &Elem{Kind: Ktext, indent: el.indent, Data: cap}	}	return el} func keys(s string) []string {	words := strings.Fields(s)	for i, w := range words {		w = strings.ToLower(w)		words[i] = strings.TrimFunc(w, unicode.IsPunct)	}	return words} func (ek *eKeys) setKeys() {	e := ek.el	ks := keys(e.Tag)	if e.Caption != nil {		ks = append(ks, keys(e.Caption.Data)...)	}	if e.Kind == Kchap || e.Kind == Khdr1 || e.Kind == Khdr2 ||		e.Kind == Khdr3 || e.Kind == Kfoot {		ks = append(ks, keys(e.Data)...)	}	for _, w := range ks {		ek.keys[w] = true	}	cmd.Dprintf("label %s %s -> %v\n", ek.el.Kind, ek.el.Nb, ks)} func (t *Text) addRef(el *Elem, k Kind) {	refs := t.refs	if refs == nil {		refs = map[Kind][]*eKeys{}	}	ek := &eKeys{el: el, keys: map[string]bool{}}	refs[k] = append(refs[k], ek)	t.refs = refs	prev := "??"	nb := 0	switch k {	case Kchap:		t.nchap++		t.nhdr1 = 0		t.nhdr2 = 0		t.nhdr3 = 0		prev = ""		nb = t.nchap	case Khdr1:		t.nhdr1++		t.nhdr2 = 0		t.nhdr3 = 0		nb = t.nhdr1		if t.nchap > 0 {			prev = fmt.Sprintf("%d.", t.nchap)		} else {			prev = ""		}	case Khdr2:		t.nhdr2++		t.nhdr3 = 0		if t.nchap > 0 {			prev = fmt.Sprintf("%d.%d.", t.nchap, t.nhdr1)		} else {			prev = fmt.Sprintf("%d.", t.nhdr1)		}		if h1 := refs[Khdr1]; len(h1) > 0 {			prev = h1[len(h1)-1].el.Nb + "."		}		nb = t.nhdr2	case Khdr3:		t.nhdr3++		if t.nchap > 0 {			prev = fmt.Sprintf("%d.%d.%d.", t.nchap, t.nhdr1, t.nhdr2)		} else {			prev = fmt.Sprintf("%d.%d.", t.nhdr1, t.nhdr2)		}		if h2 := refs[Khdr2]; len(h2) > 0 {			prev = h2[len(h2)-1].el.Nb + "."		}		nb = t.nhdr3	default:		prev = ""		nb = len(refs[k])	}	el.Nb = fmt.Sprintf("%s%d", prev, nb)	ek.setKeys()} func ntabs(s string) int {	for i := 0; i < len(s); i++ {		if s[i] != '\t' {			return i		}	}	return len(s)} func rmtabs(s string, n int) string {	for i := 0; i < len(s); i++ {		if s[i] != '\t' {			return s[i:]		}		if n--; n < 0 {			return s[i:]		}	}	return ""} // parses raw tbl data and fills e.Tblfunc (e *Elem) parseTbl() {	lines := strings.SplitN(e.Data, "\n", -1)	if len(lines) > 0 && lines[len(lines)-1] == "" {		lines = lines[:len(lines)-1]	}	if len(lines) < 2 {		e.Warn("table with not enough data")		return	}	e.Tbl = [][]string{}	n := 0	for _, ln := range lines {		toks := strings.SplitN(ln, "\t", -1)		for i := 0; i < len(toks); i++ {			toks[i] = strings.TrimSpace(toks[i])		}		e.Tbl = append(e.Tbl, toks)		if n == 0 {			n = len(toks)		} else if len(toks) != n {			e.Warn("wrong number of columns in table")			e.Tbl = nil			return		}	}} func appText(els []*Elem, k Kind, indent int, s string) []*Elem {	el := &Elem{Kind: k, indent: indent, Data: s}	if len(els) > 0 {		last := els[len(els)-1]		if last.Kind == k && last.indent == indent {			last.Data += s			return els		}	}	return append(els, el)} func splitCite(els []*Elem, k Kind, i int, key, tag, s string) ([]*Elem, string, bool) {	if !strings.HasPrefix(tag, "["+key+":") {		return els, s, false	}	s = s[len(tag):]	tag = tag[:len(tag)-1]	tag = tag[1+len(key)+1:]	tag = strings.TrimSpace(tag)	ne := &Elem{Kind: k, indent: i, Data: tag}	els = append(els, ne)	return els, s, true} var cites = map[string]Kind{	"sect": Ksref,	"fig":  Kfref,	"code": Kcref,	"tbl":  Ktref,	"eqn":  Keref,	"foot": Knref,	"url":  Kurl,	"bib":  Kbib,	"cite": Kcite,} // Split the text in the elem and add children with// inlined marks and raw text elems.func (t *Text) splitMarks(p *Elem) {	switch p.Kind {	case Ktext, Kfoot, Kenum, Kitem, Ktitle, Kchap, Khdr1, Khdr2, Khdr3:		if !strings.ContainsAny(p.Data, "*_|[") {			return		}	default:		if p.Caption != nil {			t.splitMarks(p.Caption)		}		return	}

Lines 1–500 of 867.