rat

package module
v0.0.0-...-ae06ff6 Latest Latest
Warning

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

Go to latest
Published: Feb 4, 2023 License: Apache-2.0 Imports: 7 Imported by: 0

README

Scanner-less PEG packrat parser and meta-grammar interpreter in Go

WIP GoDoc License Go Report Card Coverage

Inspired by Bryan Ford's PEG packrat parser paper.

Documentation

Overview

Package rat implements a PEG packrat parser that takes advantage of Go's unique ability to switch on type to create an interpreted meta-language consisting entirely of Go types. These types serve as tokens that any lexer would create when lexing a higher level meta language such as PEG, PEGN, or regular expressions. Passing these types to rat.Pack compiles (memoizes) them into a grammar of parsing functions not unlike compilation of regular expressions. The string representations of these structs consists of entirely of valid, compilable Go code suitable for parser code generation for parsers of any type, in any language. Simply printing a Grammar instance to a file is suitable to generate such a parser.

Prefer Pack over Make*

Although the individual Make* methods for each of the supported types have been exported publicly allowing developers to call them directly from within their own Rule implementations, most should use Pack instead. Consider it the equivalent of compiling a regular expression.

Index

Examples

Constants

View Source
const (
	ErrIsZeroT      = `zero value: %T`
	ErrNotExistT    = `does not exist: %v`
	ErrExpectedT    = `expected: %v`
	ErrBadTypeT     = `unknown type: %v (%[1]T)`
	ErrArgsT        = `missing or incorrect arguments: %v (%[1]T)`
	ErrPackTypeT    = `invalid type`
	ErrNoCheckFuncT = `no check function assigned: %v`
)

Variables

View Source
var DefaultFlatFunc = ByDepth

DefaultFlatFunc is the default FlatFunc to use when filtering for results using With* methods.

View Source
var DefaultRuleName = `Rule`

DefaultRuleName is used by NewRule and AddRule as the prefix for new, unnamed rules and is combined with the internal integer number that increments for each new such rule added.

View Source
var MaxGoroutines int

MaxGoroutines set the maximum number of goroutines by any method or function in this package (WalkByAsync, for example). By default, there is no limit (0).

View Source
var Trace int

Trace enables tracing parsing and checks while they happen. Output is with the log package.

Functions

func Walk

func Walk(root Result, do VisitFunc)

Walk calls WalkBy(DefaultFlatFunc, root, do). Use this when the order of processing matters more than speed (ASTs, etc.). Also see WalkAsync.

func WalkBy

func WalkBy(flatten FlatFunc, root Result, do VisitFunc)

WalkBy takes a function to flatten a rooted node tree (FlatFunc), creates a flattened slice of Results starting from root Result, and then passes each synchronously to the VisitFunc waiting for it to complete before doing the next. Walk calls WalkBy(DefaultFlatFunc, root, do). Use this when the order of processing matters more than speed (ASTs, etc.). Also see

Types

type CheckFunc

type CheckFunc func(r []rune, i int) Result

CheckFunc examines the []rune buffer at a specific position for a specific grammar rule and should generally only be used from an encapsulating Rule so that it has a Text identifier associated with it. One or more Rules may, however, encapsulate the same CheckFunc function.

CheckFunc MUST return a Result indicating success or failure by setting Result.X (error) for failure. (Note that this is unlike many packrat designs that return nil to indicate rule failure.)

CheckFunc MUST set Result.X (error) if unable to match the entire rule and MUST advance to the E (end) to the farthest possible position in the []rune slice before failure occurred. This allows for better recovery and specific user-facing error messages while promoting succinct rule development.

When a CheckFunc is composed of multiple sub-rules, each MUST be added to the Result.C (children) slice including any that generated errors. Some functions may opt to continue even if the result contained an error allowing recovery. Usually, however, a CheckFunc should stop on the first error and include it with the children. Usually, a CheckFunc should also set its error Result.X to that of the final Result that failed.

