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/decoder.go

297 lines
6.4 KiB

package gg
import (
"fmt"
"io"
"strconv"
"unicode"
. "code.betamike.com/mediocregopher/ginger/gg/grammar"
"code.betamike.com/mediocregopher/ginger/graph"
"golang.org/x/exp/slices"
)
var (
notNewline = RuneFunc(
"not-newline", func(r rune) bool { return r != '\n' },
)
comment = Prefixed(
Prefixed(Rune('*'), ZeroOrMore(notNewline)), Rune('\n'),
)
whitespace = ZeroOrMore(FirstOf(
Discard(RuneFunc("whitespace", unicode.IsSpace)),
Discard(comment),
))
)
func trimmed[T any](sym Symbol[T]) Symbol[T] {
sym = PrefixDiscarded(whitespace, sym)
sym = Suffixed(sym, whitespace)
return sym
}
func trimmedRune(r rune) Symbol[Located[rune]] {
return trimmed(Rune(r))
}
var (
digit = RuneFunc(
"digit", func(r rune) bool { return '0' <= r && r <= '9' },
)
positiveNumber = StringFromRunes(OneOrMore(digit))
negativeNumber = Reduction(
Rune('-'),
positiveNumber,
func(neg Located[rune], posNum Located[string]) Located[string] {
return Locate(neg.Location, string(neg.Value)+posNum.Value)
},
)
number = Named(
"number",
Mapping(
FirstOf(negativeNumber, positiveNumber),
func(str Located[string]) Located[Value] {
i, err := strconv.ParseInt(str.Value, 10, 64)
if err != nil {
panic(fmt.Errorf("parsing %q as int: %w", str, err))
}
return Locate(str.Location, Number(i))
},
),
)
)
var (
nameHead = FirstOf(
RuneFunc("letter", unicode.IsLetter),
RuneFunc("mark", unicode.IsMark),
Rune('!'),
)
nameTail = ZeroOrMore(FirstOf(nameHead, digit))
name = Named(
"name",
Reduction(
nameHead,
nameTail,
func(head Located[rune], tail []Located[rune]) Located[Value] {
name := make([]rune, 0, len(tail)+1)
name = append(name, head.Value)
for _, r := range tail {
name = append(name, r.Value)
}
return Locate(head.Location, Name(string(name)))
},
),
)
)
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 graphSym, value = func() (
Symbol[Located[Value]], Symbol[Located[Value]],
) {
type tupleState struct {
ins []*OpenEdge
oe *OpenEdge
}
type graphState struct {
g *Graph
oe *OpenEdge
}
var (
tupleEnd = Mapping(
trimmedRune(')'),
func(Located[rune]) tupleState {
// if ')', then map that to an empty state. This acts as a
// sentinel value to indicate "end of tuple".
return tupleState{}
},
)
graphEnd = Mapping(
trimmedRune('}'),
func(Located[rune]) 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.
value = new(SymbolPtr[Located[Value]])
tuple = new(SymbolPtr[*OpenEdge])
tupleTail = new(SymbolPtr[tupleState])
tupleOpenEdge = new(SymbolPtr[tupleState])
tupleOpenEdgeTail = new(SymbolPtr[tupleState])
tupleOpenEdgeValueTail = new(SymbolPtr[tupleState])
graphSym = new(SymbolPtr[Located[Value]])
graphTail = new(SymbolPtr[graphState])
graphOpenEdge = new(SymbolPtr[graphState])
graphOpenEdgeTail = new(SymbolPtr[graphState])
graphOpenEdgeValueTail = new(SymbolPtr[graphState])
)
tuple.Symbol = Named(
"tuple",
Reduction[Located[rune], tupleState, *OpenEdge](
trimmedRune('('),
tupleTail,
func(_ Located[rune], ts tupleState) *OpenEdge {
slices.Reverse(ts.ins)
return graph.TupleOut(None, ts.ins...)
},
),
)
tupleTail.Symbol = FirstOf(
tupleEnd,
Mapping[tupleState, tupleState](
tupleOpenEdge,
func(ts tupleState) tupleState {
ts.ins = append(ts.ins, ts.oe)
ts.oe = nil
return ts
},
),
)
tupleOpenEdge.Symbol = FirstOf(
Reduction[Located[Value], tupleState, tupleState](
value,
tupleOpenEdgeValueTail,
func(val Located[Value], ts tupleState) tupleState {
ts.oe = openEdgeIntoValue(val.Value, ts.oe)
return ts
},
),
Reduction[*OpenEdge, tupleState, tupleState](
tuple,
tupleOpenEdgeTail,
func(oe *OpenEdge, ts tupleState) tupleState {
ts.oe = oe
return ts
},
),
)
tupleOpenEdgeTail.Symbol = FirstOf(
tupleEnd,
Prefixed[Located[rune], tupleState](trimmedRune(','), tupleTail),
)
tupleOpenEdgeValueTail.Symbol = FirstOf[tupleState](
tupleOpenEdgeTail,
Prefixed[Located[rune], tupleState](trimmedRune('<'), tupleOpenEdge),
)
graphSym.Symbol = Named(
"graph",
Reduction[Located[rune], graphState, Located[Value]](
trimmedRune('{'),
graphTail,
func(r Located[rune], gs graphState) Located[Value] {
if gs.g == nil {
gs.g = new(Graph)
}
return Locate(r.Location, Value{Graph: gs.g})
},
),
)
graphTail.Symbol = FirstOf(
graphEnd,
Reduction(
name,
Prefixed[Located[rune], graphState](
trimmedRune('='), graphOpenEdge,
),
func(name Located[Value], gs graphState) graphState {
if gs.g == nil {
gs.g = new(Graph)
}
gs.g = gs.g.AddValueIn(name.Value, gs.oe)
gs.oe = nil
return gs
},
),
)
graphOpenEdge.Symbol = FirstOf(
Reduction[Located[Value], graphState, graphState](
value,
graphOpenEdgeValueTail,
func(val Located[Value], gs graphState) graphState {
gs.oe = openEdgeIntoValue(val.Value, gs.oe)
return gs
},
),
Reduction[*OpenEdge, graphState, graphState](
tuple,
graphOpenEdgeTail,
func(oe *OpenEdge, gs graphState) graphState {
gs.oe = oe
return gs
},
),
)
graphOpenEdgeTail.Symbol = FirstOf(
graphEnd,
Prefixed[Located[rune], graphState](trimmedRune(';'), graphTail),
)
graphOpenEdgeValueTail.Symbol = FirstOf[graphState](
graphOpenEdgeTail,
Prefixed[Located[rune], graphState](trimmedRune('<'), graphOpenEdge),
)
value.Symbol = trimmed(FirstOf[Located[Value]](name, number, graphSym))
return graphSym, value
}()
// Decoder reads Values off of an io.Reader, or return io.EOF.
type Decoder interface {
Next() (Located[Value], error)
}
type decoder struct {
r Reader
}
// NewDecoder returns a Decoder which reads off the given io.Reader. The
// io.Reader should not be read from after this call.
func NewDecoder(r io.Reader) Decoder {
return &decoder{r: NewReader(r)}
}
func (d *decoder) Next() (Located[Value], error) {
return value.Decode(d.r)
}