|
|
|
@ -9,6 +9,7 @@ import ( |
|
|
|
|
"unicode" |
|
|
|
|
|
|
|
|
|
"github.com/mediocregopher/ginger/graph" |
|
|
|
|
"golang.org/x/exp/slices" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
@ -27,7 +28,7 @@ func (str stringerStr) String() string { |
|
|
|
|
return string(str) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type term[T locatable] struct { |
|
|
|
|
type term[T any] struct { |
|
|
|
|
name fmt.Stringer |
|
|
|
|
decodeFn func(d *Decoder) (T, error) |
|
|
|
|
} |
|
|
|
@ -36,7 +37,7 @@ func (t term[T]) String() string { |
|
|
|
|
return t.name.String() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func firstOf[T locatable](terms ...*term[T]) *term[T] { |
|
|
|
|
func firstOf[T any](terms ...*term[T]) *term[T] { |
|
|
|
|
if len(terms) < 2 { |
|
|
|
|
panic("firstOfTerms requires at least 2 terms") |
|
|
|
|
} |
|
|
|
@ -67,11 +68,11 @@ func firstOf[T locatable](terms ...*term[T]) *term[T] { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func seq[Ta, Tb, Tc locatable]( |
|
|
|
|
func seq[Ta, Tb, Tc any]( |
|
|
|
|
name fmt.Stringer, |
|
|
|
|
termA *term[Ta], |
|
|
|
|
termB *term[Tb], |
|
|
|
|
fn func(Ta, Tb) (Tc, error), |
|
|
|
|
fn func(Ta, Tb) Tc, |
|
|
|
|
) *term[Tc] { |
|
|
|
|
return &term[Tc]{ |
|
|
|
|
name: name, |
|
|
|
@ -90,30 +91,25 @@ func seq[Ta, Tb, Tc locatable]( |
|
|
|
|
return zero, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
vc, err := fn(va, vb) |
|
|
|
|
if err != nil { |
|
|
|
|
return zero, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return vc, nil |
|
|
|
|
return fn(va, vb), nil |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func matchAndSkip[Ta, Tb locatable]( |
|
|
|
|
func matchAndSkip[Ta, Tb any]( |
|
|
|
|
termA *term[Ta], termB *term[Tb], |
|
|
|
|
) *term[Tb] { |
|
|
|
|
return seq(termA, termA, termB, func(_ Ta, b Tb) (Tb, error) { |
|
|
|
|
return b, nil |
|
|
|
|
return seq(termA, termA, termB, func(_ Ta, b Tb) Tb { |
|
|
|
|
return b |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func oneOrMore[T locatable](t *term[T]) *term[locatableSlice[T]] { |
|
|
|
|
return &term[locatableSlice[T]]{ |
|
|
|
|
func oneOrMore[T any](t *term[T]) *term[[]T] { |
|
|
|
|
return &term[[]T]{ |
|
|
|
|
name: stringerFn(func() string { |
|
|
|
|
return fmt.Sprintf("one or more %v", t) |
|
|
|
|
}), |
|
|
|
|
decodeFn: func(d *Decoder) (locatableSlice[T], error) { |
|
|
|
|
decodeFn: func(d *Decoder) ([]T, error) { |
|
|
|
|
var vv []T |
|
|
|
|
for { |
|
|
|
|
v, err := t.decodeFn(d) |
|
|
|
@ -135,12 +131,12 @@ func oneOrMore[T locatable](t *term[T]) *term[locatableSlice[T]] { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func zeroOrMore[T locatable](t *term[T]) *term[locatableSlice[T]] { |
|
|
|
|
return &term[locatableSlice[T]]{ |
|
|
|
|
func zeroOrMore[T any](t *term[T]) *term[[]T] { |
|
|
|
|
return &term[[]T]{ |
|
|
|
|
name: stringerFn(func() string { |
|
|
|
|
return fmt.Sprintf("zero or more %v", t) |
|
|
|
|
}), |
|
|
|
|
decodeFn: func(d *Decoder) (locatableSlice[T], error) { |
|
|
|
|
decodeFn: func(d *Decoder) ([]T, error) { |
|
|
|
|
var vv []T |
|
|
|
|
for { |
|
|
|
|
v, err := t.decodeFn(d) |
|
|
|
@ -158,7 +154,7 @@ func zeroOrMore[T locatable](t *term[T]) *term[locatableSlice[T]] { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func mapTerm[Ta locatable, Tb locatable]( |
|
|
|
|
func mapTerm[Ta, Tb any]( |
|
|
|
|
name fmt.Stringer, t *term[Ta], fn func(Ta) Tb, |
|
|
|
|
) *term[Tb] { |
|
|
|
|
return &term[Tb]{ |
|
|
|
@ -204,7 +200,7 @@ func runeTerm(r rune) *term[locatableRune] { |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func locatableRunesToString(rr locatableSlice[locatableRune]) string { |
|
|
|
|
func locatableRunesToString(rr []locatableRune) string { |
|
|
|
|
str := make([]rune, len(rr)) |
|
|
|
|
for i := range rr { |
|
|
|
|
str[i] = rr[i].r |
|
|
|
@ -213,11 +209,11 @@ func locatableRunesToString(rr locatableSlice[locatableRune]) string { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func runesToStringTerm( |
|
|
|
|
t *term[locatableSlice[locatableRune]], |
|
|
|
|
t *term[[]locatableRune], |
|
|
|
|
) *term[locatableString] { |
|
|
|
|
return mapTerm( |
|
|
|
|
t, t, func(rr locatableSlice[locatableRune]) locatableString { |
|
|
|
|
return locatableString{rr.locate(), locatableRunesToString(rr)} |
|
|
|
|
t, t, func(rr []locatableRune) locatableString { |
|
|
|
|
return locatableString{rr[0].locate(), locatableRunesToString(rr)} |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
@ -234,10 +230,8 @@ var ( |
|
|
|
|
stringerStr("negative-number"), |
|
|
|
|
runeTerm('-'), |
|
|
|
|
positiveNumberTerm, |
|
|
|
|
func(neg locatableRune, posNum locatableString) (locatableString, error) { |
|
|
|
|
return locatableString{ |
|
|
|
|
neg.locate(), string(neg.r) + posNum.str, |
|
|
|
|
}, nil |
|
|
|
|
func(neg locatableRune, posNum locatableString) locatableString { |
|
|
|
|
return locatableString{neg.locate(), string(neg.r) + posNum.str} |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
@ -269,50 +263,125 @@ var ( |
|
|
|
|
stringerStr("name"), |
|
|
|
|
letterTerm, |
|
|
|
|
letterTailTerm, |
|
|
|
|
func(head locatableRune, tail locatableSlice[locatableRune]) (Value, error) { |
|
|
|
|
func(head locatableRune, tail []locatableRune) Value { |
|
|
|
|
name := string(head.r) + locatableRunesToString(tail) |
|
|
|
|
return Value{Name: &name, Location: head.locate()}, nil |
|
|
|
|
return Value{Name: &name, Location: head.locate()} |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
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 tupleState struct { |
|
|
|
|
ins []*OpenEdge |
|
|
|
|
oe *OpenEdge |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type graphState struct { |
|
|
|
|
Location // location of last place graphState was updated
|
|
|
|
|
g *Graph |
|
|
|
|
oe *OpenEdge |
|
|
|
|
g *Graph |
|
|
|
|
oe *OpenEdge |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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{} |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
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()} |
|
|
|
|
return graphState{} |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
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[*OpenEdge]) |
|
|
|
|
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) *OpenEdge { |
|
|
|
|
slices.Reverse(ts.ins) |
|
|
|
|
return graph.TupleOut(None, ts.ins...) |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
*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( |
|
|
|
|
stringerStr("graph"), |
|
|
|
|
runeTerm('{'), |
|
|
|
|
graphTailTerm, |
|
|
|
|
func(lr locatableRune, gs graphState) (Value, error) { |
|
|
|
|
func(lr locatableRune, gs graphState) Value { |
|
|
|
|
if gs.g == nil { |
|
|
|
|
gs.g = new(Graph) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return Value{Graph: gs.g, Location: lr.locate()}, nil |
|
|
|
|
return Value{Graph: gs.g, Location: lr.locate()} |
|
|
|
|
}, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
@ -321,42 +390,48 @@ var graphTerm = func() *term[Value] { |
|
|
|
|
seq( |
|
|
|
|
nameTerm, |
|
|
|
|
nameTerm, |
|
|
|
|
matchAndSkip(runeTerm('='), graphOpenEdgeTailTerm), |
|
|
|
|
func(name Value, gs graphState) (graphState, error) { |
|
|
|
|
matchAndSkip(runeTerm('='), graphOpenEdgeTerm), |
|
|
|
|
func(name Value, gs graphState) graphState { |
|
|
|
|
if gs.g == nil { |
|
|
|
|
gs.g = new(Graph) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gs.g = gs.g.AddValueIn(name, gs.oe) |
|
|
|
|
gs.oe = nil |
|
|
|
|
gs.Location = name.locate() |
|
|
|
|
return gs, nil |
|
|
|
|
return gs |
|
|
|
|
}, |
|
|
|
|
), |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
*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, |
|
|
|
|
matchAndSkip(runeTerm(';'), graphTailTerm), |
|
|
|
|
matchAndSkip(runeTerm('<'), graphOpenEdgeTailTerm), |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
*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) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gs.Location = val.locate() |
|
|
|
|
return gs, nil |
|
|
|
|
}, |
|
|
|
|
*graphOpenEdgeValueTailTerm = *firstOf( |
|
|
|
|
graphOpenEdgeTailTerm, |
|
|
|
|
matchAndSkip(runeTerm('<'), graphOpenEdgeTerm), |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
*valueTerm = *firstOf(nameTerm, numberTerm, graphTerm) |
|
|
|
|