combinator

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Feb 19, 2018 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(desc string, parsers ...parser.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/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	number := combinator.Any("number",
		terminal.Integer(),
		terminal.Float(),
	)
	s := parsley.NewSentence(number)
	value, _, _ := s.Evaluate(text.NewReader([]byte("1.23"), "", true), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
float64 1.23

func Choice

func Choice(desc string, parsers ...parser.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/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	number := combinator.Choice("number",
		terminal.Float(),
		terminal.Integer(),
	)
	s := parsley.NewSentence(number)
	value, _, _ := s.Evaluate(text.NewReader([]byte("1.23"), "", true), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
float64 1.23

func Many

func Many(nodeBuilder ast.NodeBuilder, p parser.Parser) parser.Func

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

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []ast.Node) (interface{}, reader.Error) {
		var res string
		for _, node := range nodes {
			val, _ := node.Value(ctx)
			res += string(val.(rune))
		}
		return res, nil
	})
	p := combinator.Many(builder.All("a", concat), terminal.Rune('a', "A"))
	s := parsley.NewSentence(p)
	value, _, _ := s.Evaluate(text.NewReader([]byte("aaaaa"), "", true), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string aaaaa

func Many1

func Many1(nodeBuilder ast.NodeBuilder, p parser.Parser) parser.Func

Many1 applies the parser one or more times

Example

Let's define a parser which accepts one or many "a" characters

package main

import (
	"fmt"

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

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []ast.Node) (interface{}, reader.Error) {
		var res string
		for _, node := range nodes {
			val, _ := node.Value(ctx)
			res += string(val.(rune))
		}
		return res, nil
	})
	p := combinator.Many1(builder.All("a", concat), terminal.Rune('a', "A"))
	s := parsley.NewSentence(p)
	value, _, _ := s.Evaluate(text.NewReader([]byte("aaaaa"), "", true), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string aaaaa

func Memoize

func Memoize(p parser.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/opsidian/parsley/ast"
	"github.com/opsidian/parsley/ast/builder"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parser"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/reader"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	concat := ast.InterpreterFunc(func(ctx interface{}, nodes []ast.Node) (interface{}, reader.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 a parser.Func
	a = combinator.Memoize(combinator.Any("a or ab",
		terminal.Rune('a', "CHAR"),
		combinator.Seq(builder.All("AB", concat),
			&a,
			terminal.Rune('b', "CHAR"),
		),
	))
	s := parsley.NewSentence(a)
	value, _, _ := s.Evaluate(text.NewReader([]byte("abbbbbbbb"), "", true), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string abbbbbbbb

func Optional

func Optional(p parser.Parser) parser.Func

Optional returns the parser's matches or an empty match if it fails

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

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

	p := combinator.Seq(builder.All("AB", concat),
		terminal.Rune('a', "a"),
		combinator.Optional(terminal.Rune('b', "b")),
		terminal.Rune('c', "c"),
	)
	s := parsley.NewSentence(p)
	value, _, _ := s.Evaluate(text.NewReader([]byte("ac"), "", true), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string ac

func SepBy

func SepBy(token string, valueP parser.Parser, sepP parser.Parser, interpreter ast.Interpreter) parser.Func

SepBy applies the given value parser zero or more times separated by the separator parser It simply uses the Seq, SeqTry, Many and Memoize combinators.

Example

Let's define a simple language where you define an integer array. The language would be left recursive, but using SepBy (which is using Many and Seq) 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/builder"
	"github.com/opsidian/parsley/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/reader"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

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

	intList := combinator.SepBy("ARR", terminal.Integer(), terminal.Rune(',', "SEP"), interpreter)
	p := combinator.Seq(builder.Select(1), terminal.Rune('[', "ARR_START"), intList, terminal.Rune(']', "ARR_END"))
	s := parsley.NewSentence(p)

	value1, _, _ := s.Evaluate(text.NewReader([]byte("[]"), "", true), nil)
	fmt.Printf("%T %v\n", value1, value1)

	value2, _, _ := s.Evaluate(text.NewReader([]byte("[1, 2, 3]"), "", true), nil)
	fmt.Printf("%T %v\n", value2, value2)
}
Output:
[]int []
[]int [1 2 3]

func SepBy1

func SepBy1(token string, valueP parser.Parser, sepP parser.Parser, interpreter ast.Interpreter) parser.Parser

SepBy1 applies the given value parser one or more times separated by the separator parser It simply uses the Seq, SeqTry, Many and Memoize combinators.

Example

Let's define a simple language where you can add integer numbers. The language would be left recursive, but using SepBy1 (which is using Many and Seq) 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/combinator"
	"github.com/opsidian/parsley/parsley"
	"github.com/opsidian/parsley/reader"
	"github.com/opsidian/parsley/text"
	"github.com/opsidian/parsley/text/terminal"
)

func main() {
	interpreter := ast.InterpreterFunc(func(ctx interface{}, nodes []ast.Node) (interface{}, reader.Error) {
		sum := 0
		for i := 0; i < len(nodes); i += 2 {
			val, _ := nodes[i].Value(ctx)
			sum += val.(int)
		}
		return sum, nil
	})

	p := combinator.SepBy1("SUM", terminal.Integer(), terminal.Rune('+', "+"), interpreter)
	s := parsley.NewSentence(p)
	value1, _, _ := s.Evaluate(text.NewReader([]byte("1"), "", true), nil)
	fmt.Printf("%T %v\n", value1, value1)

	value2, _, _ := s.Evaluate(text.NewReader([]byte("1 + 2 + 3"), "", true), nil)
	fmt.Printf("%T %v\n", value2, value2)
}
Output:
int 1
int 6

func SepByOrValue

func SepByOrValue(token string, valueP parser.Parser, sepP parser.Parser, interpreter ast.Interpreter) parser.Func

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 and the interpreter won't be used It simply uses the Seq, SeqTry, Many and Memoize combinators.

func SepByOrValue1

func SepByOrValue1(token string, valueP parser.Parser, sepP parser.Parser, interpreter ast.Interpreter) parser.Parser

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 and the interpreter won't be used It simply uses the Seq, SeqTry, Many and Memoize combinators.

func Seq

func Seq(nodeBuilder ast.NodeBuilder, parsers ...parser.Parser) parser.Func

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

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

	p := combinator.Seq(builder.All("ABC", concat),
		terminal.Rune('a', "a"),
		terminal.Rune('b', "b"),
		terminal.Rune('c', "c"),
	)
	s := parsley.NewSentence(p)
	value, _, _ := s.Evaluate(text.NewReader([]byte("abc"), "", true), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string abc

func SeqTry

func SeqTry(nodeBuilder ast.NodeBuilder, min int, parsers ...parser.Parser) parser.Func

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.

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

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

	p := combinator.SeqTry(builder.All("ABC", concat), 0,
		terminal.Rune('a', "a"),
		terminal.Rune('b', "b"),
		terminal.Rune('c', "c"),
	)
	s := parsley.NewSentence(p)
	value, _, _ := s.Evaluate(text.NewReader([]byte("ab"), "", true), nil)
	fmt.Printf("%T %v\n", value, value)
}
Output:
string ab

func SuppressError

func SuppressError(p parser.Parser) parser.Func

SuppressError removes the error from the parser result

Types

This section is empty.

Jump to

Keyboard shortcuts

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