You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ginger/gg/term.go

508 lines
10 KiB

package gg
import (
"errors"
"fmt"
"io"
"strconv"
"strings"
"unicode"
"github.com/mediocregopher/ginger/graph"
"golang.org/x/exp/slices"
)
var (
errNoMatch = errors.New("not found")
)
type stringerFn func() string
func (fn stringerFn) String() string {
return fn()
}
type stringerStr string
func (str stringerStr) String() string {
return string(str)
}
type term[T any] struct {
name fmt.Stringer
decodeFn func(d *Decoder) (T, error)
}
func (t term[T]) String() string {
return t.name.String()
}
func firstOf[T any](terms ...*term[T]) *term[T] {
if len(terms) < 2 {
panic("firstOfTerms requires at least 2 terms")
}
return &term[T]{
name: stringerFn(func() string {
descrs := make([]string, len(terms))
for i := range terms {
descrs[i] = terms[i].String()
}
return strings.Join(descrs, " or ")
}),
decodeFn: func(d *Decoder) (T, error) {
var zero T
for _, t := range terms {
v, err := t.decodeFn(d)
if errors.Is(err, errNoMatch) {
continue
} else if err != nil {
return zero, err
}
return v, nil
}
return zero, errNoMatch
},
}
}
func seq[Ta, Tb, Tc any](
name fmt.Stringer,
termA *term[Ta],
termB *term[Tb],
fn func(Ta, Tb) Tc,
) *term[Tc] {
return &term[Tc]{
name: name,
decodeFn: func(d *Decoder) (Tc, error) {
var zero Tc
va, err := termA.decodeFn(d)
if err != nil {
return zero, err
}
vb, err := termB.decodeFn(d)
if errors.Is(err, errNoMatch) {
return zero, d.nextLoc().errf("expected %v", termB)
} else if err != nil {
return zero, err
}
return fn(va, vb), nil
},
}
}
func prefixed[Ta, Tb any](termA *term[Ta], termB *term[Tb]) *term[Tb] {
return seq(termA, termA, termB, func(_ Ta, b Tb) Tb {
return b
})
}
func prefixIgnored[Ta, Tb any](termA *term[Ta], termB *term[Tb]) *term[Tb] {
return &term[Tb]{
name: termB,
decodeFn: func(d *Decoder) (Tb, error) {
var zero Tb
if _, err := termA.decodeFn(d); err != nil {
return zero, err
}
return termB.decodeFn(d)
},
}
}
func suffixIgnored[Ta, Tb any](
termA *term[Ta], termB *term[Tb],
) *term[Ta] {
return seq(termA, termA, termB, func(a Ta, _ Tb) Ta {
return a
})
}
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) ([]T, error) {
var vv []T
for {
v, err := t.decodeFn(d)
if errors.Is(err, errNoMatch) {
break
} else if err != nil {
return nil, err
}
vv = append(vv, v)
}
if len(vv) == 0 {
return nil, errNoMatch
}
return vv, nil
},
}
}
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) ([]T, error) {
var vv []T
for {
v, err := t.decodeFn(d)
if errors.Is(err, errNoMatch) {
break
} else if err != nil {
return nil, err
}
vv = append(vv, v)
}
return vv, nil
},
}
}
func mapTerm[Ta, Tb any](
name fmt.Stringer, t *term[Ta], fn func(Ta) Tb,
) *term[Tb] {
return &term[Tb]{
name: name,
decodeFn: func(d *Decoder) (Tb, error) {
var zero Tb
va, err := t.decodeFn(d)
if err != nil {
return zero, err
}
return fn(va), nil
},
}
}
func runePredTerm(
name fmt.Stringer, pred func(rune) bool,
) *term[locatableRune] {
return &term[locatableRune]{
name: name,
decodeFn: func(d *Decoder) (locatableRune, error) {
lr, err := d.readRune()
if errors.Is(err, io.EOF) {
return locatableRune{}, errNoMatch
} else if err != nil {
return locatableRune{}, err
}
if !pred(lr.r) {
d.unreadRune(lr)
return locatableRune{}, errNoMatch
}
return lr, nil
},
}
}
func runeTerm(r rune) *term[locatableRune] {
return runePredTerm(
stringerStr(fmt.Sprintf("'%c'", r)),
func(r2 rune) bool { return r2 == r },
)
}
func locatableRunesToString(rr []locatableRune) string {
str := make([]rune, len(rr))
for i := range rr {
str[i] = rr[i].r
}
return string(str)
}
func runesToStringTerm(
t *term[[]locatableRune],
) *term[locatableString] {
return mapTerm(
t, t, func(rr []locatableRune) locatableString {
return locatableString{rr[0].locate(), locatableRunesToString(rr)}
},
)
}
func discard[T any](t *term[T]) *term[struct{}] {
return mapTerm(t, t, func(_ T) struct{} { return struct{}{} })
}
var (
notNewlineTerm = runePredTerm(
stringerStr("not-newline"),
func(r rune) bool { return r != '\n' },
)
commentTerm = prefixed(
prefixed(runeTerm('*'), zeroOrMore(notNewlineTerm)),
runeTerm('\n'),
)
whitespaceTerm = zeroOrMore(firstOf(
discard(runePredTerm(stringerStr("whitespace"), unicode.IsSpace)),
discard(commentTerm),
))
)
func trimmedTerm[T any](t *term[T]) *term[T] {
t = prefixIgnored(whitespaceTerm, t)
t = suffixIgnored(t, whitespaceTerm)
return t
}
func trimmedRuneTerm(r rune) *term[locatableRune] {
return trimmedTerm(runeTerm(r))
}
var (
digitTerm = runePredTerm(
stringerStr("digit"),
func(r rune) bool { return '0' <= r && r <= '9' },
)
positiveNumberTerm = runesToStringTerm(oneOrMore(digitTerm))
negativeNumberTerm = seq(
stringerStr("negative-number"),
runeTerm('-'),
positiveNumberTerm,
func(neg locatableRune, posNum locatableString) locatableString {
return locatableString{neg.locate(), string(neg.r) + posNum.str}
},
)
numberTerm = mapTerm(
stringerStr("number"),
firstOf(negativeNumberTerm, positiveNumberTerm),
func(str locatableString) Value {
i, err := strconv.ParseInt(str.str, 10, 64)
if err != nil {
panic(fmt.Errorf("parsing %q as int: %w", str, err))
}
return Value{Number: &i, Location: str.locate()}
},
)
)
var (
letterTerm = runePredTerm(
stringerStr("letter"),
func(r rune) bool {
return unicode.In(r, unicode.Letter, unicode.Mark)
},
)
letterTailTerm = zeroOrMore(firstOf(letterTerm, digitTerm))
nameTerm = seq(
stringerStr("name"),
letterTerm,
letterTailTerm,
func(head locatableRune, tail []locatableRune) Value {
name := string(head.r) + locatableRunesToString(tail)
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, valueTerm = func() (*term[Value], *term[Value]) {
type tupleState struct {
ins []*OpenEdge
oe *OpenEdge
}
type graphState struct {
g *Graph
oe *OpenEdge
}
var (
rightParenthesis = trimmedRuneTerm(')')
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 = trimmedRuneTerm('}')
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 (
// 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])
tupleOpenEdgeValueTailTerm = 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"),
trimmedRuneTerm('('),
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 = *firstOf(
seq(
valueTerm,
valueTerm,
tupleOpenEdgeValueTailTerm,
func(val Value, ts tupleState) tupleState {
ts.oe = openEdgeIntoValue(val, ts.oe)
return ts
},
),
seq(
tupleTerm,
tupleTerm,
tupleOpenEdgeTailTerm,
func(oe *OpenEdge, ts tupleState) tupleState {
ts.oe = oe
return ts
},
),
)
*tupleOpenEdgeTailTerm = *firstOf(
tupleEndTerm,
prefixed(trimmedRuneTerm(','), tupleTailTerm),
)
*tupleOpenEdgeValueTailTerm = *firstOf(
tupleOpenEdgeTailTerm,
prefixed(trimmedRuneTerm('<'), tupleOpenEdgeTerm),
)
*graphTerm = *seq(
stringerStr("graph"),
trimmedRuneTerm('{'),
graphTailTerm,
func(lr locatableRune, gs graphState) Value {
if gs.g == nil {
gs.g = new(Graph)
}
return Value{Graph: gs.g, Location: lr.locate()}
},
)
*graphTailTerm = *firstOf(
graphEndTerm,
seq(
nameTerm,
nameTerm,
prefixed(trimmedRuneTerm('='), 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
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,
prefixed(trimmedRuneTerm(';'), graphTailTerm),
)
*graphOpenEdgeValueTailTerm = *firstOf(
graphOpenEdgeTailTerm,
prefixed(trimmedRuneTerm('<'), graphOpenEdgeTerm),
)
*valueTerm = *firstOf(nameTerm, numberTerm, graphTerm)
return graphTerm, valueTerm
}()
var topLevelTerm = trimmedTerm(valueTerm)