Implemented csexp.Parse
This commit is contained in:
@ -2,10 +2,15 @@ package csexp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"git.codemonkeysoftware.net/b/gigaparsec"
|
||||
pbytes "git.codemonkeysoftware.net/b/gigaparsec/bytes"
|
||||
"git.codemonkeysoftware.net/b/gigaparsec/cursor"
|
||||
"git.codemonkeysoftware.net/b/peachy-go/shortcircuit"
|
||||
)
|
||||
|
||||
@ -82,6 +87,73 @@ func (l List) Clone() Sexp {
|
||||
return l2
|
||||
}
|
||||
|
||||
func Parse(data []byte) (Sexp, error) {
|
||||
return nil, nil
|
||||
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)
|
||||
}
|
||||
|
||||
func Parse(data []byte) (Sexp, error) {
|
||||
result, err := parseSexp(gigaparsec.MakeState(cursor.NewSlice(data)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("csexp.Parse: %w", err)
|
||||
}
|
||||
if failed, _, msg := result.Failed(); failed {
|
||||
return nil, fmt.Errorf("csexp.Parse: %v", msg)
|
||||
}
|
||||
_, _, sexp, _, _ := result.Succeeded()
|
||||
return sexp, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user