Documentation
¶
Overview ¶
Package combinator defines generator functions for creating parser combinators
Index ¶
- func Any(desc string, parsers ...parser.Parser) parser.Func
- func Choice(desc string, parsers ...parser.Parser) parser.Func
- func Many(nodeBuilder ast.NodeBuilder, p parser.Parser) parser.Func
- func Many1(nodeBuilder ast.NodeBuilder, p parser.Parser) parser.Func
- func Memoize(p parser.Parser) parser.Func
- func Optional(p parser.Parser) parser.Func
- func SepBy(token string, valueP parser.Parser, sepP parser.Parser, ...) parser.Func
- func SepBy1(token string, valueP parser.Parser, sepP parser.Parser, ...) parser.Parser
- func SepByOrValue(token string, valueP parser.Parser, sepP parser.Parser, ...) parser.Func
- func SepByOrValue1(token string, valueP parser.Parser, sepP parser.Parser, ...) parser.Parser
- func Seq(nodeBuilder ast.NodeBuilder, parsers ...parser.Parser) parser.Func
- func SeqTry(nodeBuilder ast.NodeBuilder, min int, parsers ...parser.Parser) parser.Func
- func SuppressError(p parser.Parser) parser.Func
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/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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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
Types ¶
This section is empty.