func read(buf io.ByteScanner) Literal { skipws(buf) b, err := buf.ReadByte() if err == io.EOF { panic("read: premature end of file") } if err != nil { panic("read: input error: " + err.Error()) } switch b { case ')': panic("read: unmatched close-parenthesis") case '"': return readString(buf) case '\'': return readQuote(buf) case '`': return readQuasi(buf) case ',': return readComma(buf) case '&': return readAmpersand(buf) case '(': return readList(buf) } buf.UnreadByte() return readAtom(buf) }
func (t *Terminal) Read(r io.ByteScanner) error { c, err := r.ReadByte() if err != nil { return err } switch { case c == 0x7: // bell // ignore case c == 0x8: // backspace if t.Col > 0 { t.Col-- } case c == 0x1b: return t.readEscape(r) case c == '\r': t.Col = 0 case c == '\n': t.Col = 0 t.Row++ t.fixPosition() case c == '\t': t.Col += 8 - (t.Col % 8) t.fixPosition() case c >= ' ' && c <= '~': t.writeRune(rune(c), t.Attr) default: r.UnreadByte() return t.readUTF8(r) } return nil }
func readAtom(buf io.ByteScanner) Literal { atomBuf := []byte{} loop: for { b, err := buf.ReadByte() if err != nil { break } switch b { case '(', ')', '\'', '"', ' ', '\t', '\n': buf.UnreadByte() break loop } atomBuf = append(atomBuf, b) } if len(atomBuf) == 0 { panic("read: empty atom") } atom := string(atomBuf) n, err := strconv.ParseFloat(atom, 64) if err == nil { return Number(n) } return Intern(atom) }
func readTerminator(r io.ByteScanner, term byte) (bool, error) { tok, err := r.ReadByte() if err != nil { return false, err } else if tok == term { return true, nil } return false, r.UnreadByte() }
func readComma(buf io.ByteScanner) Literal { b, err := buf.ReadByte() if err != nil { panic("read: incomplete comma") } tag := Intern("unquote") if b == '@' { tag = Intern("unquotesplicing") } else { buf.UnreadByte() } x := read(buf) return newListLiteral(tag, x) }
func scanSign(r io.ByteScanner) (neg bool, err error) { var ch byte if ch, err = r.ReadByte(); err != nil { return false, err } switch ch { case '-': neg = true case '+': // nothing to do default: r.UnreadByte() } return }
func (t *Terminal) readInt(r io.ByteScanner) (int, error) { n := 0 for i := 0; i < 20; i++ { c, err := r.ReadByte() if err != nil { return -1, err } if c >= '0' && c <= '9' { n = n*10 + int(c) - '0' } else { r.UnreadByte() return n, err } } return -1, fmt.Errorf("term: readInt overlong") }
func skipws(buf io.ByteScanner) { for { b, err := buf.ReadByte() if err != nil { break } switch b { case ' ', '\t', '\n': continue case ';': nextLine(buf) continue } buf.UnreadByte() break } }
// parseDocument parses a document. func (p *Parser) parseDocument(r io.ByteScanner) { _, err := r.ReadByte() if err != nil { return } r.UnreadByte() p.root = addElement(nil, "Document") p.current = p.root for { _, err := r.ReadByte() if err != nil { break } r.UnreadByte() p.parseLine(r) } }
func readList(buf io.ByteScanner) Literal { skipws(buf) items := []Literal{} for { b, err := buf.ReadByte() if err != nil { panic("read: premature end of file") } if b == ')' { break } buf.UnreadByte() items = append(items, read(buf)) skipws(buf) } return newListLiteral(items...) }
func consumeValue(rdr io.ByteScanner) ([]byte, error) { bs := []byte{} for c, err := rdr.ReadByte(); err != io.EOF; c, err = rdr.ReadByte() { if err != nil { return nil, err } switch c { case '{': rdr.UnreadByte() return bs, EOS case '>', '+', '~', ' ', '\t', '\n', '\f', ',', '.', '#', '[', ':': rdr.UnreadByte() return bs, nil default: bs = append(bs, c) } } return bs, nil }
// Read from reader while f holds true, unread bytes if reset == true func readWhile(reader io.ByteScanner, f func(b byte) bool, reset bool) string { s := "" for { b, err := reader.ReadByte() if err != nil { return s } if !f(b) { reader.UnreadByte() return s } if reset { defer reader.UnreadByte() } s += string(b) } return s }
// parseLine parses an unknown line. func (p *Parser) parseLine(r io.ByteScanner) { level := 0 for { c, err := r.ReadByte() if err != nil { return } if c == '*' && level == 0 { p.closeElement("Paragraph") r.UnreadByte() p.parseHeader(r) } else if c == ':' { c, err := r.ReadByte() if err != nil { r.UnreadByte() return } if c != ' ' { p.openElement("Paragraph") r.UnreadByte() p.startElement("Text") p.current.Text = ":" p.current.Attr["level"] = fmt.Sprint(level) p.parseTextLine(r) p.endElement() continue } p.closeElement("Paragraph") p.parseExampleLine(r) p.nextLine() break } else if c == ' ' { level += 1 p.nextColumn() } else if c == '\n' { p.closeElement("Paragraph") p.nextLine() break } else if c == '\r' { // Ignore CR character. p.nextColumn() } else { p.openElement("Paragraph") r.UnreadByte() p.startElement("Text") p.current.Attr["level"] = fmt.Sprint(level) p.parseTextLine(r) p.endElement() } } }
func parseCombinator(rdr io.ByteScanner, p *Link) error { rdr.UnreadByte() for c, err := rdr.ReadByte(); err != io.EOF; c, err = rdr.ReadByte() { if err != nil { return err } switch c { case '{': rdr.UnreadByte() return EOS case ',': return fmt.Errorf("Encountered ',' after combinator") case ' ', '\t', '\n', '\r', '\f': case '>', '+', '~': if p.Combinator == Descendant { p.Combinator = combinatorMap[c] } else { return fmt.Errorf("Can't combine multiple combinators") } default: rdr.UnreadByte() return nil } } return nil }
func parseSimpleAttr(rdr io.ByteScanner, sel *SimpleSelector) error { var name []byte var value []byte var c1 byte = 0 for c2, err := rdr.ReadByte(); err != io.EOF; c2, err = rdr.ReadByte() { if err != nil { return err } switch c2 { case ']': sel.AttrName = string(name) sel.Value = string(value) return nil case '=': if c1 == '~' { sel.AttrMatch = Contains } else if c1 == '|' { sel.AttrMatch = DashPrefix } else { sel.AttrMatch = Exactly } case '{': rdr.UnreadByte() return EOS case '~': case '|': // TODO(jwall): Substring matchers default: if sel.AttrMatch == Presence { name = append(name, c2) } else { value = append(value, c2) } } c1 = c2 } return fmt.Errorf("Didn't close Attribute Matcher") }
func parseSequence(rdr io.ByteScanner) (Sequence, error) { seq := []SimpleSelector{} rdr.UnreadByte() for c, err := rdr.ReadByte(); err != io.EOF; c, err = rdr.ReadByte() { if err != nil { return nil, err } switch c { case '*': seq = append(seq, SimpleSelector{Type: Universal}) case '#': sel := SimpleSelector{Type: Id, AttrName: "id"} if err := parseSimpleSelector(rdr, &sel); err != nil { return nil, err } seq = append(seq, sel) case '.': sel := SimpleSelector{Type: Class, AttrName: "class"} if err := parseSimpleSelector(rdr, &sel); err != nil { return nil, err } seq = append(seq, sel) case ':': sel := SimpleSelector{Type: PseudoClass} if err := parseSimpleSelector(rdr, &sel); err != nil { return nil, err } seq = append(seq, sel) case '[': sel := SimpleSelector{Type: Attr} if err := parseSimpleAttr(rdr, &sel); err != nil { return nil, err } seq = append(seq, sel) case '{': rdr.UnreadByte() return seq, EOS case ' ', '\t', '\n', '\r', '\f', '>', '+', '~': rdr.UnreadByte() return seq, nil default: sel := SimpleSelector{Type: Tag, Tag: string(c)} if err := parseSimpleTag(rdr, &sel); err != nil { return nil, err } seq = append(seq, sel) } } return seq, nil }
// scanExponent scans the longest possible prefix of r representing a decimal // ('e', 'E') or binary ('p') exponent, if any. It returns the exponent, the // exponent base (10 or 2), or a read or syntax error, if any. // // exponent = ( "E" | "e" | "p" ) [ sign ] digits . // sign = "+" | "-" . // digits = digit { digit } . // digit = "0" ... "9" . // // A binary exponent is only permitted if binExpOk is set. func scanExponent(r io.ByteScanner, binExpOk bool) (exp int64, base int, err error) { base = 10 var ch byte if ch, err = r.ReadByte(); err != nil { if err == io.EOF { err = nil // no exponent; same as e0 } return } switch ch { case 'e', 'E': // ok case 'p': if binExpOk { base = 2 break // ok } fallthrough // binary exponent not permitted default: r.UnreadByte() return // no exponent; same as e0 } var neg bool if neg, err = scanSign(r); err != nil { return } var digits []byte if neg { digits = append(digits, '-') } // no need to use nat.scan for exponent digits // since we only care about int64 values - the // from-scratch scan is easy enough and faster for i := 0; ; i++ { if ch, err = r.ReadByte(); err != nil { if err != io.EOF || i == 0 { return } err = nil break // i > 0 } if ch < '0' || '9' < ch { if i == 0 { r.UnreadByte() err = fmt.Errorf("invalid exponent (missing digits)") return } break // i > 0 } digits = append(digits, byte(ch)) } // i > 0 => we have at least one digit exp, err = strconv.ParseInt(string(digits), 10, 64) return }
// parseTextLine parses a text line. This does not create an element. func (p *Parser) parseTextLine(r io.ByteScanner) { for { c, err := r.ReadByte() if err != nil { return } if c == '\n' { p.nextLine() break } if c == '\r' { // Ignore CR character. p.nextColumn() continue } if c == ']' { if p.current.Name == "Link" || (p.current.Name == "Text" && p.current.Parent.Name == "Link") { r.UnreadByte() return } else { p.addError("ParseTextLink", "unexpected ]") p.nextColumn() return } } if c == '[' { if p.current.Name == "Text" { p.nextColumn() p.endElement() p.parseLink(r) p.startElement("Text") } else { p.addError("ParseTextLink", "type is "+p.current.Name) p.nextColumn() } continue } if c == '*' && p.current.Name == "Text" { if p.isInElement("Bold") { p.closeElement("Bold") } else { p.startElement("Bold") } p.startElement("Text") p.nextColumn() continue } if c == '_' && p.current.Name == "Text" { if p.isInElement("Underline") { p.closeElement("Underline") } else { p.openElement("Underline") } p.startElement("Text") p.nextColumn() continue } if c == '/' && p.current.Name == "Text" { if p.isInElement("Italic") { p.closeElement("Italic") } else { p.openElement("Italic") } p.startElement("Text") p.nextColumn() continue } p.nextColumn() p.current.Text += string(c) } }
// scan scans the number corresponding to the longest possible prefix // from r representing an unsigned number in a given conversion base. // It returns the corresponding natural number res, the actual base b, // a digit count, and a read or syntax error err, if any. // // number = [ prefix ] mantissa . // prefix = "0" [ "x" | "X" | "b" | "B" ] . // mantissa = digits | digits "." [ digits ] | "." digits . // digits = digit { digit } . // digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" . // // Unless fracOk is set, the base argument must be 0 or a value between // 2 and MaxBase. If fracOk is set, the base argument must be one of // 0, 2, 10, or 16. Providing an invalid base argument leads to a run- // time panic. // // For base 0, the number prefix determines the actual base: A prefix of // ``0x'' or ``0X'' selects base 16; if fracOk is not set, the ``0'' prefix // selects base 8, and a ``0b'' or ``0B'' prefix selects base 2. Otherwise // the selected base is 10 and no prefix is accepted. // // If fracOk is set, an octal prefix is ignored (a leading ``0'' simply // stands for a zero digit), and a period followed by a fractional part // is permitted. The result value is computed as if there were no period // present; and the count value is used to determine the fractional part. // // A result digit count > 0 corresponds to the number of (non-prefix) digits // parsed. A digit count <= 0 indicates the presence of a period (if fracOk // is set, only), and -count is the number of fractional digits found. // In this case, the actual value of the scanned number is res * b**count. // func (z nat) scan(r io.ByteScanner, base int, fracOk bool) (res nat, b, count int, err error) { // reject illegal bases baseOk := base == 0 || !fracOk && 2 <= base && base <= MaxBase || fracOk && (base == 2 || base == 10 || base == 16) if !baseOk { panic(fmt.Sprintf("illegal number base %d", base)) } // one char look-ahead ch, err := r.ReadByte() if err != nil { return } // determine actual base b = base if base == 0 { // actual base is 10 unless there's a base prefix b = 10 if ch == '0' { count = 1 switch ch, err = r.ReadByte(); err { case nil: // possibly one of 0x, 0X, 0b, 0B if !fracOk { b = 8 } switch ch { case 'x', 'X': b = 16 case 'b', 'B': b = 2 } switch b { case 16, 2: count = 0 // prefix is not counted if ch, err = r.ReadByte(); err != nil { // io.EOF is also an error in this case return } case 8: count = 0 // prefix is not counted } case io.EOF: // input is "0" res = z[:0] err = nil return default: // read error return } } } // convert string // Algorithm: Collect digits in groups of at most n digits in di // and then use mulAddWW for every such group to add them to the // result. z = z[:0] b1 := Word(b) bn, n := maxPow(b1) // at most n digits in base b1 fit into Word di := Word(0) // 0 <= di < b1**i < bn i := 0 // 0 <= i < n dp := -1 // position of decimal point for { if fracOk && ch == '.' { fracOk = false dp = count // advance if ch, err = r.ReadByte(); err != nil { if err == io.EOF { err = nil break } return } } // convert rune into digit value d1 var d1 Word switch { case '0' <= ch && ch <= '9': d1 = Word(ch - '0') case 'a' <= ch && ch <= 'z': d1 = Word(ch - 'a' + 10) case 'A' <= ch && ch <= 'Z': d1 = Word(ch - 'A' + 10) default: d1 = MaxBase + 1 } if d1 >= b1 { r.UnreadByte() // ch does not belong to number anymore break } count++ // collect d1 in di di = di*b1 + d1 i++ // if di is "full", add it to the result if i == n { z = z.mulAddWW(z, bn, di) di = 0 i = 0 } // advance if ch, err = r.ReadByte(); err != nil { if err == io.EOF { err = nil break } return } } if count == 0 { // no digits found switch { case base == 0 && b == 8: // there was only the octal prefix 0 (possibly followed by digits > 7); // count as one digit and return base 10, not 8 count = 1 b = 10 case base != 0 || b != 8: // there was neither a mantissa digit nor the octal prefix 0 err = errors.New("syntax error scanning number") } return } // count > 0 // add remaining digits to result if i > 0 { z = z.mulAddWW(z, pow(b1, i), di) } res = z.norm() // adjust for fraction, if any if dp >= 0 { // 0 <= dp <= count > 0 count = dp - count } return }
// parseLink parses a link. Format is [[link]] or [[link][text]]. func (p *Parser) parseLink(r io.ByteScanner) { // Start of link is already consumed. const ( start = iota link middle text end ) state := start p.startElement("Link") defer p.endElement() for { c, err := r.ReadByte() if err != nil { p.addError("ParseLink", "unexpected EOF") return } if c == '[' { p.nextColumn() if state == start { state = link } else if state == middle { state = text } else { p.addError("ParseLink", "unexpected [") return } } else if c == ']' { p.nextColumn() if state == link { state = middle } else if state == text { state = end } else if state == middle { break } else if state == end { return } else { p.addError("ParseLink", "unexpected ]") return } } else if c == ' ' { p.nextColumn() continue } else { if state == link { r.UnreadByte() p.parseTextLine(r) p.current.Attr["link"] += p.current.Text p.current.Text = "" } else if state == text { r.UnreadByte() p.startElement("Text") p.parseTextLine(r) p.endElement() } else { p.nextColumn() p.addError("ParseLink", "unexpected character") return } } } if len(p.current.Children) == 0 { p.startElement("Text") p.endElement() } }
func (t *Terminal) readCSI(r io.ByteScanner) error { // CSI var args []int qflag := false gtflag := false L: c, err := r.ReadByte() if err != nil { return err } switch { case c >= '0' && c <= '9': r.UnreadByte() n, err := t.readInt(r) if err != nil { return err } args = append(args, n) c, err = r.ReadByte() if err != nil { return err } if c == ';' { goto L } case c == '?': qflag = true goto L case c == '>': gtflag = true goto L } switch { case c == '@': // insert blanks n := 1 readArgs(args, &n) for i := 0; i < n; i++ { t.Lines[t.Row] = append(t.Lines[t.Row], Cell{}) } copy(t.Lines[t.Row][t.Col+n:], t.Lines[t.Row][t.Col:]) for i := 0; i < n; i++ { t.Lines[t.Row][t.Col+i] = Cell{' ', 0} } case c == 'A': // cursor up dy := 1 readArgs(args, &dy) t.Row -= dy if t.Row < 0 { log.Printf("term: cursor up off top of screen?") t.Row = 0 } t.fixPosition() case c == 'C': // cursor forward dx := 1 readArgs(args, &dx) t.Col += dx t.fixPosition() case c == 'D': // cursor back dx := 1 readArgs(args, &dx) t.Col -= dx t.fixPosition() case c == 'G': // cursor character absolute x := 1 readArgs(args, &x) t.Col = x - 1 t.fixPosition() case c == 'H': // move to position row := 1 col := 1 readArgs(args, &row, &col) t.Row = t.Top + row - 1 t.Col = col - 1 t.fixPosition() case c == 'J': // erase in display arg := 0 readArgs(args, &arg) switch arg { case 0: // erase to end t.Lines = t.Lines[:t.Row+1] t.Lines[t.Row] = t.Lines[t.Row][:t.Col] case 2: // erase all t.Lines = t.Lines[:0] t.Row = 0 t.Col = 0 t.fixPosition() default: log.Printf("term: unknown erase in display %v", args) } case c == 'K': // erase in line arg := 0 readArgs(args, &arg) switch arg { case 0: // erase to right t.Lines[t.Row] = t.Lines[t.Row][:t.Col] case 1: for i := 0; i < t.Col; i++ { t.Lines[t.Row][i] = Cell{' ', 0} } case 2: t.TODOs.Add("erase all line") default: log.Printf("term: unknown erase in line %v", args) } case c == 'L': // insert lines n := 1 readArgs(args, &n) for i := 0; i < n; i++ { t.Lines = append(t.Lines, nil) } copy(t.Lines[t.Row+n:], t.Lines[t.Row:]) for i := 0; i < n; i++ { t.Lines[t.Row+i] = make([]Cell, 0) } case c == 'P': // erase in line arg := 1 readArgs(args, &arg) l := t.Lines[t.Row] if t.Col+arg > len(l) { arg = len(l) - t.Col } copy(l[t.Col:], l[t.Col+arg:]) t.Lines[t.Row] = l[:len(l)-arg] case c == 'X': // erase characters t.TODOs.Add("erase characters %v", args) case !gtflag && c == 'c': // send device attributes (primary) t.TODOs.Add("send device attributes (primary) %v", args) case gtflag && c == 'c': // send device attributes (secondary) arg := 0 readArgs(args, &arg) switch arg { case 0: // terminal id // ID is // 0 -> VT100 // 0 -> firmware version 0 // 0 -> always-zero param _, err := t.Input.Write([]byte("\x1b[0;0;0c")) return err default: t.TODOs.Add("send device attributes (secondary) %v", args) } case c == 'd': // line position arg := 1 readArgs(args, &arg) t.Row = arg - 1 t.fixPosition() case !qflag && (c == 'h' || c == 'l'): // reset mode reset := c == 'l' arg := 0 readArgs(args, &arg) switch arg { default: t.TODOs.Add("reset mode %d %v", arg, reset) } case qflag && (c == 'h' || c == 'l'): // DEC private mode set/reset set := c == 'h' arg := 0 readArgs(args, &arg) switch arg { case 1: t.TODOs.Add("application cursor keys mode") case 7: // wraparound mode t.TODOs.Add("wraparound mode") case 12: // blinking cursor // Ignore; this appears in cnorm/cvvis as a way to adjust the // "very visible cursor" state. case 25: // show cursor t.HideCursor = !set case 1049: // alternate screen buffer t.TODOs.Add("alternate screen buffer %v", set) default: log.Printf("term: unknown dec private mode %v %v", args, set) } case c == 'm': // reset if len(args) == 0 { args = append(args, 0) } for _, arg := range args { switch { case arg == 0: t.Attr = 0 case arg == 1: t.Attr.SetBright(true) case arg == 7: t.Attr.SetInverse(true) case arg == 27: t.Attr.SetInverse(false) case arg >= 30 && arg < 40: t.Attr.SetColor(mapColor(arg-30, arg)) case arg >= 40 && arg < 50: t.Attr.SetBackColor(mapColor(arg-40, arg)) default: log.Printf("term: unknown color %v", args) } } case gtflag && c == 'n': // disable modifiers arg := 2 readArgs(args, &arg) switch arg { case 0: t.TODOs.Add("disable modify keyboard") case 1: t.TODOs.Add("disable modify cursor keys") case 2: t.TODOs.Add("disable modify function keys") case 4: t.TODOs.Add("disable modify other keys") } case c == 'n': // device status report arg := 0 readArgs(args, &arg) switch arg { case 5: _, err := t.Input.Write([]byte("\x1b[0n")) return err case 6: pos := fmt.Sprintf("\x1b[%d;%dR", t.Row+1, t.Col+1) _, err := t.Input.Write([]byte(pos)) return err default: log.Printf("term: unknown status report arg %v", args) } case c == 'r': // set scrolling region top, bot := 1, 1 readArgs(args, &top, &bot) if top == 1 && bot == t.Height { // Just setting the current region as scroll. } else { t.TODOs.Add("set scrolling region %v", args) } default: log.Printf("term: unknown CSI %v %s", args, showChar(c)) } return nil }
// Match provides a simple sequential ASCII text matcher. // It is specialized for processing well formed, structured printable ASCII. // s yields input bytes and p yields pattern bytes. // n and o correspond to the number of bytes read from s and p, respectively. // If m is non-nil, len(m) should be greater than or equal to the number of // capture-bytes in the pattern. // // Patterns: // * Printable ASCII bytes will be matched literally. // * All groups (specified by non-printable bytes in the pattern stream) // are non-greedy, and match zero or more characters. // * \x00: ASCII whitespace bytes. // * \x01: non-whitespace ASCII bytes. // * \xa0: lowercase letters. // * \xa1: uppercase letters. // * \xaf: letters. // * \xd0: decimal digits. // * \xd6: hexadecimal digits. // * \xd8: octal digits. // * \xd3: base-36 digits. // * \xfe: printable ASCII bytes. // * \xff: 8-bit bytes. // * All groups are capturing beside \x00. // * Use of other non-printable or non-ASCII bytes is undefined. func Match(s, p io.ByteScanner, m []string) (n, o int, err error) { var ( quit = false buf = make([]byte, 0, 1024) a, b byte c byte v int ) for !quit { a, err = p.ReadByte() switch { case err == io.EOF: err = nil fallthrough case err != nil: return } o++ if tab[a]&prg == 0 { c, err = s.ReadByte() if err != nil { return } n++ if a == c { continue } else if err = s.UnreadByte(); err == nil { err = ErrByteMismatch n-- } return } a = tab[a] b, err = p.ReadByte() if err == io.EOF { b = nop } else if err != nil { return } else if err = p.UnreadByte(); err != nil { o++ return } for { c, err = s.ReadByte() if err != nil { quit = true goto fill } n++ if tab[b]&prg == 0 && c == b || tab[b]&prg != 0 && tab[c]&tab[b] != 0 || a != any && tab[c]&a == 0 { break } else if a&^prg != ws { buf = append(buf, c) } } if err = s.UnreadByte(); err == nil { err = io.EOF n-- } fill: if a&^prg != ws { if _, ok := s.(*rb); ok { revbytes(buf) } m[v] = string(buf) v++ buf = buf[:0] } } return }