309 lines
6.7 KiB
Go
309 lines
6.7 KiB
Go
package gg
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"strconv"
|
|
"unicode"
|
|
|
|
. "code.betamike.com/mediocregopher/ginger/gg/grammar"
|
|
"code.betamike.com/mediocregopher/ginger/graph"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
func s(str string) fmt.Stringer {
|
|
return Stringer{S: str}
|
|
}
|
|
|
|
var (
|
|
notNewline = RuneFunc(
|
|
s("not-newline"), func(r rune) bool { return r != '\n' },
|
|
)
|
|
|
|
comment = Prefixed(
|
|
Prefixed(Rune('*'), ZeroOrMore(notNewline)), Rune('\n'),
|
|
)
|
|
|
|
whitespace = ZeroOrMore(FirstOf(
|
|
Discard(RuneFunc(s("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(
|
|
s("digit"), func(r rune) bool { return '0' <= r && r <= '9' },
|
|
)
|
|
|
|
positiveNumber = StringFromRunes(OneOrMore(digit))
|
|
|
|
negativeNumber = Reduction(
|
|
s("negative-number"),
|
|
Rune('-'),
|
|
positiveNumber,
|
|
func(neg Located[rune], posNum Located[string]) Located[string] {
|
|
return Locate(neg.Location, string(neg.Value)+posNum.Value)
|
|
},
|
|
)
|
|
|
|
number = Mapping(
|
|
s("number"),
|
|
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 (
|
|
letter = RuneFunc(
|
|
s("letter"),
|
|
func(r rune) bool {
|
|
return unicode.In(r, unicode.Letter, unicode.Mark)
|
|
},
|
|
)
|
|
|
|
nameTail = ZeroOrMore(FirstOf(letter, digit))
|
|
|
|
name = Reduction(
|
|
s("name"),
|
|
letter,
|
|
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 (
|
|
rightParen = trimmedRune(')')
|
|
tupleEnd = Mapping(
|
|
rightParen,
|
|
rightParen,
|
|
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{}
|
|
},
|
|
)
|
|
|
|
rightCurlyBrace = trimmedRune('}')
|
|
graphEnd = Mapping(
|
|
rightCurlyBrace,
|
|
rightCurlyBrace,
|
|
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 = Reduction[Located[rune], tupleState, *OpenEdge](
|
|
s("tuple"),
|
|
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,
|
|
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,
|
|
value,
|
|
tupleOpenEdgeValueTail,
|
|
func(val Located[Value], ts tupleState) tupleState {
|
|
ts.oe = openEdgeIntoValue(val.Value, ts.oe)
|
|
return ts
|
|
},
|
|
),
|
|
Reduction[*OpenEdge, tupleState, tupleState](
|
|
tuple,
|
|
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 = Reduction[Located[rune], graphState, Located[Value]](
|
|
s("graph"),
|
|
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,
|
|
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
|
|
},
|
|
),
|
|
)
|
|
log.Printf("populated graphTail with %v", graphTail)
|
|
|
|
graphOpenEdge.Symbol = FirstOf(
|
|
Reduction[Located[Value], graphState, graphState](
|
|
value,
|
|
value,
|
|
graphOpenEdgeValueTail,
|
|
func(val Located[Value], gs graphState) graphState {
|
|
gs.oe = openEdgeIntoValue(val.Value, gs.oe)
|
|
return gs
|
|
},
|
|
),
|
|
Reduction[*OpenEdge, graphState, graphState](
|
|
tuple,
|
|
tuple,
|
|
graphOpenEdgeTail,
|
|
func(oe *OpenEdge, gs graphState) graphState {
|
|
gs.oe = oe
|
|
return gs
|
|
},
|
|
),
|
|
)
|
|
|
|
graphOpenEdgeTail.Symbol = FirstOf(
|
|
graphEnd,
|
|
Prefixed[Located[rune], graphState](trimmedRune(';'), graphTail),
|
|
)
|
|
log.Printf("populated graphOpenEdgeTail with %v", graphOpenEdgeTail)
|
|
|
|
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)
|
|
}
|