Pretty Printing代码解析 agile Posted on Sep 12 2019 golang - 以下代码是关于s表达式Pretty打印 ```golang package sexpr import ( "bytes" "fmt" "reflect" ) //限定宽度 const LimitedWidth = 80 func MarshalIndent(value interface{}) (error, []byte) { p := &printer{ width: LimitedWidth, buf: &bytes.Buffer{}, } if err := p.encode(reflect.ValueOf(value)); err != nil { return err, nil } return nil, p.buf.Bytes() } type printer struct { //hole中保存了四种类型的token,分别是字符串,左括号,右括号,空格 hole []*token //根据相应顺序保存左括号和空格,他们分别都要计算size,该size分别表示左括号或者空格剩下的字符串所需要占用的大小 stack []*token buf *bytes.Buffer //当前行所剩宽度 width int //保存左括号所剩宽度 blockLevel []int //总共字符串长度 strLength int } type token struct { //表示token类型 r rune //表示字符串大小或者是左括号或者空格剩下字符串所占用的大小 size int //保存字符串 str string } //将字符串放到hole中,同时更新字符串总长 func (p *printer) string(str string) { t := &token{r: 's', size: len(str), str: str} if len(p.stack) == 0 { p.print(t) } else { p.strLength += t.size p.hole = append(p.hole, t) } } //左括号 func (p *printer) start() { if len(p.stack) == 0 { p.strLength = 1 } t := &token{r: '('} //初始化当前的size,这不是真正的size(括号中str的大小),真正的size在end中计算 t.size = -p.strLength //将左括号放到hole中,注意这里不是写左括号,这里仅仅只是将左括号的标志放到hole,标明这里是左括号,这个标志也是为了方便记录括号中的字符串长度 p.hole = append(p.hole, t) //将左括号放到stack栈中 p.stack = append(p.stack, t) //真正的将左括号写进去 p.string("(") } //将左括号或者空格退出栈 func (p *printer) pop() (top *token) { var ll = len(p.stack) top, p.stack = p.stack[ll-1], p.stack[:ll-1] return } //右括号 func (p *printer) end() { //将右括号写入 p.string(")") //同时将右括号标志写入,方便标志,括号结束 t := &token{r: ')'} p.hole = append(p.hole, t) //从栈中退出左括号,同时更新该左括号的大小,也就是该括号中字符串的大小 s := p.pop() s.size += p.strLength //如果左括号的左边是空格,此时也需要先更新下该空格的大小,也就是该空格右边包含括号的大小 if s.r == ' ' { p.pop().size += p.strLength } //如果栈中没有东西了,则就说明可以直接输出内容了 if len(p.stack) == 0 { for _, t := range p.hole { p.print(t) } p.hole = nil } } //空格 func (p *printer) space() { s := p.stack[len(p.stack)-1] //如果栈的顶部是空格的话,此时需要更新该空格大小,也就是两个空格之间字符串的长度 if s.r == ' ' { s.size += p.strLength p.pop() } //将该空格添加到hole,但仅仅只是空格标志加进去,没有写进去 t := &token{r: ' '} t.size = -p.strLength p.hole = append(p.hole, t) //同时加入stack中 p.stack = append(p.stack, t) //更新字符串总长 p.strLength++ } //打印工作 func (p *printer) print(t *token) { switch t.r { case ' ': //如果该token右边的长度大于剩余可以打印的长度 if t.size > p.width { //重置一行,但width长度跟当前"行"一样长,这个行指的是括号右边的长度 var ll = len(p.blockLevel) - 1 p.width = p.blockLevel[ll] //填充LimitedWidth-p.width空格,使得新行的长度与上一行的括号右边的长度一致 p.buf.WriteString(fmt.Sprintf("\n%*s", LimitedWidth-p.width, "")) return } //否则更新长度,同时打印空格 p.width-- p.buf.WriteString(" ") case '(': //保存新的一行长度,保存到blockLevel中,行指的是括号中的长度 pw := p.width - 1 p.blockLevel = append(p.blockLevel, pw) case ')': //退出该行 p.blockLevel = p.blockLevel[:len(p.blockLevel)-1] case 's': //打印字符串 p.width -= t.size p.buf.WriteString(t.str) } } func (p *printer) printf(format string, a ...interface{}) { p.string(fmt.Sprintf(format, a...)) } func (p *printer) encode(value reflect.Value) error { switch value.Kind() { case reflect.Invalid: p.string("nil") case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p.printf("%d", value.Int()) case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: p.printf("%d", value.Uint()) case reflect.Float32, reflect.Float64: p.printf("%f", value.Float()) case reflect.Bool: if value.Bool() { p.string("t") } else { p.string("nil") } case reflect.String: p.printf("%q", value.String()) case reflect.Ptr: return p.encode(value.Elem()) case reflect.Slice, reflect.Array: p.start() for i := 0; i < value.Len(); i++ { if i > 0 { p.space() } if err := p.encode(value.Index(i)); err != nil { return err } } p.end() case reflect.Struct: p.start() for i := 0; i < value.NumField(); i++ { if i > 0 { p.space() } p.start() p.string(value.Type().Field(i).Name) p.space() if err := p.encode(value.Field(i)); err != nil { return err } p.end() } p.end() case reflect.Map: p.start() for i, key := range value.MapKeys() { if i > 0 { p.space() } p.start() if err := p.encode(key); err != nil { return err } p.space() if err := p.encode(value.MapIndex(key)); err != nil { return err } p.end() } p.end() default: return fmt.Errorf("unsupported type: %s", value.Type()) } return nil } ``` 全键盘Vimium快捷键 golang零散知识点