type ErrArgs

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

func (ErrArgs) Error

func (e ErrArgs) Error() string

type ErrExpected

type ErrExpected struct{ V any }

func (ErrExpected) Error

func (e ErrExpected) Error() string

type ErrIsZero

type ErrIsZero struct{ V any }

func (ErrIsZero) Error

func (e ErrIsZero) Error() string

type ErrNoCheckFunc

type ErrNoCheckFunc struct{ V any }

func (ErrNoCheckFunc) Error

func (e ErrNoCheckFunc) Error() string

type ErrNotFound

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

type FlatFunc

type FlatFunc func(root Result) []Result

FlatFunc is function that returns a flattened rooted-node tree.

type Grammar

type Grammar struct {
	Trace int              // activate logs for debug visibility
	Rules map[string]*Rule // keyed to Rule.Name (not Text)
	Saved map[string]*Rule // dynamically created literals from Sav
	Main  *Rule            // entry point for Check or Scan
	// contains filtered or unexported fields
}

Grammar is an aggregation of cached (memoized) rules with methods to add rules and check data against them. The Main rule is the entry point. Grammars may have multiple unrelated rules added and change the Main entry point dynamically as needed, but most will use the sequence rule created by calling the Pack method or it's rat.Pack equivalent constructor. Trace may be incremented during debugging to gain performant visibility into grammar construction and scanning.

Memoization

All Make* methods check the Rules map/cache for a match for the String form of the rat/x expression and return it directly if found rather than create a new Rule with an identical CheckFunc. The MakeNamed creates an additional entry (pointing to the same *Rule) for the specified name.

func Pack

func Pack(seq ...any) *Grammar

Pack interprets a sequence of any valid Go types into a Grammar suitable for checking and parsing any UTF-8 input. All arguments passed are assumed to rat/x expressions or literal forms (%q). These have special meaning corresponding to the fundamentals expressions of an PEG or regular expression. Consider these the tokens created by any lexer when processing any meta-language. Any grammar or structured data format that uses UTF-8 encoding can be fully expressed as compilable Go code using this method of interpretation.

Alternative Call

Pack is actually just shorthand equivalent to the following:

g := new(Grammar)
rule := g.MakeRule()

Memoization

Memoization is a fundamental requirement for any PEG packrat parser. Pack automatically memoizes all expressions using closure functions and map entries matching the specific arguments to a specific expression. Results are always integer pointers to specific positions within the data passed so there is never wasteful redundancy. This maximizes performance and minimizes memory utilization.

Example (End)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.Any{2}, x.End{})
	g.Print()

	g.Scan(`fo`).PrintText()
	g.Scan(`fo`).Print()

	g.Scan(`foo`).Print()

}
Output:

x.Seq{x.Any{2}, x.End{}}
fo
{"B":0,"E":2,"C":[{"B":0,"E":2},{"B":2,"E":2}],"R":"fo"}
{"B":0,"E":2,"X":"expected: x.End{}","C":[{"B":0,"E":2},{"B":2,"E":2,"X":"expected: x.End{}"}],"R":"foo"}
Example (Is)
package main

import (
	"unicode"

	"github.com/rwxrob/rat"
)

func main() {

	IsPrint := unicode.IsPrint

	g := rat.Pack(IsPrint)
	g.Print()

	g.Scan(`foo`).PrintText()
	g.Scan(`foo`).Print()

	g.Scan("\x00foo").Print()

}
Output:

x.Is{IsPrint}
f
{"B":0,"E":1,"R":"foo"}
{"B":0,"E":0,"X":"expected: x.Is{IsPrint}","R":"\x00foo"}
Example (Mmx)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.Mmx{1, 3, `foo`})
	g.Print()

	g.Scan(`foo`).PrintText()
	g.Scan(`foo`).Print()

	g.Scan(`foofoo`).PrintText()
	g.Scan(`foofoo`).Print()

	g.Scan(`foofoofoo`).PrintText()
	g.Scan(`foofoofoo`).Print()

	g.Scan(`foofoofoofoo`).PrintText()
	g.Scan(`foofoofoofoo`).Print()

	g.Scan(`barfoofoo`).Print()

}
Output:

