combinator

package
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2018 License: MPL-2.0 Imports: 8 Imported by: 5

Documentation

Overview

Package combinator defines generator functions for creating parser combinators

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Any

func Any(name string, parsers ...parsley.Parser) *parser.NamedFunc

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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/data"
	"github.com/opsidian/parsley/parser"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/parsley/parsleyfakes"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/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("number",
		terminal.Integer(),
		terminal.Float(),
	)
	r := text.NewReader(text.NewFile("example.file", []byte("1.23")))
	ctx := parsley.NewContext(r)
	value, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value, value)

}

var _ = Describe("Any", func() {

	var (
		p                 *parser.NamedFunc
		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
		n1, n2            *parsleyfakes.FakeNode
		ctx               *parsley.Context
	)

	BeforeEach(func() {
		r = &parsleyfakes.FakeReader{}
		ctx = parsley.NewContext(r)
		p1 = &parsleyfakes.FakeParser{}
		p1.NameReturns("p1")
		p2 = &parsleyfakes.FakeParser{}
		p2.NameReturns("p2")
		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
		n1 = nil
		n2 = nil
	})

	JustBeforeEach(func() {
		p1.ParseReturnsOnCall(0, p1Res, p1CP)
		p2.ParseReturnsOnCall(0, p2Res, p2CP)

		p = combinator.Any("test", parsers...)
		res, cp = p.Parse(ctx, leftRecCtx, pos)
	})

	Context("when no parsers are given", func() {
		It("should panic", func() {
			Expect(func() { combinator.Any("test") }).To(Panic())
		})
	})

	Context("when there is only one parser", func() {

		BeforeEach(func() {
			parsers = []parsley.Parser{p1}
			p1CP = data.NewIntSet(1)
			p1Res = n1
		})

		It("should return the result of that parser", func() {
			Expect(cp).To(Equal(p1CP))
			Expect(res).To(Equal(p1Res))

			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 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
			})
			It("should return with all results", func() {
				Expect(res).To(Equal(ast.NodeList([]parsley.Node{n1, n2})))
			})
		})
	})

})
Output:
float64 1.23

func Choice

func Choice(name string, parsers ...parsley.Parser) *parser.NamedFunc

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/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/data"
	"github.com/opsidian/parsley/parser"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/parsley/parsleyfakes"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/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("number",
		terminal.Float(),
		terminal.Integer(),
	)
	r := text.NewReader(text.NewFile("example.file", []byte("1.23")))
	ctx := parsley.NewContext(r)
	value, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value, value)
}

var _ = Describe("Choice", func() {

	var (
		p                 *parser.NamedFunc
		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
		n1, n2            *parsleyfakes.FakeNode
		ctx               *parsley.Context
	)

	BeforeEach(func() {
		r = &parsleyfakes.FakeReader{}
		ctx = parsley.NewContext(r)
		p1 = &parsleyfakes.FakeParser{}
		p1.NameReturns("p1")
		p2 = &parsleyfakes.FakeParser{}
		p2.NameReturns("p2")
		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
		n1 = nil
		n2 = nil
	})

	JustBeforeEach(func() {
		p1.ParseReturnsOnCall(0, p1Res, p1CP)
		p2.ParseReturnsOnCall(0, p2Res, p2CP)

		p = combinator.Choice("test", parsers...)
		res, cp = p.Parse(ctx, leftRecCtx, pos)
	})

	Context("when no parsers are given", func() {
		It("should panic", func() {
			Expect(func() { combinator.Choice("test") }).To(Panic())
		})
	})

	Context("when there is only one parser", func() {

		BeforeEach(func() {
			parsers = []parsley.Parser{p1}
			p1CP = data.NewIntSet(1)
			p1Res = n1
		})

		It("should return the result of that parser", func() {
			Expect(cp).To(Equal(p1CP))
			Expect(res).To(Equal(p1Res))

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

func Memoize(p parsley.Parser) *parser.NamedFunc

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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parser"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		var res string
		for _, node := range nodes {
			val, _ := node.Value(ctx)
			if runeVal, ok := val.(rune); ok {
				res += string(runeVal)
			} else {
				res += val.(string)
			}
		}
		return res, nil
	})

	var p parser.NamedFunc
	p = *combinator.Memoize(combinator.Any("a or ab",
		terminal.Rune('a'),
		combinator.Seq("AB", "", &p, terminal.Rune('b')).Bind(concat),
	))
	f := text.NewFile("example.file", []byte("abbbbbbbb"))
	r := text.NewReader(f)
	s := combinator.Sentence(&p)
	ctx := parsley.NewContext(r)
	value, _ := parsley.Evaluate(ctx, s, nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string abbbbbbbb

func Optional

func Optional(p parsley.Parser) parser.Func

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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		var res string
		for _, node := range nodes {
			if node != nil {
				val, _ := node.Value(ctx)
				if val != nil {
					res += string(val.(rune))
				}
			}
		}
		return res, nil
	})

	p := combinator.Seq("AB", "",
		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(r)
	value, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string ac

func Single added in v0.8.2

func Single(p parsley.Parser) parser.Func

Single will change the result of p if it returns with a non terminal node with only one child. In this case directly the child will returned.

Types

type Recursive added in v0.6.0

type Recursive struct {
	// contains filtered or unexported fields
}

Recursive is a recursive and-type combinator

func Many

func Many(p parsley.Parser) *Recursive

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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		var res string
		for _, node := range nodes {
			val, _ := node.Value(ctx)
			res += string(val.(rune))
		}
		return res, nil
	})
	p := combinator.Many(terminal.Rune('a')).Bind(concat)
	r := text.NewReader(text.NewFile("example.file", []byte("aaaaa")))
	ctx := parsley.NewContext(r)
	value, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)

	fmt.Printf("%T %v\n", value, value)
}
Output:
string aaaaa

