Compare commits

..

No commits in common. "303d40a6c3238735eff9446ba598bc487c69ef02" and "e113e96f1fe6ffbe64d5bccce2a42b00b07517fd" have entirely different histories.

7 changed files with 100 additions and 248 deletions

View File

@ -5,19 +5,20 @@
<name> ::= (<letter> | <mark>) (<letter> | <mark> | <digit>)* <name> ::= (<letter> | <mark>) (<letter> | <mark> | <digit>)*
<value> ::= <name> | <number> | <tuple> | <graph>
<tuple> ::= "(" <tuple-tail> <tuple> ::= "(" <tuple-tail>
<tuple-tail> ::= ")" | <tuple-open-edge> <tuple-tail> ::= ")" | <value> <tuple-post-value>
<tuple-open-edge> ::= <value> <tuple-open-edge-tail> <tuple-post-value> ::= ")"
<tuple-open-edge-tail> ::= ")"
| "," <tuple-tail> | "," <tuple-tail>
| "<" <tuple-open-edge> | "<" <value> <tuple-post-value>
<graph> ::= "{" <graph-tail> <graph> ::= "{" <graph-tail>
<graph-tail> ::= "}" | <name> "=" <graph-open-edge> <graph-tail> ::= "}" | <name> "=" <graph-open-edge>
<graph-open-edge> ::= <value> <graph-open-edge-value-tail> <graph-open-edge> ::= <value> <graph-open-edge-tail>
| <tuple> <graph-open-edge-tail> <graph-open-edge-tail> ::= "}"
<graph-open-edge-tail> ::= "}" | ";" <graph-tail> | ";" <graph-tail>
<graph-open-edge-value-tail> ::= <graph-open-edge-tail> | "<" <graph-open-edge> | "<" <graph-open-edge>
<value> ::= <name> | <number> | <graph> <top-level-value> ::= <name> | <number> | <graph>
<gg> ::= <eof> | <value> <gg> <gg> ::= <eof> | <top-level-value> <gg>

View File

