mirror of https://github.com/rwxrob/bonzai
Working commit to preserve refactoring
parent
a50179bb1d
commit
51d622e1c1
@ -1,66 +0,0 @@
|
||||
// Copyright 2022 Robert S. Muhlestein.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package Z_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
Z "github.com/rwxrob/bonzai"
|
||||
)
|
||||
|
||||
func ExampleArgsFrom() {
|
||||
fmt.Printf("%q\n", Z.ArgsFrom(`greet hi french`))
|
||||
fmt.Printf("%q\n", Z.ArgsFrom(`greet hi french `))
|
||||
// Output:
|
||||
// ["greet" "hi" "french"]
|
||||
// ["greet" "hi" "french" ""]
|
||||
}
|
||||
|
||||
func ExampleArgsOrIn_read_Nil() {
|
||||
|
||||
orig := os.Stdin
|
||||
defer func() { os.Stdin = orig }()
|
||||
os.Stdin, _ = os.Open(`testdata/in`)
|
||||
|
||||
fmt.Println(Z.ArgsOrIn(nil))
|
||||
|
||||
// Output:
|
||||
// some thing
|
||||
}
|
||||
|
||||
func ExampleArgsOrIn_read_Zero_Args() {
|
||||
|
||||
orig := os.Stdin
|
||||
defer func() { os.Stdin = orig }()
|
||||
os.Stdin, _ = os.Open(`testdata/in`)
|
||||
|
||||
fmt.Println(Z.ArgsOrIn([]string{}))
|
||||
|
||||
// Output:
|
||||
// some thing
|
||||
}
|
||||
|
||||
func ExampleArgsOrIn_args_Joined() {
|
||||
|
||||
fmt.Println(Z.ArgsOrIn([]string{"some", "thing"}))
|
||||
|
||||
// Output:
|
||||
// some thing
|
||||
}
|
||||
|
||||
func ExampleEsc() {
|
||||
fmt.Println(Z.Esc("|&;()<>![]"))
|
||||
fmt.Printf("%q", Z.Esc(" \n\r"))
|
||||
// Output:
|
||||
// \|\&\;\(\)\<\>\!\[\]
|
||||
// "\\ \\\n\\\r"
|
||||
}
|
||||
|
||||
func ExampleEscAll() {
|
||||
list := []string{"so!me", "<here>", "other&"}
|
||||
fmt.Println(Z.EscAll(list))
|
||||
// Output:
|
||||
// [so\!me \<here\> other\&]
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/rwxrob/bonzai v0.0.39 h1:EJ1aVVVwnm4mdPKJEeCLI3wVttgbD8ulggXKZiwEO4w=
|
||||
github.com/rwxrob/bonzai v0.0.39/go.mod h1:PyKG44H68o3DZ2Xh6ouViGW9IAbYZVw0OYqLMdCM1RI=
|
||||
github.com/rwxrob/config v0.3.1 h1:YwnPEuEFDZz7gD5ZJwYkYEF/oSjvM9ryXV6oK+aDvJc=
|
||||
github.com/rwxrob/config v0.3.1/go.mod h1:aqV/tWGH+Tz2ciADZDDYoodAniZ2iQTm8WZNM0wRyCA=
|
||||
github.com/rwxrob/fn v0.3.0 h1:R4kcZhInEc9Fn3lsWbn3O6ZOoZ/D43Y1l3SS5Nxm1wc=
|
||||
github.com/rwxrob/fn v0.3.0/go.mod h1:omPqOqEB+dDna09z5pi5YFxq4IZqDvv3wFPUCES5LvY=
|
||||
github.com/rwxrob/fs v0.4.3 h1:ntu9TZnk7NHd1Yen+p4+xruBmkQMugKtFU0OLfAMa+M=
|
||||
github.com/rwxrob/fs v0.4.3/go.mod h1:vO8AeluD7rnrO7zC54745xTEBFgHPUpHL0hbp1NnsVo=
|
||||
github.com/rwxrob/json v0.4.1 h1:b4ToZe4mrQO8rRL/kRFglzZszyZZnGv6JRHj6jrI3f4=
|
||||
github.com/rwxrob/json v0.4.1/go.mod h1:DU3TQKCWY4bK7sQ0wu80cRmTs96b6M//OYvT7Eg2mJA=
|
||||
github.com/rwxrob/structs v0.5.0 h1:pjLsfyYHS+gB1CtzRj3H39wRYL4lI5pTpFf8kl91guw=
|
||||
github.com/rwxrob/structs v0.5.0/go.mod h1:2gIte2ZI9zUok6q6F3v3l41ZXo7Zg5Kf1GUTP2+pXyQ=
|
||||
github.com/rwxrob/term v0.1.5 h1:jKvNgEYlsT6sLRqIuR9ITcNNh4woGkWxcAe71pPJReQ=
|
||||
github.com/rwxrob/term v0.1.5/go.mod h1:IVE7hG+VQlM4R+kS4pY6uhfMHoG0QECrZF7d7bKcdsk=
|
||||
github.com/rwxrob/to v0.2.1 h1:ZYBNEa8LJT5VDQUHm2wSwiRf61xSqU87UqbYvitV3eY=
|
||||
github.com/rwxrob/to v0.2.1/go.mod h1:8qdgCWkh50Avs8sRpV6/P7lAQgVf3KLRSKMZahV/W48=
|
||||
github.com/rwxrob/y2j v0.3.1 h1:qOCU7J6g0Q/7KlLAabCMLx6/wG1/NelG6QTOVpESAQg=
|
||||
github.com/rwxrob/y2j v0.3.1/go.mod h1:/3eS+LPnOF1F2VfoqZr3Upkr8q4ByziAi3eB6FIgzoE=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY=
|
||||
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@ -1,52 +0,0 @@
|
||||
package help
|
||||
|
||||
/*
|
||||
var Help = &Z.Cmd{
|
||||
Name: `help`,
|
||||
Aliases: []string{"h"},
|
||||
Call: func(x *Z.Cmd, args ...string) error {
|
||||
|
||||
var buf string
|
||||
|
||||
buf += util.Emph("**NAME**", 0, -1) + "\n " + x.Title() + "\n\n"
|
||||
buf += util.Emph("**SYNOPSIS**", 0, -1) + "\n " + x.Name + " " + x.Usage + "\n\n"
|
||||
|
||||
if len(x.Commands.M) > 0 {
|
||||
buf += util.Emph("**COMMANDS**", 0, -1) + "\n" + x.Titles(7, 20) + "\n\n"
|
||||
}
|
||||
|
||||
if len(x.Description) > 0 {
|
||||
buf +=
|
||||
util.Emph("**DESCRIPTION**", 0, -1) + "\n" +
|
||||
util.Emph(x.Description, 7, 65) + "\n\n"
|
||||
}
|
||||
|
||||
if x.Source != "" || x.Issues != "" || x.Site != "" {
|
||||
|
||||
buf += util.Emph("**LINKS**", 0, -1) + "\n"
|
||||
|
||||
if x.Site != "" {
|
||||
buf += " Site: " + x.Site + "\n"
|
||||
}
|
||||
|
||||
if x.Source != "" {
|
||||
buf += " Source: " + x.Source + "\n"
|
||||
}
|
||||
|
||||
if x.Issues != "" {
|
||||
buf += " Issues: " + x.Issues + "\n"
|
||||
}
|
||||
|
||||
buf += "\n"
|
||||
|
||||
}
|
||||
|
||||
if x.Copyright != "" {
|
||||
buf += util.Emph("**LEGAL**", 0, -1) + "\n" + util.Indent(x.Legal(), 7) + "\n\n"
|
||||
}
|
||||
|
||||
return buf
|
||||
|
||||
},
|
||||
}
|
||||
*/
|
@ -1,6 +0,0 @@
|
||||
package z
|
||||
|
||||
// keep only compound expressions here
|
||||
|
||||
var WS = I{' ', '\n', '\t', '\r'}
|
||||
var Digit = R{0, 9}
|
@ -1,182 +0,0 @@
|
||||
// Copyright 2022 Robert S. Muhlestein.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
Package z (often imported as "is") defines the Bonzai Parsing Expression
|
||||
Grammar Notation (BPEGN) (aka "Bonzai Scanner Expect" language)
|
||||
implemented entirely using Go types (mostly slices and structs). BPEGN
|
||||
can be 100% transpiled to and from the Parsing Expression Grammer
|
||||
Notation (PEGN). Code in one grammar and use the bonzai command to
|
||||
easily generate the other.
|
||||
|
||||
Nodes and Expressions
|
||||
|
||||
Nodes (z.Nd) indicate something to be captured as a part of the resulting
|
||||
abstract syntax tree. They are functionally equivalent to parenthesis in
|
||||
regular expressions but with the obvious advantage of capturing a rooted
|
||||
node tree instead of an array. Expressions (z.X) indicate a sequence to
|
||||
be scanned but not captured unless the expression itself is within
|
||||
a node (z.Nd).
|
||||
|
||||
Tokens
|
||||
|
||||
The BPEGN token strings are contained in the "tk" package can be used as
|
||||
is. See the "tk" package for details.
|
||||
|
||||
Parameterized Struct and Set Slice Expressions
|
||||
|
||||
BPEGN uses Go structs and slices to represent scannable expressions with
|
||||
obvious similarities to regular expressions. These expressions have
|
||||
one-to-parity with PEGN expressions. Slices represent sets of
|
||||
possibilities. Structs provide parameters for more complex expressions
|
||||
and are are guaranteed never to change allowing them to be dependably
|
||||
used in assignment without struct field names using Go's inline
|
||||
composable syntax. Some editors may need configuring to allow this since
|
||||
in general practice this can create subtle (but substantial) foot-guns
|
||||
for maintainers.
|
||||
|
||||
Look-Ahead and Advancing Expressions
|
||||
|
||||
"Advancing" expressions will advance the scan to the end of the
|
||||
expression match. "Look-ahead" expressions simply check for a match but
|
||||
do not advance the scan. Developers should take careful note of the
|
||||
difference in the documentation.
|
||||
|
||||
Composites
|
||||
|
||||
Composites are compound expressions composed of others. They represent
|
||||
the tokens and classes from PEGN and other grammars and are designed to
|
||||
simplify grammar development at a higher level. Pull requests are
|
||||
welcome for missing, commonly used composite candidates.
|
||||
|
||||
Hook Functions
|
||||
|
||||
Hook functions are not strictly an expression type and are declared in
|
||||
the scan package itself (to avoid a cyclical import dependency since it
|
||||
is passed a scan.R). A Hook is passed the *scan.R and can return an error.
|
||||
See scan.Hook for more information.
|
||||
*/
|
||||
package z
|
||||
|
||||
// ------------------------------- core -------------------------------
|
||||
|
||||
// P ("parse") is a named sequence of expressions that will be parsed
|
||||
// and captured as a new Node and added to the scan.R.Nodes field
|
||||
// effectively turning the scan.R into a parser as well. The first
|
||||
// string must always be the name which can be any valid Go string. If
|
||||
// any expression fails to match the scan fails. Otherwise, a new
|
||||
// scan.Node is added under the current node and the scan proceeds.
|
||||
// Nodes must either contain other nodes or no nodes at all. If the
|
||||
// first item in the sequence after the name is not also a node (z.P)
|
||||
// then the node is marked as "edge" (or "leaf") and any nodes detected
|
||||
// further in the sequence will cause the scan to fail with a syntax
|
||||
// error.
|
||||
type P []any
|
||||
|
||||
// X ("expression") is a sequence of expressions. If any are not the
|
||||
// scan fails. (Equal to (?foo) in regular expressions.)
|
||||
type X []any
|
||||
|
||||
// ------------------------------- sets -------------------------------
|
||||
|
||||
// Y ("yes") is a set of positive lookahead expressions. If any are
|
||||
// seen at the current cursor position the scan will proceed without
|
||||
// consuming them (unlike z.O and z.I). If none are found the scan
|
||||
// fails. (Equal to ampersand (&) in
|
||||
// PEGN.)
|
||||
type Y []any
|
||||
|
||||
// N ("not") is a set of negative lookahead expressions. If any are seen
|
||||
// at the current cursor position the scan will fail and the scan is
|
||||
// never advanced. This is useful when everything from one expression is
|
||||
// wanted except for a few negative exceptions. (Equal to exclamation
|
||||
// point (!) in PEGN.)
|
||||
type N []any
|
||||
|
||||
// I ("in","include") 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 parser developers to prioritize common expressions at the
|
||||
// beginning of the slice.
|
||||
type I []any
|
||||
|
||||
// O is a set of optional advancing expressions. If any expression is
|
||||
// found the scan is advanced (unlike is.It, which does not advance).
|
||||
type O []any
|
||||
|
||||
// T ("to") is a set of advancing expressions that mark an exclusive
|
||||
// boundary at which the scan should stop returning a cursor just before
|
||||
// the boundary.
|
||||
type T []any
|
||||
|
||||
// Ti ("to inclusive") is an inclusive version of z.T returning
|
||||
// a cursor that points to the last rune of the boundary itself.
|
||||
type Ti []any
|
||||
|
||||
// --------------------------- parameterized --------------------------
|
||||
|
||||
// MM ("minmax") is a parameterized advancing expression that matches an
|
||||
// inclusive minimum and maximum count of the given expression (This).
|
||||
type MM struct {
|
||||
Min int
|
||||
Max int
|
||||
This any
|
||||
}
|
||||
|
||||
// M ("min") is a parameterized advancing expression that matches an
|
||||
// inclusive minimum number of the given expression item (This). Use
|
||||
// within is.It to disable advancement.
|
||||
type M struct {
|
||||
Min int
|
||||
This any
|
||||
}
|
||||
|
||||
// M1 is shorthand for z.M{1,This}.
|
||||
type M1 struct{ This any }
|
||||
|
||||
// C is a parameterized advancing expression that matches an exact count
|
||||
// of the given expression (This). Use within is.It to disable
|
||||
// advancement.
|
||||
type C struct {
|
||||
N int
|
||||
This any
|
||||
}
|
||||
|
||||
// C2 is shorthand for z.C{2,This}.
|
||||
type C2 struct{ This any }
|
||||
|
||||
// C3 is shorthand for z.C{3,This}.
|
||||
type C3 struct{ This any }
|
||||
|
||||
// C4 is shorthand for z.C{4,This}.
|
||||
type C4 struct{ This any }
|
||||
|
||||
// C5 is shorthand for z.C{5,This}.
|
||||
type C5 struct{ This any }
|
||||
|
||||
// C6 is shorthand for z.C{6,This}.
|
||||
type C6 struct{ This any }
|
||||
|
||||
// C7 is shorthand for z.C{7,This}.
|
||||
type C7 struct{ This any }
|
||||
|
||||
// C8 is shorthand for z.C{8,This}.
|
||||
type C8 struct{ This any }
|
||||
|
||||
// C9 is shorthand for z.C{9,This}.
|
||||
type C9 struct{ This any }
|
||||
|
||||
// A is short for z.C{tk.ANY}. (Mnemonic: "any", "asterisk")
|
||||
type A struct {
|
||||
N int
|
||||
}
|
||||
|
||||
// R ("range") is a parameterized advancing expression that matches
|
||||
// a single Unicode code point (rune, int32) from an inclusive
|
||||
// consecutive set from First to Last (First,Last). (Mnemonic: "range",
|
||||
// "rune")
|
||||
type R struct {
|
||||
First rune
|
||||
Last rune
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
# Bonzai Mark (Simple Subset of CommonMark)
|
||||
|
||||
<https://github.com/pegn/spec>
|
@ -1,383 +0,0 @@
|
||||
package mark
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/rwxrob/bonzai/term/esc"
|
||||
)
|
||||
|
||||
// OpenItalic opens italic emphasis. Default: ANSI italic.
|
||||
var OpenItalic = esc.Italic
|
||||
|
||||
// CloseItalic closes italic emphasis. Default: ANSI reset.
|
||||
var CloseItalic = esc.Reset
|
||||
|
||||
// OpenBold opens bold emphasis. Default: ANSI bold.
|
||||
var OpenBold = esc.Bold
|
||||
|
||||
// CloseBold closes bold emphasis. Default: ANSI reset.
|
||||
var CloseBold = esc.Reset
|
||||
|
||||
// OpenBoldItalic opens bold italic emphasis. Default: ANSI bold italic.
|
||||
var OpenBoldItalic = esc.BoldItalic
|
||||
|
||||
// CloseBoldItalic closes bold italic emphasis. Default: ANSI reset.
|
||||
var CloseBoldItalic = esc.Reset
|
||||
|
||||
// OpenUnderline open underline emphasis. Default: ANSI underline.
|
||||
var OpenUnderline = esc.Underline
|
||||
|
||||
// CloseUnderline closes underline emphasis. Default: ANSI reset.
|
||||
var CloseUnderline = esc.Reset
|
||||
|
||||
/*
|
||||
func init() {
|
||||
if !term.IsTerminal() {
|
||||
reset = ""
|
||||
italic = ""
|
||||
bold = ""
|
||||
bolditalic = ""
|
||||
underline = ""
|
||||
return
|
||||
}
|
||||
emphFromLess()
|
||||
}
|
||||
*/
|
||||
|
||||
func emphFromLess() {
|
||||
var x string
|
||||
x = os.Getenv("LESS_TERMCAP_us")
|
||||
if x != "" {
|
||||
OpenItalic = x
|
||||
}
|
||||
x = os.Getenv("LESS_TERMCAP_md")
|
||||
if x != "" {
|
||||
OpenBold = x
|
||||
}
|
||||
x = os.Getenv("LESS_TERMCAP_mb")
|
||||
if x != "" {
|
||||
OpenBoldItalic = x
|
||||
}
|
||||
x = os.Getenv("LESS_TERMCAP_us")
|
||||
if x != "" {
|
||||
OpenUnderline = x
|
||||
}
|
||||
}
|
||||
|
||||
// FormatWrapped takes a command documentation format string (an
|
||||
// extremely limited version of Markdown that is also Godoc friendly)
|
||||
// and transforms it as follows:
|
||||
//
|
||||
// * Initial and trailing blank lines are removed.
|
||||
//
|
||||
// * Indentation is removed - the number of spaces preceeding the first
|
||||
// word of the first line are ignored in every line (including raw
|
||||
// text blocks).
|
||||
//
|
||||
// * Raw text ignored - any line beginning with four or more spaces
|
||||
// (after convenience indentation is removed) will be kept as it is
|
||||
// exactly (code examples, etc.) but should never exceed 80 characters
|
||||
// (including the spaces).
|
||||
//
|
||||
// * Blocks are unwrapped - any non-blank (without three or less initial
|
||||
// spaces) will be trimmed line following a line will be joined to the
|
||||
// preceding line recursively (unless hard break).
|
||||
//
|
||||
// * Hard breaks kept - like Markdown any line that ends with two or
|
||||
// more spaces will automatically force a line return.
|
||||
//
|
||||
// * URL links argument names and anything else within angle brackets
|
||||
// (<url>), will trigger underline in both text blocks
|
||||
// and usage sections.
|
||||
//
|
||||
// * Italic, Bold, and BoldItalic inline emphasis using one, two, or
|
||||
// three stars respectively will be observed and cannot be intermixed
|
||||
// or intra-word. Each opener must be preceded by a UNICODE space (or
|
||||
// nothing) and followed by a non-space rune. Each closer must be
|
||||
// preceded by a non-space rune and followed by a UNICODE space (or
|
||||
// nothing).
|
||||
//
|
||||
// For historic reasons the following environment variables will be
|
||||
// observed if found (and also provide color support for the less pager
|
||||
// utility):
|
||||
//
|
||||
// * Italic LESS_TERMCAP_so
|
||||
// * Bold LESS_TERMCAP_md
|
||||
// * BoldItalic LESS_TERMCAP_mb
|
||||
// * Underline LESS_TERMCAP_us
|
||||
//
|
||||
func FormatWrapped(input string, indent, width int) (output string) {
|
||||
|
||||
// this scanner could be waaaay more lexy
|
||||
// but suits the need and clear to read
|
||||
|
||||
var strip int
|
||||
var blockbuf string
|
||||
|
||||
// standard state machine approach
|
||||
inblock := false
|
||||
inraw := false
|
||||
inhard := false
|
||||
gotindent := false
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(input))
|
||||
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
trimmed := strings.TrimSpace(txt)
|
||||
|
||||
// ignore blank lines
|
||||
if !(inraw || inblock) && len(trimmed) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// infer the indent to strip for every line
|
||||
if !gotindent && len(trimmed) > 0 {
|
||||
for i, v := range txt {
|
||||
if v != ' ' {
|
||||
strip = i
|
||||
gotindent = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// strip convenience indent
|
||||
if len(txt) >= strip {
|
||||
txt = txt[strip:]
|
||||
}
|
||||
|
||||
// raw block start
|
||||
if !inblock && !inraw && len(txt) > 4 && txt[0:4] == " " && len(trimmed) > 0 {
|
||||
inraw = true
|
||||
output += "\n\n" + txt
|
||||
continue
|
||||
}
|
||||
|
||||
// in raw block
|
||||
if inraw && len(txt) > 4 {
|
||||
output += "\n" + txt
|
||||
continue
|
||||
}
|
||||
|
||||
// raw block end
|
||||
if inraw && len(trimmed) == 0 {
|
||||
inraw = false
|
||||
continue
|
||||
}
|
||||
|
||||
// another block line, join it
|
||||
if inblock && len(trimmed) > 0 {
|
||||
if len(txt) >= 2 && txt[len(txt)-2:] == " " {
|
||||
inhard = true
|
||||
}
|
||||
space := " "
|
||||
if inhard {
|
||||
space = "\n"
|
||||
}
|
||||
blockbuf += space + trimmed
|
||||
continue
|
||||
}
|
||||
|
||||
// beginning of a new block
|
||||
if !inblock && len(trimmed) > 0 {
|
||||
inhard = false
|
||||
inblock = true
|
||||
if len(txt) >= 2 && txt[len(txt)-2:] == " " {
|
||||
inhard = true
|
||||
}
|
||||
blockbuf = trimmed
|
||||
continue
|
||||
}
|
||||
|
||||
// end block
|
||||
if inblock && len(trimmed) == 0 {
|
||||
inblock = false
|
||||
output += "\n\n" + Format(Wrap(blockbuf, width-strip-4))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// flush last block
|
||||
if inblock {
|
||||
output += "\n\n" + Format(Wrap(blockbuf, width-strip-4))
|
||||
}
|
||||
output = Indent(strings.TrimSpace(output), indent)
|
||||
return
|
||||
}
|
||||
|
||||
// Format replaces minimal Markdown-like syntax with *Italic*,
|
||||
// **Bold**, ***BoldItalic***, and <bracketed>
|
||||
func Format(buf string) string {
|
||||
// TODO add functional parser, for fun
|
||||
log.Println("would Format")
|
||||
return buf
|
||||
}
|
||||
|
||||
// Indent indents each line the set number of spaces.
|
||||
func Indent(buf string, spaces int) string {
|
||||
nbuf := ""
|
||||
scanner := bufio.NewScanner(strings.NewReader(buf))
|
||||
scanner.Scan()
|
||||
for n := 0; n < spaces; n++ {
|
||||
nbuf += " "
|
||||
}
|
||||
nbuf += scanner.Text()
|
||||
for scanner.Scan() {
|
||||
nbuf += "\n"
|
||||
for n := 0; n < spaces; n++ {
|
||||
nbuf += " "
|
||||
}
|
||||
nbuf += scanner.Text()
|
||||
}
|
||||
return nbuf
|
||||
}
|
||||
|
||||
// Wrapped is the same as Format but without any emphasis.
|
||||
func Wrapped(input string, indent, width int) (output string) {
|
||||
|
||||
// this scanner could be waaaay more lexy
|
||||
// but suits the need and clear to read
|
||||
|
||||
var strip int
|
||||
var blockbuf string
|
||||
|
||||
// standard state machine approach
|
||||
inblock := false
|
||||
inraw := false
|
||||
inhard := false
|
||||
gotindent := false
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(input))
|
||||
|
||||
for scanner.Scan() {
|
||||
txt := scanner.Text()
|
||||
trimmed := strings.TrimSpace(txt)
|
||||
|
||||
// ignore blank lines
|
||||
if !(inraw || inblock) && len(trimmed) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// infer the indent to strip for every line
|
||||
if !gotindent && len(trimmed) > 0 {
|
||||
for i, v := range txt {
|
||||
if v != ' ' {
|
||||
strip = i
|
||||
gotindent = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// strip convenience indent
|
||||
if len(txt) >= strip {
|
||||
txt = txt[strip:]
|
||||
}
|
||||
|
||||
// raw block start
|
||||
if !inblock && !inraw && len(txt) > 4 && txt[0:4] == " " && len(trimmed) > 0 {
|
||||
inraw = true
|
||||
output += "\n\n" + txt
|
||||
continue
|
||||
}
|
||||
|
||||
// in raw block
|
||||
if inraw && len(txt) > 4 {
|
||||
output += "\n" + txt
|
||||
continue
|
||||
}
|
||||
|
||||
// raw block end
|
||||
if inraw && len(trimmed) == 0 {
|
||||
inraw = false
|
||||
continue
|
||||
}
|
||||
|
||||
// another block line, join it
|
||||
if inblock && len(trimmed) > 0 {
|
||||
if len(txt) >= 2 && txt[len(txt)-2:] == " " {
|
||||
inhard = true
|
||||
}
|
||||
space := " "
|
||||
if inhard {
|
||||
space = "\n"
|
||||
}
|
||||
blockbuf += space + trimmed
|
||||
continue
|
||||
}
|
||||
|
||||
// beginning of a new block
|
||||
if !inblock && len(trimmed) > 0 {
|
||||
inhard = false
|
||||
inblock = true
|
||||
if len(txt) >= 2 && txt[len(txt)-2:] == " " {
|
||||
inhard = true
|
||||
}
|
||||
blockbuf = trimmed
|
||||
continue
|
||||
}
|
||||
|
||||
// end block
|
||||
if inblock && len(trimmed) == 0 {
|
||||
inblock = false
|
||||
output += "\n\n" + Wrap(blockbuf, width-strip-4)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// flush last block
|
||||
if inblock {
|
||||
output += "\n\n" + Wrap(blockbuf, width-strip-4)
|
||||
}
|
||||
output = Indent(strings.TrimSpace(output), indent)
|
||||
return
|
||||
}
|
||||
|
||||
// peekWord returns the runes up to the next space.
|
||||
func peekWord(buf []rune, start int) []rune {
|
||||
word := []rune{}
|
||||
for _, r := range buf[start:] {
|
||||
if unicode.IsSpace(r) {
|
||||
break
|
||||
}
|
||||
word = append(word, r)
|
||||
}
|
||||
return word
|
||||
}
|
||||
|
||||
// Wrap wraps the string to the given width using spaces to separate
|
||||
// words. If passed a negative width will effectively join all words in
|
||||
// the buffer into a single line with no wrapping.
|
||||
func Wrap(buf string, width int) string {
|
||||
if width == 0 {
|
||||
return buf
|
||||
}
|
||||
nbuf := ""
|
||||
curwidth := 0
|
||||
for i, r := range []rune(buf) {
|
||||
// hard breaks always as is
|
||||
if r == '\n' {
|
||||
nbuf += "\n"
|
||||
curwidth = 0
|
||||
continue
|
||||
}
|
||||
if unicode.IsSpace(r) {
|
||||
// FIXME: don't peek every word, only after passed width
|
||||
// change the space to a '\n' in the buffer slice directly
|
||||
next := peekWord([]rune(buf), i+1)
|
||||
if width > 0 && (curwidth+len(next)+1) > width {
|
||||
nbuf += "\n"
|
||||
curwidth = 0
|
||||
continue
|
||||
}
|
||||
}
|
||||
nbuf += string(r)
|
||||
curwidth++
|
||||
}
|
||||
return nbuf
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package mark
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
want := []string{
|
||||
OpenItalic + "Italic" + CloseItalic,
|
||||
OpenBold + "Bold" + CloseBold,
|
||||
OpenBoldItalic + "BoldItalic" + CloseBoldItalic,
|
||||
"<" + OpenUnderline + "bracketed" + CloseUnderline + ">",
|
||||
}
|
||||
args := []string{"*Italic*", "**Bold**", "***BoldItalic***", "<bracketed>"}
|
||||
for i, arg := range args {
|
||||
t.Logf("testing: %v\n", arg)
|
||||
got := Format(arg)
|
||||
if got != want[i] {
|
||||
t.Errorf("\nwant: %q\ngot: %q\n", want[i], got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatWrapped(t *testing.T) {
|
||||
text := `
|
||||
Something *easy* to write here that can be indented however you like
|
||||
and wrapped and have each line indented and with <code>:
|
||||
|
||||
This will not be messed with.
|
||||
Nor this.
|
||||
|
||||
So it's a lot like a **simple** version of Markdown that only supports
|
||||
what is likely going to be used in stuff similar to man pages.
|
||||
|
||||
Let's try a hard
|
||||
return.`
|
||||
|
||||
want := " Something " + OpenItalic + "easy" + CloseItalic + " to write here that can be indented however\n you like and wrapped and have each line indented and with\n <" + OpenUnderline + "code" + CloseUnderline + ">:\n \n This will not be messed with.\n Nor this.\n \n So it's a lot like a " + OpenBold + "simple" + CloseBold + " version of Markdown that only\n supports what is likely going to be used in stuff similar to\n man pages.\n \n Let's try a hard\n return."
|
||||
|
||||
got := FormatWrapped(text, 5, 70)
|
||||
t.Log("\n" + got)
|
||||
if want != got {
|
||||
t.Errorf("\nwant:\n%q\ngot:\n%q\n", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrapped(t *testing.T) {
|
||||
text := `
|
||||
Something *easy* to write here that can be indented however you like
|
||||
and wrapped and have each line indented and with <code>:
|
||||
|
||||
This will not be messed with.
|
||||
Nor this.
|
||||
|
||||
So it's a lot like a **simple** version of Markdown that only supports
|
||||
what is likely going to be used in stuff similar to man pages.
|
||||
|
||||
Let's try a hard
|
||||
return.`
|
||||
|
||||
want := " Something *easy* to write here that can be indented however\n you like and wrapped and have each line indented and with\n <code>:\n \n This will not be messed with.\n Nor this.\n \n So it's a lot like a **simple** version of Markdown that only\n supports what is likely going to be used in stuff similar to\n man pages.\n \n Let's try a hard\n return."
|
||||
|
||||
got := Wrapped(text, 5, 70)
|
||||
t.Log("\n" + got)
|
||||
if want != got {
|
||||
t.Errorf("\nwant:\n%q\ngot:\n%q\n", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPeekWord(t *testing.T) {
|
||||
var buf []rune
|
||||
var word string
|
||||
buf = []rune(`some thing`)
|
||||
word = string(peekWord(buf, 0))
|
||||
t.Logf("%q", word)
|
||||
if word != "some" {
|
||||
t.Fail()
|
||||
}
|
||||
word = string(peekWord(buf, 5))
|
||||
t.Logf("%q", word)
|
||||
if word != "thing" {
|
||||
t.Fail()
|
||||
}
|
||||
word = string(peekWord(buf, 4))
|
||||
t.Logf("%q", word)
|
||||
if word != "" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
buf := "Here's a string that's not long."
|
||||
want := "Here's a\nstring\nthat's not\nlong."
|
||||
got := Wrap(buf, 10)
|
||||
if want != got {
|
||||
t.Errorf("\nwant: %q\ngot: %q\n", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrap_none(t *testing.T) {
|
||||
if Wrap("some thing", 0) != "some thing" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
package ast
|
@ -1,36 +0,0 @@
|
||||
# BonzaiMark (v1.0.0-alpha) mark.bonzai.dev
|
||||
# Copyright 2022 Robert S. Muhlestein
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# <-- "contains nodes"
|
||||
# <- "does not contain nodes"
|
||||
|
||||
# Must be parsed in three passes:
|
||||
#
|
||||
# 1. Unindent every line
|
||||
# 1. Chop into blocks
|
||||
# 1. Parse each block
|
||||
|
||||
Indented <-- FirstLine Lines*
|
||||
FirstLine <- Indentation (!EndLine .)*
|
||||
_Indent <-- # number of spaces before first nongraphic
|
||||
|
||||
|
||||
Block <-- Bulleted / Numbered / Paragraph / Verbatim
|
||||
|
||||
|
||||
|
||||
EndBlock <- EndLine{2,} / EOD
|
||||
Paragraph <-- (!EndBlock <Span>)+ EndBlock
|
||||
Bulleted <-- (!EndLine '*' SP <Span*>)+
|
||||
Numbered <-- (!EndLine '1.' SP <Span*>)+
|
||||
Verbatim <-- (!EndLine SP{4} <ugraphic*> EndLine)+
|
||||
Span <- BoldItalic / Bold / Italic / Bracketed / Plain
|
||||
BoldItalic <-- ('***' !ws) (!'***' ugraphic)+ (!ws '***')
|
||||
Bold <-- ('**' !ws) (!'**' ugraphic)+ (!ws '**')
|
||||
Italic <-- ('*' !ws) (!'*' ugraphic)+ (!ws '*')
|
||||
Bracketed <-- ('<' !ws) <(!'>' ugraphic)+> (!ws '>')
|
||||
Plain <-- ugraphic+
|
||||
EndLine <- LF / CRLF / CR
|
||||
EOD <- # end of document
|
||||
|
@ -1,75 +0,0 @@
|
||||
package mark
|
||||
|
||||
import (
|
||||
"github.com/rwxrob/scan"
|
||||
z "github.com/rwxrob/scan/is"
|
||||
"github.com/rwxrob/scan/pg"
|
||||
"github.com/rwxrob/structs/tree"
|
||||
)
|
||||
|
||||
func Parse(in any) (*tree.E[string], error) {
|
||||
|
||||
names := []string{
|
||||
`Grammar`, // 1
|
||||
`Bulleted`, // 2
|
||||
`Numbered`, // ...
|
||||
`Verbatim`,
|
||||
`Paragraph`,
|
||||
`BoldItalic`,
|
||||
`Bold`,
|
||||
`Italic`,
|
||||
`Bracketed`,
|
||||
`Plain`,
|
||||
}
|
||||
s := scan.New(names)
|
||||
s.X(Grammar)
|
||||
s.Print()
|
||||
s.Tree.Root.Print()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func Grammar(s *scan.R) bool { return s.X(z.P{1, z.X{Block, pg.EndLine}}) }
|
||||
|
||||
func Block(s *scan.R) bool {
|
||||
return s.X(z.X{z.I{Bulleted, Numbered, Paragraph, Verbatim}, EndBlock})
|
||||
}
|
||||
|
||||
func EndBlock(s *scan.R) bool { return s.X(z.I{z.M{2, pg.EndLine}, scan.EOD}) }
|
||||
|
||||
func Bulleted(s *scan.R) bool {
|
||||
return s.X(z.P{2, z.M1{z.X{z.N{pg.EndLine}, '*', ' ', z.M0{Span}}}})
|
||||
}
|
||||
|
||||
func Numbered(s *scan.R) bool {
|
||||
return s.X(z.P{3, z.M1{z.X{z.N{pg.EndLine}, "1.", ' ', z.M0{Span}}}})
|
||||
}
|
||||
|
||||
func Verbatim(s *scan.R) bool {
|
||||
return s.X(z.P{4, z.M1{z.X{z.N{pg.EndLine}, z.C{4, ' '}, z.M1{pg.UGraphic}}}})
|
||||
}
|
||||
|
||||
func Paragraph(s *scan.R) bool {
|
||||
return s.X(z.P{4, z.M1{z.X{z.N{z.I{z.C{2, pg.EndLine}, scan.EOD}}, Span}}})
|
||||
}
|
||||
|
||||
func Span(s *scan.R) bool { return s.X(z.I{BoldItalic, Bold, Italic, Bracketed, Plain}) }
|
||||
|
||||
func BoldItalic(s *scan.R) bool {
|
||||
return s.X(z.P{6, z.X{"***", z.N{pg.WS}, z.M1{Plain}, z.N{pg.WS}, "***"}})
|
||||
}
|
||||
|
||||
func Bold(s *scan.R) bool {
|
||||
return s.X(z.P{7, z.X{"**", z.N{pg.WS}, z.M1{Plain}, z.N{pg.WS}, "**"}})
|
||||
}
|
||||
|
||||
func Italic(s *scan.R) bool {
|
||||
return s.X(z.P{8, z.X{"*", z.N{pg.WS}, z.M1{Plain}, z.N{pg.WS}, "*"}})
|
||||
}
|
||||
|
||||
func Bracketed(s *scan.R) bool {
|
||||
//return s.X(z.P{9, z.X{'<', z.N{pg.WS}, z.M1{Plain}, z.N{pg.WS}, '>'}})
|
||||
return s.X('<', z.P{9, z.X{z.M1{z.X{z.N{'>'}, pg.UGraphic}}}}, '>')
|
||||
}
|
||||
|
||||
func Plain(s *scan.R) bool { return s.X(z.P{10, z.M1{pg.UGraphic}}) }
|
@ -1,28 +0,0 @@
|
||||
package mark_test
|
||||
|
||||
import (
|
||||
"github.com/rwxrob/bonzai/mark"
|
||||
"github.com/rwxrob/scan"
|
||||
)
|
||||
|
||||
func ExamplePlain() {
|
||||
s := scan.New("some thing here")
|
||||
//s.TraceX()
|
||||
s.X(mark.Plain)
|
||||
s.Print()
|
||||
s.Tree.Root.Print()
|
||||
// Output:
|
||||
// <EOD>
|
||||
// {"T":1,"N":[{"T":10,"V":"some thing here"}]}
|
||||
}
|
||||
|
||||
func ExampleBracketed() {
|
||||
s := scan.New("<thing>")
|
||||
s.TraceX()
|
||||
s.X(mark.Bracketed)
|
||||
s.Print()
|
||||
s.Tree.Root.Print()
|
||||
// Output:
|
||||
// <EOD>
|
||||
// {"T":1,"N":[{"T":10,"V":"some thing here"}]}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// LogPanic is used with defer to trap any panic and log it.
|
||||
func (s *R) LogPanic() {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
log.Printf("%v at %v", r, s)
|
||||
}
|
||||
}
|
||||
|
||||
// LogPanic is used with defer to trap any panic and print it.
|
||||
func (s *R) PrintPanic() {
|
||||
r := recover()
|
||||
if r != nil {
|
||||
fmt.Printf("%v at %v", r, s)
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------- PEGN support ---------------------------
|
||||
// The following are directly correlated to supported PEGN expressions
|
||||
// and are intended to be generated from PEGN grammars specifically
|
||||
// (altough other languages generators could easily be adapted).
|
||||
|
||||
// Rune matches the exact rune specified or panics.
|
||||
func (s *R) Rune(r rune) {
|
||||
if s.Cur.Rune != r {
|
||||
panic(fmt.Sprintf("expected %q", r))
|
||||
}
|
||||
s.Scan()
|
||||
}
|
||||
|
||||
// Str iterates over all of the runes in the string as if Rune were
|
||||
// called on each.
|
||||
func (s *R) Str(v string) {
|
||||
for _, v := range []rune(v) {
|
||||
if v != s.Cur.Rune {
|
||||
panic(fmt.Sprintf("expected %q", v))
|
||||
}
|
||||
s.Scan()
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package scan_test
|
||||
|
||||
import "github.com/rwxrob/scan"
|
||||
|
||||
func ExampleR_Any() {
|
||||
s, _ := scan.New("so")
|
||||
s.Print()
|
||||
s.Any(2)
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// <EOD>
|
||||
}
|
||||
|
||||
func ExampleR_Rune() {
|
||||
s, _ := scan.New("some thing")
|
||||
//s.Any(3)
|
||||
s.Rune('s')
|
||||
s.Rune('o')
|
||||
s.Rune('m')
|
||||
s.Print() // same as Any(3)
|
||||
// Output:
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
}
|
||||
|
||||
func ExampleR_Rune_fail() {
|
||||
s, _ := scan.New("some thing")
|
||||
defer s.PrintPanic()
|
||||
s.Rune('s')
|
||||
s.Rune('o')
|
||||
s.Rune('m')
|
||||
s.Rune('e')
|
||||
s.Rune('\t')
|
||||
// Output:
|
||||
// expected '\t' at U+0020 ' ' 1,5-5 (5-5)
|
||||
}
|
||||
|
||||
func ExampleR_Str() {
|
||||
s, _ := scan.New("some thing")
|
||||
s.Str("som")
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
}
|
||||
|
||||
func ExampleR_Str_fail() {
|
||||
s, _ := scan.New("some thing")
|
||||
defer s.PrintPanic()
|
||||
s.Str("some\t")
|
||||
// Output:
|
||||
// expected '\t' at U+0020 ' ' 1,5-5 (5-5)
|
||||
}
|
@ -1,549 +0,0 @@
|
||||
package scan
|
||||
|
||||
/*
|
||||
|
||||
// Parse creates a new Node reference from the string returned by Look.
|
||||
func (s *R) Parse(to *Cur) *Node {
|
||||
n := new(Node)
|
||||
n.V = s.Look(to)
|
||||
return n
|
||||
}
|
||||
|
||||
// ParseSlice creates a new Node reference from the string returned by
|
||||
// LookSlice.
|
||||
func (s *R) ParseSlice(b *Cur, e *Cur) *Node {
|
||||
n := new(Node)
|
||||
n.V = s.LookSlice(b, e)
|
||||
return n
|
||||
}
|
||||
|
||||
// The Expect method is primarily designed for ease of use quickly by
|
||||
// allowing BPEGN expressions to be coded much faster than traditional
|
||||
// parser functions. In fact, entire applications can be generated
|
||||
// automatically from BPEGN, PEGN, ABNF, EBNF, or any other meta syntax
|
||||
// language by adding additional scan.Hooks to do work besides just
|
||||
// parsing what is scanned. Generate the BPEGN Go expressions to pass to
|
||||
// Expect and code the hook callbacks. That's it. Coding in BPEGN comes
|
||||
// naturally when putting together a parser quickly and without the
|
||||
// cognitive overhead of switching between grammars. The BPEGN
|
||||
// expressions passed to Expect are 100% Go. For full documentation of
|
||||
// BPEGN expressions see the z ("is") package and the source of this
|
||||
// Expect method.
|
||||
//
|
||||
// Warning: While it is nothing for most developers to be concerned
|
||||
// with, the Expect method does do a fair amount of functional recursion
|
||||
// for the sake of simplicity and to support BPEGN syntax. Those wishing
|
||||
// to gain the maximum performance should consider using other scan.R
|
||||
// methods instead in such cases. Developers are encouraged to do their
|
||||
// own benchmarking and perhaps start with BPEGN until they can create
|
||||
// more optimized parsers when and if necessary. Most will discover
|
||||
// other more substantial bottlenecks. The Bonzai project places
|
||||
// priority on speed and quality of developer delivery over run-time
|
||||
// performance. Delivery time is far more costly than the minimal gains
|
||||
// in run-time performance. "Premature optimization is the root of all
|
||||
// evil," as they say.
|
||||
func (s *R) Expect(expr any) (*Cur, error) {
|
||||
s.Last = s.Cur
|
||||
|
||||
// please keep the most common expressions types at the top
|
||||
|
||||
switch v := expr.(type) {
|
||||
|
||||
case rune: // ------------------------------------------------------
|
||||
if s.Cur.Rune != v {
|
||||
err := s.ErrorExpected(v)
|
||||
return nil, err
|
||||
}
|
||||
s.Scan()
|
||||
return s.Last, nil
|
||||
|
||||
case tk.Token: // --------------------------------------------------
|
||||
switch v {
|
||||
case tk.ANY: // A, A1
|
||||
s.Scan()
|
||||
case tk.A2:
|
||||
s.ScanN(2)
|
||||
case tk.A3:
|
||||
s.ScanN(3)
|
||||
case tk.A4:
|
||||
s.ScanN(4)
|
||||
case tk.A5:
|
||||
s.ScanN(5)
|
||||
case tk.A6:
|
||||
s.ScanN(6)
|
||||
case tk.A7:
|
||||
s.ScanN(7)
|
||||
case tk.A8:
|
||||
s.ScanN(8)
|
||||
case tk.A9:
|
||||
s.ScanN(9)
|
||||
}
|
||||
return s.Last, nil
|
||||
|
||||
case string: // ----------------------------------------------------
|
||||
if v == "" {
|
||||
return s.Mark(), nil
|
||||
}
|
||||
|
||||
// avoid the temptation to look directly at bytes since it would
|
||||
// allow none runes to be passed within "strings"
|
||||
for _, v := range []rune(v) {
|
||||
if v != s.Cur.Rune {
|
||||
return nil, s.ErrorExpected(v)
|
||||
}
|
||||
s.Scan()
|
||||
}
|
||||
|
||||
return s.Last, nil
|
||||
|
||||
case z.X: // -----------------------------------------------------
|
||||
var err error
|
||||
b := s.Mark()
|
||||
m := s.Mark()
|
||||
for _, i := range v {
|
||||
m, err = s.Expect(i)
|
||||
if err != nil {
|
||||
s.Jump(b)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case z.O: // -------------------------------------------------------
|
||||
for _, i := range v {
|
||||
m, _ := s.Expect(i)
|
||||
if m != nil {
|
||||
return m, nil
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("scanner: unsupported input type: %t", i)
|
||||
}
|
||||
s.BufLen = len(s.Buf)
|
||||
if s.BufLen == 0 {
|
||||
return fmt.Errorf("scanner: no input")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// Parse creates a new Node reference from the string returned by Look.
|
||||
func (s *R) Parse(to *Cur) *Node {
|
||||
n := new(Node)
|
||||
n.V = s.Look(to)
|
||||
return n
|
||||
}
|
||||
|
||||
// ParseSlice creates a new Node reference from the string returned by
|
||||
// LookSlice.
|
||||
func (s *R) ParseSlice(b *Cur, e *Cur) *Node {
|
||||
n := new(Node)
|
||||
n.V = s.LookSlice(b, e)
|
||||
return n
|
||||
}
|
||||
|
||||
// Look returns a string containing all the bytes from the current
|
||||
// scanner cursor position ahead or behind to the passed cursor
|
||||
// position. Neither the internal nor the passed cursor position is
|
||||
// changed. Also see Peek and LookSlice.
|
||||
func (s *R) Look(to *Cur) string {
|
||||
if to.Byte < s.Cur.Byte {
|
||||
return string(s.Buf[to.Byte:s.Cur.Next])
|
||||
}
|
||||
return string(s.Buf[s.Cur.Byte:to.Next])
|
||||
}
|
||||
|
||||
// LookSlice returns a string containing all the bytes from the first
|
||||
// cursor up to the second cursor. Neither cursor position is changed.
|
||||
func (s *R) LookSlice(beg *Cur, end *Cur) string {
|
||||
return string(s.Buf[beg.Byte:end.Next])
|
||||
}
|
||||
|
||||
// The Expect method is primarily designed for ease of use quickly by
|
||||
// allowing BPEGN expressions to be coded much faster than traditional
|
||||
// parser functions. In fact, entire applications can be generated
|
||||
// automatically from BPEGN, PEGN, ABNF, EBNF, or any other meta syntax
|
||||
// language by adding additional scan.Hooks to do work besides just
|
||||
// parsing what is scanned. Generate the BPEGN Go expressions to pass to
|
||||
// Expect and code the hook callbacks. That's it. Coding in BPEGN comes
|
||||
// naturally when putting together a parser quickly and without the
|
||||
// cognitive overhead of switching between grammars. The BPEGN
|
||||
// expressions passed to Expect are 100% Go. For full documentation of
|
||||
// BPEGN expressions see the z ("is") package and the source of this
|
||||
// Expect method.
|
||||
//
|
||||
// Warning: While it is nothing for most developers to be concerned
|
||||
// with, the Expect method does do a fair amount of functional recursion
|
||||
// for the sake of simplicity and to support BPEGN syntax. Those wishing
|
||||
// to gain the maximum performance should consider using other scan.R
|
||||
// methods instead in such cases. Developers are encouraged to do their
|
||||
// own benchmarking and perhaps start with BPEGN until they can create
|
||||
// more optimized parsers when and if necessary. Most will discover
|
||||
// other more substantial bottlenecks. The Bonzai project places
|
||||
// priority on speed and quality of developer delivery over run-time
|
||||
// performance. Delivery time is far more costly than the minimal gains
|
||||
// in run-time performance. "Premature optimization is the root of all
|
||||
// evil," as they say.
|
||||
func (s *R) Expect(expr any) (*Cur, error) {
|
||||
s.Last = s.Cur
|
||||
|
||||
// please keep the most common expressions types at the top
|
||||
|
||||
switch v := expr.(type) {
|
||||
|
||||
case rune: // ------------------------------------------------------
|
||||
if s.Cur.Rune != v {
|
||||
err := s.ErrorExpected(v)
|
||||
return nil, err
|
||||
}
|
||||
s.Scan()
|
||||
return s.Last, nil
|
||||
|
||||
case tk.Token: // --------------------------------------------------
|
||||
switch v {
|
||||
case tk.ANY: // A, A1
|
||||
s.Scan()
|
||||
case tk.A2:
|
||||
s.ScanN(2)
|
||||
case tk.A3:
|
||||
s.ScanN(3)
|
||||
case tk.A4:
|
||||
s.ScanN(4)
|
||||
case tk.A5:
|
||||
s.ScanN(5)
|
||||
case tk.A6:
|
||||
s.ScanN(6)
|
||||
case tk.A7:
|
||||
s.ScanN(7)
|
||||
case tk.A8:
|
||||
s.ScanN(8)
|
||||
case tk.A9:
|
||||
s.ScanN(9)
|
||||
}
|
||||
return s.Last, nil
|
||||
|
||||
case string: // ----------------------------------------------------
|
||||
if v == "" {
|
||||
return s.Mark(), nil
|
||||
}
|
||||
// avoid the temptation to look directly at bytes since it would
|
||||
// allow none runes to be passed within "strings"
|
||||
for _, v := range []rune(v) {
|
||||
if v != s.Cur.Rune {
|
||||
return nil, s.ErrorExpected(v)
|
||||
}
|
||||
s.Scan()
|
||||
}
|
||||
return s.Last, nil
|
||||
|
||||
case z.X: // -----------------------------------------------------
|
||||
var err error
|
||||
b := s.Mark()
|
||||
m := s.Mark()
|
||||
for _, i := range v {
|
||||
m, err = s.Expect(i)
|
||||
if err != nil {
|
||||
s.Jump(b)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case z.O: // -------------------------------------------------------
|
||||
for _, i := range v {
|
||||
m, _ := s.Expect(i)
|
||||
if m != nil {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
case z.Ti: // -----------------------------------------------------
|
||||
back := s.Mark()
|
||||
for s.Cur.Rune != tk.EOD {
|
||||
for _, i := range v {
|
||||
m, _ := s.Expect(i)
|
||||
if m != nil {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
s.Scan()
|
||||
}
|
||||
s.Jump(back)
|
||||
return nil, s.ErrorExpected(v)
|
||||
|
||||
case z.T: // -----------------------------------------------------
|
||||
m := s.Mark()
|
||||
b4 := s.Mark()
|
||||
for s.Cur.Rune != tk.EOD {
|
||||
for _, i := range v {
|
||||
b := s.Mark()
|
||||
c, _ := s.Expect(i)
|
||||
if c != nil {
|
||||
s.Jump(b)
|
||||
return b4, nil
|
||||
}
|
||||
}
|
||||
b4 = s.Mark()
|
||||
s.Scan()
|
||||
}
|
||||
s.Jump(m)
|
||||
return nil, s.ErrorExpected(v)
|
||||
|
||||
case z.Y: // ----------------------------------------------------
|
||||
var m *Cur
|
||||
b := s.Mark()
|
||||
for _, i := range v {
|
||||
m, _ = s.Expect(i)
|
||||
if m != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if m == nil {
|
||||
return nil, s.ErrorExpected(v)
|
||||
}
|
||||
s.Jump(b)
|
||||
return b, nil
|
||||
|
||||
case z.N: // ----------------------------------------------------
|
||||
m := s.Mark()
|
||||
for _, i := range v {
|
||||
if c, _ := s.Expect(i); c != nil {
|
||||
s.Jump(m)
|
||||
err := s.ErrorExpected(v, i)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case z.I: // -----------------------------------------------------
|
||||
var m *Cur
|
||||
for _, i := range v {
|
||||
var err error
|
||||
last := s.Mark()
|
||||
m, err = s.Expect(i)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
s.Jump(last)
|
||||
}
|
||||
if m == nil {
|
||||
return nil, s.ErrorExpected(v)
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case z.MM: // ----------------------------------------------------
|
||||
c := 0
|
||||
last := s.Mark()
|
||||
var err error
|
||||
var m, end *Cur
|
||||
for {
|
||||
m, err = s.Expect(v.This)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
last = m
|
||||
c++
|
||||
}
|
||||
if c == 0 && v.Min == 0 {
|
||||
if end == nil {
|
||||
end = last
|
||||
}
|
||||
}
|
||||
if !(v.Min <= c && c <= v.Max) {
|
||||
s.Jump(last)
|
||||
return nil, s.ErrorExpected(v)
|
||||
}
|
||||
return end, nil
|
||||
|
||||
case z.M1: // ----------------------------------------------------
|
||||
m := s.Mark()
|
||||
c, err := s.Expect(z.M{1, v.This})
|
||||
if err != nil {
|
||||
s.Jump(m)
|
||||
return nil, s.ErrorExpected(v)
|
||||
}
|
||||
return c, nil
|
||||
|
||||
case z.M: // ----------------------------------------------------
|
||||
c := 0
|
||||
last := s.Mark()
|
||||
var err error
|
||||
var m *Cur
|
||||
for {
|
||||
m, err = s.Expect(v.This)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
last = m
|
||||
c++
|
||||
}
|
||||
if c < v.Min {
|
||||
s.Jump(last)
|
||||
return nil, s.ErrorExpected(v)
|
||||
}
|
||||
return last, nil
|
||||
|
||||
case z.A: // ----------------------------------------------------
|
||||
for n := 0; n < v.N; n++ {
|
||||
s.Scan()
|
||||
}
|
||||
s.Scan()
|
||||
return s.Last, nil
|
||||
// see rune for A2-9
|
||||
|
||||
case z.R: // ----------------------------------------------------
|
||||
if !(v.First <= s.Cur.Rune && s.Cur.Rune <= v.Last) {
|
||||
err := s.ErrorExpected(v)
|
||||
return nil, err
|
||||
}
|
||||
s.Scan()
|
||||
return s.Last, nil
|
||||
|
||||
case z.C: // ------------------------------------------------------
|
||||
return s.expcount(v.N, v.This)
|
||||
case z.C2:
|
||||
return s.expcount(2, v.This)
|
||||
case z.C3:
|
||||
return s.expcount(3, v.This)
|
||||
case z.C4:
|
||||
return s.expcount(4, v.This)
|
||||
case z.C5:
|
||||
return s.expcount(5, v.This)
|
||||
case z.C6:
|
||||
return s.expcount(6, v.This)
|
||||
case z.C7:
|
||||
return s.expcount(7, v.This)
|
||||
case z.C8:
|
||||
return s.expcount(8, v.This)
|
||||
case z.C9:
|
||||
return s.expcount(9, v.This)
|
||||
|
||||
case Hook: // ------------------------------------------------------
|
||||
if err := v(s); err != nil {
|
||||
return nil, s.ErrorExpected(v, err)
|
||||
}
|
||||
return s.Cur, nil
|
||||
|
||||
case func(r *R) error:
|
||||
if err := v(s); err != nil {
|
||||
return nil, s.ErrorExpected(v, err)
|
||||
}
|
||||
return s.Cur, nil
|
||||
|
||||
}
|
||||
return nil, fmt.Errorf("unknown expression (%T)", expr)
|
||||
}
|
||||
|
||||
// handles all C* expressions
|
||||
func (s *R) expcount(n int, expr any) (*Cur, error) {
|
||||
b := s.Mark()
|
||||
m := s.Mark()
|
||||
for i := 0; i < n; i++ {
|
||||
m, _ := s.Expect(expr)
|
||||
if m == nil {
|
||||
s.Jump(b)
|
||||
return nil, s.ErrorExpected(expr)
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ErrorExpected returns a verbose, one-line error describing what was
|
||||
// expected when it encountered whatever the scanner last scanned. All
|
||||
// expression types are supported. See Expect.
|
||||
func (s *R) ErrorExpected(this any, args ...any) error {
|
||||
var msg string
|
||||
but := fmt.Sprintf(` at %v`, s)
|
||||
if s.Cur != nil && s.Cur.Rune == tk.EOD && s.Cur.Len == 0 {
|
||||
runes := `runes`
|
||||
if s.Cur.Pos.Rune == 1 {
|
||||
runes = `rune`
|
||||
}
|
||||
but = fmt.Sprintf(`, exceeded data length (%v %v)`,
|
||||
s.Cur.Pos.Rune, runes)
|
||||
}
|
||||
switch v := this.(type) {
|
||||
case rune: // otherwise will use uint32
|
||||
msg = fmt.Sprintf(`expected rune %q`, v)
|
||||
case z.Y:
|
||||
if len(v) > 1 {
|
||||
msg = fmt.Sprintf(`expected one of %q`, v)
|
||||
} else {
|
||||
msg = fmt.Sprintf(`expected %q`, v[0])
|
||||
}
|
||||
case z.N:
|
||||
msg = fmt.Sprintf(`unexpected %q`, args[0])
|
||||
case z.I:
|
||||
str := `expected one of %q`
|
||||
msg = fmt.Sprintf(str, v)
|
||||
case z.X:
|
||||
//str := `expected %q in sequence %q at %v beginning`
|
||||
//msg = fmt.Sprintf(str, args[0], v, args[1])
|
||||
str := `expected %q in sequence`
|
||||
msg = fmt.Sprintf(str, v)
|
||||
case z.O:
|
||||
str := `expected an optional %v`
|
||||
msg = fmt.Sprintf(str, v)
|
||||
case z.M1:
|
||||
str := `expected one or more %q`
|
||||
msg = fmt.Sprintf(str, v.This)
|
||||
case z.M:
|
||||
str := `expected min %v of %q`
|
||||
msg = fmt.Sprintf(str, v.Min, v.This)
|
||||
case z.MM:
|
||||
str := `expected min %v, max %v of %q`
|
||||
msg = fmt.Sprintf(str, v.Min, v.Max, v.This)
|
||||
case z.R:
|
||||
str := `expected range [%v-%v]`
|
||||
msg = fmt.Sprintf(str, string(v.First), string(v.Last))
|
||||
case z.Ti:
|
||||
str := `%q not found`
|
||||
if len(v) > 1 {
|
||||
str = `none of %q found`
|
||||
}
|
||||
msg = fmt.Sprintf(str, v)
|
||||
case z.T:
|
||||
msg = fmt.Sprintf(`none of %q found`, v)
|
||||
case Hook:
|
||||
msg = fmt.Sprintf("%v: %v", strings.ToLower(util.FuncName(v)), args[0])
|
||||
case func(s *R) error:
|
||||
msg = fmt.Sprintf("%v: %v", strings.ToLower(util.FuncName(v)), args[0])
|
||||
default:
|
||||
msg = fmt.Sprintf(`expected %T %q`, v, v)
|
||||
}
|
||||
return errors.New(msg + but)
|
||||
}
|
||||
|
||||
// --------------------------- new functions --------------------------
|
||||
|
||||
func (s *R) Any(n int) bool {
|
||||
for i := 0; i < n; i++ {
|
||||
s.Scan()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *R) Str(strs ...string) {
|
||||
for _, it := range strs {
|
||||
for _, r := range []rune(it) {
|
||||
if r != s.Cur.Rune {
|
||||
panic(fmt.Sprintf("expecting %q", r))
|
||||
}
|
||||
s.Scan()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *R) Opt(strs ...string) {
|
||||
s.Snap()
|
||||
OUT:
|
||||
for _, it := range strs {
|
||||
for _, r := range []rune(it) {
|
||||
if r != s.Cur.Rune {
|
||||
s.Back()
|
||||
continue OUT
|
||||
}
|
||||
s.Scan()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,619 +0,0 @@
|
||||
package scan_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/rwxrob/bonzai/scan"
|
||||
z "github.com/rwxrob/bonzai/scan/is"
|
||||
"github.com/rwxrob/bonzai/scan/tk"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
func ExampleNew_string() {
|
||||
s, err := scan.New("some thing")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
s.Print()
|
||||
s.ScanN(3)
|
||||
s.Print()
|
||||
//Output:
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
}
|
||||
|
||||
func ExampleNew_bytes() {
|
||||
s, err := scan.New([]byte{'s', 'o', 'm'})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
s.Print()
|
||||
s.ScanN(3)
|
||||
s.Print()
|
||||
//Output:
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// <EOD>
|
||||
}
|
||||
|
||||
func ExampleNew_reader() {
|
||||
r := strings.NewReader("some thing")
|
||||
s, err := scan.New(r)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
s.Print()
|
||||
s.ScanN(3)
|
||||
s.Print()
|
||||
//Output:
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
}
|
||||
|
||||
func ExampleInit() {
|
||||
s, err := scan.New("some thing")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
s.Init("other stuff entirely")
|
||||
s.Print()
|
||||
s.ScanN(3)
|
||||
s.Print()
|
||||
s.Scan()
|
||||
s.Print()
|
||||
//Output:
|
||||
// U+006F 'o' 1,1-1 (1-1)
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
// U+0072 'r' 1,5-5 (5-5)
|
||||
}
|
||||
|
||||
func ExampleMark() {
|
||||
s, err := scan.New("some thing")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
m := s.Mark()
|
||||
fmt.Println(s.Cur != m)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleJump() {
|
||||
s1, _ := scan.New("some thing")
|
||||
s1.ScanN(5)
|
||||
s1.Print() // t
|
||||
|
||||
s2, _ := scan.New("other thing")
|
||||
s2.ScanN(6)
|
||||
s2.Print() // t
|
||||
s1.Jump(s2.Cur) // WRONG, must be same source buffer
|
||||
s1.Print()
|
||||
|
||||
s3, _ := scan.New("some thing") // identical
|
||||
s3.ScanN(6)
|
||||
s3.Print() // h
|
||||
s1.Jump(s3.Cur)
|
||||
s1.Print()
|
||||
s3.ScanN(2)
|
||||
s1.Jump(s3.Cur)
|
||||
s1.Print()
|
||||
s3.Print()
|
||||
|
||||
// Output:
|
||||
// U+0074 't' 1,6-6 (6-6)
|
||||
// U+0074 't' 1,7-7 (7-7)
|
||||
// U+0074 't' 1,7-7 (7-7)
|
||||
// U+0068 'h' 1,7-7 (7-7)
|
||||
// U+0068 'h' 1,7-7 (7-7)
|
||||
// U+006E 'n' 1,9-9 (9-9)
|
||||
// U+006E 'n' 1,9-9 (9-9)
|
||||
|
||||
}
|
||||
|
||||
func ExampleNewLine() {
|
||||
s, _ := scan.New("some thing")
|
||||
s.Print()
|
||||
s.NewLine()
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// U+0073 's' 2,1-1 (1-1)
|
||||
}
|
||||
|
||||
/*
|
||||
func ExampleR_Expect_parse_Single_Success() {
|
||||
const FOO = `FOO`
|
||||
s, _ := scan.New("some thing")
|
||||
//c, _ := s.Expect(z.P{"some", ' ', z.I{'t', 'T'}})// FIXME
|
||||
c, _ := s.Expect(z.P{FOO, "some", ' ', 't'})
|
||||
c.Print() // same as "some t", points at 'h'
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+0074 't' 1,6-6 (6-6)
|
||||
// U+0068 'h' 1,7-7 (7-7)
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
func ExampleR_Expect_parse_Success_One_Deep() {
|
||||
const P = `PHRASE`
|
||||
const S = `STRING`
|
||||
const R = `RUNE`
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.P{P, z.P{S, "some"}, z.P{R, ' '}, z.P{R, 't'}})
|
||||
c.Print() // same as "some t", points at 't'
|
||||
//s.Print() // advances to 'h
|
||||
for _, v := range s.Nodes {
|
||||
fmt.Println(v)
|
||||
}
|
||||
// Output:
|
||||
// U+0074 't' 1,6-6 (6-6)
|
||||
// U+0068 'h' 1,7-7 (7-7)
|
||||
// not null
|
||||
}
|
||||
|
||||
func ExampleR_Expect_parse_Succes_Mixed() {
|
||||
const P = `PHRASE`
|
||||
const R = `RUNE`
|
||||
s, _ := scan.New("some thing")
|
||||
c, e := s.Expect(z.P{P, "some", z.P{R, ' '}, "thing"})
|
||||
fmt.Println(e)
|
||||
c.Print() // same as "some t", points at 'h'
|
||||
s.Print()
|
||||
//s.CurNode.Print()
|
||||
//fmt.Println(s.CurNode == s.Nodes[0])
|
||||
// Output:
|
||||
// U+0068 'h' 1,7-7 (7-7)
|
||||
// U+0068 'h' 1,7-7 (7-7)
|
||||
// {"T":"FOO","V":"some t"}
|
||||
// true
|
||||
}
|
||||
*/
|
||||
|
||||
func ExampleErrorExpected() {
|
||||
s, _ := scan.New("some thing")
|
||||
fmt.Println(s.ErrorExpected("foo"))
|
||||
fmt.Println(s.ErrorExpected('f'))
|
||||
fmt.Println(s.ErrorExpected([]byte{'f', 'o', 'o'}))
|
||||
// Output:
|
||||
// expected string "foo" at U+0073 's' 1,1-1 (1-1)
|
||||
// expected rune 'f' at U+0073 's' 1,1-1 (1-1)
|
||||
// expected []uint8 "foo" at U+0073 's' 1,1-1 (1-1)
|
||||
}
|
||||
|
||||
func ExampleExpect_string() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect("som")
|
||||
c.Print() // same as s.ScanN(2), last is 'm'
|
||||
s.Print() // point to next scan 'e'
|
||||
// Output:
|
||||
// U+006D 'm' 1,3-3 (3-3)
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
}
|
||||
|
||||
func ExampleExpect_compound_Expr_String() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.X{"some ", "thin"})
|
||||
c.Print()
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+006E 'n' 1,9-9 (9-9)
|
||||
// U+0067 'g' 1,10-10 (10-10)
|
||||
}
|
||||
|
||||
func ExampleExpect_compound_Expr_Rune() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.X{"some", ' ', "thin"})
|
||||
c.Print()
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+006E 'n' 1,9-9 (9-9)
|
||||
// U+0067 'g' 1,10-10 (10-10)
|
||||
}
|
||||
|
||||
func ExampleExpect_it_Success() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.Y{"some"})
|
||||
c.Print() // even though true, not moved
|
||||
s.Print() // scanner also not moved
|
||||
// Output:
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
}
|
||||
|
||||
func ExampleExpect_it_Success_Middle() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.X{"some", z.Y{' '}})
|
||||
c.Print() // advanced up to (but not including) ' '
|
||||
s.Print() // scanner also not moved
|
||||
// Output:
|
||||
// U+0020 ' ' 1,5-5 (5-5)
|
||||
// U+0020 ' ' 1,5-5 (5-5)
|
||||
}
|
||||
|
||||
func ExampleExpect_it_Fail() {
|
||||
s, _ := scan.New("some thing")
|
||||
_, err := s.Expect(z.X{"some", z.Y{"thing"}})
|
||||
fmt.Println(err)
|
||||
s.Print() // but scanner did get "some" so advanced
|
||||
// Output:
|
||||
// expected "thing" at U+0020 ' ' 1,5-5 (5-5)
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
}
|
||||
|
||||
func ExampleExpect_it_Fail_X() {
|
||||
s, _ := scan.New("some thing")
|
||||
_, err := s.Expect(z.X{"some", z.Y{"thing"}})
|
||||
fmt.Println(err)
|
||||
s.Print() // but scanner did get "some" so advanced
|
||||
// Output:
|
||||
// expected "thing" at U+0020 ' ' 1,5-5 (5-5)
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
}
|
||||
|
||||
func ExampleExpect_not_Success() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.N{"foo"})
|
||||
c.Print() // not advanced, but also not <nil>
|
||||
s.Print() // not advanced at all
|
||||
// Output:
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
}
|
||||
|
||||
func ExampleExpect_not_Fail() {
|
||||
s, _ := scan.New("some thing")
|
||||
_, err := s.Expect(z.N{"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_X_Fail() {
|
||||
s, _ := scan.New("some thing wonderful")
|
||||
_, err := s.Expect(z.X{z.N{'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_X_Success() {
|
||||
s, _ := scan.New("some thing wonderful")
|
||||
c, _ := s.Expect(z.X{z.N{`n`}, z.I{`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_to_Success_Mid() {
|
||||
s, _ := scan.New("some thing wonderful")
|
||||
c, _ := s.Expect(z.T{"wo"})
|
||||
c.Print() // "wo" not inc, same as "some thing ", so ' '
|
||||
s.Print() // advances to 'w'
|
||||
// Output:
|
||||
// U+0020 ' ' 1,11-11 (11-11)
|
||||
// U+0077 'w' 1,12-12 (12-12)
|
||||
}
|
||||
|
||||
func ExampleExpect_avoid_Not_with_In() {
|
||||
s, _ := scan.New("some thing")
|
||||
s.Snap()
|
||||
c, _ := s.Expect(z.I{z.N{'s'}, z.R{'a', 'z'}})
|
||||
c.Print() // unexpected success
|
||||
s.Print() // advanced to 'o'
|
||||
s.Back()
|
||||
// use z.X instead
|
||||
_, err := s.Expect(z.X{z.N{'s'}, z.R{'a', 'z'}})
|
||||
fmt.Println(err)
|
||||
s.Print() // not advanced
|
||||
// Output:
|
||||
// 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(z.P{"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(z.X{"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(z.X{"some", ` `, z.N{`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(z.X{"some", ' ', z.N{`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 wonderful")
|
||||
c, _ := s.Expect(tk.ANY)
|
||||
c.Print() // same as `s` or s.Scan()
|
||||
s.Print() // advances
|
||||
c, _ = s.Expect(tk.A)
|
||||
c.Print() // same as `o` or s.Scan()
|
||||
s.Print() // advances
|
||||
c, _ = s.Expect(tk.A1)
|
||||
// we'll skip tk.A2 - tk.A8
|
||||
s.Print()
|
||||
c, _ = s.Expect(tk.A9)
|
||||
s.Print() // should advance 9 to pos 13
|
||||
// Output:
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// U+006F 'o' 1,2-2 (2-2)
|
||||
// U+006F 'o' 1,2-2 (2-2)
|
||||
// U+006D 'm' 1,3-3 (3-3)
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
// U+006F 'o' 1,13-13 (13-13)
|
||||
}
|
||||
|
||||
func ExampleExpect_any_Success() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.A{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_o_Optional_Success() {
|
||||
s, _ := scan.New("some thing")
|
||||
//c, _ := s.Expect(z.O{"thing", "some"})
|
||||
c, _ := s.Expect("some")
|
||||
c.Print() // same as "some", points to 'e'
|
||||
s.Print() // advances to space ' '
|
||||
// Output:
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
// U+0020 ' ' 1,5-5 (5-5)
|
||||
}
|
||||
|
||||
func ExampleExpect_minimum_One() {
|
||||
s, _ := scan.New("sommme thing")
|
||||
start := s.Mark()
|
||||
s.ScanN(2)
|
||||
c, _ := s.Expect(z.M1{'m'}) // goggles up all three
|
||||
c.Print()
|
||||
s.Print()
|
||||
s.Jump(start)
|
||||
c, _ = s.Expect(z.M1{'s'}) // yep, just one
|
||||
c.Print()
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+006D 'm' 1,5-5 (5-5)
|
||||
// U+0065 'e' 1,6-6 (6-6)
|
||||
// U+0073 's' 1,1-1 (1-1)
|
||||
// U+006F 'o' 1,2-2 (2-2)
|
||||
}
|
||||
|
||||
func ExampleExpect_minimum() {
|
||||
s, _ := scan.New("sssoommme thing")
|
||||
c, _ := s.Expect(z.M{2, 's'})
|
||||
c.Print() // needs 2, but will consume all three to last 's'
|
||||
s.Print() // advances to next after ('o')
|
||||
// Output:
|
||||
// U+0073 's' 1,3-3 (3-3)
|
||||
// U+006F 'o' 1,4-4 (4-4)
|
||||
}
|
||||
|
||||
func ExampleExpect_mMx() {
|
||||
s, _ := scan.New("sommme thing")
|
||||
s.Snap()
|
||||
s.ScanN(2)
|
||||
s.Print()
|
||||
s.Expect(z.MM{1, 3, 'm'}) // goggles up all three
|
||||
s.Print()
|
||||
s.Back()
|
||||
s.Expect(z.MM{1, 3, 's'}) // yep, at least one
|
||||
s.Print()
|
||||
_, err := s.Expect(z.MM{1, 3, 'X'}) // nope
|
||||
fmt.Println(err)
|
||||
// Output:
|
||||
// U+006D 'm' 1,3-3 (3-3)
|
||||
// U+0065 'e' 1,6-6 (6-6)
|
||||
// U+006F 'o' 1,2-2 (2-2)
|
||||
// expected min 1, max 3 of 'X' at U+006F 'o' 1,2-2 (2-2)
|
||||
}
|
||||
|
||||
func ExampleExpect_c() {
|
||||
s, _ := scan.New("sommme thing")
|
||||
s.Snap()
|
||||
s.ScanN(2)
|
||||
s.Print()
|
||||
s.Expect(z.C{3, 'm'}) // goggles up all three
|
||||
s.Print()
|
||||
s.Back()
|
||||
s.Expect(z.C{1, 's'}) // yes, but silly since 's' is easier
|
||||
s.Print()
|
||||
_, err := s.Expect(z.C{3, 'X'}) // nope
|
||||
fmt.Println(err)
|
||||
// Output:
|
||||
// U+006D 'm' 1,3-3 (3-3)
|
||||
// U+0065 'e' 1,6-6 (6-6)
|
||||
// U+006F 'o' 1,2-2 (2-2)
|
||||
// expected rune 'X' at U+006F 'o' 1,2-2 (2-2)
|
||||
}
|
||||
|
||||
func ExampleExpect_rng() {
|
||||
s, _ := scan.New("some thing")
|
||||
s.Scan()
|
||||
c1, _ := s.Expect(z.R{'l', 'p'})
|
||||
c1.Print()
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+006F 'o' 1,2-2 (2-2)
|
||||
// U+006D 'm' 1,3-3 (3-3)
|
||||
}
|
||||
|
||||
func FailHook(s *scan.R) error { return fmt.Errorf("imma fail") }
|
||||
|
||||
func ExampleExpect_hook() {
|
||||
|
||||
// plain function signature
|
||||
WouldSave := scan.Hook(func(s *scan.R) error {
|
||||
fmt.Println("would save")
|
||||
return nil
|
||||
})
|
||||
|
||||
// as scan.Hook
|
||||
WouldScan := scan.Hook(func(s *scan.R) error {
|
||||
s.Scan()
|
||||
return nil
|
||||
})
|
||||
|
||||
// FailHook defined outside of Example function (see source)
|
||||
|
||||
s, _ := scan.New("some thing")
|
||||
s.Scan()
|
||||
s.Expect(WouldSave)
|
||||
s.Print() // hook didn't advance
|
||||
s.Expect(WouldScan)
|
||||
s.Print() // hook advanced scan by one
|
||||
_, e := s.Expect(FailHook)
|
||||
fmt.Println(e)
|
||||
|
||||
// Output:
|
||||
// would save
|
||||
// U+006F 'o' 1,2-2 (2-2)
|
||||
// U+006D 'm' 1,3-3 (3-3)
|
||||
// failhook: imma fail at U+006D 'm' 1,3-3 (3-3)
|
||||
|
||||
}
|
||||
|
||||
func ExampleExpect_to_Success() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.T{'e'})
|
||||
c.Print() // same as "som", points to 'm'
|
||||
s.Print() // scanned next after ('e')
|
||||
// Output:
|
||||
// U+006D 'm' 1,3-3 (3-3)
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
}
|
||||
|
||||
func ExampleExpect_to_Inclusive() {
|
||||
s, _ := scan.New("some thing")
|
||||
c, _ := s.Expect(z.Ti{'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() {
|
||||
s, _ := scan.New("some thing")
|
||||
s.ScanN(3)
|
||||
s.Snap()
|
||||
s.Print()
|
||||
s.ScanN(4)
|
||||
s.Print()
|
||||
s.Back()
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
// U+0069 'i' 1,8-8 (8-8)
|
||||
// U+0065 'e' 1,4-4 (4-4)
|
||||
}
|
||||
|
||||
func ExampleScan() {
|
||||
defer log.SetOutput(os.Stderr)
|
||||
defer log.SetFlags(log.Flags())
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(0)
|
||||
s, _ := scan.New(`s😈me thing`)
|
||||
s.Scan()
|
||||
s.Print()
|
||||
s.Scan()
|
||||
s.Print()
|
||||
s.Log()
|
||||
// Output:
|
||||
// U+1F608 '😈' 1,2-2 (2-2)
|
||||
// U+006D 'm' 1,3-6 (3-6)
|
||||
// U+006D 'm' 1,3-6 (3-6)
|
||||
}
|
||||
|
||||
func ExampleStr() {
|
||||
s, _ := scan.New("some thing")
|
||||
s.Str("some")
|
||||
s.Print()
|
||||
s.Str(" ", "th")
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+0020 ' ' 1,5-5 (5-5)
|
||||
// U+0069 'i' 1,8-8 (8-8)
|
||||
}
|
||||
|
||||
func ExampleAny() {
|
||||
s, _ := scan.New("some thing")
|
||||
s.Any(4)
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+0020 ' ' 1,5-5 (5-5)
|
||||
}
|
||||
|
||||
func ExampleOpt() {
|
||||
s, _ := scan.New("some thing")
|
||||
defer s.PrintPanic()
|
||||
s.Opt("S", "s")
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+006F 'o' 1,2-2 (2-2)
|
||||
}
|
||||
|
||||
func Example_all() {
|
||||
s, _ := scan.New("some thing")
|
||||
defer s.PrintPanic()
|
||||
s.Opt("S", "s")
|
||||
s.Str("ome", " ", "thi")
|
||||
s.Print()
|
||||
// Output:
|
||||
// U+006E 'n' 1,9-9 (9-9)
|
||||
}
|
||||
|
||||
func Example() {
|
||||
s, _ := scan.New("some thing")
|
||||
defer s.PrintPanic()
|
||||
s.Opt("S", "s")
|
||||
s.Str("ome", " ", "thi")
|
||||
s.Print()
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package scan
|
||||
|
||||
import "github.com/rwxrob/structs/tree"
|
||||
|
||||
// FIXME These currently do not create the needed Node structures
|
||||
// properly
|
||||
|
||||
// Slice is like "string" but marks the beginning and ending in the
|
||||
// buffer string data instead of containing the actual data.
|
||||
type Slice struct {
|
||||
Beg *Cur
|
||||
End *Cur
|
||||
}
|
||||
|
||||
// Beg begins a new Node containing a Slice at the current location and
|
||||
// pushes onto Parsing.
|
||||
func (s *R) Beg(t int) {
|
||||
|
||||
if s.CurNode == nil {
|
||||
tr := tree.New[*Slice]([]string{})
|
||||
s.CurNode = tr.Root
|
||||
s.Trees = append(s.Trees, tr)
|
||||
}
|
||||
|
||||
sl := &Slice{Beg: s.Mark()}
|
||||
n := s.CurNode.Add(t, sl)
|
||||
s.Parsing.Push(n)
|
||||
}
|
||||
|
||||
// End ends a new Node at the current location and pops it off of
|
||||
// Parsing placing adding it under the node in the current tree. End is
|
||||
// automatically implied when the end of data is reached for every Beg
|
||||
// that has not yet been closed.
|
||||
func (s *R) End() {
|
||||
if s.CurNode == nil {
|
||||
s.Warn(`no current node open, possibly forgot Beg? at %v`, s.Cur)
|
||||
return
|
||||
}
|
||||
n := s.Parsing.Pop()
|
||||
n.V.End = s.Mark()
|
||||
if n.P != nil {
|
||||
s.CurNode = n.P
|
||||
}
|
||||
}
|
@ -0,0 +1,203 @@
|
||||
// Copyright 2022 Robert S. Muhlestein.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package Z_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
Z "github.com/rwxrob/bonzai/z"
|
||||
)
|
||||
|
||||
func ExampleArgsFrom() {
|
||||
fmt.Printf("%q\n", Z.ArgsFrom(`greet hi french`))
|
||||
fmt.Printf("%q\n", Z.ArgsFrom(`greet hi french `))
|
||||
// Output:
|
||||
// ["greet" "hi" "french"]
|
||||
// ["greet" "hi" "french" ""]
|
||||
}
|
||||
|
||||
func ExampleArgsOrIn_read_Nil() {
|
||||
|
||||
orig := os.Stdin
|
||||
defer func() { os.Stdin = orig }()
|
||||
os.Stdin, _ = os.Open(`testdata/in`)
|
||||
|
||||
fmt.Println(Z.ArgsOrIn(nil))
|
||||
|
||||
// Output:
|
||||
// some thing
|
||||
}
|
||||
|
||||
func ExampleArgsOrIn_read_Zero_Args() {
|
||||
|
||||
orig := os.Stdin
|
||||
defer func() { os.Stdin = orig }()
|
||||
os.Stdin, _ = os.Open(`testdata/in`)
|
||||
|
||||
fmt.Println(Z.ArgsOrIn([]string{}))
|
||||
|
||||
// Output:
|
||||
// some thing
|
||||
}
|
||||
|
||||
func ExampleArgsOrIn_args_Joined() {
|
||||
|
||||
fmt.Println(Z.ArgsOrIn([]string{"some", "thing"}))
|
||||
|
||||
// Output:
|
||||
// some thing
|
||||
}
|
||||
|
||||
func ExampleEsc() {
|
||||
fmt.Println(Z.Esc("|&;()<>![]"))
|
||||
fmt.Printf("%q", Z.Esc(" \n\r"))
|
||||
// Output:
|
||||
// \|\&\;\(\)\<\>\!\[\]
|
||||
// "\\ \\\n\\\r"
|
||||
}
|
||||
|
||||
func ExampleEscAll() {
|
||||
list := []string{"so!me", "<here>", "other&"}
|
||||
fmt.Println(Z.EscAll(list))
|
||||
// Output:
|
||||
// [so\!me \<here\> other\&]
|
||||
}
|
||||
|
||||
func ExampleInferredUsage_optional_Param() {
|
||||
x := &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
Call: func(_ *Z.Cmd, _ ...string) error { return nil },
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
// Output:
|
||||
// (p1|p2)?
|
||||
}
|
||||
|
||||
func ExampleInferredUsage_min_One_Param() {
|
||||
x := &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
MinParm: 1,
|
||||
Call: func(_ *Z.Cmd, _ ...string) error { return nil },
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
// Output:
|
||||
// (p1|p2)
|
||||
}
|
||||
|
||||
func ExampleInferredUsage_min_3_Param() {
|
||||
x := &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
MinParm: 3,
|
||||
Call: func(_ *Z.Cmd, _ ...string) error { return nil },
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
// Output:
|
||||
// (p1|p2){3,}
|
||||
}
|
||||
|
||||
func ExampleInferredUsage_commands() {
|
||||
x := &Z.Cmd{
|
||||
Commands: []*Z.Cmd{
|
||||
&Z.Cmd{Name: "foo", Aliases: []string{"f"}},
|
||||
&Z.Cmd{Name: "bar"},
|
||||
},
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
// Output:
|
||||
// ((f|foo)|bar)
|
||||
}
|
||||
|
||||
func ExampleInferredUsage_commands_and_Params() {
|
||||
x := &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
Commands: []*Z.Cmd{
|
||||
&Z.Cmd{Name: "foo", Aliases: []string{"f"}},
|
||||
&Z.Cmd{Name: "bar"},
|
||||
},
|
||||
Call: func(_ *Z.Cmd, _ ...string) error { return nil },
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
// Output:
|
||||
// ((p1|p2)?|((f|foo)|bar))
|
||||
}
|
||||
|
||||
func ExampleInferredUsage_error_No_Call_or_Command() {
|
||||
x := &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
// Output:
|
||||
// {ERROR: neither Call nor Commands defined}
|
||||
}
|
||||
|
||||
func ExampleInferredUsage_error_Params_without_Call() {
|
||||
x := &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
Commands: []*Z.Cmd{
|
||||
&Z.Cmd{Name: "foo", Aliases: []string{"f"}},
|
||||
&Z.Cmd{Name: "bar"},
|
||||
},
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
// Output:
|
||||
// {ERROR: Params without Call: p1, p2}
|
||||
}
|
||||
|
||||
/*
|
||||
func ExampleInferredUsage() {
|
||||
|
||||
// call method, params, aliases, commands, and hidden commands
|
||||
// TODO
|
||||
|
||||
// [(p1|p2)|(h|help)]
|
||||
|
||||
// Call with optional params and also Commands
|
||||
x := &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
Commands: []*Z.Cmd{&Z.Cmd{Name: "foo"}, &Z.Cmd{Name: "bar"}},
|
||||
Call: func(_ *Z.Cmd, _ ...string) error { return nil },
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
|
||||
// no Call, Command required
|
||||
x = &Z.Cmd{
|
||||
Commands: []*Z.Cmd{&Z.Cmd{Name: "foo"}, &Z.Cmd{Name: "bar"}},
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
|
||||
// no Call with unused params (ERROR)
|
||||
x = &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
|
||||
// Call with optional Params, but no commands
|
||||
x = &Z.Cmd{
|
||||
Params: []string{"p1", "p2"},
|
||||
Call: func(_ *Z.Cmd, _ ...string) error { return nil },
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
|
||||
// Call with optional Commands, but no params
|
||||
x = &Z.Cmd{
|
||||
Commands: []*Z.Cmd{&Z.Cmd{Name: "foo"}, &Z.Cmd{Name: "bar"}},
|
||||
Call: func(_ *Z.Cmd, _ ...string) error { return nil },
|
||||
}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
|
||||
// no Call, Commands, or Params (ERROR)
|
||||
x = &Z.Cmd{}
|
||||
fmt.Println(Z.InferredUsage(x))
|
||||
|
||||
// Output:
|
||||
// [p1|p2|foo|bar]
|
||||
// (foo|bar)
|
||||
// {ERROR: Params without Call: p1, p2}
|
||||
// [p1|p2]
|
||||
// [foo|bar]
|
||||
// {ERROR: neither Call nor Commands defined}
|
||||
|
||||
}
|
||||
*/
|
Loading…
Reference in New Issue