From 2d3c8748bee9f50f43e2e4705fd34cc7bc062f80 Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Tue, 17 Sep 2024 21:40:47 -0600 Subject: [PATCH] Implemented csexp.Parse --- csexp/sexp.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++-- go.mod | 9 ++++-- go.sum | 4 +-- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/csexp/sexp.go b/csexp/sexp.go index e9dd54c..6f696e9 100644 --- a/csexp/sexp.go +++ b/csexp/sexp.go @@ -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 } diff --git a/go.mod b/go.mod index 78d0c2a..061b6c9 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,13 @@ module git.codemonkeysoftware.net/b/peachy-go -go 1.22.1 +go 1.23 + +toolchain go1.23.0 require ( + git.codemonkeysoftware.net/b/gigaparsec v0.0.0-20240917174243-4fa3c2a46611 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c - github.com/shoenig/test v1.9.0 + github.com/shoenig/test v1.11.0 pgregory.net/rapid v1.1.0 zombiezen.com/go/sqlite v1.3.0 ) @@ -22,3 +25,5 @@ require ( modernc.org/memory v1.7.2 // indirect modernc.org/sqlite v1.29.1 // indirect ) + +replace git.codemonkeysoftware.net/b/gigaparsec v0.0.0-20240917174243-4fa3c2a46611 => ../gigaparsec diff --git a/go.sum b/go.sum index 939ba2a..22fc0bd 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/shoenig/test v1.9.0 h1:PWtSP8Ty2N0F+Ndh4p0a8SOofFmTEIX/nYh/c3vRCbo= -github.com/shoenig/test v1.9.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= +github.com/shoenig/test v1.11.0 h1:NoPa5GIoBwuqzIviCrnUJa+t5Xb4xi5Z+zODJnIDsEQ= +github.com/shoenig/test v1.11.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=