@ -25,6 +25,10 @@ func (e LocatedError) Error() string {
return fmt.Sprintf("%d:%d: %v", e.Row, e.Col, e.Err) return fmt.Sprintf("%d:%d: %v", e.Row, e.Col, e.Err)
} }
type locatable interface {
locate() Location
}
type locatableRune struct { type locatableRune struct {
Location Location
r rune r rune
@ -34,3 +38,12 @@ type locatableString struct {
Location Location
str string str string
} }
type locatableSlice[T locatable] []T
func (s locatableSlice[T]) locate() Location {
if len(s) == 0 {
panic("can't locate empty locatableSlice")
}
return s[0].locate()
}

View File

@ -9,7 +9,6 @@ import (
"unicode" "unicode"
"github.com/mediocregopher/ginger/graph" "github.com/mediocregopher/ginger/graph"
"golang.org/x/exp/slices"
) )
var ( var (
@ -28,7 +27,7 @@ func (str stringerStr) String() string {
return string(str) return string(str)
} }
type term[T any] struct { type term[T locatable] struct {
name fmt.Stringer name fmt.Stringer
decodeFn func(d *Decoder) (T, error) decodeFn func(d *Decoder) (T, error)
} }
@ -37,7 +36,7 @@ func (t term[T]) String() string {
return t.name.String() return t.name.String()
} }
func firstOf[T any](terms ...*term[T]) *term[T] { func firstOf[T locatable](terms ...*term[T]) *term[T] {
if len(terms) < 2 { if len(terms) < 2 {
panic("firstOfTerms requires at least 2 terms") panic("firstOfTerms requires at least 2 terms")
} }
@ -68,11 +67,11 @@ func firstOf[T any](terms ...*term[T]) *term[T] {
} }
} }
func seq[Ta, Tb, Tc any]( func seq[Ta, Tb, Tc locatable](
name fmt.Stringer, name fmt.Stringer,
termA *term[Ta], termA *term[Ta],
termB *term[Tb], termB *term[Tb],
fn func(Ta, Tb) Tc, fn func(Ta, Tb) (Tc, error),
) *term[Tc] { ) *term[Tc] {
return &term[Tc]{ return &term[Tc]{
name: name, name: name,
@ -91,25 +90,30 @@ func seq[Ta, Tb, Tc any](
return zero, err return zero, err
} }
return fn(va, vb), nil vc, err := fn(va, vb)
if err != nil {
return zero, err
}
return vc, nil
}, },
} }
} }
func matchAndSkip[Ta, Tb any]( func matchAndSkip[Ta, Tb locatable](
termA *term[Ta], termB *term[Tb], termA *term[Ta], termB *term[Tb],
) *term[Tb] { ) *term[Tb] {
return seq(termA, termA, termB, func(_ Ta, b Tb) Tb { return seq(termA, termA, termB, func(_ Ta, b Tb) (Tb, error) {
return b return b, nil
}) })
} }
func oneOrMore[T any](t *term[T]) *term[[]T] { func oneOrMore[T locatable](t *term[T]) *term[locatableSlice[T]] {
return &term[[]T]{ return &term[locatableSlice[T]]{
name: stringerFn(func() string { name: stringerFn(func() string {
return fmt.Sprintf("one or more %v", t) return fmt.Sprintf("one or more %v", t)
}), }),
decodeFn: func(d *Decoder) ([]T, error) { decodeFn: func(d *Decoder) (locatableSlice[T], error) {
var vv []T var vv []T
for { for {
v, err := t.decodeFn(d) v, err := t.decodeFn(d)
@ -131,12 +135,12 @@ func oneOrMore[T any](t *term[T]) *term[[]T] {
} }
} }
func zeroOrMore[T any](t *term[T]) *term[[]T] { func zeroOrMore[T locatable](t *term[T]) *term[locatableSlice[T]] {
return &term[[]T]{ return &term[locatableSlice[T]]{
name: stringerFn(func() string { name: stringerFn(func() string {
return fmt.Sprintf("zero or more %v", t) return fmt.Sprintf("zero or more %v", t)
}), }),
decodeFn: func(d *Decoder) ([]T, error) { decodeFn: func(d *Decoder) (locatableSlice[T], error) {
var vv []T var vv []T
for { for {
v, err := t.decodeFn(d) v, err := t.decodeFn(d)
@ -154,7 +158,7 @@ func zeroOrMore[T any](t *term[T]) *term[[]T] {
} }
} }
func mapTerm[Ta, Tb any]( func mapTerm[Ta locatable, Tb locatable](
name fmt.Stringer, t *term[Ta], fn func(Ta) Tb, name fmt.Stringer, t *term[Ta], fn func(Ta) Tb,
) *term[Tb] { ) *term[Tb] {
return &term[Tb]{ return &term[Tb]{
@ -200,7 +204,7 @@ func runeTerm(r rune) *term[locatableRune] {
) )
} }
func locatableRunesToString(rr []locatableRune) string { func locatableRunesToString(rr locatableSlice[locatableRune]) string {
str := make([]rune, len(rr)) str := make([]rune, len(rr))
for i := range rr { for i := range rr {
str[i] = rr[i].r str[i] = rr[i].r
@ -209,11 +213,11 @@ func locatableRunesToString(rr []locatableRune) string {
} }
func runesToStringTerm( func runesToStringTerm(
t *term[[]locatableRune], t *term[locatableSlice[locatableRune]],
) *term[locatableString] { ) *term[locatableString] {
return mapTerm( return mapTerm(
t, t, func(rr []locatableRune) locatableString { t, t, func(rr locatableSlice[locatableRune]) locatableString {
return locatableString{rr[0].locate(), locatableRunesToString(rr)} return locatableString{rr.locate(), locatableRunesToString(rr)}
}, },
) )
} }
@ -230,8 +234,10 @@ var (
stringerStr("negative-number"), stringerStr("negative-number"),
runeTerm('-'), runeTerm('-'),
positiveNumberTerm, positiveNumberTerm,
func(neg locatableRune, posNum locatableString) locatableString { func(neg locatableRune, posNum locatableString) (locatableString, error) {
return locatableString{neg.locate(), string(neg.r) + posNum.str} return locatableString{
neg.locate(), string(neg.r) + posNum.str,
}, nil
}, },
) )
@ -263,125 +269,50 @@ var (
stringerStr("name"), stringerStr("name"),
letterTerm, letterTerm,
letterTailTerm, letterTailTerm,
func(head locatableRune, tail []locatableRune) Value { func(head locatableRune, tail locatableSlice[locatableRune]) (Value, error) {
name := string(head.r) + locatableRunesToString(tail) name := string(head.r) + locatableRunesToString(tail)
return Value{Name: &name, Location: head.locate()} return Value{Name: &name, Location: head.locate()}, nil
}, },
) )
) )
func openEdgeIntoValue(val Value, oe *OpenEdge) *OpenEdge {
switch {
case oe == nil:
return graph.ValueOut(None, val)
case !oe.EdgeValue().Valid:
return oe.WithEdgeValue(Some(val))
default:
return graph.TupleOut(Some(val), oe)
}
}
var graphTerm = func() *term[Value] { var graphTerm = func() *term[Value] {
type tupleState struct {
ins []*OpenEdge
oe *OpenEdge
}
type graphState struct { type graphState struct {
Location // location of last place graphState was updated
g *Graph g *Graph
oe *OpenEdge oe *OpenEdge
} }
var (
rightParenthesis = runeTerm(')')
tupleEndTerm = mapTerm(
rightParenthesis,
rightParenthesis,
func(lr locatableRune) tupleState {
// if ')', then map that to an empty state. This acts as a
// sentinel value to indicate "end of tuple".
return tupleState{}
},
)
rightCurlyBrace = runeTerm('}')
graphEndTerm = mapTerm(
rightCurlyBrace,
rightCurlyBrace,
func(lr locatableRune) graphState {
// if '}', then map that to an empty state. This acts as a
// sentinel value to indicate "end of graph".
return graphState{}
},
)
)
var ( var (
// pre-define these, and then fill in the pointers after, in order to // pre-define these, and then fill in the pointers after, in order to
// deal with recursive dependencies between them. // deal with recursive dependencies between them.
valueTerm = new(term[Value])
tupleTerm = new(term[*OpenEdge])
tupleTailTerm = new(term[tupleState])
tupleOpenEdgeTerm = new(term[tupleState])
tupleOpenEdgeTailTerm = new(term[tupleState])
graphTerm = new(term[Value]) graphTerm = new(term[Value])
graphTailTerm = new(term[graphState]) graphTailTerm = new(term[graphState])
graphOpenEdgeTerm = new(term[graphState]) graphOpenEdgeTerm = new(term[graphState])
graphOpenEdgeTailTerm = new(term[graphState]) graphOpenEdgeTailTerm = new(term[graphState])
graphOpenEdgeValueTailTerm = new(term[graphState]) valueTerm = new(term[Value])
)
*tupleTerm = *seq( rightCurlyBrace = runeTerm('}')
stringerStr("tuple"), graphEndTerm = mapTerm(
runeTerm('('), rightCurlyBrace,
tupleTailTerm, rightCurlyBrace, func(lr locatableRune) graphState {
func(lr locatableRune, ts tupleState) *OpenEdge { // if '}', then map that to an empty state. This acts as a
slices.Reverse(ts.ins) // sentinel value to indicate "end of graph".
return graph.TupleOut(None, ts.ins...) return graphState{Location: lr.locate()}
}, },
) )
*tupleTailTerm = *firstOf(
tupleEndTerm,
mapTerm(
tupleOpenEdgeTerm,
tupleOpenEdgeTerm,
func(ts tupleState) tupleState {
ts.ins = append(ts.ins, ts.oe)
ts.oe = nil
return ts
},
),
)
*tupleOpenEdgeTerm = *seq(
valueTerm,
valueTerm,
tupleOpenEdgeTailTerm,
func(val Value, ts tupleState) tupleState {
ts.oe = openEdgeIntoValue(val, ts.oe)
return ts
},
)
*tupleOpenEdgeTailTerm = *firstOf(
tupleEndTerm,
matchAndSkip(runeTerm(','), tupleTailTerm),
matchAndSkip(runeTerm('<'), tupleOpenEdgeTerm),
) )
*graphTerm = *seq( *graphTerm = *seq(
stringerStr("graph"), stringerStr("graph"),
runeTerm('{'), runeTerm('{'),
graphTailTerm, graphTailTerm,
func(lr locatableRune, gs graphState) Value { func(lr locatableRune, gs graphState) (Value, error) {
if gs.g == nil { if gs.g == nil {
gs.g = new(Graph) gs.g = new(Graph)
} }
return Value{Graph: gs.g, Location: lr.locate()} return Value{Graph: gs.g, Location: lr.locate()}, nil
}, },
) )
@ -390,48 +321,42 @@ var graphTerm = func() *term[Value] {
seq( seq(
nameTerm, nameTerm,
nameTerm, nameTerm,
matchAndSkip(runeTerm('='), graphOpenEdgeTerm), matchAndSkip(runeTerm('='), graphOpenEdgeTailTerm),
func(name Value, gs graphState) graphState { func(name Value, gs graphState) (graphState, error) {
if gs.g == nil { if gs.g == nil {
gs.g = new(Graph) gs.g = new(Graph)
} }
gs.g = gs.g.AddValueIn(name, gs.oe) gs.g = gs.g.AddValueIn(name, gs.oe)
gs.oe = nil gs.oe = nil
return gs gs.Location = name.locate()
return gs, nil
}, },
), ),
) )
*graphOpenEdgeTerm = *firstOf( *graphOpenEdgeTerm = *firstOf(
seq(
valueTerm,
valueTerm,
graphOpenEdgeValueTailTerm,
func(val Value, gs graphState) graphState {
gs.oe = openEdgeIntoValue(val, gs.oe)
return gs
},
),
seq(
tupleTerm,
tupleTerm,
graphOpenEdgeTailTerm,
func(oe *OpenEdge, gs graphState) graphState {
gs.oe = oe
return gs
},
),
)
*graphOpenEdgeTailTerm = *firstOf(
graphEndTerm, graphEndTerm,
matchAndSkip(runeTerm(';'), graphTailTerm), matchAndSkip(runeTerm(';'), graphTailTerm),
matchAndSkip(runeTerm('<'), graphOpenEdgeTailTerm),
) )
*graphOpenEdgeValueTailTerm = *firstOf( *graphOpenEdgeTailTerm = *seq(
graphOpenEdgeTailTerm, valueTerm,
matchAndSkip(runeTerm('<'), graphOpenEdgeTerm), valueTerm,
graphOpenEdgeTerm,
func(val Value, gs graphState) (graphState, error) {
if gs.oe == nil {
gs.oe = graph.ValueOut(None, val)
} else if !gs.oe.EdgeValue().Valid {
gs.oe = gs.oe.WithEdgeValue(Some(val))
} else {
gs.oe = graph.TupleOut(Some(val), gs.oe)
}
gs.Location = val.locate()
return gs, nil
},
) )
*valueTerm = *firstOf(nameTerm, numberTerm, graphTerm) *valueTerm = *firstOf(nameTerm, numberTerm, graphTerm)

View File

@ -84,7 +84,7 @@ func TestTermDecoding(t *testing.T) {
{in: `{}`, exp: expGraph(1, 1, new(Graph))}, {in: `{}`, exp: expGraph(1, 1, new(Graph))},
{in: `{`, expErr: `1:2: expected '}' or name`}, {in: `{`, expErr: `1:2: expected '}' or name`},
{in: `{a}`, expErr: `1:3: expected '='`}, {in: `{a}`, expErr: `1:3: expected '='`},
{in: `{a=}`, expErr: `1:4: expected name or number or graph or tuple`}, {in: `{a=}`, expErr: `1:4: expected name or number or graph`},
{ {
in: `{foo=a}`, in: `{foo=a}`,
exp: expGraph( exp: expGraph(
@ -217,89 +217,4 @@ func TestTermDecoding(t *testing.T) {
), ),
}, },
}) })
runTests(t, "tuple", graphTerm, []test{
{
in: `{foo=(a)}`,
exp: expGraph(
1, 1, new(Graph).
AddValueIn(
expName(2, 1, "foo"),
graph.ValueOut(None, expName(6, 1, "a")),
),
),
},
{
in: `{foo=(a<b)}`,
exp: expGraph(
1, 1, new(Graph).
AddValueIn(
expName(2, 1, "foo"),
graph.ValueOut(
Some(expName(6, 1, "a")),
expName(8, 1, "b"),
),
),
),
},
{
in: `{foo=a<(b)}`,
exp: expGraph(
1, 1, new(Graph).
AddValueIn(
expName(2, 1, "foo"),
graph.ValueOut(
Some(expName(6, 1, "a")),
expName(8, 1, "b"),
),
),
),
},
{
in: `{foo=a<(b,c)}`,
exp: expGraph(
1, 1, new(Graph).
AddValueIn(
expName(2, 1, "foo"),
graph.TupleOut(
Some(expName(6, 1, "a")),
graph.ValueOut(None, expName(8, 1, "b")),
graph.ValueOut(None, expName(10, 1, "c")),
),
),
),
},
{
in: `{foo=a<(b<c)}`,
exp: expGraph(
1, 1, new(Graph).
AddValueIn(
expName(2, 1, "foo"),
graph.TupleOut(
Some(expName(6, 1, "a")),
graph.TupleOut(
Some(expName(6, 1, "b")),
graph.ValueOut(None, expName(8, 1, "c")),
),
),
),
),
},
//{
// in: `{foo=a<(b<(c))}`,
// exp: expGraph(
// 1, 1, new(Graph).
// AddValueIn(
// expName(2, 1, "foo"),
// graph.TupleOut(
// Some(expName(6, 1, "a")),
// graph.TupleOut(
// Some(expName(6, 1, "b")),
// graph.ValueOut(None, expName(8, 1, "c")),
// ),
// ),
// ),
// ),
//},
})
} }

1
go.mod
View File

@ -7,6 +7,5 @@ require github.com/stretchr/testify v1.7.0
require ( require (
github.com/davecgh/go-spew v1.1.0 // indirect github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
) )

2
go.sum
View File

@ -5,8 +5,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=

View File

@ -126,7 +126,7 @@ func TupleOut[E, V Value](edgeVal E, ins ...*OpenEdge[E, V]) *OpenEdge[E, V] {
if len(ins) == 1 { if len(ins) == 1 {
var ( var (
zero E zero V
in = ins[0] in = ins[0]
) )
@ -353,6 +353,7 @@ type reducedEdge[Ea, Va Value, Vb any] struct {
// If a value or edge is connected to multiple times within the root OpenEdge it // If a value or edge is connected to multiple times within the root OpenEdge it
// will only be mapped/reduced a single time, and the result of that single // will only be mapped/reduced a single time, and the result of that single
// map/reduction will be passed to each dependant operation. // map/reduction will be passed to each dependant operation.
//
func MapReduce[Ea, Va Value, Vb any]( func MapReduce[Ea, Va Value, Vb any](
root *OpenEdge[Ea, Va], root *OpenEdge[Ea, Va],
mapVal func(Va) (Vb, error), mapVal func(Va) (Vb, error),