Skip to content

Commit

Permalink
add sequence factory to decoder
Browse files Browse the repository at this point in the history
update names of map/builder to work with the seq/builder
  • Loading branch information
ionous committed Dec 9, 2023
1 parent 95116c1 commit 49e2e8e
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 65 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func ExampleDocument() {
// maps/imap contains a slice based ordered map implementation.
// maps/stdmap generates standard (unordered) go maps.
// maps/orderedmap uses Ian Coleman's ordered map implementation.
doc := decode.NewDocument(imap.Builder, notes.DiscardComments())
doc := decode.NewDocument(imap.Make, notes.DiscardComments())
// ReadDoc takes a string reader
if res, e := doc.ReadDoc(strings.NewReader(str)); e != nil {
panic(e)
Expand Down
8 changes: 4 additions & 4 deletions collect/imap/imBuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package imap

import "github.com/ionous/tell/collect"

// return a builder can generate ItemMap
func Builder(reserve bool) collect.Builder {
// return a builder that generates ItemMap
func Make(reserve bool) collect.MapWriter {
var cnt int
if reserve {
cnt = 1
Expand All @@ -16,7 +16,7 @@ type mapBuilder struct {
}

// panic if adding the blank key but no space for a blank key was reserved.
func (b mapBuilder) Add(key string, val any) collect.Builder {
func (b mapBuilder) MapValue(key string, val any) collect.MapWriter {
if len(key) == 0 { // there should be only one blank key; at the start
if len(b.values) == 0 || len(b.values[0].Key) != 0 {
// could adjust the slice. but the program should know better.
Expand All @@ -29,6 +29,6 @@ func (b mapBuilder) Add(key string, val any) collect.Builder {
}

// returns ItemMap
func (b mapBuilder) Map() any {
func (b mapBuilder) GetMap() any {
return b.values
}
33 changes: 23 additions & 10 deletions collect/maps.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
package collect

// write to a map
type Builder interface {
type MapWriter interface {
// add the passed pair to the in-progress map
// returns a new builder ( not guaranteed to be the original one )
// future: add uniqueness check and error
Add(key string, val any) Builder
// return the completed map
Map() any
// returns a new writer ( not guaranteed to be the original one )
MapValue(key string, val any) MapWriter
// return the implementation specific representation of a map
GetMap() any
}

// a function which returns a new builder
// reserve indicates whether to keep space for an blank key
// (ie. comments)
type BuilderFactory func(reserve bool) Builder
// a function which returns a new writer
// reserve indicates whether to keep space for a comment key
type MapFactory func(reserve bool) MapWriter

// a function which returns a new writer
// reserve indicates whether to keep space for comments
type SequenceFactory func(reserve bool) SequenceWriter

type SequenceWriter interface {
// add the passed value to the in-progress sequence
// returns a new writer ( not guaranteed to be the original one )
// indices are guaranteed to increase by one each time
// ( stating with 1 if reserve was true )
// except for comments, which are written last at index 0
IndexValue(idx int, val any) SequenceWriter
// return the implementation specific representation of a sequence
GetSequence() any
}
6 changes: 3 additions & 3 deletions collect/orderedmap/omBuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package orderedmap
import "github.com/ionous/tell/collect"

// return a builder which generates a ItemMap
func Builder(reserve bool) collect.Builder {
func Make(reserve bool) collect.MapWriter {
var keys []string
if reserve {
keys = make([]string, 1)
Expand All @@ -25,7 +25,7 @@ type sliceBuilder struct {
values OrderedMap
}

func (b sliceBuilder) Add(key string, val any) collect.Builder {
func (b sliceBuilder) MapValue(key string, val any) collect.MapWriter {
if len(key) == 0 { // there should be only one blank key; at the start
if _, exists := b.values.Get(key); !exists {
// could adjust the slice. but the program should know better.
Expand All @@ -37,7 +37,7 @@ func (b sliceBuilder) Add(key string, val any) collect.Builder {
}

// returns an OrderedMap
func (b sliceBuilder) Map() any {
func (b sliceBuilder) GetMap() any {
return b.values
}

Expand Down
7 changes: 4 additions & 3 deletions collect/stdmap/stdmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "github.com/ionous/tell/collect"

type StdMap map[string]any

func Builder(reserve bool) (ret collect.Builder) {
func Make(reserve bool) (ret collect.MapWriter) {
if reserve {
ret = StdMap{"": nil}
} else {
Expand All @@ -13,7 +13,7 @@ func Builder(reserve bool) (ret collect.Builder) {
return
}

func (m StdMap) Add(key string, val any) collect.Builder {
func (m StdMap) MapValue(key string, val any) collect.MapWriter {
if len(key) == 0 { // there should be only one blank key; at the start
if _, exists := m[key]; !exists {
// could adjust the slice. but the program should know better.
Expand All @@ -24,6 +24,7 @@ func (m StdMap) Add(key string, val any) collect.Builder {
return m
}

func (m StdMap) Map() any {
// returns map[string]any
func (m StdMap) GetMap() any {
return (map[string]any)(m)
}
29 changes: 29 additions & 0 deletions collect/stdseq/stdseq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package stdseq

import "github.com/ionous/tell/collect"

type StdSequence []any

func Make(reserve bool) (ret collect.SequenceWriter) {
if reserve {
ret = StdSequence{""}
} else {
ret = StdSequence{} // nil doesnt work. needs some place for the interface value i guess.
}
return
}

func (m StdSequence) IndexValue(idx int, val any) (ret collect.SequenceWriter) {
if idx < len(m) {
m[idx] = val
ret = m
} else {
ret = append(m, val)
}
return
}

// returns []any
func (m StdSequence) GetSequence() any {
return ([]any)(m)
}
30 changes: 16 additions & 14 deletions decode/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ type pendingValue interface {
finalize() any // return the collection
}

func newMapping(key string, values collect.Builder, comments *strings.Builder) pendingValue {
return &pendingMap{key: key, values: values, comments: comments}
func newMapping(key string, values collect.MapWriter, comments *strings.Builder) pendingValue {
return &pendingMap{key: key, maps: values, comments: comments}
}

type pendingMap struct {
key string
values collect.Builder
maps collect.MapWriter
comments *strings.Builder
}

Expand All @@ -30,9 +30,9 @@ func (p *pendingMap) finalize() (ret any) {
}
if p.comments != nil {
str := clearComments(&p.comments)
p.values.Add("", str)
p.maps.MapValue("", str)
}
return p.values.Map()
return p.maps.GetMap()
}

func (p *pendingMap) setKey(key string) (err error) {
Expand All @@ -50,24 +50,25 @@ func (p *pendingMap) setValue(val any) (err error) {
if len(p.key) == 0 {
err = errors.New("missing key")
} else {
p.values = p.values.Add(p.key, val)
p.maps = p.maps.MapValue(p.key, val)
p.key = ""
}
return
}

func newSequence(comments *strings.Builder) pendingValue {
var values []any
func newSequence(values collect.SequenceWriter, comments *strings.Builder) pendingValue {
var index int
if comments != nil {
values = make([]any, 1)
index++
}
return &pendingSeq{dashed: true, values: values, comments: comments}
return &pendingSeq{dashed: true, index: index, values: values, comments: comments}
}

type pendingSeq struct {
dashed bool
values []any
values collect.SequenceWriter
comments *strings.Builder
index int
}

func (p *pendingSeq) finalize() (ret any) {
Expand All @@ -76,9 +77,9 @@ func (p *pendingSeq) finalize() (ret any) {
}
if p.comments != nil {
str := clearComments(&p.comments)
p.values[0] = str
p.values = p.values.IndexValue(0, str)
}
return p.values
return p.values.GetSequence()
}

func (p *pendingSeq) setKey(key string) (err error) {
Expand All @@ -96,8 +97,9 @@ func (p *pendingSeq) setValue(val any) (err error) {
if !p.dashed {
err = errors.New("expected a dash before adding values to a sequence")
} else {
p.values = append(p.values, val)
p.values = p.values.IndexValue(p.index, val)
p.dashed = false
p.index++
}
return
}
Expand Down
27 changes: 13 additions & 14 deletions decode/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ import (
"github.com/ionous/tell/token"
)

func MakeDecoder(maps collect.BuilderFactory,
comments notes.Commentator) Decoder {
return Decoder{
mapMaker: mapMaker{maps},
memo: makeMemo(comments),
}
// configure the production of sequences
func (d *Decoder) SetSequencer(seq collect.SequenceFactory) {
d.collector.seqs = seq
}

// configure the production of mappings
func (d *Decoder) SetMapper(maps collect.BuilderFactory) {
d.mapMaker.create = maps
func (d *Decoder) SetMapper(maps collect.MapFactory) {
d.collector.maps = maps
}

// configure the production of comment blocks
Expand Down Expand Up @@ -55,11 +52,13 @@ func (d *Decoder) Decode(src io.RuneReader) (ret any, err error) {
return
}

// the decoder is *not* ready to use by default
// the mapper, sequencer, and notes need to be set.
type Decoder struct {
out output
mapMaker mapMaker
memo memo
state func(token.Pos, token.Type, any) error
out output
collector collector
memo memo
state func(token.Pos, token.Type, any) error
// configure the tokenizer for the next decode
UseFloats bool
}
Expand Down Expand Up @@ -95,7 +94,7 @@ func (d *Decoder) docStart(at token.Pos, tokenType token.Type, val any) (err err

case token.Key:
key := val.(string)
d.out.setPending(at, d.mapMaker.newCollection(key, d.memo.newComments()))
d.out.setPending(at, d.collector.newCollection(key, d.memo.newComments()))
d.state = d.waitForValue

case token.Bool, token.Number, token.InterpretedString, token.RawString:
Expand Down Expand Up @@ -172,7 +171,7 @@ func (d *Decoder) waitForValue(at token.Pos, tokenType token.Type, val any) (err
case token.Key:
if key := val.(string); at.X > d.out.pos.X {
// a new collection
p := d.mapMaker.newCollection(key, d.memo.newComments())
p := d.collector.newCollection(key, d.memo.newComments())
d.out.push(at, p)
} else {
// the key is for the same or an earlier collection
Expand Down
7 changes: 6 additions & 1 deletion decode/doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/ionous/tell/charm"
"github.com/ionous/tell/collect/imap"
"github.com/ionous/tell/collect/stdseq"
"github.com/ionous/tell/decode"
"github.com/ionous/tell/notes"
)
Expand Down Expand Up @@ -52,7 +53,11 @@ func test(t *testing.T, nameInputExpect ...any) {
} else {
var res any
str := strings.TrimLeftFunc(input, unicode.IsSpace)
dec := decode.MakeDecoder(imap.Builder, notes.DiscardComments())

var dec decode.Decoder
dec.SetMapper(imap.Make)
dec.SetSequencer(stdseq.Make)
dec.UseNotes(notes.DiscardComments())
if val, e := dec.Decode(strings.NewReader(str)); e != nil {
res = e
} else {
Expand Down
11 changes: 6 additions & 5 deletions decode/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,18 @@ func (out *output) uncheckedPop(at int) (ret int, err error) {
return
}

type mapMaker struct {
create collect.BuilderFactory
type collector struct {
maps collect.MapFactory
seqs collect.SequenceFactory
}

func (f *mapMaker) newCollection(key string, comments *strings.Builder) pendingValue {
func (f *collector) newCollection(key string, comments *strings.Builder) pendingValue {
var p pendingValue
switch {
case len(key) == 0:
p = newSequence(comments)
p = newSequence(f.seqs(comments != nil), comments)
default:
p = newMapping(key, f.create(comments != nil), comments)
p = newMapping(key, f.maps(comments != nil), comments)
}
return p
}
24 changes: 18 additions & 6 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/ionous/tell/collect"
"github.com/ionous/tell/collect/stdmap"
"github.com/ionous/tell/collect/stdseq"
"github.com/ionous/tell/decode"
"github.com/ionous/tell/notes"
)
Expand All @@ -27,18 +28,29 @@ func NewDecoder(src io.Reader) *Decoder {
} else {
rr = bufio.NewReader(src)
}
return &Decoder{src: rr, inner: decode.MakeDecoder(
stdmap.Builder,
notes.DiscardComments(),
)}
return &Decoder{src: rr, inner: makeDefaultDecoder()}
}

func makeDefaultDecoder() decode.Decoder {
var dec decode.Decoder
dec.SetMapper(stdmap.Make)
dec.SetSequencer(stdseq.Make)
dec.UseNotes(notes.DiscardComments())
return dec
}

// control the creation of mappings for the upcoming Decode.
// the default is to create native maps ( via stdmap.Builder )
func (d *Decoder) SetMapper(maps collect.BuilderFactory) {
// the default is to create native maps ( via stdmap.Make )
func (d *Decoder) SetMapper(maps collect.MapFactory) {
d.inner.SetMapper(maps)
}

// control the creation of sequences for the upcoming Decode.
// the default is to create native slices ( via stdseq.Make )
func (d *Decoder) SetSequencer(seq collect.SequenceFactory) {
d.inner.SetSequencer(seq)
}

// control the creation of comment blocks for the upcoming Decode.
// the default is to discard comments.
func (d *Decoder) UseNotes(comments notes.Commentator) {
Expand Down
Loading

0 comments on commit 49e2e8e

Please sign in to comment.