implement Parseman; include "sys.m"; sys: Sys; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "man.m"; FONT_LITERAL: con -1; init(): string{ sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; if (bufio == nil) return sys->sprint("cannot load module: %r"); return nil;} ParseState: adt[T] for{ T => textwidth: fn(t: self T, text: Text): int; }{ metrics: Metrics; ql: int; # quote Literal text margin: int; mstack: list of int; istack: list of int; indent: int; ntlsetindent: int; #copy prevailindent to indent on n.t.l prevailindent: int; curfont: int; curattr: int; verbatim: int; pspace: int; curline: list of (int, Text); # most recent first curwidth: int; newpara: int; heading: int; igto: string; link: string; viewer: T; setline: chan of list of (int, Text); # addstring() is simply an addtext() of the current font addstring: fn(s: self ref ParseState, s: string); addtext: fn(s: self ref ParseState, t: list of Text); brk: fn(s: self ref ParseState); paragraph: fn( s: self ref ParseState);}; parseman[T](fd: ref Sys->FD, metrics: Metrics, ql: int, viewer: T, setline: chan of list of (int, Text)) for{ T => textwidth: fn(t: self T, text: Text): int; }{ iob := bufio->fopen(fd, Sys->OREAD); state := ref ParseState[T](metrics, ql, 0, nil, nil, 0, 0, metrics.indent, FONT_ROMAN, 0, 0, 1, nil, 0, 1, 0, "", nil, viewer, setline); while ((l := iob.gets('\n')) != nil) { if (l[len l -1] == '\n') l = l[0: len l - 1]; if (state.igto != nil && state.igto != l) continue; state.igto = nil; parseline(state, l); } state.pspace = 2; state.pspace = 1; state.paragraph(); footer := Text(FONT_ROMAN, 0, "Inferno Manual", 0, nil); textw := state.viewer.textwidth(footer);#should do 'center' in addtext (state.justify = CENTER) state.indent = (state.metrics.pagew - textw) / 2; state.addtext(footer::nil); state.brk(); setline <- = nil;} parseline[T](state: ref ParseState[T], t: string) for{ T => textwidth: fn(t: self T, text: Text): int; }{ if (t == nil) { if (state.verbatim) { blank := Text(state.curfont, state.curattr, "", 0, ""); state.setline <- = (0, blank)::nil; } else state.paragraph(); return; } ntlsetindent := state.ntlsetindent; state.ntlsetindent = 0; if (t[0] == '.' || t[0] == '\'') parsemacro(state, t[1:]); else { state.addtext(parsetext(state, t)); if (state.verbatim) state.brk(); } if (ntlsetindent) { state.indent = state.prevailindent; if (state.curwidth + state.metrics.en > state.indent + state.margin) state.brk(); }} parsemacro[T](state: ref ParseState[T], t: string) for{ T => textwidth: fn(t: self T, text: Text): int; }{ for (n := 0; n < len t && n < 2; n++) if (t[n] == ' ' || t[n] == '\t') break; macro := t[0:n]; params: list of string; quote := 0; param := 0; esc := 0; p := ""; for (; n < len t; n++) { if (esc) esc = 0; else { case t[n] { ' ' or '\t' => if (!quote) { if (param) { params = p :: params; p = ""; param = 0; } continue; } '"' => param = 1; quote = !quote; continue; '\\' => esc = 1; } } param = 1; p[len p] = t[n]; } if (param) params = p :: params; plist: list of string; for (; params != nil; params = tl params) plist = hd params :: plist; params = plist; case macro { "ig" => igto := ".."; if (params != nil) igto = "." + hd params; state.brk(); state.igto = igto; "sp" => sp := "1"; if(params != nil) sp = hd params; d := tval(state.metrics, sp, 'v'); gap := d / state.metrics.V; if (gap < 1) gap = 1; while (gap--) state.paragraph(); "br" => state.brk(); "nf" => state.verbatim = 1; "fi" => state.verbatim = 0; "ti" => state.brk(); #i := 0; #if(params != nil) # i = tval(state.metrics, hd params, 'n'); #state.ntlsetindent = 1; #state.prevailindent = i; "in" => state.brk(); #i := 0; #if(params != nil) # i = tval(state.metrics, hd params, 'n'); #state.indent = i; #state.prevailindent = state.indent; "1C" => state.brk(); # not implemented "2C" => state.brk(); # not implemented "BI" => altattr(state, FONT_BOLD, FONT_ITALIC, params); "BR" => altattr(state, FONT_BOLD, FONT_ROMAN, params); "IB" => altattr(state, FONT_ITALIC, FONT_BOLD, params); "IR" => # need to determine link if params of valid form state.link = convlink(params);; altattr(state, FONT_ITALIC, FONT_ROMAN, params); state.link = nil; "RB" => altattr(state, FONT_ROMAN, FONT_BOLD, params); "RI" => altattr(state, FONT_ROMAN, FONT_ITALIC, params); "B" => state.curfont = FONT_BOLD; if (params != nil) { for (; params != nil; params = tl params) { textl := parsetext(state, hd params); for (; textl != nil; textl = tl textl) state.addtext(hd textl::nil); } state.curfont = FONT_ROMAN; } "I" => state.curfont = FONT_ITALIC; if (params != nil) { for (; params != nil; params = tl params) { textl := parsetext(state, hd params); for (; textl != nil; textl = tl textl) state.addtext(hd textl::nil); } state.curfont = FONT_ROMAN; } "SM"=> state.curattr |= ATTR_SMALL; if (params != nil) { for (; params != nil; params = tl params) state.addstring(hd params); state.curattr &= ~ATTR_SMALL; } "L" => state.curfont = FONT_LITERAL; if (params != nil) { str := "`"; for (pl := params; pl != nil;) { str += hd pl; if ((pl = tl pl) != nil) str += " "; else break; } str += "'"; state.addstring(str); state.curfont = FONT_ROMAN; } "LR" => if (params != nil) { l := Text(FONT_LITERAL, state.curattr, hd params, 0, nil); t: list of Text; params = tl params; if (params == nil) t = l :: nil; else { r := Text(FONT_ROMAN, state.curattr, hd params, 0, nil); t = l :: r :: nil; } state.addtext(t); } "RL" => if (params != nil) { r := Text(FONT_ROMAN, state.curattr, hd params, 0, nil); t: list of Text; params = tl params; if (params == nil) t = r :: nil; else { l := Text(FONT_LITERAL, state.curattr, hd params, 0, nil); t = r :: l :: nil; } state.addtext(t); } "DT" => # not yet supported ; "EE" => state.brk(); state.verbatim = 0; state.curfont = FONT_ROMAN; "EX" => state.brk(); state.verbatim = 1; state.curfont = FONT_BOLD; "HP" => state.paragraph(); i := state.metrics.indent; if (params != nil) i = tval(state.metrics, hd params, 'n'); state.prevailindent = state.indent + i; "IP" => state.paragraph(); i := state.metrics.indent; if (params != nil) { tag := hd params; params = tl params; state.addtext(parsetext(state, tag)); if (params != nil) i = tval(state.metrics, hd params, 'n'); } state.indent = state.metrics.indent + i; state.prevailindent = state.indent; "PD" => state.pspace = 1; if (params != nil) { v := tval(state.metrics, hd params, 'v') / state.metrics.V; state.pspace = v; } "LP" or "PP" => state.paragraph(); state.prevailindent = state.indent; "RE" => state.brk(); if (state.mstack == nil || state.istack == nil) break; state.margin = hd state.mstack; state.mstack = tl state.mstack; state.prevailindent = hd state.istack; state.indent = state.prevailindent; state.istack = tl state.istack; "RS" => state.brk(); i := state.prevailindent - state.metrics.indent; if (params != nil) i = tval(state.metrics, hd params, 'n'); state.mstack = state.margin :: state.mstack; state.istack = state.prevailindent :: state.istack; state.margin += i; state.indent = 2 * state.metrics.indent; state.prevailindent = state.indent; "SH" => state.paragraph(); state.prevailindent = state.indent; state.curfont = FONT_ROMAN; state.curattr = 0; state.indent = 0; state.heading = 1; state.verbatim = 0; for (pl := params; pl != nil; pl = tl pl) state.addstring(hd pl); state.heading = 0; state.brk(); state.newpara = 1; state.pspace = 1; "SS" => state.paragraph(); state.prevailindent = state.indent; state.curfont = FONT_ROMAN; state.curattr = 0; state.indent = state.metrics.ssindent; state.heading = 2; for (pl := params; pl != nil; pl = tl pl) state.addstring(hd pl); state.heading = 0; state.brk(); state.newpara = 1; state.pspace = 1; "TF" => state.brk(); state.pspace = 0; i := state.metrics.indent; if (params != nil) { str := hd params; text := Text(FONT_BOLD, 0, str, 0, nil); w := state.viewer.textwidth(text) + 2*state.metrics.em; if (w > i) i = w; } state.indent = state.metrics.indent;; state.prevailindent = state.indent + i; "TH" => state.brk(); if (len params < 2) break; str := hd params + "(" + hd tl params + ")"; txt := Text(FONT_ROMAN, 0, str, 0, nil); txtw := state.viewer.textwidth(txt); state.indent = 0; state.addtext(txt::nil); state.indent = state.metrics.pagew - txtw; state.addtext(txt::nil); state.indent = 0; state.brk(); "TP" => state.paragraph(); if (state.prevailindent == state.metrics.indent) state.prevailindent += state.metrics.indent; state.indent = state.metrics.indent; state.ntlsetindent = 1; if (params != nil) { i := tval(state.metrics, hd params, 'n'); if (i == 0) i = state.metrics.indent; state.prevailindent = state.indent + i; } * => ; } if (state.verbatim) state.brk();} parsetext[T](state: ref ParseState[T], t: string): list of Text for{ T => textwidth: fn(t: self T, text: Text): int; }{ # need to do better here - spot inline font changes etc # we also currently cannot support troff tab stops textl: list of Text; line := ""; curfont := state.curfont; prevfont := state.curfont; # should perhaps be in State step := 1; for (i := 0; i < len t; i += step) { step = 1; ch := t[i]; if (ch == '\\') { i++; width := len t - i; if (width <= 0) break; case t[i] { '-' or '.' or '\\' => ch = t[i]; ' ' => ch = ' '; 'e' => ch = '\\'; '|' or '&' => continue; '(' => if (width > 3) width = 3; step = width; if (step != 3) continue; case t[i+1:i+3] { "bu" => ch = '•'; "em" => ch = '—'; "mi" => ch = '-'; "mu" => ch = '×'; "*m" => ch = 'µ'; "*G" => ch = 'Γ'; "*p" => ch = 'π'; "*b" => ch = 'β'; "<=" => ch = '≤'; "->" => ch = '→'; * => continue; } 'f' => if (width == 1) continue; if (t[i+1] == '(') { if (width > 4) width = 4; step = width; continue; } i++; case t[i] { '0' or 'R' => curfont = FONT_ROMAN; '1' or 'I' => curfont = FONT_ITALIC; '2' => # should be bold but our 'bold' font is constant width curfont = FONT_ROMAN;Lines 1–500 of 806. Load more
implement Parseman; include "sys.m"; sys: Sys; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "man.m"; FONT_LITERAL: con -1; init(): string { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; if (bufio == nil) return sys->sprint("cannot load module: %r"); return nil; } ParseState: adt[T] for{ T => textwidth: fn(t: self T, text: Text): int; }{ metrics: Metrics; ql: int; # quote Literal text margin: int; mstack: list of int; istack: list of int; indent: int; ntlsetindent: int; #copy prevailindent to indent on n.t.l prevailindent: int; curfont: int; curattr: int; verbatim: int; pspace: int; curline: list of (int, Text); # most recent first curwidth: int; newpara: int; heading: int; igto: string; link: string; viewer: T; setline: chan of list of (int, Text); # addstring() is simply an addtext() of the current font addstring: fn(s: self ref ParseState, s: string); addtext: fn(s: self ref ParseState, t: list of Text); brk: fn(s: self ref ParseState); paragraph: fn( s: self ref ParseState); }; parseman[T](fd: ref Sys->FD, metrics: Metrics, ql: int, viewer: T, setline: chan of list of (int, Text)) for{ T => textwidth: fn(t: self T, text: Text): int; } { iob := bufio->fopen(fd, Sys->OREAD); state := ref ParseState[T](metrics, ql, 0, nil, nil, 0, 0, metrics.indent, FONT_ROMAN, 0, 0, 1, nil, 0, 1, 0, "", nil, viewer, setline); while ((l := iob.gets('\n')) != nil) { if (l[len l -1] == '\n') l = l[0: len l - 1]; if (state.igto != nil && state.igto != l) continue; state.igto = nil; parseline(state, l); } state.pspace = 2; state.pspace = 1; state.paragraph(); footer := Text(FONT_ROMAN, 0, "Inferno Manual", 0, nil); textw := state.viewer.textwidth(footer); #should do 'center' in addtext (state.justify = CENTER) state.indent = (state.metrics.pagew - textw) / 2; state.addtext(footer::nil); state.brk(); setline <- = nil; } parseline[T](state: ref ParseState[T], t: string) for{ T => textwidth: fn(t: self T, text: Text): int; } { if (t == nil) { if (state.verbatim) { blank := Text(state.curfont, state.curattr, "", 0, ""); state.setline <- = (0, blank)::nil; } else state.paragraph(); return; } ntlsetindent := state.ntlsetindent; state.ntlsetindent = 0; if (t[0] == '.' || t[0] == '\'') parsemacro(state, t[1:]); else { state.addtext(parsetext(state, t)); if (state.verbatim) state.brk(); } if (ntlsetindent) { state.indent = state.prevailindent; if (state.curwidth + state.metrics.en > state.indent + state.margin) state.brk(); } } parsemacro[T](state: ref ParseState[T], t: string) for{ T => textwidth: fn(t: self T, text: Text): int; } { for (n := 0; n < len t && n < 2; n++) if (t[n] == ' ' || t[n] == '\t') break; macro := t[0:n]; params: list of string; quote := 0; param := 0; esc := 0; p := ""; for (; n < len t; n++) { if (esc) esc = 0; else { case t[n] { ' ' or '\t' => if (!quote) { if (param) { params = p :: params; p = ""; param = 0; } continue; } '"' => param = 1; quote = !quote; continue; '\\' => esc = 1; } } param = 1; p[len p] = t[n]; } if (param) params = p :: params; plist: list of string; for (; params != nil; params = tl params) plist = hd params :: plist; params = plist; case macro { "ig" => igto := ".."; if (params != nil) igto = "." + hd params; state.brk(); state.igto = igto; "sp" => sp := "1"; if(params != nil) sp = hd params; d := tval(state.metrics, sp, 'v'); gap := d / state.metrics.V; if (gap < 1) gap = 1; while (gap--) state.paragraph(); "br" => state.brk(); "nf" => state.verbatim = 1; "fi" => state.verbatim = 0; "ti" => state.brk(); #i := 0; #if(params != nil) # i = tval(state.metrics, hd params, 'n'); #state.ntlsetindent = 1; #state.prevailindent = i; "in" => state.brk(); #i := 0; #if(params != nil) # i = tval(state.metrics, hd params, 'n'); #state.indent = i; #state.prevailindent = state.indent; "1C" => state.brk(); # not implemented "2C" => state.brk(); # not implemented "BI" => altattr(state, FONT_BOLD, FONT_ITALIC, params); "BR" => altattr(state, FONT_BOLD, FONT_ROMAN, params); "IB" => altattr(state, FONT_ITALIC, FONT_BOLD, params); "IR" => # need to determine link if params of valid form state.link = convlink(params);; altattr(state, FONT_ITALIC, FONT_ROMAN, params); state.link = nil; "RB" => altattr(state, FONT_ROMAN, FONT_BOLD, params); "RI" => altattr(state, FONT_ROMAN, FONT_ITALIC, params); "B" => state.curfont = FONT_BOLD; if (params != nil) { for (; params != nil; params = tl params) { textl := parsetext(state, hd params); for (; textl != nil; textl = tl textl) state.addtext(hd textl::nil); } state.curfont = FONT_ROMAN; } "I" => state.curfont = FONT_ITALIC; if (params != nil) { for (; params != nil; params = tl params) { textl := parsetext(state, hd params); for (; textl != nil; textl = tl textl) state.addtext(hd textl::nil); } state.curfont = FONT_ROMAN; } "SM"=> state.curattr |= ATTR_SMALL; if (params != nil) { for (; params != nil; params = tl params) state.addstring(hd params); state.curattr &= ~ATTR_SMALL; } "L" => state.curfont = FONT_LITERAL; if (params != nil) { str := "`"; for (pl := params; pl != nil;) { str += hd pl; if ((pl = tl pl) != nil) str += " "; else break; } str += "'"; state.addstring(str); state.curfont = FONT_ROMAN; } "LR" => if (params != nil) { l := Text(FONT_LITERAL, state.curattr, hd params, 0, nil); t: list of Text; params = tl params; if (params == nil) t = l :: nil; else { r := Text(FONT_ROMAN, state.curattr, hd params, 0, nil); t = l :: r :: nil; } state.addtext(t); } "RL" => if (params != nil) { r := Text(FONT_ROMAN, state.curattr, hd params, 0, nil); t: list of Text; params = tl params; if (params == nil) t = r :: nil; else { l := Text(FONT_LITERAL, state.curattr, hd params, 0, nil); t = r :: l :: nil; } state.addtext(t); } "DT" => # not yet supported ; "EE" => state.brk(); state.verbatim = 0; state.curfont = FONT_ROMAN; "EX" => state.brk(); state.verbatim = 1; state.curfont = FONT_BOLD; "HP" => state.paragraph(); i := state.metrics.indent; if (params != nil) i = tval(state.metrics, hd params, 'n'); state.prevailindent = state.indent + i; "IP" => state.paragraph(); i := state.metrics.indent; if (params != nil) { tag := hd params; params = tl params; state.addtext(parsetext(state, tag)); if (params != nil) i = tval(state.metrics, hd params, 'n'); } state.indent = state.metrics.indent + i; state.prevailindent = state.indent; "PD" => state.pspace = 1; if (params != nil) { v := tval(state.metrics, hd params, 'v') / state.metrics.V; state.pspace = v; } "LP" or "PP" => state.paragraph(); state.prevailindent = state.indent; "RE" => state.brk(); if (state.mstack == nil || state.istack == nil) break; state.margin = hd state.mstack; state.mstack = tl state.mstack; state.prevailindent = hd state.istack; state.indent = state.prevailindent; state.istack = tl state.istack; "RS" => state.brk(); i := state.prevailindent - state.metrics.indent; if (params != nil) i = tval(state.metrics, hd params, 'n'); state.mstack = state.margin :: state.mstack; state.istack = state.prevailindent :: state.istack; state.margin += i; state.indent = 2 * state.metrics.indent; state.prevailindent = state.indent; "SH" => state.paragraph(); state.prevailindent = state.indent; state.curfont = FONT_ROMAN; state.curattr = 0; state.indent = 0; state.heading = 1; state.verbatim = 0; for (pl := params; pl != nil; pl = tl pl) state.addstring(hd pl); state.heading = 0; state.brk(); state.newpara = 1; state.pspace = 1; "SS" => state.paragraph(); state.prevailindent = state.indent; state.curfont = FONT_ROMAN; state.curattr = 0; state.indent = state.metrics.ssindent; state.heading = 2; for (pl := params; pl != nil; pl = tl pl) state.addstring(hd pl); state.heading = 0; state.brk(); state.newpara = 1; state.pspace = 1; "TF" => state.brk(); state.pspace = 0; i := state.metrics.indent; if (params != nil) { str := hd params; text := Text(FONT_BOLD, 0, str, 0, nil); w := state.viewer.textwidth(text) + 2*state.metrics.em; if (w > i) i = w; } state.indent = state.metrics.indent;; state.prevailindent = state.indent + i; "TH" => state.brk(); if (len params < 2) break; str := hd params + "(" + hd tl params + ")"; txt := Text(FONT_ROMAN, 0, str, 0, nil); txtw := state.viewer.textwidth(txt); state.indent = 0; state.addtext(txt::nil); state.indent = state.metrics.pagew - txtw; state.addtext(txt::nil); state.indent = 0; state.brk(); "TP" => state.paragraph(); if (state.prevailindent == state.metrics.indent) state.prevailindent += state.metrics.indent; state.indent = state.metrics.indent; state.ntlsetindent = 1; if (params != nil) { i := tval(state.metrics, hd params, 'n'); if (i == 0) i = state.metrics.indent; state.prevailindent = state.indent + i; } * => ; } if (state.verbatim) state.brk(); } parsetext[T](state: ref ParseState[T], t: string): list of Text for{ T => textwidth: fn(t: self T, text: Text): int; } { # need to do better here - spot inline font changes etc # we also currently cannot support troff tab stops textl: list of Text; line := ""; curfont := state.curfont; prevfont := state.curfont; # should perhaps be in State step := 1; for (i := 0; i < len t; i += step) { step = 1; ch := t[i]; if (ch == '\\') { i++; width := len t - i; if (width <= 0) break; case t[i] { '-' or '.' or '\\' => ch = t[i]; ' ' => ch = ' '; 'e' => ch = '\\'; '|' or '&' => continue; '(' => if (width > 3) width = 3; step = width; if (step != 3) continue; case t[i+1:i+3] { "bu" => ch = '•'; "em" => ch = '—'; "mi" => ch = '-'; "mu" => ch = '×'; "*m" => ch = 'µ'; "*G" => ch = 'Γ'; "*p" => ch = 'π'; "*b" => ch = 'β'; "<=" => ch = '≤'; "->" => ch = '→'; * => continue; } 'f' => if (width == 1) continue; if (t[i+1] == '(') { if (width > 4) width = 4; step = width; continue; } i++; case t[i] { '0' or 'R' => curfont = FONT_ROMAN; '1' or 'I' => curfont = FONT_ITALIC; '2' => # should be bold but our 'bold' font is constant width curfont = FONT_ROMAN;