x.Mmx{1, 3, x.Str{"foo"}}
foo
{"B":0,"E":3,"C":[{"B":0,"E":3}],"R":"foo"}
foofoo
{"B":0,"E":6,"C":[{"B":0,"E":3},{"B":3,"E":6}],"R":"foofoo"}
foofoofoo
{"B":0,"E":9,"C":[{"B":0,"E":3},{"B":3,"E":6},{"B":6,"E":9}],"R":"foofoofoo"}
foofoofoo
{"B":0,"E":9,"C":[{"B":0,"E":3},{"B":3,"E":6},{"B":6,"E":9},{"B":9,"E":12}],"R":"foofoofoofoo"}
{"B":0,"E":0,"X":"expected: x.Mmx{1, 3, x.Str{\"foo\"}}","R":"barfoofoo"}
Example (Named)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.N{`foo`, true})
	g.Print()

	g.Scan(`true`).Print()

}
Output:

x.N{"foo", x.Str{"true"}}
{"N":"foo","B":0,"E":4,"R":"true"}
Example (Not)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.Not{`foo`})
	g.Print()

	g.Scan(`fo`).PrintText()
	g.Scan(`fo`).Print()

	g.Scan(`bar`).PrintText()
	g.Scan(`bar`).Print()

	g.Scan(`fooooo`).Print()

}
Output:

x.Not{x.Str{"foo"}}

{"B":0,"E":0,"R":"fo"}

{"B":0,"E":0,"R":"bar"}
{"B":0,"E":0,"X":"expected: x.Not{x.Str{\"foo\"}}","R":"fooooo"}
Example (One)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.One{`foo`, `bar`})
	g.Print()

	g.Scan(`foobar`).PrintText()
	g.Scan(`foobar`).Print()

	g.Scan(`barfoo`).PrintText()
	g.Scan(`barfoo`).Print()

	g.Scan(`baz`).Print()

}
Output:

x.One{x.Str{"foo"}, x.Str{"bar"}}
foo
{"B":0,"E":3,"C":[{"B":0,"E":3}],"R":"foobar"}
bar
{"B":0,"E":3,"C":[{"B":0,"E":3}],"R":"barfoo"}
{"B":0,"E":0,"X":"expected: x.One{x.Str{\"foo\"}, x.Str{\"bar\"}}","R":"baz"}
Example (One_Named)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	one := x.One{`foo`, `bar`}
	Foo := x.N{`Foo`, one}
	g := rat.Pack(Foo)
	g.Print()

	// foo
	g.Scan(`foobar`).Print()
	g.Scan(`foobar`).PrintText()

	// bar
	g.Scan(`barrr`).Print()
	g.Scan(`barrr`).PrintText()

	// bork
	g.Scan(`fobar`).Print()

}
Output:

x.N{"Foo", x.One{x.Str{"foo"}, x.Str{"bar"}}}
{"N":"Foo","B":0,"E":3,"C":[{"B":0,"E":3}],"R":"foobar"}
foo
{"N":"Foo","B":0,"E":3,"C":[{"B":0,"E":3}],"R":"barrr"}
bar
{"N":"Foo","B":0,"E":0,"X":"expected: x.One{x.Str{\"foo\"}, x.Str{\"bar\"}}","R":"fobar"}
Example (Ref)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.Ref{`Foo`})
	g.MakeRule(x.N{`Foo`, `foo`})
	g.Print()

	g.Rules[`x.Str{"foo"}`].Print()
	g.Rules[`Foo`].Print()

	g.Scan(`foo`).Print()
	g.Rules[`x.Str{"foo"}`].Scan(`foo`).Print()

}
Output:

