Further simplifications to grammar
This commit is contained in:
parent
9139d4830d
commit
3ef69920c7
119
gg/decoder.go
119
gg/decoder.go
@ -12,13 +12,9 @@ import (
|
||||
"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' },
|
||||
"not-newline", func(r rune) bool { return r != '\n' },
|
||||
)
|
||||
|
||||
comment = Prefixed(
|
||||
@ -26,7 +22,7 @@ var (
|
||||
)
|
||||
|
||||
whitespace = ZeroOrMore(FirstOf(
|
||||
Discard(RuneFunc(s("whitespace"), unicode.IsSpace)),
|
||||
Discard(RuneFunc("whitespace", unicode.IsSpace)),
|
||||
Discard(comment),
|
||||
))
|
||||
)
|
||||
@ -43,13 +39,12 @@ func trimmedRune(r rune) Symbol[Located[rune]] {
|
||||
|
||||
var (
|
||||
digit = RuneFunc(
|
||||
s("digit"), func(r rune) bool { return '0' <= r && r <= '9' },
|
||||
"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] {
|
||||
@ -57,23 +52,25 @@ var (
|
||||
},
|
||||
)
|
||||
|
||||
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))
|
||||
}
|
||||
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))
|
||||
},
|
||||
return Locate(str.Location, Number(i))
|
||||
},
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
var (
|
||||
letter = RuneFunc(
|
||||
s("letter"),
|
||||
"letter",
|
||||
func(r rune) bool {
|
||||
return unicode.In(r, unicode.Letter, unicode.Mark)
|
||||
},
|
||||
@ -81,18 +78,20 @@ var (
|
||||
|
||||
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)))
|
||||
},
|
||||
name = Named(
|
||||
"name",
|
||||
Reduction(
|
||||
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)))
|
||||
},
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@ -122,10 +121,8 @@ var graphSym, value = func() (
|
||||
}
|
||||
|
||||
var (
|
||||
rightParen = trimmedRune(')')
|
||||
tupleEnd = Mapping(
|
||||
rightParen,
|
||||
rightParen,
|
||||
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".
|
||||
@ -133,10 +130,8 @@ var graphSym, value = func() (
|
||||
},
|
||||
)
|
||||
|
||||
rightCurlyBrace = trimmedRune('}')
|
||||
graphEnd = Mapping(
|
||||
rightCurlyBrace,
|
||||
rightCurlyBrace,
|
||||
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".
|
||||
@ -163,20 +158,21 @@ var graphSym, value = func() (
|
||||
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...)
|
||||
},
|
||||
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,
|
||||
tupleOpenEdge,
|
||||
func(ts tupleState) tupleState {
|
||||
ts.ins = append(ts.ins, ts.oe)
|
||||
@ -188,7 +184,6 @@ var graphSym, value = func() (
|
||||
|
||||
tupleOpenEdge.Symbol = FirstOf(
|
||||
Reduction[Located[Value], tupleState, tupleState](
|
||||
value,
|
||||
value,
|
||||
tupleOpenEdgeValueTail,
|
||||
func(val Located[Value], ts tupleState) tupleState {
|
||||
@ -197,7 +192,6 @@ var graphSym, value = func() (
|
||||
},
|
||||
),
|
||||
Reduction[*OpenEdge, tupleState, tupleState](
|
||||
tuple,
|
||||
tuple,
|
||||
tupleOpenEdgeTail,
|
||||
func(oe *OpenEdge, ts tupleState) tupleState {
|
||||
@ -217,23 +211,24 @@ var graphSym, value = func() (
|
||||
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)
|
||||
}
|
||||
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})
|
||||
},
|
||||
return Locate(r.Location, Value{Graph: gs.g})
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
graphTail.Symbol = FirstOf(
|
||||
graphEnd,
|
||||
Reduction(
|
||||
name,
|
||||
name,
|
||||
Prefixed[Located[rune], graphState](
|
||||
trimmedRune('='), graphOpenEdge,
|
||||
@ -253,7 +248,6 @@ var graphSym, value = func() (
|
||||
|
||||
graphOpenEdge.Symbol = FirstOf(
|
||||
Reduction[Located[Value], graphState, graphState](
|
||||
value,
|
||||
value,
|
||||
graphOpenEdgeValueTail,
|
||||
func(val Located[Value], gs graphState) graphState {
|
||||
@ -262,7 +256,6 @@ var graphSym, value = func() (
|
||||
},
|
||||
),
|
||||
Reduction[*OpenEdge, graphState, graphState](
|
||||
tuple,
|
||||
tuple,
|
||||
graphOpenEdgeTail,
|
||||
func(oe *OpenEdge, gs graphState) graphState {
|
||||
|
@ -41,7 +41,7 @@ func TestDecoder(t *testing.T) {
|
||||
}
|
||||
|
||||
expNum := func(row, col int, n int64) Located[Value] {
|
||||
return Located[Value]{Location{row, col}, Number(n)}
|
||||
return Locate(Location{Row: row, Col: col}, Number(n))
|
||||
}
|
||||
|
||||
runTests(t, "number", number, []test{
|
||||
@ -53,11 +53,11 @@ func TestDecoder(t *testing.T) {
|
||||
})
|
||||
|
||||
expName := func(row, col int, name string) Located[Value] {
|
||||
return Located[Value]{Location{row, col}, Name(name)}
|
||||
return Locate(Location{Row: row, Col: col}, Name(name))
|
||||
}
|
||||
|
||||
expGraph := func(row, col int, g *Graph) Located[Value] {
|
||||
return Located[Value]{Location{row, col}, Value{Graph: g}}
|
||||
return Locate(Location{Row: row, Col: col}, Value{Graph: g})
|
||||
}
|
||||
|
||||
runTests(t, "name", name, []test{
|
||||
|
@ -21,9 +21,9 @@ package:
|
||||
<number> ::= <negative-number> | <positive-number>
|
||||
|
||||
<list-el-tail> ::= <element> <list-tail>
|
||||
<list-tail> ::= ")" | "," <list-el-tail>
|
||||
<list-head> ::= ")" | <list-el-tail>
|
||||
<list> ::= "(" <list-head>
|
||||
<list-tail> ::= ")" | "," <list-el-tail>
|
||||
<list-head> ::= ")" | <list-el-tail>
|
||||
<list> ::= "(" <list-head>
|
||||
|
||||
<element> ::= <number> | <list>
|
||||
```
|
||||
@ -48,19 +48,14 @@ func (e Element) String() string {
|
||||
return fmt.Sprint(e.Number)
|
||||
}
|
||||
|
||||
func s(str string) fmt.Stringer {
|
||||
return grammar.Stringer{S: str}
|
||||
}
|
||||
|
||||
var (
|
||||
digit = grammar.RuneFunc(
|
||||
s("digit"), func(r rune) bool { return '0' <= r && r <= '9' },
|
||||
"digit", func(r rune) bool { return '0' <= r && r <= '9' },
|
||||
)
|
||||
|
||||
positiveNumber = grammar.StringFromRunes(grammar.OneOrMore(digit))
|
||||
|
||||
negativeNumber = grammar.Reduction(
|
||||
s("negative-number"),
|
||||
grammar.Rune('-'),
|
||||
positiveNumber,
|
||||
func(
|
||||
@ -70,16 +65,18 @@ var (
|
||||
},
|
||||
)
|
||||
|
||||
number = grammar.Mapping(
|
||||
s("number"),
|
||||
grammar.FirstOf(negativeNumber, positiveNumber),
|
||||
func(str grammar.Located[string]) Element {
|
||||
i, err := strconv.ParseInt(str.Value, 10, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("parsing %q as int: %w", str, err))
|
||||
}
|
||||
return Element{Number: i}
|
||||
},
|
||||
number = grammar.Named(
|
||||
"number",
|
||||
grammar.Mapping(
|
||||
grammar.FirstOf(negativeNumber, positiveNumber),
|
||||
func(str grammar.Located[string]) Element {
|
||||
i, err := strconv.ParseInt(str.Value, 10, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("parsing %q as int: %w", str, err))
|
||||
}
|
||||
return Element{Number: i}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
// Because the list/element definitions are recursive it requires using
|
||||
@ -95,19 +92,13 @@ var (
|
||||
element = new(grammar.SymbolPtr[Element])
|
||||
|
||||
// Right parenthesis indicates the end of a list, at which point we
|
||||
// can initialize the state which gets returned down the stack. We
|
||||
// pass rightParen as the Stringer because we want that to still be
|
||||
// the string identifier for this branch of the symbol, for error
|
||||
// purposes.
|
||||
rightParen = grammar.Rune(')')
|
||||
listTerm = grammar.Mapping(
|
||||
rightParen,
|
||||
rightParen,
|
||||
// can initialize the state which gets returned down the stack.
|
||||
listTerm = grammar.Mapping(
|
||||
grammar.Rune(')'),
|
||||
func(grammar.Located[rune]) listState { return listState{} },
|
||||
)
|
||||
|
||||
listElTail = grammar.Reduction[Element, listState, listState](
|
||||
element, // Stringer
|
||||
element,
|
||||
listTail,
|
||||
func(el Element, ls listState) listState {
|
||||
@ -124,16 +115,16 @@ var (
|
||||
grammar.Prefixed(grammar.Rune(','), listElTail),
|
||||
)
|
||||
|
||||
list.Symbol = grammar.Reduction[
|
||||
grammar.Located[rune], listState, Element,
|
||||
](
|
||||
s("list"),
|
||||
grammar.Rune('('),
|
||||
listHead,
|
||||
func(_ grammar.Located[rune], ls listState) Element {
|
||||
slices.Reverse(ls)
|
||||
return Element{List: []Element(ls)}
|
||||
},
|
||||
list.Symbol = grammar.Named(
|
||||
"list",
|
||||
grammar.Reduction[grammar.Located[rune], listState, Element](
|
||||
grammar.Rune('('),
|
||||
listHead,
|
||||
func(_ grammar.Located[rune], ls listState) Element {
|
||||
slices.Reverse(ls)
|
||||
return Element{List: []Element(ls)}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
element.Symbol = grammar.FirstOf[Element](number, list)
|
||||
|
@ -47,11 +47,24 @@ type SymbolPtr[T any] struct {
|
||||
Symbol[T]
|
||||
}
|
||||
|
||||
func named[T any](stringer fmt.Stringer, sym Symbol[T]) Symbol[T] {
|
||||
return &symbol[T]{
|
||||
stringer,
|
||||
sym.Decode,
|
||||
}
|
||||
}
|
||||
|
||||
// Named wraps the given Symbol such that its String method returns the given
|
||||
// name.
|
||||
func Named[T any](name string, sym Symbol[T]) Symbol[T] {
|
||||
return named(Stringer{S: name}, sym)
|
||||
}
|
||||
|
||||
// RuneFunc matches and produces any rune for which the given function returns
|
||||
// true.
|
||||
func RuneFunc(stringer fmt.Stringer, fn func(rune) bool) Symbol[Located[rune]] {
|
||||
func RuneFunc(name string, fn func(rune) bool) Symbol[Located[rune]] {
|
||||
return &symbol[Located[rune]]{
|
||||
stringer,
|
||||
Stringer{S: name},
|
||||
func(rr Reader) (Located[rune], error) {
|
||||
var zero Located[rune]
|
||||
|
||||
@ -75,7 +88,7 @@ func RuneFunc(stringer fmt.Stringer, fn func(rune) bool) Symbol[Located[rune]] {
|
||||
// Rune matches and produces the given rune.
|
||||
func Rune(r rune) Symbol[Located[rune]] {
|
||||
return RuneFunc(
|
||||
Stringer{S: fmt.Sprintf("'%c'", r)},
|
||||
fmt.Sprintf("'%c'", r),
|
||||
func(r2 rune) bool { return r == r2 },
|
||||
)
|
||||
}
|
||||
@ -84,7 +97,7 @@ func Rune(r rune) Symbol[Located[rune]] {
|
||||
// given Symbol. The slice must not be empty. StringFromRunes does not match if
|
||||
// the given Symbol does not match.
|
||||
func StringFromRunes(sym Symbol[[]Located[rune]]) Symbol[Located[string]] {
|
||||
return Mapping(sym, sym, func(runes []Located[rune]) Located[string] {
|
||||
return Mapping(sym, func(runes []Located[rune]) Located[string] {
|
||||
if len(runes) == 0 {
|
||||
panic("StringFromRunes used on empty set of runes")
|
||||
}
|
||||
@ -101,10 +114,10 @@ func StringFromRunes(sym Symbol[[]Located[rune]]) Symbol[Located[string]] {
|
||||
// Symbol and passing it through the given mapping function. If the given Symbol
|
||||
// doesn't match then neither does Map.
|
||||
func Mapping[Ta, Tb any](
|
||||
stringer fmt.Stringer, sym Symbol[Ta], fn func(Ta) Tb,
|
||||
sym Symbol[Ta], fn func(Ta) Tb,
|
||||
) Symbol[Tb] {
|
||||
return &symbol[Tb]{
|
||||
stringer,
|
||||
sym,
|
||||
func(rr Reader) (Tb, error) {
|
||||
var zero Tb
|
||||
va, err := sym.Decode(rr)
|
||||
@ -214,13 +227,12 @@ func FirstOf[T any](syms ...Symbol[T]) Symbol[T] {
|
||||
// If symA does not match then Reduction does not match. If symA matches but
|
||||
// symB does not then also match then Reduction produces a LocatedError.
|
||||
func Reduction[Ta, Tb, Tc any](
|
||||
stringer fmt.Stringer,
|
||||
symA Symbol[Ta],
|
||||
symB Symbol[Tb],
|
||||
fn func(Ta, Tb) Tc,
|
||||
) Symbol[Tc] {
|
||||
return &symbol[Tc]{
|
||||
stringer,
|
||||
symA,
|
||||
func(rr Reader) (Tc, error) {
|
||||
var zero Tc
|
||||
|
||||
@ -248,9 +260,9 @@ func Reduction[Ta, Tb, Tc any](
|
||||
// If prefixSym does not match then Prefixed does not match. If prefixSym
|
||||
// matches but sym does not also match then Prefixed produces a LocatedError.
|
||||
func Prefixed[Ta, Tb any](prefixSym Symbol[Ta], sym Symbol[Tb]) Symbol[Tb] {
|
||||
return Reduction(prefixSym, prefixSym, sym, func(_ Ta, b Tb) Tb {
|
||||
return named(prefixSym, Reduction(prefixSym, sym, func(_ Ta, b Tb) Tb {
|
||||
return b
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
// PrefixDiscarded is similar to Prefixed, except that if sym does not match
|
||||
@ -282,13 +294,13 @@ func PrefixDiscarded[Ta, Tb any](prefixSym Symbol[Ta], sym Symbol[Tb]) Symbol[Tb
|
||||
// If sym does not match then Suffixed does not match. If sym matches but
|
||||
// suffixSym does not also match then Suffixed produces a LocatedError.
|
||||
func Suffixed[Ta, Tb any](sym Symbol[Ta], suffixSym Symbol[Tb]) Symbol[Ta] {
|
||||
return Reduction(sym, sym, suffixSym, func(a Ta, _ Tb) Ta {
|
||||
return named(sym, Reduction(sym, suffixSym, func(a Ta, _ Tb) Ta {
|
||||
return a
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
// Discard matches if the given Symbol does, but discards the value it produces,
|
||||
// producing an empty value instead.
|
||||
func Discard[T any](sym Symbol[T]) Symbol[struct{}] {
|
||||
return Mapping(sym, sym, func(T) struct{} { return struct{}{} })
|
||||
return Mapping(sym, func(T) struct{} { return struct{}{} })
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user