Skip to content

Commit

Permalink
add collect function
Browse files Browse the repository at this point in the history
  • Loading branch information
emicklei committed Sep 9, 2024
1 parent f87be44 commit f4a7743
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 6 deletions.
40 changes: 40 additions & 0 deletions core/collect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package core

import (
"bytes"
"fmt"

"github.com/emicklei/melrose/notify"
)

type Collect struct {
Target HasValue
Replaceable HasValue
Each Sequenceable
}

func (c Collect) S() Sequence {
tv := c.Target.Value()
t, ok := tv.(HasSequenceables)
if !ok {
notify.Warnf("target does not have sequences")
return EmptySequence
}
rv := c.Replaceable.Value()
r, ok := rv.(Replaceable)
if !ok {
notify.Warnf("function does not allow replacement")
return EmptySequence
}
targets := make([]Sequenceable, len(t.Sequenceables()))
for i, each := range t.Sequenceables() {
targets[i] = r.Replaced(c.Each, each)
}
return SequenceableList{Target: targets}.S()
}

func (c Collect) Storex() string {
var b bytes.Buffer
fmt.Fprintf(&b, "collect(%s,%s)", Storex(c.Target), Storex(c.Replaceable))
return b.String()
}
4 changes: 4 additions & 0 deletions core/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,7 @@ type NameAware interface {
type HasIndex interface {
Index() HasValue
}

type HasSequenceables interface {
Sequenceables() []Sequenceable
}
16 changes: 16 additions & 0 deletions core/sequence_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package core

type SequenceableList struct {
Target []Sequenceable
}

func (l SequenceableList) S() Sequence {
if len(l.Target) == 0 {
return EmptySequence
}
joined := l.Target[0].S()
for i := 1; i < len(l.Target); i++ {
joined = joined.SequenceJoin(l.Target[i].S())
}
return joined
}
24 changes: 22 additions & 2 deletions dsl/eval_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ The first parameter controls the fraction of the note, e.g. 1 = whole, 2 = half,
Fraction can also be an exact float value between 0 and 1.
`,
Prefix: "fra",
Alias: "frac",
IsComposer: true,
Template: `fraction(${1:object},${2:object})`,
Samples: `fraction(8,sequence('e f')) // => 8E 8F , shorten the notes from quarter to eight`,
Expand Down Expand Up @@ -438,7 +437,6 @@ l = loop(bpm(speedup),sequence('c e g'),next(speedup))`,
Title: "Sequence creator",
Description: `create a Sequence using this <a href="/docs/reference/notations/#sequence">format</a>`,
Prefix: "se",
Alias: "seq",
Template: `sequence('${1:space-separated-notes}')`,
Samples: `sequence('c d e')
sequence('(8c d e)') // => (8C D E)
Expand Down Expand Up @@ -1290,5 +1288,27 @@ onkey('c4',onoff('e')) // uses default input and default output MIDI device`,
}
},
})

registerFunction(eval, "collect", Function{
Title: "Collect function",
Description: "",
Template: "collect(${1:collection},${2:function-with-underscore})",
Samples: "",
Func: func(collection any, function any) any {
if _, ok := getValue(collection).(core.HasSequenceables); !ok {
return notify.Panic(errors.New("collection must have sequenceables"))
}
if _, ok := getValue(collection).(core.Replaceable); !ok {
return notify.Panic(errors.New("function must allow replacement"))
}
each, _ := ctx.Variables().Get("_")
return core.Collect{
Target: core.On(collection),
Replaceable: core.On(function),
Each: each.(core.Sequenceable), // todo check
}
},
})

return eval
}
12 changes: 12 additions & 0 deletions dsl/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,15 @@ func TestEuclidean(t *testing.T) {
t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
}
}

func TestCollect(t *testing.T) {
e := newTestEvaluator()
r, err := e.EvaluateProgram(`
c = collect(join(note('e')), fraction(8,_))
`)
checkError(t, err)
if got, want := r.(core.Collect).Storex(), "collect(join(note('E')),fraction(8,_))"; got != want {
t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want)
}
}
6 changes: 5 additions & 1 deletion dsl/varstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type VariableStore struct {
// NewVariableStore returns a new
func NewVariableStore() *VariableStore {
return &VariableStore{
variables: map[string]interface{}{},
variables: map[string]interface{}{"_": core.EmptySequence},
}
}

Expand Down Expand Up @@ -94,6 +94,10 @@ func ListVariables(storage core.VariableStorage, args []string) notify.Message {
sort.Strings(keys)
for _, k := range keys {
v := variables[k]
// skip empty temporary var
if k == "_" {
continue
}
if s, ok := v.(core.Storable); ok {
fmt.Printf("%s = %s\n", strings.Repeat(" ", width-len(k))+k, s.Storex())
} else {
Expand Down
11 changes: 8 additions & 3 deletions op/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ type Join struct {
Target []core.Sequenceable
}

// SequenceableList is part of core.HasSequenceables
func (j Join) Sequenceables() []core.Sequenceable {
return j.Target
}

func (j Join) Storex() string {
var b bytes.Buffer
fmt.Fprintf(&b, "join(")
Expand All @@ -23,11 +28,11 @@ func (j Join) S() core.Sequence {
if len(j.Target) == 0 {
return core.EmptySequence
}
head := j.Target[0].S()
joined := j.Target[0].S()
for i := 1; i < len(j.Target); i++ {
head = head.SequenceJoin(j.Target[i].S())
joined = joined.SequenceJoin(j.Target[i].S())
}
return head
return joined
}

// Replaced is part of Replaceable
Expand Down

0 comments on commit f4a7743

Please sign in to comment.