x.Ref{"Foo"}
x.Str{"foo"}
x.N{"Foo", x.Str{"foo"}}
{"N":"Foo","B":0,"E":3,"R":"foo"}
{"B":0,"E":3,"R":"foo"}
Example (Rng)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.Rng{'😀', '🙏'})
	g.Print()

	g.Scan(`🙉`).PrintText()
	g.Scan(`🙉`).Print()

	g.Scan(`🚆`).Print()

}
Output:

x.Rng{'😀', '🙏'}
🙉
{"B":0,"E":1,"R":"🙉"}
{"B":0,"E":0,"X":"expected: x.Rng{'😀', '🙏'}","R":"🚆"}
Example (Save)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := new(rat.Grammar).Init()
	g.MakeRule(x.N{`Post`, x.Mmx{3, 8, '`'}})
	g.Pack(x.N{`Fenced`, x.Seq{x.Sav{`Post`}, x.To{x.Val{`Post`}}, x.Val{`Post`}}})
	g.Print()

	// one step at a time
	g.Rules[`x.Sav{"Post"}`].Scan("````").Print()
	g.Rules[`Post`].Scan("````").Print()
	g.Rules[`x.Val{"Post"}`].Scan("````````").Print()
	g.Rules[`x.To{x.Val{"Post"}}`].Scan("....``````").Print()

	// combined
	g.Scan("```.......`````").PrintText()
	g.Scan("```.......`````").Print()

}
Output:

x.N{"Fenced", x.Seq{x.Sav{"Post"}, x.To{x.Val{"Post"}}, x.Val{"Post"}}}
{"N":"Post","B":0,"E":4,"C":[{"B":0,"E":1},{"B":1,"E":2},{"B":2,"E":3},{"B":3,"E":4}],"R":"````"}
{"N":"Post","B":0,"E":4,"C":[{"B":0,"E":1},{"B":1,"E":2},{"B":2,"E":3},{"B":3,"E":4}],"R":"````"}
{"B":0,"E":4,"R":"````````"}
{"B":0,"E":4,"R":"....``````"}
```.......```
{"N":"Fenced","B":0,"E":13,"C":[{"N":"Post","B":0,"E":3,"C":[{"B":0,"E":1},{"B":1,"E":2},{"B":2,"E":3}]},{"B":3,"E":10},{"B":10,"E":13}],"R":"```.......`````"}
Example (See)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.See{`foo`})
	g.Print()

	g.Scan(`fooooo`).PrintText()
	g.Scan(`fooooo`).Print()

	g.Scan(`fo`).Print()
	g.Scan(`bar`).Print()

}
Output:

x.See{x.Str{"foo"}}

{"B":0,"E":0,"R":"fooooo"}
{"B":0,"E":0,"X":"expected: x.See{x.Str{\"foo\"}}","R":"fo"}
{"B":0,"E":0,"X":"expected: x.See{x.Str{\"foo\"}}","R":"bar"}
Example (Seq)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(`foo`, `bar`, x.One{true, false}, x.Mmx{0, 1, `baz`})
	g.Print()

	g.Scan(`foobartrue`).PrintText()
	g.Scan(`foobartruebaz`).PrintText()
	g.Scan(`foobarfalsebaz`).PrintText()
	g.Scan(`foo`).Print()
	g.Scan(`foobarbaz`).PrintError()

}
Output:

x.Seq{x.Str{"foobar"}, x.One{x.Str{"true"}, x.Str{"false"}}, x.Mmx{0, 1, x.Str{"baz"}}}
foobartrue
foobartruebaz
foobarfalsebaz
{"B":0,"E":3,"X":"expected: b","C":[{"B":0,"E":3,"X":"expected: b"}],"R":"foo"}
expected: x.One{x.Str{"true"}, x.Str{"false"}}
Example (To)
package main

import (
	"github.com/rwxrob/rat"
	"github.com/rwxrob/rat/x"
)

