More BPEGN additions and examples

pull/53/head v0.0.15
rwxrob 2 years ago
parent 36c83cab0b
commit f50f5ad24c
No known key found for this signature in database
GPG Key ID: 2B9111F33082AE77

@ -2,5 +2,5 @@ package is
// keep only compound expressions here
var WS = Any{' ', '\n', '\t', '\r'}
var WS = In{' ', '\n', '\t', '\r'}
var Digit = Rng{0, 9}

@ -55,21 +55,21 @@ type Seq []any
// Lk is a set of positive lookahead expressions. If any are seen at
// the current cursor position the scan will proceed without consuming
// them (unlike is.Opt and is.Any). If none are found the scan fails.
// them (unlike is.Opt and is.In). If none are found the scan fails.
type Lk []any
// Not is a set of negative lookahead expressions. If any are seen at
// the current cursor position the scan will fail. Otherwise, the scan
// proceeds from that same position.
// the current cursor position the scan will fail and the scan is never
// advanced.
type Not []any
// Any is a set of advancing expressions. If any scannable in the slice
// In is a set of advancing expressions. If any expression in the slice
// is found the scan advances to the end of that expression and
// continues. If none of the expressions is found the scan fails.
// Evaluation of expressions is always left to right allowing allowing
// Evaluation of expressions is always left to right allowing
// parser developers to prioritize common expressions at the beginning
// of the slice.
type Any []any
type In []any
// Opt is a set of optional advancing expressions. If any expression is
// found the scan is advanced (unlike Lk, which does not advance).
@ -86,19 +86,13 @@ type Opt []any
//
type To []any
// Inc is a set of advancing expressions that mark an inclusive boundary
// Toi is a set of advancing expressions that mark an inclusive boundary
// after which the scan should stop. The matching expression will be
// included in the advance (unlike is.To).
type Inc []any
type Toi []any
// --------------------------- parameterized --------------------------
// EscTo is identical to is.To{s.Seq{is.Not{E,To}}}.
type EscTo struct {
E any
To any
}
// MMx is a parameterized advancing expression that matches an inclusive
// minimum and maximum count of the given expression (This). Use within
// is.Lk to disable advancement.
@ -127,6 +121,11 @@ type N struct {
This any
}
// Any is short for is.N{tk.ANY}.
type Any struct {
N int
}
// Rng is a parameterized advancing expression that matches a single
// Unicode code point (rune, uint32) from an inclusive consecutive set
// from First to Last (First,Last). Use within is.Lk to disable

