2024-08-29 05:01:38 +00:00
|
|
|
package csexp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2024-09-18 03:40:47 +00:00
|
|
|
"errors"
|
2024-08-29 05:01:38 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"slices"
|
2024-09-18 03:40:47 +00:00
|
|
|
"strconv"
|
2024-08-29 05:01:38 +00:00
|
|
|
|
2024-09-18 03:40:47 +00:00
|
|
|
"git.codemonkeysoftware.net/b/gigaparsec"
|
|
|
|
pbytes "git.codemonkeysoftware.net/b/gigaparsec/bytes"
|
|
|
|
"git.codemonkeysoftware.net/b/gigaparsec/cursor"
|
2024-08-29 05:01:38 +00:00
|
|
|
"git.codemonkeysoftware.net/b/peachy-go/shortcircuit"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Sexp interface {
|
|
|
|
isSexp()
|
|
|
|
WriteTo(w io.Writer) (n int64, err error)
|
|
|
|
String() string
|
|
|
|
Equal(Sexp) bool
|
|
|
|
Clone() Sexp
|
|
|
|
}
|
|
|
|
|
|
|
|
type Atom []byte
|
|
|
|
|
|
|
|
func (a Atom) isSexp() {}
|
|
|
|
|
|
|
|
func (a Atom) WriteTo(w io.Writer) (int64, error) {
|
|
|
|
if scw, ok := w.(*shortcircuit.Writer); ok && scw.Failed() {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
n, err := fmt.Fprintf(w, "%d:%s", len(a), []byte(a))
|
|
|
|
return int64(n), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a Atom) String() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
a.WriteTo(&buf)
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a Atom) Equal(s Sexp) bool {
|
|
|
|
a2, ok := s.(Atom)
|
|
|
|
return ok && bytes.Equal([]byte(a), []byte(a2))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a Atom) Clone() Sexp {
|
|
|
|
return Atom(bytes.Clone([]byte(a)))
|
|
|
|
}
|
|
|
|
|
|
|
|
type List []Sexp
|
|
|
|
|
|
|
|
func (l List) isSexp() {}
|
|
|
|
|
|
|
|
func (l List) WriteTo(w io.Writer) (int64, error) {
|
|
|
|
scw := shortcircuit.EnsureWriter(w)
|
|
|
|
if scw.Failed() {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
io.WriteString(scw, "(")
|
|
|
|
for _, child := range l {
|
|
|
|
child.WriteTo(scw)
|
|
|
|
}
|
|
|
|
io.WriteString(scw, ")")
|
|
|
|
return scw.Status()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l List) String() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
l.WriteTo(&buf)
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l List) Equal(s Sexp) bool {
|
|
|
|
l2, ok := s.(List)
|
|
|
|
return ok && slices.EqualFunc(l, l2, Sexp.Equal)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l List) Clone() Sexp {
|
|
|
|
l2 := make(List, len(l))
|
|
|
|
for i, child := range l {
|
|
|
|
l2[i] = child.Clone()
|
|
|
|
}
|
|
|
|
return l2
|
|
|
|
}
|
|
|
|
|
2024-09-18 03:40:47 +00:00
|
|
|
var parseLength = gigaparsec.Map(pbytes.Regexp(`0|[1-9]\d*`), func(s string) uint64 {
|
|
|
|
n, err := strconv.ParseUint(s, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
})
|
|
|
|
|
|
|
|
func acceptN(n uint64) gigaparsec.Parser[byte, []byte] {
|
|
|
|
expected := fmt.Sprintf("%d bytes", n)
|
|
|
|
return func(s gigaparsec.State[byte]) (gigaparsec.Result[byte, []byte], error) {
|
|
|
|
if n == 0 {
|
|
|
|
return gigaparsec.Succeed[byte, []byte](true, nil, s, gigaparsec.MessageOK(s.Pos())), nil
|
|
|
|
}
|
|
|
|
dst := make([]byte, n)
|
|
|
|
_, next, err := s.Read(dst)
|
|
|
|
if errors.Is(err, io.EOF) {
|
|
|
|
return gigaparsec.Fail[byte, []byte](false, gigaparsec.MessageEnd(s.Pos(), expected)), nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return gigaparsec.Result[byte, []byte]{}, err
|
|
|
|
}
|
|
|
|
return gigaparsec.Succeed(true, dst, next, gigaparsec.MessageOK(s.Pos())), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var parseAtom = gigaparsec.Map(gigaparsec.Bind2(
|
|
|
|
parseLength,
|
|
|
|
gigaparsec.Pipe[byte, byte, uint64](gigaparsec.Match[byte](':')),
|
|
|
|
acceptN,
|
|
|
|
), func(s []byte) Sexp { return Atom(s) })
|
|
|
|
|
|
|
|
func parseSexp(input gigaparsec.State[byte]) (gigaparsec.Result[byte, Sexp], error) {
|
|
|
|
return gigaparsec.Choose(parseAtom, parseList)(input)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseRestOfList(input gigaparsec.State[byte]) (gigaparsec.Result[byte, []Sexp], error) {
|
|
|
|
return gigaparsec.Choose(
|
|
|
|
gigaparsec.Map(gigaparsec.Match[byte](')'), func(byte) []Sexp { return nil }),
|
|
|
|
gigaparsec.Bind(
|
|
|
|
parseSexp,
|
|
|
|
func(s Sexp) gigaparsec.Parser[byte, []Sexp] {
|
|
|
|
return gigaparsec.Map(
|
|
|
|
parseRestOfList,
|
|
|
|
func(rest []Sexp) []Sexp { return append([]Sexp{s}, rest...) },
|
|
|
|
)
|
|
|
|
},
|
|
|
|
),
|
|
|
|
)(input)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseList(input gigaparsec.State[byte]) (gigaparsec.Result[byte, Sexp], error) {
|
|
|
|
return gigaparsec.Map(gigaparsec.Seq2(
|
|
|
|
gigaparsec.Match[byte]('('),
|
|
|
|
parseRestOfList,
|
|
|
|
func(_ byte, rest []Sexp) []Sexp { return rest },
|
|
|
|
), func(sexps []Sexp) Sexp { return List(sexps) })(input)
|
|
|
|
}
|
|
|
|
|
2024-08-29 05:01:38 +00:00
|
|
|
func Parse(data []byte) (Sexp, error) {
|
2024-09-18 17:25:19 +00:00
|
|
|
parser := gigaparsec.Seq2(
|
|
|
|
parseSexp,
|
|
|
|
gigaparsec.End[byte](),
|
|
|
|
func(s Sexp, _ struct{}) Sexp { return s },
|
|
|
|
)
|
2024-09-18 19:02:23 +00:00
|
|
|
result, err := gigaparsec.Run(parser, cursor.NewSlice(data))
|
2024-09-18 03:40:47 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("csexp.Parse: %w", err)
|
|
|
|
}
|
2024-09-18 19:02:23 +00:00
|
|
|
return result, nil
|
2024-08-29 05:01:38 +00:00
|
|
|
}
|