func main() {

	g := rat.Pack(x.To{`foo`})
	g.Print()

	g.Scan(`...foo`).PrintText()
	g.Scan(`...foo`).Print()
	g.Scan(`foofoo`).PrintText()
	g.Scan(`foofoo`).Print()
	g.Scan(`.foofo`).PrintText()
	g.Scan(`.foofo`).Print()

	g.Scan(`...fo`).Print()
	g.Scan(`...bar`).Print()

}
Output:

x.To{x.Str{"foo"}}
...
{"B":0,"E":3,"R":"...foo"}

{"B":0,"E":0,"R":"foofoo"}
.
{"B":0,"E":1,"R":".foofo"}
{"B":0,"E":5,"X":"expected: x.To{x.Str{\"foo\"}}","R":"...fo"}
{"B":0,"E":6,"X":"expected: x.To{x.Str{\"foo\"}}","R":"...bar"}

func (*Grammar) AddRule

func (g *Grammar) AddRule(rule *Rule) *Rule

AddRule adds a new rule to the grammar cache keyed to the rule.Name. If a rule was already keyed to that name it is overwritten. If rule.Name is empty a new incremental name is created with the DefaultRuleName prefix. Avoid changing the rule.Name values after added since the key in the grammar cache is hard-coded to the rule.Name when called. If the rule.Name is not important consider NewRule instead (which uses these defaults and requires no argument). Returns self for convenience.

func (*Grammar) Check

func (g *Grammar) Check(r []rune, i int) Result

Check delegates to g.Main.Check.

func (*Grammar) Init

func (g *Grammar) Init() *Grammar

Init initializes the Grammar emptying the Rules if any or creating a new Rules map and setting internal rule ID to 0 and disabling Trace, and emptying Main.

func (*Grammar) MakeAny

func (g *Grammar) MakeAny(in x.Any) *Rule

func (*Grammar) MakeEnd

func (g *Grammar) MakeEnd(in x.End) *Rule

func (*Grammar) MakeIs

func (g *Grammar) MakeIs(in x.Is) *Rule

MakeIs takes an x.IsFunc (which is just a func(r rune) bool) or x.Is type and calls that function in its Check.

func (*Grammar) MakeMmx

func (g *Grammar) MakeMmx(in x.Mmx) *Rule

func (*Grammar) MakeNamed

func (g *Grammar) MakeNamed(in x.N) *Rule

MakeNamed makes two rules pointing to the same CheckFunc, one unnamed and other named (first argument). Both produce results that have the Name field set.

func (*Grammar) MakeNot

func (g *Grammar) MakeNot(in x.Not) *Rule

func (*Grammar) MakeOne

func (g *Grammar) MakeOne(one x.One) *Rule

func (*Grammar) MakeRef

func (g *Grammar) MakeRef(in x.Ref) *Rule

func (*Grammar) MakeRng

func (g *Grammar) MakeRng(in x.Rng) *Rule

func (*Grammar) MakeRule

func (g *Grammar) MakeRule(in any) *Rule

MakeRule fulfills the MakeRule interface. The input argument is usually a rat/x ("ratex") expression type including x.IsFunc functions. Anything else is interpreted as a literal string by using its String method or converting it into a string using the %v (string, []rune, [] byte, rune) or %q representation. Note that MakeRule itself does not check the Rules cache for existing Rules not does it add the rule to that cache. This work is left to the Make* methods themselves or to the AddRule method. The result, however, is the same since MakeRule delegates to those Make* methods.

func (*Grammar) MakeSave

func (g *Grammar) MakeSave(in x.Sav) *Rule

func (*Grammar) MakeSee

func (g *Grammar) MakeSee(in x.See) *Rule

func (*Grammar) MakeSeq

func (g *Grammar) MakeSeq(seq x.Seq) *Rule

func (*Grammar) MakeStr

func (g *Grammar) MakeStr(in any) *Rule

func (*Grammar) MakeTo

func (g *Grammar) MakeTo(in x.To) *Rule

func (*Grammar) MakeVal

