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) }