func Many1

func Many1(p parsley.Parser) *Recursive

Many1 applies the parser one or more times

func NewRecursive added in v0.6.0

func NewRecursive(token string, name func() string, returnSingle bool, parserLookUp func(int) parsley.Parser, lenCheck func(int) bool) *Recursive

NewRecursive creates a new recursive instance

func Sentence added in v0.6.0

func Sentence(p parsley.Parser) *Recursive

Sentence matches the given parser until the end of input

func SepBy

func SepBy(valueP parsley.Parser, sepP parsley.Parser) *Recursive

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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/ast/interpreter"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	arr := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		var res []int64
		for i := 0; i < len(nodes); i += 2 {
			val, _ := nodes[i].Value(ctx)
			res = append(res, val.(int64))
		}
		return res, nil
	})

	intList := combinator.SepBy(terminal.Integer(), terminal.Rune(',')).Bind(arr)
	p := combinator.Seq("ARR", "array",
		terminal.Rune('['),
		intList,
		terminal.Rune(']'),
	).Bind(interpreter.Select(1))

	r := text.NewReader(text.NewFile("example.file", []byte("[]")))
	ctx := parsley.NewContext(r)

	value1, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value1, value1)

	r = text.NewReader(text.NewFile("example.file", []byte("[1,2,3]")))
	ctx = parsley.NewContext(r)
	value2, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value2, value2)

}
Output:
[]int64 []
[]int64 [1 2 3]

func SepBy1

func SepBy1(valueP parsley.Parser, sepP parsley.Parser) *Recursive

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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	interpreter := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		if len(nodes) == 0 {
			return "<empty>", nil
		}
		var sum int64
		for i := 0; i < len(nodes); i += 2 {
			val, _ := nodes[i].Value(ctx)
			sum += val.(int64)
		}
		return sum, nil
	})

	p := combinator.SepBy1(terminal.Integer(), terminal.Rune('+')).Bind(interpreter)

	r := text.NewReader(text.NewFile("example.file", []byte("")))
	ctx := parsley.NewContext(r)
	value1, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value1, value1)

	r = text.NewReader(text.NewFile("example.file", []byte("1")))
	ctx = parsley.NewContext(r)
	value2, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value2, value2)

	r = text.NewReader(text.NewFile("example.file", []byte("1+2+3")))
	ctx = parsley.NewContext(r)
	value3, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value3, value3)
}
Output:
<nil> <nil>
int64 1
int64 6

func SepByOrValue

func SepByOrValue(valueP parsley.Parser, sepP parsley.Parser) *Recursive