func (g *Grammar) MakeVal(in x.Val) *Rule

func (*Grammar) NewRule

func (g *Grammar) NewRule() *Rule

NewRule creates a new rule in the grammar cache using the defaults. It is a convenience when a Name is not needed. See AddRule for details.

func (*Grammar) Pack

func (g *Grammar) Pack(in ...any) *Grammar

Pack allows multiple rules to be passed (unlike MakeRule). If one argument, returns MakeRule for it. If more than one argument, delegates to MakeSeq. Pack is called from the package function of the same name, which describes the valid argument types. As a convenience, a self-reference is returned.

func (Grammar) Print

func (g Grammar) Print()

func (*Grammar) Scan

func (g *Grammar) Scan(in any) Result

Scan checks the input against the current g.Main rule. It is functionally identical to Check but accepts []rune, string, []byte, and io.Reader as input. The error (X) on Result is set if there is a problem.

func (Grammar) String

func (g Grammar) String() string

String fulfills the fmt.Stringer interface by producing compilable Go code containing the Main rule (usually a rat/x.Seq). In this way, code generators for specific, dynamically created grammars can easily be created.

type IsFunc

type IsFunc func(r rune) bool

IsFunc functions return true if the passed rune is contained in a set of runes. The unicode package contains several examples.

type Result

type Result struct {
	N string   // string name (x.Name)
	I int      // integer identifier (x.ID)
	B int      // beginning (inclusive)
	E int      // ending (non-inclusive)
	X error    // error, eXpected something else
	C []Result // children, results within this result
	R []rune   // reference data (underlying slice array shared)
}

Result contains the result of an evaluated Rule function along with its own []rune slice (R).

N (for "Name") is a string name for a result mapped to the x.Name rule. This makes for easy reading and walking of results trees since the string name is included in the output JSON (see String). Normally, mixing x.ID and x.Name rules is avoided.

I (for "ID") is an integer mapped to the x.ID rule. Integer IDs are preferable to names (N) in cases where the use of names would increase the parse tree output JSON (see String) beyond acceptable levels since integer identifiers rarely take more than 2 runes each. Normally, mixing x.ID and x.Name rules is avoided.

B (for "beginning") is the inclusive beginning position index of the first rune in Buf that matches.

E (for "ending") is the exclusive ending position index of the end of the match (and Beg of next match). End must be advanced to the farthest possible point even if the rule fails (and an Err set). This allows recovery attempts from that position.

C (for "children") contains results within this result, sub-matches, equivalent to parenthesized patterns of a regular expression.

X contains any error encountered while parsing.

Note that B == E does not indicate failure. E is usually greater than B, but not necessarily, for example, for lookahead rules are successful without advancing the position at all. E is also greater than B if a partial match was made that still resulted in an error. Only checking X can absolutely confirm a rule failure.

Avoid taking reference to Result

A Result is already made up of references so no further dereferencing is required. The buffer (R) is a slice and therefore all slices point to the same underlying array in memory. And no actual string data is saved in any Result. Rather, the beginning and ending positions within the buffer data are stored and retrieved when needed with methods such as Text().

func ByDepth

func ByDepth(root Result) []Result

ByDepth flattens a rooted node tree of Result structs by traversing in a synchronous, depth-first, preorder way.

func (Result) MarshalJSON

func (m Result) MarshalJSON() ([]byte, error)

MarshalJSON fulfills the encoding.JSONMarshaler interface. The begin (B), end (E) are always included. The name (N), id (I), buffer (R), error (X) and child sub-matches (C) are only included if not empty. Child sub-matches omit the buffer (R). The order of fields is guaranteed not to change. Output is always a single line. There is no dependency on the reflect package. The buffer (R) is rendered as a quoted string (%q) with no further escaping (unlike built-in Go JSON marshaling which escapes things unnecessarily producing unreadable output). The buffer (R) is never included for children (which is the same). An error is never returned.

func (Result) Print

