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. Load more
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 output func (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 lines func (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.Tbl func (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 }