combinator

package
v0.17.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2021 License: MPL-2.0 Imports: 6 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(parsers ...parsley.Parser) parser.Func

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

func Choice(parsers ...parsley.Parser) parser.Func

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

func Memoize(p parsley.Parser) parser.Func

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

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

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.

func SuppressError

func SuppressError(p parsley.Parser) parser.Func

SuppressError removes the error from the parser result

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

func Many(p parsley.Parser) *Sequence

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 Many1

func Many1(p parsley.Parser) *Sequence

Many1 applies the parser one or more times

func Sentence added in v0.6.0

func Sentence(p parsley.Parser) *Sequence

Sentence matches the given parser until the end of input

func SepBy

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

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

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

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

func Seq(token string, parserLookUp func(int) parsley.Parser, lenCheck func(int) bool) *Sequence

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

func SeqFirstOrAll(parsers ...parsley.Parser) *Sequence

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

func SeqOf(parsers ...parsley.Parser) *Sequence

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

func SeqTry(parsers ...parsley.Parser) *Sequence

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

func (s *Sequence) Name(name string) *Sequence

Name overrides the returned error if its position is the same as the reader's position The error will be: "was expecting <name>"

func (*Sequence) Parse added in v0.17.0

func (s *Sequence) Parse(ctx *parsley.Context, leftRecCtx data.IntMap, pos parsley.Pos) (parsley.Node, data.IntSet, parsley.Error)

Parse parses the given input

func (*Sequence) Token added in v0.17.0

func (s *Sequence) Token(token string) *Sequence

Token sets the result token

Jump to

Keyboard shortcuts

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