func (m Result) Print()

Print is shortcut for fmt.Println(String).

func (Result) PrintError

func (m Result) PrintError()

PrintError is short for fmt.Println(m.X) but adds position.

func (Result) PrintText

func (m Result) PrintText()

PrintText is short for fmt.Println(m.Text()).

func (Result) String

func (m Result) String() string

String fulfills the fmt.Stringer interface as JSON by calling MarshalJSON. If JSON marshaling fails for any reason a "null" string is returned.

func (Result) Text

func (m Result) Text() string

Text returns the text between beginning (B) and ending (E) (non-inclusively) It is a shortcut for string(res.R[res.B:res.E]).

func (Result) WithName

func (m Result) WithName(names ...string) []Result

WithName returns all results with any of the passed names. Returns zero length slice if no results. As a convenience, multiple names may be passed and all matches for each will be grouped together in the order provided. See WalkDefault for details on the algorithm used.

Example
package main

import (
	"github.com/rwxrob/rat"
)

func main() {

	foo := rat.Result{N: `foo`, I: 1, B: 2, E: 3}
	r1 := rat.Result{N: `r1`, B: 1, E: 3}
	r2 := r1
	r1a := rat.Result{N: `r1a`, B: 1, E: 2}
	r1b := rat.Result{N: `r1b`, B: 2, E: 3, C: []rat.Result{foo}}
	foo.I = 2
	r1.C = []rat.Result{r1a, r1b}
	r2.N = `r2`
	r2.C = []rat.Result{foo}
	foo.I = 3

	root := rat.Result{
		N: `Root`, B: 1, E: 3, C: []rat.Result{r1, r2, foo},
	}

	for _, result := range root.WithName(`foo`) {
		result.Print()
	}

}
Output:

{"N":"foo","I":1,"B":2,"E":3}
{"N":"foo","I":2,"B":2,"E":3}
{"N":"foo","I":3,"B":2,"E":3}

type Rule

type Rule struct {
	Name  string    // uniquely identifying name (sometimes dynamically assigned)
	Text  string    // prefer rat/x compatible expression (ex: x.Seq{"foo", "bar"})
	Check CheckFunc // closure created with a RuleMaker
}

Rule encapsulates a CheckFunc with a Name and Text representation. The Name is use as the unique key in the Grammar.Rules cache. Text can be anything, but it is strongly recommended that it contain rat/x compatible expression so that it can be used directly for code generation.

Rules are created by implementations of RuleMaker the most important of which is Grammar. Almost every Rule encapsulates a different set of arguments enclosed in its CheckFunc. Once created, a Rule should be considered immutable. Field values must not change so that they correspond with the enclosed values within the CheckFunc closure and so that the Name can be used to uniquely identify the Rule.

func (Rule) Print

func (r Rule) Print()

Print is a shortcut for fmt.Println(rule) which calls String.

func (Rule) Scan

func (r Rule) Scan(in any) Result

func (Rule) String

func (r Rule) String() string

String implements the fmt.Stringer interface by returning the Rule.Text.

type RuleMaker

type RuleMaker interface {
	MakeRule(in any) *Rule
}

RuleMaker implementations must return a new Rule created from any input (but usually from rat/x expressions and other Go types). Implementations may choose to cache the newly created rule and simply return a previously cached rule if the input arguments are identified as representing an identical previous rule. This fulfills the PEG packrat parsing requirement for functional memoization.

type VisitFunc

type VisitFunc func(a Result)

VisitFunc is a first-class function passed one result. Typically these functions will enclose variables, contexts, or a channel outside of its own scope to be updated for each visit. Functional recursion is usually used, which may present some limitations depending on the depth required.

Directories

Path Synopsis
x
Package x (as in "expressions") contains the rat/x (pronounced "ratex") language in the form of Go []any types.
Package x (as in "expressions") contains the rat/x (pronounced "ratex") language in the form of Go []any types.

Jump to

Keyboard shortcuts

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