@ -2,16 +2,17 @@
// SPDX-License-Identifier: Apache-2.0
/*
Package scan implements a non-linear, rune-centric, buffered data
scanner that includes its own high-level syntax comprised of scannable
structures from the "is" subpackage making parser generation (by hand or
code generation) trivial from any structured meta languages such as
PEGN, PEG, EBNF, ABNF, etc. Most will use the scanner to create parsers
quickly by hand where a regular expression will not suffice. See the
"is" and "tk" packages for a growing number of common, centrally
maintain scannables for your parsing pleasure. Also see the "mark"
(BonzaiMark) subpackage for a working example of the scanner in action,
which is used by the included help.Cmd command.
Package scan implements a non-linear, rune-centric, buffered data,
extendable scanner that includes its own high-level parsing expression
grammar (BPEGN) comprised of Go slices and structs used as expressions
from the "is" subpackage making parser generation (by hand or code)
trivial from any structured meta languages such as PEGN, PEG, EBNF,
ABNF, etc. Most will use the scanner to create parsers quickly where
a regular expression will not suffice. See the "is" and "tk" packages
for a growing number of common, centrally maintain expressions for your
parsing pleasure. Also see the "mark" (BonzaiMark) subpackage for
a working examples of the scanner in action, which is used by the
included Bonzai help.Cmd command and others.
*/
package scan
@ -234,7 +235,7 @@ func (s *R) LookSlice(beg *Cur, end *Cur) string {
// string - "foo" simple string
// rune - 'f' uint32, but "rune" in errors
// is.Not{any...} - negative look-ahead set (slice)
// is.Any{any...} - one positive look-ahead from set (slice)
// is.In{any...} - one positive look-ahead from set (slice)
// is.Seq{any...} - required positive look-ahead sequence (slice)
// is.Opt{any...} - optional positive look-ahead set (slice)
// is.Min{n,any} - minimum positive look-aheads
@ -247,15 +248,24 @@ func (s *R) LookSlice(beg *Cur, end *Cur) string {
// allows for very readable functional grammar parsers to be created
// quickly without exceptional overhead from additional function calls
// and indirection. As some have said, "it's regex without the regex."
func (s *R) Expect(scannables ...any) (*Cur, error) {
func (s *R) Expect(expr ...any) (*Cur, error) {
var beg, end *Cur
beg = s.Cur
for _, m := range scannables {
for _, m := range expr {
// please keep the most common at the top
switch v := m.(type) {
case rune: // ------------------------------------------------------
if v != tk.ANY && s.Cur.Rune != v {
err := s.ErrorExpected(m)
s.Jump(beg)
return nil, err
}
end = s.Mark()
s.Scan()
case string: // ----------------------------------------------------
if v == "" {
return nil, fmt.Errorf("expect: cannot parse empty string")
@ -270,16 +280,7 @@ func (s *R) Expect(scannables ...any) (*Cur, error) {
s.Scan()
}
case rune: // ------------------------------------------------------
if s.Cur.Rune != v {
err := s.ErrorExpected(m)
s.Jump(beg)
return nil, err
}
end = s.Mark()
s.Scan()
case is.Inc: // -----------------------------------------------------
case is.Toi: // -----------------------------------------------------
var m *Cur
for m == nil && s.Cur.Rune != tk.EOD {
for _, i := range v {
@ -295,7 +296,7 @@ func (s *R) Expect(scannables ...any) (*Cur, error) {
end = m
case is.To: // -----------------------------------------------------
var m *Cur
var m, b4 *Cur
OUT:
for s.Cur.Rune != tk.EOD {
for _, i := range v {
@ -304,14 +305,14 @@ func (s *R) Expect(scannables ...any) (*Cur, error) {
break OUT
}
}
b4 = s.Mark()
s.Scan()
}
if m == nil {
err := s.ErrorExpected(v)
s.Jump(beg)
return nil, err
}
end = m
end = b4
case is.Lk: // ----------------------------------------------------
var m *Cur
@ -327,16 +328,16 @@ func (s *R) Expect(scannables ...any) (*Cur, error) {
end = s.Mark()
case is.Not: // ----------------------------------------------------
m := s.Mark()
for _, i := range v {
if _, e := s.check(i); e == nil {
if c, _ := s.check(i); c != nil {
err := s.ErrorExpected(v, i)
s.Jump(beg)
return nil, err
}
}
end = s.Mark()
end = m
case is.Any: // -----------------------------------------------------
case is.In: // -----------------------------------------------------
var m *Cur
for _, i := range v {
var err error
@ -353,12 +354,13 @@ func (s *R) Expect(scannables ...any) (*Cur, error) {
end = m
case is.Seq: // ----------------------------------------------------
m, err := s.Expect(v...)
m := s.Mark()
c, err := s.Expect(v...)
if err != nil {
s.Jump(beg)
s.Jump(m)
return nil, err
}
end = m
end = c
case is.Opt: // ----------------------------------------------------
var m *Cur
@ -432,6 +434,13 @@ func (s *R) Expect(scannables ...any) (*Cur, error) {
}
end = m
case is.Any: // ----------------------------------------------------
for n := 0; n < v.N; n++ {
s.Scan()
}
end = s.Mark()
s.Scan()
case is.Rng: // ----------------------------------------------------
if !(v.First <= s.Cur.Rune && s.Cur.Rune <= v.Last) {
err := s.ErrorExpected(v)
@ -460,7 +469,7 @@ func (s *R) Expect(scannables ...any) (*Cur, error) {
end = s.Mark()
default: // --------------------------------------------------------
return nil, fmt.Errorf("expect: unscannable expression (%T)", m)
return nil, fmt.Errorf("expect: unexpr expression (%T)", m)
}
}
return end, nil
@ -468,7 +477,7 @@ func (s *R) Expect(scannables ...any) (*Cur, error) {
// ErrorExpected returns a verbose, one-line error describing what was
// expected when it encountered whatever the scanner last scanned. All
// scannable types are supported. See Expect.
// expression types are supported. See Expect.
func (s *R) ErrorExpected(this any, args ...any) error {
var msg string
but := fmt.Sprintf(` at %v`, s)
@ -487,7 +496,7 @@ func (s *R) ErrorExpected(this any, args ...any) error {
msg = fmt.Sprintf(`expected %q`, v)
case is.Not:
msg = fmt.Sprintf(`unexpected %q`, args[0])
case is.Any:
case is.In:
str := `expected one of %q`
msg = fmt.Sprintf(str, v)
case is.Seq:
@ -497,7 +506,7 @@ func (s *R) ErrorExpected(this any, args ...any) error {
str := `expected an optional %v`
msg = fmt.Sprintf(str, v)
case is.Mn1:
str := `expected at least one of %q`
str := `expected one or more %q`
msg = fmt.Sprintf(str, v.This)
case is.Min:
str := `expected min %v of %q`
@ -511,7 +520,7 @@ func (s *R) ErrorExpected(this any, args ...any) error {
case is.Rng:
str := `expected range [%v-%v]`
msg = fmt.Sprintf(str, string(v.First), string(v.Last))
case is.To, is.Inc:
case is.To, is.Toi:
str := `%q not found`
msg = fmt.Sprintf(str, v)
default:
@ -524,8 +533,8 @@ func (s *R) ErrorExpected(this any, args ...any) error {
func (s *R) NewLine() { s.Cur.NewLine() }
// check behaves exactly like Expect but jumps back to the original
// cursor position after scanning for expected scannable values.
func (s *R) check(scannables ...any) (*Cur, error) {
// cursor position after scanning for expected expression values.
func (s *R) check(expr ...any) (*Cur, error) {
defer s.Jump(s.Mark())
return s.Expect(scannables...)
return s.Expect(expr...)
}

@ -8,6 +8,7 @@ import (
"github.com/rwxrob/bonzai/scan"
"github.com/rwxrob/bonzai/scan/is"
"github.com/rwxrob/bonzai/scan/tk"
)
func ExampleNew_string() {
@ -176,10 +177,10 @@ func ExampleErrorExpected() {
func ExampleExpect_basic() {
s, _ := scan.New("some thing")
c, _ := s.Expect("some", ' ', "thin")
c.Print()
s.Print()
s.Scan()
s.Print()
c.Print() // pointing to 'n' since last match
s.Print() // pointing to next after 'n' ('g')
s.Scan() // advance one
s.Print() // <EOD> since no more data
// Output:
// U+006E 'n' 1,9-9 (9-9)
// U+0067 'g' 1,10-10 (10-10)
@ -196,7 +197,7 @@ func ExampleExpect_lk() {
s.Print()
c, _ = s.Expect(is.Lk{is.Rng{'l', 'p'}})
s.Print() // not advanced
c, _ = s.Expect(is.Any{is.Rng{'l', 'p'}})
c, _ = s.Expect(is.Rng{'l', 'p'})
s.Print() // advanced
// Output:
// expected ["foo"] at U+0073 's' 1,1-1 (1-1)
@ -206,28 +207,66 @@ func ExampleExpect_lk() {
// U+0065 'e' 1,4-4 (4-4)
}
func ExampleExpect_not() {
func ExampleExpect_not_Success() {
s, _ := scan.New("some thing")
c1, e1 := s.Expect(is.Not{"foo"})
c1.Print()
fmt.Println(e1)
c2, e2 := s.Expect(is.Not{"some"})
c2.Print()
fmt.Println(e2)
c, _ := s.Expect(is.Not{"foo"})
c.Print() // not advanced, but also not <nil>
s.Print() // not advanced at all
// Output:
// U+0073 's' 1,1-1 (1-1)
// <nil>
// <nil>
// U+0073 's' 1,1-1 (1-1)
}
func ExampleExpect_not_Fail() {
s, _ := scan.New("some thing")
_, err := s.Expect(is.Not{"some"})
fmt.Println(err)
s.Print() // not advanced at all
// Output:
// unexpected "some" at U+0073 's' 1,1-1 (1-1)
// U+0073 's' 1,1-1 (1-1)
}
func ExampleExpect_not_Seq_Fail() {
s, _ := scan.New("some thing wonderful")
_, err := s.Expect(is.Seq{is.Not{`s`}, `o`})
fmt.Println(err) // sees the s so fails
s.Print() // not advanced
// Output:
// unexpected "s" at U+0073 's' 1,1-1 (1-1)
// U+0073 's' 1,1-1 (1-1)
}
func ExampleExpect_not_Seq_Success() {
s, _ := scan.New("some thing wonderful")
c, _ := s.Expect(is.Seq{is.Not{`n`}, is.In{`som`}})
c.Print() // pointing to last in match 'm'
s.Print() // advanced to next after match 'e'
// Output:
// U+006D 'm' 1,3-3 (3-3)
// U+0065 'e' 1,4-4 (4-4)
}
func ExampleExpect_any() {
/*
func ExampleExpect_not_To_Seq_Success() {
s, _ := scan.New("some thing wonderful")
//c, _ := s.Expect(is.To{is.Seq{is.Not{`s`}, `o`}})
c, _ := s.Expect(is.To{"wo"})
c.Print() // pointing to only 'o' not preceeded by s
s.Print() // advanced to next after second 'o' ('n')
// Output:
// U+006F 'o' 1,13-13 (13-13)
//
}
*/
func ExampleExpect_in() {
s, _ := scan.New("some thing")
s.Scan()
c, _ := s.Expect(is.Any{'O', 'o', "ome"})
c, _ := s.Expect(is.In{'O', 'o', "ome"})
c.Print()
s.Print()
_, err := s.Expect(is.Any{'x', 'y', "zee"})
_, err := s.Expect(is.In{'x', 'y', "zee"})
fmt.Println(err)
// Output:
// U+006F 'o' 1,2-2 (2-2)
@ -235,19 +274,82 @@ func ExampleExpect_any() {
// expected one of ['x' 'y' "zee"] at U+006D 'm' 1,3-3 (3-3)
}
func ExampleExpect_seq() {
func ExampleExpect_avoid_Not_with_In() {
s, _ := scan.New("some thing")
s.Snap()
s.Expect(is.Seq{"some", ' ', "thing"})
s.Print()
c, _ := s.Expect(is.In{is.Not{'s'}, is.Rng{'a', 'z'}})
c.Print() // unexpected success
s.Print() // advanced to 'o'
s.Back()
s.Print()
_, err := s.Expect(is.Seq{"some", "thing"})
// use is.Seq instead
_, err := s.Expect(is.Seq{is.Not{'s'}, is.Rng{'a', 'z'}})
fmt.Println(err)
s.Print() // not advanced
// Output:
// <EOD>
// U+0073 's' 1,1-1 (1-1)
// U+006F 'o' 1,2-2 (2-2)
// unexpected 's' at U+0073 's' 1,1-1 (1-1)
// U+0073 's' 1,1-1 (1-1)
}
func ExampleExpect_seq_Success() {
s, _ := scan.New("some thing")
c, _ := s.Expect(is.Seq{"some", ' ', "thin"})
c.Print() // same as "some thin", points at 'n'
s.Print() // advanced to 'g'
// Output:
// U+006E 'n' 1,9-9 (9-9)
// U+0067 'g' 1,10-10 (10-10)
}
func ExampleExpect_seq_Fail() {
s, _ := scan.New("some thing")
_, err := s.Expect(is.Seq{"some", "thin"})
fmt.Println(err)
s.Print() // not advanced at all
// Output:
// expected rune 't' at U+0020 ' ' 1,5-5 (5-5)
// U+0073 's' 1,1-1 (1-1)
}
func ExampleExpect_seq_Not_Success() {
s, _ := scan.New("some thing")
c, _ := s.Expect(is.Seq{"some", ` `, is.Not{`T`}, "thin"})
c.Print() // same as "some thin"
s.Print() // advanced to next after ('g')
// Output:
// U+006E 'n' 1,9-9 (9-9)
// U+0067 'g' 1,10-10 (10-10)
}
func ExampleExpect_seq_Not_Fail() {
s, _ := scan.New("some Thing")
_, err := s.Expect(is.Seq{"some", ` `, is.Not{`T`}, "ignored"})
fmt.Println(err)
s.Print() // not advanced at all
// Output:
// unexpected "T" at U+0054 'T' 1,6-6 (6-6)
// U+0073 's' 1,1-1 (1-1)
}
func ExampleExpect_token_ANY() {
s, _ := scan.New("some thing")
c, _ := s.Expect(tk.ANY)
c.Print() // same as `s` or s.Scan()
s.Print() // advances
// Output:
// U+0073 's' 1,1-1 (1-1)
// U+006F 'o' 1,2-2 (2-2)
}
func ExampleExpect_any_Success() {
s, _ := scan.New("some thing")
c, _ := s.Expect(is.Any{5})
c.Print() // same as "some "
s.Print() // advanced to next after ('t')
// Output:
// U+0074 't' 1,6-6 (6-6)
// U+0068 'h' 1,7-7 (7-7)
}
func ExampleExpect_opt() {
@ -383,23 +485,34 @@ func ExampleExpect_hook() {
}
func ExampleExpect_inc() {
func ExampleExpect_to_Success() {
s, _ := scan.New("some thing")
s.Expect(is.Inc{`i`})
s.Print()
c, _ := s.Expect(is.To{`e`})
c.Print() // same as "som", points to 'm'
s.Print() // scanned next after ('e')
// Output:
// U+006E 'n' 1,9-9 (9-9)
// U+006D 'm' 1,3-3 (3-3)
// U+0065 'e' 1,4-4 (4-4)
}
func ExampleExpect_to() {
func ExampleExpect_to_Seq_Success() {
s, _ := scan.New("some thing")
s.Expect(is.To{`i`})
s.Print()
_, err := s.Expect(is.To{`z`})
fmt.Println(err)
c, _ := s.Expect(is.To{is.Seq{`e`}})
c.Print() // same as "som", points to 'm'
s.Print() // scanned next after ('e')
// Output:
// U+0069 'i' 1,8-8 (8-8)
// ["z"] not found at <EOD>
// U+006D 'm' 1,3-3 (3-3)
// U+0065 'e' 1,4-4 (4-4)
}
func ExampleExpect_toi() {
s, _ := scan.New("some thing")
c, _ := s.Expect(is.Toi{`e`})
c.Print() // same as "some", points to 'e'
s.Print() // scanned next after (' ')
// Output:
// U+0065 'e' 1,4-4 (4-4)
// U+0020 ' ' 1,5-5 (5-5)
}
func ExampleSnap() {

@ -1,6 +1,18 @@
/*
Package tk contains tokens that when detected in the Expect expression stream cause specific behavior or change the state of the scan.R.
*/
package tk
// EOD is a special value that is returned when the end of data is
// reached enabling functional parser functions to look for it reliably
// no matter what is being parsed.
const EOD = 1<<31 - 1 // max int32
const (
// EOD is a special value that is returned when the end of data is
// reached enabling functional parser functions to look for it reliably
// no matter what is being parsed. Since rune is alias for int32 and
// Unicode (currently) ends at \U+FFFD we are safe to use the largest
// possible valid rune value.
EOD rune = 1<<31 - (iota + 1) // max int32
// ANY represents any possible single rune similar to question mark (?)
// when globing and dot (.) in most regular expression syntaxes.
ANY
)

@ -4,26 +4,49 @@
package tree
import (
"encoding/json"
_json "encoding/json"
"fmt"
"log"
"strings"
"github.com/rwxrob/bonzai/each"
"github.com/rwxrob/bonzai/json"
"github.com/rwxrob/bonzai/util"
"github.com/rwxrob/bonzai/scan"
"github.com/rwxrob/bonzai/scan/is"
)
// Tree is an encapsulating struct to contain the Trunk of Nodes and
// their Types. The New method also ensures that every Node knows about
// the Tree to which it belongs. Most designs will then fetch the
// t.Trunk (root Node) and use that. As with most Bonzai structs, Tree
// implements the PrintAsJSON interface.
const UNKNOWN = 0
// Tree is an encapsulating struct to contain the Root Node and all
// possible Types for any Node. The tree.New method should be called
// (rather than new(Tree)) to ensure that every Node knows about the
// Tree to which it belongs. Most users of a Tree will make direct use
// of Tree.Root (which can safely be swapped out for a root Node
// reference at any time). Tree implements the PrintAsJSON interface and
// uses custom methods for MarshalJSON and UnmarshalJSON to facilitate
// storage, transfer, and documentation.
type Tree struct {
Trunk Node
Root *Node
types []string
typesm map[string]int
}
// used to Marshal/Unmarshal
type _Tree struct {
Root *Node
Types []string
}
// SetTypes sets the internal types slice and index map corresponding to
// the given integer. It is normally called from New when creating a new
// Tree.
func (t *Tree) SetTypes(types []string) {
t.types = []string{"UNKNOWN"}
t.types = append(t.types, types...)
t.typesm = map[string]int{"UNKNOWN": 0}
for n, v := range types {
t.typesm[v] = n + 1
}
}
// Seed returns new detached Node from the same Tree
func (t *Tree) Seed(i ...any) *Node {
leaf := new(Node)
@ -38,24 +61,20 @@ func (t *Tree) Seed(i ...any) *Node {
return leaf
}
const (
UNKNOWN = 0
)
// New creates and initializes a new tree creating it's Trunk (root
// Node) and assigning it the type of 1 (0 is reserved for UNKNOWN),
// which corresponds to the first Type string. Calling this constructor
// is preferred over creating Tree references directly since it requires
// specifying the types used in the tree. Some uses, however, may not
// know the types in advance and need to assign them later to
// Tree.Types. Most will proceed to use the t.Trunk (root node) after
// New creates and initializes a new tree creating it's Root Node
// reference and assigning it the type of 1 (0 is reserved for UNKNOWN),
// which corresponds to the first Type string (index 0). Calling this
// constructor is preferred over creating Tree references directly since
// it requires specifying the types used in the tree. Some uses,
// however, may not know the types in advance and need to assign them
// later to Tree.Types. Most will proceed to use the t.Root after
// calling New.
func New(types []string) *Tree {
t := new(Tree)
t.Types = []string{"UNKNOWN"}
t.Types = append(t.Types, types...)
t.Trunk.T = 1
t.Trunk.tree = t
t.SetTypes(types)
t.Root = new(Node)
t.Root.T = 1
t.Root.tree = t
return t
}
@ -73,411 +92,50 @@ func (s *Tree) Print() { fmt.Println(s.JSON()) }
// Log implements PrintAsJSON.
func (s *Tree) Log() { log.Print(s.JSON()) }
// ------------------------------- Node -------------------------------
// Nodes are for constructing rooted node trees of typed strings based
// on the tenet of the UNIX philosophy that suggests to focus on
// parsable text above all and converting when needed later. Usually,
// you will start with the Trunk (root Node) of a new Tree so that you
// can specify the types of your Nodes.
//
// Branch or Leaf
//
// A Node can either be a "branch" or a "leaf" but not both. Branches
// have other leaves and branches under them. Leaves do not. A leaf can
// transform into a branch if a branch or leaf is added under it. For
// the same of efficiency, any method that transforms a leaf into
// a branch for any reason will automatically discard its value without
// warning.
//
// Types
//
// An empty Node has type of 0 and must display as "[]". Types must have
// both a positive integer and a consistent name or tag to go with it.
// A new Tree will always assign the type 1 to the root Node. Types will
// Print as integers when printing in short form and provide the fastest
// parsing. Type names and whitespace are added when PrettyPrint is
// called.
type Node struct {
T int // type
V string // value, zero-ed out when anything added under
tree *Tree // source of Types, etc.
up *Node // branch
left *Node // previous
right *Node // next
first *Node // first sub
last *Node // last sub
types *[]string
}
// Branch returns the current branch this Node is on, or nil.
func (n *Node) Branch() *Node { return n.up }
// Left returns the Node to immediate left or nil.
func (n *Node) Left() *Node { return n.left }
// Right returns the Node to immediate right or nil.
func (n *Node) Right() *Node { return n.right }
// FirstUnder returns the first Node under current Node.
func (n *Node) FirstUnder() *Node { return n.first }
// LastUnder returns the last Node under current Node.
func (n *Node) LastUnder() *Node { return n.last }
// AllUnder returns all Nodes under the current Node or nil.
func (n *Node) AllUnder() []*Node {
if n.first == nil {
return nil
}
cur := n.first
c := []*Node{cur}
for {
cur = cur.right
if cur == nil {
break
}
c = append(c, cur)
}
return c
}
// IsRoot is not currently on any branch even though it might be
// associated still with a given Tree.
func (n *Node) IsRoot() bool { return n.up == nil }
// IsDetached returns true if Node has no attachments to any other Node.
func (n *Node) IsDetached() bool {
return n.up == nil && n.first == nil &&
n.last == nil && n.left == nil && n.right == nil
}
// IsLeaf returns true if Node has no branch of its own but does have
// a value. Note that a leaf can transform into a branch once a leaf or
// branch is added under it.
func (n *Node) IsLeaf() bool { return n.first == nil && n.V != "" }
// IsBranch returns true if Node has anything under it at all
func (n *Node) IsBranch() bool { return n.first != nil }
// IsNull returns true if Node has no value and nothing under it but is
// OnBranch.
func (n *Node) IsNull() bool { return n.first == nil && n.V == "" }
// Info logs a summary of the properties of the Node mostly for
// use when debugging. Remember to log.SetOutput(os.Stdout) and
// log.SetFlags(0) when using this in Go example tests.
func (n *Node) Info() {
each.Log(util.Lines(fmt.Sprintf(`------
Type: %v
Value: %q
IsRoot: %v
IsDetached: %v
IsLeaf: %v
IsBranch: %v
IsNull: %v`,
n.T, n.V, n.IsRoot(), n.IsDetached(),
n.IsLeaf(), n.IsBranch(), n.IsNull())))
}
// ----------------- Node PrintAsJSON interface (plus) ----------------
// MarshalJSON fulfills the interface and avoids use of slower
// reflection-based parsing. Nodes must be either branches ([1,[]]) or
// leafs ([1,"foo"]). Branches are allowed to have nothing on them but
// usually have other branches and leaves. This design means that every
// possible Node can be represented by a highly efficient two-element
// array. This MarshalJSON implementation uses the Bonzai json package
// which more closely follows the JSON standard for acceptable string
// data, notably Unicode characters are not escaped and remain readable.
func (n *Node) MarshalJSON() ([]byte, error) {
list := n.AllUnder()
if len(list) == 0 {
if n.V == "" {
if n.T == 0 {
return []byte("[]"), nil
}
return []byte(fmt.Sprintf(`[%d]`, n.T)), nil
}
return []byte(fmt.Sprintf(`[%d,"%v"]`, n.T, json.Escape(n.V))), nil
}
byt, _ := list[0].MarshalJSON()
buf := "[" + string(byt)
for _, u := range list[1:] {
byt, _ = u.MarshalJSON() // no error ever returned
buf += "," + string(byt)
}
buf += "]"
return []byte(fmt.Sprintf(`[%d,%v]`, n.T, buf)), nil
}
// PrettyPrint uses type names instead of their integer
// equivalents and adds indentation and whitespace.
func (n *Node) PrettyPrint() {
fmt.Println(n.pretty(0))
}
// called recursively to build the JSONL string
func (n *Node) pretty(depth int) string {
buf := ""
indent := strings.Repeat(" ", depth*2)
depth++
buf += fmt.Sprintf(`%v["%v", `, indent, n.tree.Types[n.T])
if n.first != nil {
buf += "[\n"
under := n.AllUnder()
for i, c := range under {
buf += c.pretty(depth)
if i != len(under)-1 {
buf += ",\n"
} else {
buf += fmt.Sprintf("\n%v]", indent)
}
}
buf += "]"
} else {
buf += fmt.Sprintf(`"%v"]`, json.Escape(n.V))
}
return buf
}
// JSON implements PrintAsJSON multi-line, 2-space indent JSON output.
func (s *Node) JSON() string { b, _ := s.MarshalJSON(); return string(b) }
// String implements PrintAsJSON and fmt.Stringer interface as JSON.
func (s Node) String() string { return s.JSON() }
// Print implements PrintAsJSON.
func (s *Node) Print() { fmt.Println(s.JSON()) }
// Log implements PrintAsJSON.
func (s Node) Log() { log.Print(s.JSON()) }
// ------------------------------- Nodes ------------------------------
// NewRight creates a new Node and grafts it to the right of the current
// one on the same branch. The type and initial value can optionally be
// passed as arguments.
func (n *Node) NewRight(i ...any) *Node {
leaf := n.tree.Seed(i...)
n.GraftRight(leaf)
return leaf
}
// NewLeft creates a new Node and grafts it to the left of current one
// on the same branch. The type and initial value can optionally be
// passed as arguments.
func (n *Node) NewLeft(i ...any) *Node {
leaf := n.tree.Seed(i...)
n.GraftLeft(leaf)
return leaf
}
// NewUnder creates a new Node and grafts it down below the current one
// adding it to the left of other branches and leaves below. The type
// and initial value can optionally be passed as arguments.
func (n *Node) NewUnder(i ...any) *Node {
leaf := n.tree.Seed(i...)
n.GraftUnder(leaf)
return leaf
// MarshalJSON implements the json.Marshaler interface to include the
// otherwise private Types list.
func (s *Tree) MarshalJSON() ([]byte, error) {
return json.Marshal(_Tree{s.Root, s.types})
}
// Graft replaces current node with a completely new Node and returns
// it. Anything under the grafted node will remain and anything under
// the node being replaced will go with it.
func (n *Node) Graft(c *Node) *Node {
c.up = n.up
c.left = n.left
c.right = n.right
// update branch parent
if n.up.last == n {
n.up.last = c
}
if n.up.first == n {
n.up.first = c
// UnmarshalJSON implements the json.Unmarshaler interface to include
// the otherwise private Types list.
func (s *Tree) UnmarshalJSON(in []byte) error {
m := new(_Tree)
if err := json.Unmarshal(in, m); err != nil {
return err
}
// update peers
if n.left != nil {
n.left.right = c
}
if n.right != nil {
n.right.left = c
}
// detach
n.up = nil
n.right = nil
n.left = nil
return c
s.Root = m.Root
s.types = m.Types
return nil
}
// GraftRight adds existing Node to the right of itself as a peer and
// returns it.
func (n *Node) GraftRight(r *Node) *Node {
r.up = n.up
if n.right == nil {
r.left = n
n.right = r
if n.up != nil {
n.up.last = r
}
return r
}
r.right = n.right
r.left = n
n.right.left = r
n.right = r
return r
}
// ------------------------------- parse ------------------------------
// GraftLeft adds existing Node to the left of itself and returns it.
func (n *Node) GraftLeft(l *Node) *Node {
l.up = n.up
if n.left == nil {
l.right = n
n.left = l
if n.up != nil {
n.up.first = l
}
return l
// Parse takes a string, []byte, or io.Reader of compressed or "pretty"
// JSON data and returns a new tree. See Node.MarshalJSON for more.
func Parse(in any, types []string) (*Tree, error) {
t := New(types)
s, err := scan.New(in)
if err != nil {
return nil, err
}
l.left = n.left
l.right = n
n.left.right = l
n.left = l
return l
}
// GraftUnder adds existing node under current node to the right of
// others already underneath and returns it.
func (n *Node) GraftUnder(c *Node) *Node {
c.up = n
if n.first == nil {
n.first = c
n.last = c
return c
}
return n.last.GraftRight(c)
}
// shortcuts
ws := is.Opt{is.WS}
dq := is.Seq{'\\', '"'}
// Prune removes and returns itself and grafts everything together to
// fill void.
func (n *Node) Prune() *Node {
if n.up != nil {
if n.up.first == n {
n.up.first = n.right
}
if n.up.last == n {
n.up.last = n.left
}
}
if n.left != nil {
n.left.right = n.right
}
if n.right != nil {
n.right.left = n.left
}
n.up = nil
n.right = nil
n.left = nil
return n
}
// Take takes everything under target Node and adds underneath itself.
func (n *Node) Take(from *Node) {
if from.first == nil {
return
}
c := from.first.Prune()
n.GraftUnder(c)
n.Take(from)
}
// Action is a first-class function type used when Visiting each Node.
// The return value will be sent to a channel as each Action completes.
// It can be an error or anything else.
type Action func(n *Node) any
// Visit will call the Action function passing it every node traversing
// in the most predictable way, from top to bottom and left to right on
// each level of depth. If the optional rvals channel is passed the
// return values for the actions will be sent to it synchronously. This
// may be preferable for gathering data from the node tree in some
// cases. The Action could also be implemented as a closure function
// enclosing some state variable. If the rvals channel is nil it will
// not be opened.
func (n *Node) Visit(act Action, rvals chan interface{}) {
if rvals == nil {
act(n)
} else {
rvals <- act(n)
}
if n.first == nil {
return
}
for _, c := range n.AllUnder() {
c.Visit(act, rvals)
}
return
}
// nodes
jstrc := is.Seq{is.Esc{'\\', '"', is.Ugraphic}}
jstr := is.Seq{'"', is.Mn1{jstrc}, '"'}
ntype := is.Seq{is.Not{'"'}, is.Uletter}
ntyp := is.In{is.Mn1{is.Digit}}
null := is.Seq{'[', ws, ntyp, ws, ']'}
leaf := is.Seq{'[', ws, ntyp, ws, jstr, ']'}
// VisitAsync walks a parent Node and all its Children asynchronously by
// flattening the Node tree into a one-dimensional array and then
// sending each Node its own goroutine Action call. The limit must
// set the maximum number of simultaneous goroutines (which can usually
// be in the thousands) and must be 2 or more or will panic. If the
// channel of return values is not nil it will be sent all return values
// as Actions complete. Note that this method uses twice the memory of
// the synchronous version and requires slightly more startup time as
// the node collection is done (which actually calls Visit in order to
// build the flattened list of all nodes). Therefore, VisitAsync should
// only be used when the action is likely to take a non-trivial amount
// of time to execute, for example, when there is significant IO
// involved (disk, Internet, etc.).
func (n *Node) VisitAsync(act Action, lim int, rvals chan any) {
nodes := []*Node{}
if lim < 2 {
panic("limit must be 2 or more")
}
add := func(node *Node) any {
nodes = append(nodes, node)
return nil
}
n.Visit(add, nil)
// use buffered channel to throttle
sem := make(chan interface{}, lim)
for _, node := range nodes {
sem <- true
if rvals == nil {
go func(node *Node) {
defer func() { <-sem }()
act(node)
}(node)
continue
} else {
go func(node *Node) {
defer func() { <-sem }()
rvals <- act(node)
}(node)
}
}
// waits for all (keeps filling until full again)
for i := 0; i < cap(sem); i++ {
sem <- true
}
// all goroutines have now finished
if rvals != nil {
close(rvals)
}
// consume optional initial whitespace
s.Expect(ws)
s.Log()
return t, nil
}

@ -4,7 +4,7 @@ import (
"fmt"
"log"
"os"
"time"
"testing"
"github.com/rwxrob/bonzai/tree"
)
@ -13,7 +13,7 @@ func ExampleTree() {
t := tree.New([]string{"foo", "bar"})
t.Print()
// Output:
// {"Trunk":[1],"Types":["UNKNOWN","foo","bar"]}
// {"Root":[1],"Types":["UNKNOWN","foo","bar"]}
}
func ExampleTree_Log() {
@ -24,7 +24,7 @@ func ExampleTree_Log() {
t := tree.New([]string{"foo", "bar"})
t.Log()
// Output:
// {"Trunk":[1],"Types":["UNKNOWN","foo","bar"]}
// {"Root":[1],"Types":["UNKNOWN","foo","bar"]}
}
func ExampleTree_Seed() {
@ -39,302 +39,33 @@ func ExampleTree_Seed() {
// true true true
}
func ExampleNode_Log() {
defer log.SetOutput(os.Stderr)
defer log.SetFlags(log.Flags())
log.SetOutput(os.Stdout)
log.SetFlags(0)
n := tree.New([]string{"foo", "bar"}).Trunk
n.Log()
// Output:
// [1]
}
func ExampleNode_Info() {
n := tree.New([]string{"foo", "bar"}).Trunk
n.Info()
// Type: 1
// Value: ""
// IsRoot: true
// IsDetached: true
// IsLeaf: false
// IsBranch: false
// IsNull: true
}
func ExampleNode_leaf() {
n := tree.New([]string{"foo", "bar"}).Trunk
n.Print()
n.V = "something"
n.Print()
n.T = 0
n.V = ""
n.Print()
// Output:
// [1]
// [1,"something"]
// []
}
func ExampleNode_branch() {
n := tree.New([]string{"foo", "bar"}).Trunk
n.NewUnder(2, "something")
n.Print()
n.PrettyPrint()
// Output:
// [1,[[2,"something"]]]
// ["foo", [
// ["bar", "something"]
// ]]
}
func ExampleNode_NewRight() {
n := tree.New([]string{"foo", "bar"}).Trunk
n.Print()
t := n.NewRight(2, "some")
t.Print()
n.Right().Print()
//Output:
// [1]
// [2,"some"]
// [2,"some"]
}
func ExampleNode_NewLeft() {
n := tree.New([]string{"foo", "bar"}).Trunk
n.Print()
t := n.NewLeft(2, "some")
t.Print()
n.Left().Print()
//Output:
// [1]
// [2,"some"]
// [2,"some"]
}
func ExampleNode_NewUnder() {
n := tree.New([]string{"foo", "bar"}).Trunk
n.Print()
t := n.NewUnder(2, "some")
t.Print()
n.FirstUnder().Print()
n.LastUnder().Print()
n.Print()
//Output:
// [1]
// [2,"some"]
// [2,"some"]
// [2,"some"]
// [1,[[2,"some"]]]
}
func ExampleNode_Graft() {
t := tree.New([]string{"foo", "😭", "💔"})
n := t.Trunk
u := n.NewUnder(2, "graftme")
u.NewUnder(3, "under1") // should go with orig
u.NewUnder(3, "under2") // should go with orig
u.NewLeft(3, "left")
u.NewRight(3, "right")
x := u.Graft(t.Seed(3, "newbie"))
fmt.Println(u.AllUnder()) // still has leaves and branches
fmt.Println(x.AllUnder()) // never had them
fmt.Println(x.Left()) // new left
fmt.Println(x.Right()) // new right
fmt.Println(u.Left()) // detached
fmt.Println(u.Right()) // detached
fmt.Println(x.Branch())
//Output:
// [[3,"under1"] [3,"under2"]]
// []
// [3,"left"]
// [3,"right"]
// <nil>
// <nil>
// [1,[[3,"left"],[3,"newbie"],[3,"right"]]]
}
func ExampleNode_GraftRight() {
t := tree.New([]string{"foo", "😭", "💔"})
n := t.Trunk
u := n.NewUnder(3, "under")
x := u.GraftRight(t.Seed(3, "newbie"))
n.Print()
u.Print()
x.Left().Print()
//Output:
// [1,[[3,"under"],[3,"newbie"]]]
// [3,"under"]
// [3,"under"]
}
func ExampleNode_GraftLeft() {
t := tree.New([]string{"foo", "😭", "💔"})
n := t.Trunk
u := n.NewUnder(3, "under")
x := u.GraftLeft(t.Seed(3, "newbie"))
n.Print()
u.Print()
x.Right().Print()
//Output:
// [1,[[3,"newbie"],[3,"under"]]]
// [3,"under"]
// [3,"under"]
}
func ExampleNode_GraftUnder() {
t := tree.New([]string{"foo", "😭", "💔"})
n := t.Trunk
u := n.NewUnder(3, "under")
x := u.GraftUnder(t.Seed(3, "newbie"))
n.Print()
u.Print()
x.Branch().Print()
//Output:
// [1,[[3,[[3,"newbie"]]]]]
// [3,[[3,"newbie"]]]
// [3,[[3,"newbie"]]]
}
func ExampleNode_Prune() {
t := tree.New([]string{"foo", "😭", "💔"})
n := t.Trunk
u := n.NewUnder(2, "pruneme")
u1 := u.NewUnder(3, "under") // should go with orig
left := u.NewLeft(3, "left")
right := u.NewRight(3, "right")
x := u.Prune()
x.Print() // should take stuff under with it
u1.Branch().Print() // same as previous
left.Right().Print() // should be joined to the left now
right.Left().Print() // should be joined to right now
//Output:
// [2,[[3,"under"]]]
// [2,[[3,"under"]]]
// [3,"right"]
// [3,"left"]
}
func ExampleNode_Take() {
n := tree.New([]string{"foo", "😭", "💔"}).Trunk
u1 := n.NewUnder(2)
u2 := n.NewUnder(3)
u2.NewUnder(3)
u2.NewUnder(3)
u1.Print()
u2.Print()
u1.Take(u2)
u1.Print()
u2.Print()
// Output:
// [2]
// [3,[[3],[3]]]
// [2,[[3],[3]]]
// [3]
}
func ExampleNode_Visit() {
n := tree.New([]string{"🌴", "😭", "💔", "💀"}).Trunk
l2 := n.NewUnder(2, "two")
l_2 := l2.NewUnder(2, "_two")
l3 := n.NewUnder(3, "three")
l_3 := l3.NewUnder(3, "_three")
l4 := n.NewUnder(4, "four")
l_4 := l4.NewUnder(4, "_four")
n.Print()
l2.Print()
l_2.Print()
l3.Print()
l_3.Print()
l4.Print()
l_4.Print()
n.Visit(
func(n *tree.Node) any {
n.Print()
return nil
}, nil)
// Output:
// [1,[[2,[[2,"_two"]]],[3,[[3,"_three"]]],[4,[[4,"_four"]]]]]
// [2,[[2,"_two"]]]
// [2,"_two"]
// [3,[[3,"_three"]]]
// [3,"_three"]
// [4,[[4,"_four"]]]
// [4,"_four"]
// [1,[[2,[[2,"_two"]]],[3,[[3,"_three"]]],[4,[[4,"_four"]]]]]
// [2,[[2,"_two"]]]
// [2,"_two"]
// [3,[[3,"_three"]]]
// [3,"_three"]
// [4,[[4,"_four"]]]
// [4,"_four"]
}
func ExampleNode_VisitAsync() {
n := tree.New([]string{"🌴", "😭", "💔", "💀"}).Trunk
n.NewUnder(2, "two").NewUnder(2, "_two")
n.NewUnder(3, "three").NewUnder(3, "_three")
n.NewUnder(4, "four").NewUnder(4, "_four")
n.VisitAsync(
func(n *tree.Node) any {
n.Print()
time.Sleep(30 * time.Millisecond)
return nil
}, 3, nil)
// note that "unordered" output is required
// Unordered Output:
// [1,[[2,[[2,"_two"]]],[3,[[3,"_three"]]],[4,[[4,"_four"]]]]]
// [2,[[2,"_two"]]]
// [2,"_two"]
// [3,[[3,"_three"]]]
// [3,"_three"]
// [4,[[4,"_four"]]]
// [4,"_four"]
}
func ExampleNode_AllUnder() {
n := tree.New([]string{"🌴", "😭", "💔", "💀"}).Trunk
n.NewUnder(2)
n.NewUnder(3)
n.NewUnder(4)
n.Print()
// Output:
// [1,[[2],[3],[4]]]
}
func ExampleNode_JSON() {
n := tree.New([]string{"🌴", "😭", "💔", "💀"}).Trunk
n.NewUnder(2)
n.NewUnder(3)
n.NewUnder(4)
n.NewUnder(5)
n.Print()
// Output:
// [1,[[2],[3],[4],[5]]]
func TestParse(t *testing.T) {
types := []string{"root", "foo", "bar"}
tests := [][]string{
{` []`, `[]`},
/*
{`[] `, `[]`},
{"\n[]", `[]`},
{"[]\n", `[]`},
{"\t[]", `[]`},
{`[]`},
{`["root"]`, `[1]`},
{`[2]`},
{`[2,"some val"]`},
*/
}
for _, tt := range tests {
tr, err := tree.Parse(tt[0], types)
if err != nil {
t.Error(err)
}
want := tt[0]
got := tr.Root.String()
if len(tt) > 1 {
want = tt[1]
}
if got != want {
t.Errorf("\nwant: %v\ngot: %v\n", want, got)
}
}
}

Loading…
Cancel
Save