1// Copyright 2015 Jean Niklas L'orange. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package edn 6 7import ( 8 "bytes" 9 "io" 10) 11 12func tokNeedsDelim(t tokenType) bool { 13 switch t { 14 case tokenString, tokenListStart, tokenListEnd, tokenVectorStart, 15 tokenVectorEnd, tokenMapEnd, tokenMapStart, tokenSetStart, tokenDiscard, tokenError: 16 return false 17 } 18 return true 19} 20 21func delimits(r rune) bool { 22 switch r { 23 case '{', '}', '[', ']', '(', ')', '\\', '"': 24 return true 25 } 26 return isWhitespace(r) 27} 28 29// Compact appends to dst a compacted form of the EDN-encoded src. It does not 30// remove discard values. 31func Compact(dst *bytes.Buffer, src []byte) error { 32 origLen := dst.Len() 33 var lex lexer 34 lex.reset() 35 buf := bytes.NewBuffer(src) 36 start, pos := 0, 0 37 needsDelim := false 38 prevIgnore := '\uFFFD' 39 r, size, err := buf.ReadRune() 40 for ; err == nil; r, size, err = buf.ReadRune() { 41 ls := lex.state(r) 42 ppos := pos 43 pos += size 44 switch ls { 45 case lexCont: 46 if ppos == start && needsDelim && !delimits(r) { 47 dst.WriteRune(prevIgnore) 48 } 49 continue 50 case lexIgnore: 51 prevIgnore = r 52 start = pos 53 case lexError: 54 dst.Truncate(origLen) 55 return lex.err 56 case lexEnd: 57 // here we might want to discard #_ and the like. Currently we don't. 58 dst.Write(src[start:pos]) 59 needsDelim = tokNeedsDelim(lex.token) 60 lex.reset() 61 start = pos 62 case lexEndPrev: 63 dst.Write(src[start:ppos]) 64 lex.reset() 65 lss := lex.state(r) 66 needsDelim = tokNeedsDelim(lex.token) 67 switch lss { 68 case lexIgnore: 69 prevIgnore = r 70 start = pos 71 case lexCont: 72 start = ppos 73 case lexEnd: 74 dst.WriteRune(r) 75 lex.reset() 76 start = pos 77 case lexEndPrev: 78 dst.Truncate(origLen) 79 return errInternal 80 case lexError: 81 dst.Truncate(origLen) 82 return lex.err 83 } 84 } 85 } 86 if err != io.EOF { 87 return err 88 } 89 ls := lex.eof() 90 switch ls { 91 case lexEnd: 92 dst.Write(src[start:pos]) 93 case lexError: 94 dst.Truncate(origLen) 95 return lex.err 96 } 97 return nil 98} 99