Documentation ¶
Overview ¶
Package combinator defines generator functions for creating parser combinators
Index ¶
- func Any(parsers ...parsley.Parser) parser.Func
- func Choice(parsers ...parsley.Parser) parser.Func
- func Memoize(p parsley.Parser) parser.Func
- func Optional(p parsley.Parser) parser.Func
- func Single(p parsley.Parser) parser.Func
- func SuppressError(p parsley.Parser) parser.Func
- type SeqResultHandler
- type SeqResultHandlerFunc
- type Sequence
- func Many(p parsley.Parser) *Sequence
- func Many1(p parsley.Parser) *Sequence
- func Sentence(p parsley.Parser) *Sequence
- func SepBy(valueP parsley.Parser, sepP parsley.Parser) *Sequence
- func SepBy1(valueP parsley.Parser, sepP parsley.Parser) *Sequence
- func Seq(token string, parserLookUp func(int) parsley.Parser, lenCheck func(int) bool) *Sequence
- func SeqFirstOrAll(parsers ...parsley.Parser) *Sequence
- func SeqOf(parsers ...parsley.Parser) *Sequence
- func SeqTry(parsers ...parsley.Parser) *Sequence
- func (s *Sequence) Bind(interpreter parsley.Interpreter) *Sequence
- func (s *Sequence) HandleResult(resultHandler SeqResultHandler) *Sequence
- func (s *Sequence) Name(name string) *Sequence
- func (s *Sequence) Parse(ctx *parsley.Context, leftRecCtx data.IntMap, pos parsley.Pos) (parsley.Node, data.IntSet, parsley.Error)
- func (s *Sequence) Token(token string) *Sequence
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Any ¶
Any tries all the given parsers independently and merges the results
Example ¶
Let's define a parser which accepts integer or float numbers. The parser would return with all matches, so both 1 and 1.23.
package main import ( "fmt" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/data" "github.com/conflowio/parsley/parser" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/parsley/parsleyfakes" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) // Let's define a parser which accepts integer or float numbers. // The parser would return with all matches, so both 1 and 1.23. func main() { p := combinator.Any( terminal.Integer("integer"), terminal.Float("number"), ) f := text.NewFile("example.file", []byte("1.23")) fs := parsley.NewFileSet(f) r := text.NewReader(f) ctx := parsley.NewContext(fs, r) value, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value, value) } var _ = Describe("Any", func() { var ( p parser.Func r *parsleyfakes.FakeReader parsers []parsley.Parser p1, p2 *parsleyfakes.FakeParser leftRecCtx data.IntMap pos parsley.Pos cp, p1CP, p2CP data.IntSet res, p1Res, p2Res parsley.Node parserErr, p1Err, p2Err parsley.Error n1, n2 *parsleyfakes.FakeNode ctx *parsley.Context ) BeforeEach(func() { r = &parsleyfakes.FakeReader{} ctx = parsley.NewContext(parsley.NewFileSet(), r) p1 = &parsleyfakes.FakeParser{} p2 = &parsleyfakes.FakeParser{} leftRecCtx = data.EmptyIntMap parsers = []parsley.Parser{p1, p2} pos = parsley.Pos(1) n1 = &parsleyfakes.FakeNode{} n1.TokenReturns("n1") n2 = &parsleyfakes.FakeNode{} n2.TokenReturns("n2") p1CP = data.EmptyIntSet p2CP = data.EmptyIntSet p1Res = nil p2Res = nil p1Err = nil p2Err = nil n1 = nil n2 = nil }) JustBeforeEach(func() { p1.ParseReturnsOnCall(0, p1Res, p1CP, p1Err) p2.ParseReturnsOnCall(0, p2Res, p2CP, p2Err) p = combinator.Any(parsers...) res, cp, parserErr = p.Parse(ctx, leftRecCtx, pos) }) Context("when no parsers are given", func() { It("should panic", func() { Expect(func() { combinator.Any() }).To(Panic()) }) }) Context("when there is only one parser", func() { Context("if it returns a result", func() { BeforeEach(func() { parsers = []parsley.Parser{p1} p1CP = data.NewIntSet(1) p1Res = n1 p1Err = nil }) It("should return the result of that parser", func() { Expect(cp).To(Equal(p1CP)) Expect(res).To(Equal(p1Res)) Expect(parserErr).ToNot(HaveOccurred()) Expect(p1.ParseCallCount()).To(Equal(1)) passedCtx, passedLeftRecCtx, passedPos := p1.ParseArgsForCall(0) Expect(passedCtx).To(BeEquivalentTo(ctx)) Expect(passedLeftRecCtx).To(BeEquivalentTo(leftRecCtx)) Expect(passedPos).To(Equal(pos)) }) }) Context("if it returns an error", func() { BeforeEach(func() { parsers = []parsley.Parser{p1} p1CP = data.NewIntSet(1) p1Res = nil p1Err = parsley.NewErrorf(parsley.Pos(2), "some error") }) It("should return the error of that parser", func() { Expect(cp).To(Equal(p1CP)) Expect(res).To(BeNil()) Expect(parserErr).To(Equal(p1Err)) Expect(p1.ParseCallCount()).To(Equal(1)) passedCtx, passedLeftRecCtx, passedPos := p1.ParseArgsForCall(0) Expect(passedCtx).To(BeEquivalentTo(ctx)) Expect(passedLeftRecCtx).To(BeEquivalentTo(leftRecCtx)) Expect(passedPos).To(Equal(pos)) }) }) }) Context("with multiple parsers", func() { BeforeEach(func() { parsers = []parsley.Parser{p1, p2} p1CP = data.NewIntSet(1) p2CP = data.NewIntSet(2) }) It("should call all parsers", func() { Expect(p1.ParseCallCount()).To(Equal(1)) passedCtx, passedLeftRecCtx, passedPos := p1.ParseArgsForCall(0) Expect(passedCtx).To(BeEquivalentTo(ctx)) Expect(passedLeftRecCtx).To(BeEquivalentTo(leftRecCtx)) Expect(passedPos).To(Equal(pos)) Expect(p2.ParseCallCount()).To(Equal(1)) passedCtx, passedLeftRecCtx, passedPos = p2.ParseArgsForCall(0) Expect(passedCtx).To(BeEquivalentTo(ctx)) Expect(passedLeftRecCtx).To(BeEquivalentTo(leftRecCtx)) Expect(passedPos).To(Equal(pos)) }) It("should merge the curtailing parsers", func() { Expect(cp).To(Equal(p1CP.Union(p2CP))) }) Context("when there are multiple errors", func() { BeforeEach(func() { p1Err = parsley.NewErrorf(parsley.Pos(1), "err1") p2Err = parsley.NewErrorf(parsley.Pos(2), "err2") }) It("should return with the error with the higher position", func() { Expect(parserErr).To(MatchError("err2")) }) }) Context("when no parsers match", func() { It("should return nil", func() { Expect(res).To(BeNil()) }) }) Context("when one parser matches", func() { BeforeEach(func() { p1Res = n1 }) It("should return the result of that parser", func() { Expect(res).To(Equal(p1Res)) }) }) Context("when multiple parsers match", func() { BeforeEach(func() { p1Res = n1 p2Res = n2 p1Err = parsley.NewErrorf(parsley.Pos(1), "some error") }) It("should return with all results", func() { Expect(res).To(Equal(ast.NodeList([]parsley.Node{n1, n2}))) Expect(parserErr).ToNot(HaveOccurred()) }) }) }) })
Output: float64 1.23
func Choice ¶
Choice tries to apply the given parsers until one of them succeeds
Example ¶
Let's define a parser which accepts integer or float numbers. The parser would return only the first match so in this case we have to put the float parser first.
package main import ( "fmt" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/data" "github.com/conflowio/parsley/parser" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/parsley/parsleyfakes" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) // Let's define a parser which accepts integer or float numbers. // The parser would return only the first match so in this case we have to put the float parser first. func main() { p := combinator.Choice( terminal.Float("number"), terminal.Integer("integer"), ) r := text.NewReader(text.NewFile("example.file", []byte("1.23"))) ctx := parsley.NewContext(parsley.NewFileSet(), r) value, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value, value) } var _ = Describe("Choice", func() { var ( p parser.Func r *parsleyfakes.FakeReader parsers []parsley.Parser p1, p2 *parsleyfakes.FakeParser leftRecCtx data.IntMap pos parsley.Pos cp, p1CP, p2CP data.IntSet res, p1Res, p2Res parsley.Node parserErr, p1Err, p2Err parsley.Error n1, n2 *parsleyfakes.FakeNode ctx *parsley.Context ) BeforeEach(func() { r = &parsleyfakes.FakeReader{} ctx = parsley.NewContext(parsley.NewFileSet(), r) p1 = &parsleyfakes.FakeParser{} p2 = &parsleyfakes.FakeParser{} leftRecCtx = data.EmptyIntMap parsers = []parsley.Parser{p1, p2} pos = parsley.Pos(1) n1 = &parsleyfakes.FakeNode{} n1.TokenReturns("n1") n2 = &parsleyfakes.FakeNode{} n2.TokenReturns("n2") p1CP = data.EmptyIntSet p2CP = data.EmptyIntSet p1Res = nil p2Res = nil p1Err = nil p2Err = nil n1 = nil n2 = nil }) JustBeforeEach(func() { p1.ParseReturnsOnCall(0, p1Res, p1CP, p1Err) p2.ParseReturnsOnCall(0, p2Res, p2CP, p2Err) p = combinator.Choice(parsers...) res, cp, parserErr = p.Parse(ctx, leftRecCtx, pos) }) Context("when no parsers are given", func() { It("should panic", func() { Expect(func() { combinator.Choice() }).To(Panic()) }) }) Context("when there is only one parser", func() { Context("if it returns a result", func() { BeforeEach(func() { parsers = []parsley.Parser{p1} p1CP = data.NewIntSet(1) p1Res = n1 p1Err = nil }) It("should return the result of that parser", func() { Expect(cp).To(Equal(p1CP)) Expect(res).To(Equal(p1Res)) Expect(parserErr).ToNot(HaveOccurred()) Expect(p1.ParseCallCount()).To(Equal(1)) passedCtx, passedLeftRecCtx, passedPos := p1.ParseArgsForCall(0) Expect(passedCtx).To(BeEquivalentTo(ctx)) Expect(passedLeftRecCtx).To(BeEquivalentTo(leftRecCtx)) Expect(passedPos).To(Equal(pos)) }) }) Context("if it returns an error", func() { BeforeEach(func() { parsers = []parsley.Parser{p1} p1CP = data.NewIntSet(1) p1Res = nil p1Err = parsley.NewErrorf(parsley.Pos(2), "some error") }) It("should return the error of that parser", func() { Expect(cp).To(Equal(p1CP)) Expect(res).To(BeNil()) Expect(parserErr).To(Equal(p1Err)) Expect(p1.ParseCallCount()).To(Equal(1)) passedCtx, passedLeftRecCtx, passedPos := p1.ParseArgsForCall(0) Expect(passedCtx).To(BeEquivalentTo(ctx)) Expect(passedLeftRecCtx).To(BeEquivalentTo(leftRecCtx)) Expect(passedPos).To(Equal(pos)) }) }) }) Context("with multiple parsers", func() { BeforeEach(func() { parsers = []parsley.Parser{p1, p2} p1CP = data.NewIntSet(1) p2CP = data.NewIntSet(2) }) It("should merge the curtailing parsers", func() { Expect(cp).To(Equal(p1CP.Union(p2CP))) }) Context("when there are multiple errors", func() { BeforeEach(func() { p1Err = parsley.NewErrorf(parsley.Pos(1), "err1") p2Err = parsley.NewErrorf(parsley.Pos(2), "err2") }) It("should return with the error with the higher position", func() { Expect(parserErr).To(MatchError("err2")) }) }) Context("when no parsers match", func() { It("should return nil", func() { Expect(res).To(BeNil()) }) It("should call all parsers", func() { Expect(p1.ParseCallCount()).To(Equal(1)) passedCtx, passedLeftRecCtx, passedPos := p1.ParseArgsForCall(0) Expect(passedCtx).To(BeEquivalentTo(ctx)) Expect(passedLeftRecCtx).To(BeEquivalentTo(leftRecCtx)) Expect(passedPos).To(Equal(pos)) Expect(p2.ParseCallCount()).To(Equal(1)) passedCtx, passedLeftRecCtx, passedPos = p2.ParseArgsForCall(0) Expect(passedCtx).To(BeEquivalentTo(ctx)) Expect(passedLeftRecCtx).To(BeEquivalentTo(leftRecCtx)) Expect(passedPos).To(Equal(pos)) }) }) Context("when one parser matches", func() { BeforeEach(func() { p1Res = n1 }) It("should return the result of that parser", func() { Expect(res).To(Equal(p1Res)) }) It("should not call the remaining parsers", func() { Expect(p2.ParseCallCount()).To(Equal(0)) }) }) }) })
Output: float64 1.23
func Memoize ¶
Memoize handles result cache and curtailing left recursion
Example ¶
Let's define a left-recursive language where we need to curtail left-recursion and also cache previous parser matches with Memoize. Grammar: S -> A, A -> a | Ab
package main import ( "fmt" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/parser" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) func main() { concat := ast.InterpreterFunc(func(userCtx interface{}, node parsley.NonTerminalNode) (interface{}, parsley.Error) { var res string for _, node := range node.Children() { val, _ := parsley.EvaluateNode(userCtx, node) if runeVal, ok := val.(rune); ok { res += string(runeVal) } else { res += val.(string) } } return res, nil }) var p parser.Func p = combinator.Memoize(combinator.Any( terminal.Rune('a'), combinator.SeqOf(&p, terminal.Rune('b')).Bind(concat), )) f := text.NewFile("example.file", []byte("abbbbbbbb")) r := text.NewReader(f) s := combinator.Sentence(&p) ctx := parsley.NewContext(parsley.NewFileSet(), r) value, _ := parsley.Evaluate(ctx, s) fmt.Printf("%T %v\n", value, value) }
Output: string abbbbbbbb
func Optional ¶
Optional returns the parser's matches and an empty match
Example ¶
Let's define a parser which accepts "a", an optional "b" and a "c" character. The optional parser will result in a nil node so in the interpreter we have to handle that.
package main import ( "fmt" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) func main() { concat := ast.InterpreterFunc(func(userCtx interface{}, node parsley.NonTerminalNode) (interface{}, parsley.Error) { var res string for _, node := range node.Children() { if node != nil { val, _ := parsley.EvaluateNode(userCtx, node) if val != nil { res += string(val.(rune)) } } } return res, nil }) p := combinator.SeqOf( terminal.Rune('a'), combinator.Optional(terminal.Rune('b')), terminal.Rune('c'), ).Bind(concat) r := text.NewReader(text.NewFile("example.file", []byte("ac"))) ctx := parsley.NewContext(parsley.NewFileSet(), r) value, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value, value) }
Output: string ac
Types ¶
type SeqResultHandler ¶ added in v0.17.0
type SeqResultHandler interface {
HandleResult(pos parsley.Pos, token string, nodes []parsley.Node, interpreter parsley.Interpreter) parsley.Node
}
SeqResultHandler is an interface to handle the result of a Sequence parser Make sure to make a copy of the nodes slice
type SeqResultHandlerFunc ¶ added in v0.17.0
type SeqResultHandlerFunc func(pos parsley.Pos, token string, nodes []parsley.Node, interpreter parsley.Interpreter) parsley.Node
SeqResultHandlerFunc is a function to handle the result of a Sequence parser Make sure to make a copy of the nodes slice
func ReturnSingle ¶ added in v0.17.0
func ReturnSingle() SeqResultHandlerFunc
ReturnSingle returns the node, if the sequence parser only matched a single node
func (SeqResultHandlerFunc) HandleResult ¶ added in v0.17.0
func (f SeqResultHandlerFunc) HandleResult(pos parsley.Pos, token string, nodes []parsley.Node, interpreter parsley.Interpreter) parsley.Node
type Sequence ¶ added in v0.17.0
type Sequence struct {
// contains filtered or unexported fields
}
Sequence is a recursive and-type combinator
func Many ¶
Many applies the parser zero or more times
Example ¶
Let's define a parser which accepts any number of "a" characters
package main import ( "fmt" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) func main() { concat := ast.InterpreterFunc(func(userCtx interface{}, node parsley.NonTerminalNode) (interface{}, parsley.Error) { var res string for _, node := range node.Children() { val, _ := parsley.EvaluateNode(userCtx, node) res += string(val.(rune)) } return res, nil }) p := combinator.Many(terminal.Rune('a')).Bind(concat) f := text.NewFile("example.file", []byte("aaaaa")) fs := parsley.NewFileSet(f) r := text.NewReader(f) ctx := parsley.NewContext(fs, r) value, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value, value) }
Output: string aaaaa
func SepBy ¶
SepBy applies the given value parser zero or more times separated by the separator parser
Example ¶
Let's define a simple language where you define an integer array. The language would be left recursive, but using SepBy we can avoid this. The grammar is: S -> [I(,I)*], I -> any integer
package main import ( "fmt" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/ast/interpreter" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) func main() { arr := ast.InterpreterFunc(func(userCtx interface{}, node parsley.NonTerminalNode) (interface{}, parsley.Error) { nodes := node.Children() var res []int64 for i := 0; i < len(nodes); i += 2 { val, _ := parsley.EvaluateNode(userCtx, nodes[i]) res = append(res, val.(int64)) } return res, nil }) intList := combinator.SepBy(terminal.Integer("integer"), terminal.Rune(',')).Bind(arr) p := combinator.SeqOf( terminal.Rune('['), intList, terminal.Rune(']'), ).Bind(interpreter.Select(1)) r := text.NewReader(text.NewFile("example.file", []byte("[]"))) ctx := parsley.NewContext(parsley.NewFileSet(), r) value1, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value1, value1) r = text.NewReader(text.NewFile("example.file", []byte("[1,2,3]"))) ctx = parsley.NewContext(parsley.NewFileSet(), r) value2, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value2, value2) }
Output: []int64 [] []int64 [1 2 3]
func SepBy1 ¶
SepBy1 applies the given value parser one or more times separated by the separator parser
Example ¶
Let's define a simple language where you can add integer numbers. The language would be left recursive, but using SepBy1 we can avoid this. The grammar is: S -> I(+I)*, I -> any integer The "<empty>" result will never be returned as the SepBy1 doesn't match zero p occurrences.
package main import ( "fmt" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) func main() { interpreter := ast.InterpreterFunc(func(userCtx interface{}, node parsley.NonTerminalNode) (interface{}, parsley.Error) { nodes := node.Children() if len(nodes) == 0 { return "<empty>", nil } var sum int64 for i := 0; i < len(nodes); i += 2 { val, _ := parsley.EvaluateNode(userCtx, nodes[i]) sum += val.(int64) } return sum, nil }) p := combinator.SepBy1(terminal.Integer("integer"), terminal.Rune('+')).Bind(interpreter) r := text.NewReader(text.NewFile("example.file", []byte(""))) ctx := parsley.NewContext(parsley.NewFileSet(), r) value1, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value1, value1) r = text.NewReader(text.NewFile("example.file", []byte("1"))) ctx = parsley.NewContext(parsley.NewFileSet(), r) value2, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value2, value2) r = text.NewReader(text.NewFile("example.file", []byte("1+2+3"))) ctx = parsley.NewContext(parsley.NewFileSet(), r) value3, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value3, value3) }
Output: <nil> <nil> int64 1 int64 6
func Seq ¶
Seq tries to apply all parsers after each other and returns with all combinations of the results. The parsers should be generated by the parserLookUp function for the given index. When there is no parser for the given index then nil should be returned. The lenCheck function should return true if the longest possible match is valid
func SeqFirstOrAll ¶ added in v0.17.0
SeqFirstOrAll tries to apply all parsers after each other and returns with all combinations of the results. If it can't match all parsers, but it can match the first one it will return with the result of the first one.
Example ¶
Let's define a parser which does a simple integer addition
package main import ( "fmt" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) func main() { add := ast.InterpreterFunc(func(userCtx interface{}, node parsley.NonTerminalNode) (interface{}, parsley.Error) { children := node.Children() val1, _ := parsley.EvaluateNode(userCtx, children[0]) if len(children) == 1 { return val1.(int64), nil } val2, _ := parsley.EvaluateNode(userCtx, children[2]) return val1.(int64) + val2.(int64), nil }) p := combinator.SeqFirstOrAll( terminal.Integer("integer"), terminal.Rune('+'), terminal.Integer("integer"), ).Bind(add) r := text.NewReader(text.NewFile("example.file", []byte("1"))) ctx := parsley.NewContext(parsley.NewFileSet(), r) value, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value, value) r2 := text.NewReader(text.NewFile("example.file", []byte("1+2"))) ctx2 := parsley.NewContext(parsley.NewFileSet(), r2) value2, _ := parsley.Evaluate(ctx2, combinator.Sentence(p)) fmt.Printf("%T %v\n", value2, value2) }
Output: int64 1 int64 3
func SeqOf ¶ added in v0.17.0
SeqOf tries to apply all parsers after each other and returns with all combinations of the results. Only matches are returned where all parsers were applied successfully.
Example ¶
Let's define a parser which accepts "a", "b", "c" characters in order.
package main import ( "fmt" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) func main() { concat := ast.InterpreterFunc(func(userCtx interface{}, node parsley.NonTerminalNode) (interface{}, parsley.Error) { var res string for _, node := range node.Children() { val, _ := parsley.EvaluateNode(userCtx, node) res += string(val.(rune)) } return res, nil }) p := combinator.SeqOf( terminal.Rune('a'), terminal.Rune('b'), terminal.Rune('c'), ).Bind(concat) r := text.NewReader(text.NewFile("example.file", []byte("abc"))) ctx := parsley.NewContext(parsley.NewFileSet(), r) value, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value, value) }
Output: string abc
func SeqTry ¶
SeqTry tries to apply all parsers after each other and returns with all combinations of the results. It needs to match the first parser at least
Example ¶
Let's define a parser which accepts any prefix of the "abc" string.
package main import ( "fmt" "github.com/conflowio/parsley/ast" "github.com/conflowio/parsley/combinator" "github.com/conflowio/parsley/parsley" "github.com/conflowio/parsley/text" "github.com/conflowio/parsley/text/terminal" ) func main() { concat := ast.InterpreterFunc(func(userCtx interface{}, node parsley.NonTerminalNode) (interface{}, parsley.Error) { var res string for _, node := range node.Children() { val, _ := parsley.EvaluateNode(userCtx, node) res += string(val.(rune)) } return res, nil }) p := combinator.SeqTry( terminal.Rune('a'), terminal.Rune('b'), terminal.Rune('c'), ).Bind(concat) r := text.NewReader(text.NewFile("example.file", []byte("ab"))) ctx := parsley.NewContext(parsley.NewFileSet(), r) value, _ := parsley.Evaluate(ctx, combinator.Sentence(p)) fmt.Printf("%T %v\n", value, value) }
Output: string ab
func (*Sequence) Bind ¶ added in v0.17.0
func (s *Sequence) Bind(interpreter parsley.Interpreter) *Sequence
Bind binds the given interpreter
func (*Sequence) HandleResult ¶ added in v0.17.0
func (s *Sequence) HandleResult(resultHandler SeqResultHandler) *Sequence
HandleResult sets the result handler If you use a custom function, make sure to make a copy of the nodes slice
func (*Sequence) Name ¶ added in v0.17.0
Name overrides the returned error if its position is the same as the reader's position The error will be: "was expecting <name>"