SepByOrValue applies the given value parser zero or more times separated by the separator parser If there is only one value then the value node will be returned

Example

Let's define a simple language where you concatenate chars. The language would be left recursive, but using SepBy we can avoid this. The grammar is: S -> [V(+V)*], V -> any char In the second call the value of the char node will be returned directly and the interpreter is not used.

package main

import (
	"fmt"

	"github.com/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		var res string
		for i := 0; i < len(nodes); i += 2 {
			val, _ := nodes[i].Value(ctx)
			res = res + string(val.(rune))
		}
		return res, nil
	})

	p := combinator.SepByOrValue(terminal.Char(), terminal.Rune('+')).Bind(concat)

	r := text.NewReader(text.NewFile("example.file", []byte(`'a'+'b'`)))
	ctx := parsley.NewContext(r)

	value1, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value1, value1)

	r = text.NewReader(text.NewFile("example.file", []byte("'a'")))
	ctx = parsley.NewContext(r)
	value2, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value2, value2)

}
Output:
string ab
int32 97

func SepByOrValue1

func SepByOrValue1(valueP parsley.Parser, sepP parsley.Parser) *Recursive

SepByOrValue1 applies the given value parser one or more times separated by the separator parser If there is only one value then the value node will be returned

func Seq

func Seq(token string, name string, parsers ...parsley.Parser) *Recursive

Seq tries to apply all parsers after each other matching effectively a sequence of tokens 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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		var res string
		for _, node := range nodes {
			val, _ := node.Value(ctx)
			res += string(val.(rune))
		}
		return res, nil
	})

	p := combinator.Seq("ABC", "",
		terminal.Rune('a'),
		terminal.Rune('b'),
		terminal.Rune('c'),
	).Bind(concat)
	r := text.NewReader(text.NewFile("example.file", []byte("abc")))
	ctx := parsley.NewContext(r)
	value, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string abc

func SeqTry

func SeqTry(token string, name string, parsers ...parsley.Parser) *Recursive

SeqTry tries to apply all parsers after each other matching effectively the longest possible sequences of tokens 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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		var res string
		for _, node := range nodes {
			val, _ := node.Value(ctx)
			res += string(val.(rune))
		}
		return res, nil
	})

	p := combinator.SeqTry("ABC", "",
		terminal.Rune('a'),
		terminal.Rune('b'),
		terminal.Rune('c'),
	).Bind(concat)
	r := text.NewReader(text.NewFile("example.file", []byte("ab")))
	ctx := parsley.NewContext(r)
	value, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string ab

func SeqTryOrValue added in v0.8.1

func SeqTryOrValue(token string, name string, parsers ...parsley.Parser) *Recursive

SeqTryOrValue tries to apply all parsers after each other matching effectively the longest possible sequences of tokens and returns with all combinations of the results. It needs to match the first parser at least. If only the first parser matches it will return the result of the first parser directly.

Example

When using the SeqTryOrValue the result will be the value of node 'a' and the interpreter is not used.

package main

import (
	"fmt"

	"github.com/opsidian/parsley/ast"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []parsley.Node) (interface{}, parsley.Error) {
		var res string
		for _, node := range nodes {
			val, _ := node.Value(ctx)
			res += string(val.(rune))
		}
		return res, nil
	})

	p := combinator.SeqTryOrValue("ABC", "",
		terminal.Rune('a'),
		terminal.Rune('b'),
		terminal.Rune('c'),
	).Bind(concat)
	r := text.NewReader(text.NewFile("example.file", []byte("a")))
	ctx := parsley.NewContext(r)
	value, _ := parsley.Evaluate(ctx, combinator.Sentence(p), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
int32 97

func (*Recursive) Bind added in v0.6.0

func (rp *Recursive) Bind(interpreter parsley.Interpreter) *Recursive

Bind binds the given interpreter

func (*Recursive) Name added in v0.6.0

func (rp *Recursive) Name() string

Name returns with the parser's descriptive name

func (*Recursive) Parse added in v0.6.0

func (rp *Recursive) Parse(ctx *parsley.Context, leftRecCtx data.IntMap, pos parsley.Pos) (parsley.Node, data.IntSet)

Parse parses the given input

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL