99 lines
2.0 KiB
Go
99 lines
2.0 KiB
Go
// Copyright 2015 Jean Niklas L'orange. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package edn
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
)
|
|
|
|
func tokNeedsDelim(t tokenType) bool {
|
|
switch t {
|
|
case tokenString, tokenListStart, tokenListEnd, tokenVectorStart,
|
|
tokenVectorEnd, tokenMapEnd, tokenMapStart, tokenSetStart, tokenDiscard, tokenError:
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func delimits(r rune) bool {
|
|
switch r {
|
|
case '{', '}', '[', ']', '(', ')', '\\', '"':
|
|
return true
|
|
}
|
|
return isWhitespace(r)
|
|
}
|
|
|
|
// Compact appends to dst a compacted form of the EDN-encoded src. It does not
|
|
// remove discard values.
|
|
func Compact(dst *bytes.Buffer, src []byte) error {
|
|
origLen := dst.Len()
|
|
var lex lexer
|
|
lex.reset()
|
|
buf := bytes.NewBuffer(src)
|
|
start, pos := 0, 0
|
|
needsDelim := false
|
|
prevIgnore := '\uFFFD'
|
|
r, size, err := buf.ReadRune()
|
|
for ; err == nil; r, size, err = buf.ReadRune() {
|
|
ls := lex.state(r)
|
|
ppos := pos
|
|
pos += size
|
|
switch ls {
|
|
case lexCont:
|
|
if ppos == start && needsDelim && !delimits(r) {
|
|
dst.WriteRune(prevIgnore)
|
|
}
|
|
continue
|
|
case lexIgnore:
|
|
prevIgnore = r
|
|
start = pos
|
|
case lexError:
|
|
dst.Truncate(origLen)
|
|
return lex.err
|
|
case lexEnd:
|
|
// here we might want to discard #_ and the like. Currently we don't.
|
|
dst.Write(src[start:pos])
|
|
needsDelim = tokNeedsDelim(lex.token)
|
|
lex.reset()
|
|
start = pos
|
|
case lexEndPrev:
|
|
dst.Write(src[start:ppos])
|
|
lex.reset()
|
|
lss := lex.state(r)
|
|
needsDelim = tokNeedsDelim(lex.token)
|
|
switch lss {
|
|
case lexIgnore:
|
|
prevIgnore = r
|
|
start = pos
|
|
case lexCont:
|
|
start = ppos
|
|
case lexEnd:
|
|
dst.WriteRune(r)
|
|
lex.reset()
|
|
start = pos
|
|
case lexEndPrev:
|
|
dst.Truncate(origLen)
|
|
return errInternal
|
|
case lexError:
|
|
dst.Truncate(origLen)
|
|
return lex.err
|
|
}
|
|
}
|
|
}
|
|
if err != io.EOF {
|
|
return err
|
|
}
|
|
ls := lex.eof()
|
|
switch ls {
|
|
case lexEnd:
|
|
dst.Write(src[start:pos])
|
|
case lexError:
|
|
dst.Truncate(origLen)
|
|
return lex.err
|
|
}
|
|
return nil
|
|
}
|