WIP: added in tuples
This commit is contained in:
parent
e113e96f1f
commit
b2f67afe50
29
gg/v2/gg.bnf
29
gg/v2/gg.bnf
@ -5,20 +5,19 @@
|
||||
|
||||
<name> ::= (<letter> | <mark>) (<letter> | <mark> | <digit>)*
|
||||
|
||||
<value> ::= <name> | <number> | <tuple> | <graph>
|
||||
<tuple> ::= "(" <tuple-tail>
|
||||
<tuple-tail> ::= ")" | <tuple-open-edge>
|
||||
<tuple-open-edge> ::= <value> <tuple-open-edge-tail>
|
||||
<tuple-open-edge-tail> ::= ")"
|
||||
| "," <tuple-tail>
|
||||
| "<" <tuple-open-edge>
|
||||
|
||||
<tuple> ::= "(" <tuple-tail>
|
||||
<tuple-tail> ::= ")" | <value> <tuple-post-value>
|
||||
<tuple-post-value> ::= ")"
|
||||
| "," <tuple-tail>
|
||||
| "<" <value> <tuple-post-value>
|
||||
<graph> ::= "{" <graph-tail>
|
||||
<graph-tail> ::= "}" | <name> "=" <graph-open-edge>
|
||||
<graph-open-edge> ::= <value> <graph-open-edge-value-tail>
|
||||
| <tuple> <graph-open-edge-tail>
|
||||
<graph-open-edge-tail> ::= "}" | ";" <graph-tail>
|
||||
<graph-open-edge-value-tail> ::= <graph-open-edge-tail> | "<" <graph-open-edge>
|
||||
|
||||
<graph> ::= "{" <graph-tail>
|
||||
<graph-tail> ::= "}" | <name> "=" <graph-open-edge>
|
||||
<graph-open-edge> ::= <value> <graph-open-edge-tail>
|
||||
<graph-open-edge-tail> ::= "}"
|
||||
| ";" <graph-tail>
|
||||
| "<" <graph-open-edge>
|
||||
|
||||
<top-level-value> ::= <name> | <number> | <graph>
|
||||
<gg> ::= <eof> | <top-level-value> <gg>
|
||||
<value> ::= <name> | <number> | <graph>
|
||||
<gg> ::= <eof> | <value> <gg>
|
||||
|
157
gg/v2/term.go
157
gg/v2/term.go
@ -9,6 +9,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/mediocregopher/ginger/graph"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -27,6 +28,10 @@ func (str stringerStr) String() string {
|
||||
return string(str)
|
||||
}
|
||||
|
||||
// TODO in the end I don't think locatable is actually needed here... I think
|
||||
// seq is the only place where locating anything is required generally, and
|
||||
// that's done using Decoder.nextLoc. Otherwise the Location is used when
|
||||
// constructing Value's, which only really requires runes to be locatable.
|
||||
type term[T locatable] struct {
|
||||
name fmt.Stringer
|
||||
decodeFn func(d *Decoder) (T, error)
|
||||
@ -71,7 +76,7 @@ func seq[Ta, Tb, Tc locatable](
|
||||
name fmt.Stringer,
|
||||
termA *term[Ta],
|
||||
termB *term[Tb],
|
||||
fn func(Ta, Tb) (Tc, error),
|
||||
fn func(Ta, Tb) (Tc, error), // TODO probably don't need error return
|
||||
) *term[Tc] {
|
||||
return &term[Tc]{
|
||||
name: name,
|
||||
@ -276,7 +281,29 @@ var (
|
||||
)
|
||||
)
|
||||
|
||||
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] {
|
||||
type locatableOpenEdge struct {
|
||||
Location
|
||||
oe *OpenEdge
|
||||
}
|
||||
|
||||
type tupleState struct {
|
||||
Location // location of last place tupleState was updated
|
||||
ins []*OpenEdge
|
||||
oe *OpenEdge
|
||||
}
|
||||
|
||||
type graphState struct {
|
||||
Location // location of last place graphState was updated
|
||||
g *Graph
|
||||
@ -284,18 +311,22 @@ var graphTerm = func() *term[Value] {
|
||||
}
|
||||
|
||||
var (
|
||||
// pre-define these, and then fill in the pointers after, in order to
|
||||
// deal with recursive dependencies between them.
|
||||
graphTerm = new(term[Value])
|
||||
graphTailTerm = new(term[graphState])
|
||||
graphOpenEdgeTerm = new(term[graphState])
|
||||
graphOpenEdgeTailTerm = new(term[graphState])
|
||||
valueTerm = new(term[Value])
|
||||
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{Location: lr.locate()}
|
||||
},
|
||||
)
|
||||
|
||||
rightCurlyBrace = runeTerm('}')
|
||||
graphEndTerm = mapTerm(
|
||||
rightCurlyBrace,
|
||||
rightCurlyBrace, func(lr locatableRune) graphState {
|
||||
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{Location: lr.locate()}
|
||||
@ -303,6 +334,67 @@ var graphTerm = func() *term[Value] {
|
||||
)
|
||||
)
|
||||
|
||||
var (
|
||||
// pre-define these, and then fill in the pointers after, in order to
|
||||
// deal with recursive dependencies between them.
|
||||
valueTerm = new(term[Value])
|
||||
|
||||
tupleTerm = new(term[locatableOpenEdge])
|
||||
tupleTailTerm = new(term[tupleState])
|
||||
tupleOpenEdgeTerm = new(term[tupleState])
|
||||
tupleOpenEdgeTailTerm = new(term[tupleState])
|
||||
|
||||
graphTerm = new(term[Value])
|
||||
graphTailTerm = new(term[graphState])
|
||||
graphOpenEdgeTerm = new(term[graphState])
|
||||
graphOpenEdgeTailTerm = new(term[graphState])
|
||||
graphOpenEdgeValueTailTerm = new(term[graphState])
|
||||
)
|
||||
|
||||
*tupleTerm = *seq(
|
||||
stringerStr("tuple"),
|
||||
runeTerm('('),
|
||||
tupleTailTerm,
|
||||
func(lr locatableRune, ts tupleState) (locatableOpenEdge, error) {
|
||||
slices.Reverse(ts.ins)
|
||||
oe := graph.TupleOut(None, ts.ins...)
|
||||
return locatableOpenEdge{
|
||||
Location: lr.locate(),
|
||||
oe: oe,
|
||||
}, nil
|
||||
},
|
||||
)
|
||||
|
||||
*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, error) {
|
||||
ts.oe = openEdgeIntoValue(val, ts.oe)
|
||||
ts.Location = val.locate()
|
||||
return ts, nil
|
||||
},
|
||||
)
|
||||
|
||||
*tupleOpenEdgeTailTerm = *firstOf(
|
||||
tupleEndTerm,
|
||||
matchAndSkip(runeTerm(','), tupleTailTerm),
|
||||
matchAndSkip(runeTerm('<'), tupleOpenEdgeTerm),
|
||||
)
|
||||
|
||||
*graphTerm = *seq(
|
||||
stringerStr("graph"),
|
||||
runeTerm('{'),
|
||||
@ -321,7 +413,7 @@ var graphTerm = func() *term[Value] {
|
||||
seq(
|
||||
nameTerm,
|
||||
nameTerm,
|
||||
matchAndSkip(runeTerm('='), graphOpenEdgeTailTerm),
|
||||
matchAndSkip(runeTerm('='), graphOpenEdgeTerm),
|
||||
func(name Value, gs graphState) (graphState, error) {
|
||||
if gs.g == nil {
|
||||
gs.g = new(Graph)
|
||||
@ -336,27 +428,36 @@ var graphTerm = func() *term[Value] {
|
||||
)
|
||||
|
||||
*graphOpenEdgeTerm = *firstOf(
|
||||
graphEndTerm,
|
||||
matchAndSkip(runeTerm(';'), graphTailTerm),
|
||||
matchAndSkip(runeTerm('<'), graphOpenEdgeTailTerm),
|
||||
seq(
|
||||
valueTerm,
|
||||
valueTerm,
|
||||
graphOpenEdgeValueTailTerm,
|
||||
func(val Value, gs graphState) (graphState, error) {
|
||||
gs.oe = openEdgeIntoValue(val, gs.oe)
|
||||
gs.Location = val.locate()
|
||||
return gs, nil
|
||||
},
|
||||
),
|
||||
seq(
|
||||
tupleTerm,
|
||||
tupleTerm,
|
||||
graphOpenEdgeTailTerm,
|
||||
func(loe locatableOpenEdge, gs graphState) (graphState, error) {
|
||||
gs.oe = loe.oe
|
||||
gs.Location = loe.locate()
|
||||
return gs, nil
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
*graphOpenEdgeTailTerm = *seq(
|
||||
valueTerm,
|
||||
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)
|
||||
}
|
||||
*graphOpenEdgeTailTerm = *firstOf(
|
||||
graphEndTerm,
|
||||
matchAndSkip(runeTerm(';'), graphTailTerm),
|
||||
)
|
||||
|
||||
gs.Location = val.locate()
|
||||
return gs, nil
|
||||
},
|
||||
*graphOpenEdgeValueTailTerm = *firstOf(
|
||||
graphOpenEdgeTailTerm,
|
||||
matchAndSkip(runeTerm('<'), graphOpenEdgeTerm),
|
||||
)
|
||||
|
||||
*valueTerm = *firstOf(nameTerm, numberTerm, graphTerm)
|
||||
|
@ -84,7 +84,7 @@ func TestTermDecoding(t *testing.T) {
|
||||
{in: `{}`, exp: expGraph(1, 1, new(Graph))},
|
||||
{in: `{`, expErr: `1:2: expected '}' or name`},
|
||||
{in: `{a}`, expErr: `1:3: expected '='`},
|
||||
{in: `{a=}`, expErr: `1:4: expected name or number or graph`},
|
||||
{in: `{a=}`, expErr: `1:4: expected name or number or graph or tuple`},
|
||||
{
|
||||
in: `{foo=a}`,
|
||||
exp: expGraph(
|
||||
@ -217,4 +217,89 @@ 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
1
go.mod
@ -7,5 +7,6 @@ require github.com/stretchr/testify v1.7.0
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.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
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -5,6 +5,8 @@ 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/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
|
@ -126,7 +126,7 @@ func TupleOut[E, V Value](edgeVal E, ins ...*OpenEdge[E, V]) *OpenEdge[E, V] {
|
||||
if len(ins) == 1 {
|
||||
|
||||
var (
|
||||
zero V
|
||||
zero E
|
||||
in = ins[0]
|
||||
)
|
||||
|
||||
@ -353,7 +353,6 @@ type reducedEdge[Ea, Va Value, Vb any] struct {
|
||||
// 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
|
||||
// map/reduction will be passed to each dependant operation.
|
||||
//
|
||||
func MapReduce[Ea, Va Value, Vb any](
|
||||
root *OpenEdge[Ea, Va],
|
||||
mapVal func(Va) (Vb, error),
|
||||
|
Loading…
Reference in New Issue
Block a user