Documentation
¶
Index ¶
- Variables
- type Cursor
- type Position
- type S
- func (s *S) Beginning() bool
- func (s *S) Buffer(b any) error
- func (s *S) Bytes() *[]byte
- func (s *S) CopyBB(m Cursor) string
- func (s *S) CopyBE(m Cursor) string
- func (s *S) CopyEB(m Cursor) string
- func (s *S) CopyEE(m Cursor) string
- func (s *S) Finished() bool
- func (s *S) Goto(c Cursor)
- func (s *S) Is(a string) bool
- func (s S) Log()
- func (s *S) Mark() Cursor
- func (s *S) Match(re *regexp.Regexp) int
- func (s *S) Open(path string) error
- func (s *S) Peek(a string) bool
- func (s *S) PeekMatch(re *regexp.Regexp) int
- func (s S) Pos() Position
- func (s S) Positions(p ...int) []Position
- func (s S) Print()
- func (s *S) Revert(m Cursor) bool
- func (s *S) Rune() rune
- func (s *S) RuneB() int
- func (s *S) RuneE() int
- func (s *S) Scan() bool
- func (s *S) SetMaxErr(i int)
- func (s *S) SetViewLen(a int)
- func (s S) String() string
- func (s *S) TraceOff()
- func (s *S) TraceOn()
- func (s *S) ViewLen() int
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var Trace int
Trace sets the trace for everything that uses this package. Use TraceOn/Off for specific scanner tracing.
var ViewLenDefault = 10 // default length of preview window
Functions ¶
This section is empty.
Types ¶
type Cursor ¶
type Cursor struct {
Buf *[]byte // pointer to actual bytes buffer
R rune // last rune scanned
B int // beginning of last rune scanned
E int // effective end of last rune scanned (beginning of next)
}
Cursor contains a cursor pointer to a bytes slice buffer and information pointing to a specific location in the bytes buffer. The order of fields is guaranteed to never change.
type Position ¶
type Position struct {
Rune rune // rune at this location
BufByte int // byte offset in file
BufRune int // rune offset in file
Line int // line offset
LByte int // line column byte offset
LRune int // line column rune offset
}
Position contains the human-friendly information about the position within a give text file. Note that all values begin with 1 and not 0.
func (Position) Log ¶
func (p Position) Log()
Log calls log.Println on the cursor itself in String form. See String.
type S ¶
type S struct {
Buf []byte // full buffer for lookahead or behind
R rune // last decoded/scanned rune, maybe >1byte
B int // index pointing beginning of R
E int // index pointing to end (after) R
NewLine []string // []string{"\r\n","\n"} by default
Trace int // non-zero activates tracing
// contains filtered or unexported fields
}
S (to avoid stuttering) implements a buffered data, non-linear, rune-centric, scanner with regular expression support
Example (Init) ¶
package main
import (
"fmt"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
// * extremely minimal initialization
// * order guaranteed never to change
s := scanner.New(`some thing`)
fmt.Println(s)
}
Example (Package_Trace) ¶
package main
import (
"log"
"os"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
// take over stderr just for this test
defer log.SetFlags(log.Flags())
defer log.SetOutput(os.Stderr)
defer func() { scanner.Trace = 0 }()
log.SetOutput(os.Stdout)
log.SetFlags(0)
s := scanner.New(`foo`)
scanner.Trace++
s.Scan()
s.Scan()
s.Scan()
}
Output: 'f' 0-1 "oo" 'o' 1-2 "o" 'o' 2-3 ""
func New ¶
New is a high-level scanner constructor and initializer that takes a single optional argument containing any valid Buffer() argument. Invalid arguments will fail (not fatal) with log output.
func (*S) Beginning ¶
Beginning returns true if and only if the scanner is currently pointing to the beginning of the buffer without anything scanned at all.
func (*S) Buffer ¶
Buffer sets the internal bytes buffer (Buf) and resets the existing cursor values to their initial state (null, 0,0). This is useful when testing in order to buffer strings as well as content from any io.Reader, []byte, []rune, or string. Fulfills Scanner.
func (*S) Finished ¶
Finished returns true if scanner has nothing more to scan.
Example ¶
package main
import (
"fmt"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New(`foo`)
s.Print()
s.Scan()
s.Print()
fmt.Println(s.Finished())
s.Scan()
s.Print()
fmt.Println(s.Finished())
s.Scan()
s.Print()
fmt.Println(s.Finished())
}
Output: '\x00' 0-0 "foo" 'f' 0-1 "oo" false 'o' 1-2 "o" false 'o' 2-3 "" true
func (*S) Is ¶
Is returns true if the passed string matches the last scanned rune and the runes ahead matching the length of the string. Returns false if the string would go beyond the length of buffer (len(s.Buf)).
Example ¶
package main
import (
"fmt"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New(`foo`)
s.Scan() // never forget to scan with Is (use Peek otherwise)
fmt.Println(s.Is("fo"))
fmt.Println(s.Is("bar"))
}
Output: true false
Example (Not) ¶
package main
import (
"fmt"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New("\r\n")
s.Scan() // never forget to scan with Is (use Peek otherwise)
fmt.Println(s.Is("\r"))
fmt.Println(s.Is("\r\n"))
fmt.Println(s.Is("\n"))
}
Output: true true false
func (*S) Match ¶
Match checks for a regular expression match at the last position in the buffer (s.B) providing a mechanism for positive and negative lookahead expressions. It returns the length of the match. Successful matches might be zero (see regexp.Regexp.FindIndex). A negative value is returned if no match is found. Note that Go regular expressions now include the Unicode character classes (ex: \p{L|d}) that should be used over dated alternatives (ex: \w).
Example ¶
package main
import (
"fmt"
"regexp"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New(`foo`)
s.Scan() // never forget to scan (use PeekMatch otherwise)
f := regexp.MustCompile(`f`)
F := regexp.MustCompile(`F`)
o := regexp.MustCompile(`o`)
fmt.Println(s.Match(f))
fmt.Println(s.Match(F))
fmt.Println(s.Match(o))
}
Output: 1 -1 -1
func (*S) Peek ¶
Peek returns true if the passed string matches from current position in the buffer (s.B) forward. Returns false if the string would go beyond the length of buffer (len(s.Buf)). Peek does not advance the Scanner.
Example ¶
package main
import (
"fmt"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New(`foo`)
fmt.Println(s.Peek("fo"))
s.Scan()
fmt.Println(s.Peek("fo"))
fmt.Println(s.Peek("oo"))
}
Output: true false true
func (*S) PeekMatch ¶
PeekMatch checks for a regular expression match at the current position in the buffer providing a mechanism for positive and negative lookahead expressions. It returns the length of the match. Successful matches might be zero (see regexp.Regexp.FindIndex). A negative value is returned if no match is found. Note that Go regular expressions now include the Unicode character classes (ex: \p{L|d}) that should be used over dated alternatives (ex: \w).
func (S) Pos ¶
Example ¶
package main
import (
"github.com/rwxrob/bonzai/scanner"
)
func main() {
//😟 WARNING: uses risky jumps (assigning s.E)
s := scanner.New("one line\nand another\r\nand yet another")
s.E = 2
s.Pos().Print()
s.E = 0
s.Scan()
s.Scan()
s.Pos().Print()
s.E = 12
s.Pos().Print()
s.E = 27
s.Pos().Print()
}
Output: U+006E 'n' 1,2-2 (2-2) U+006E 'n' 1,2-2 (2-2) U+0064 'd' 2,3-3 (12-12) U+0079 'y' 3,5-5 (27-27)
func (S) Positions ¶
Positions returns human-friendly Position information (which can easily be used to populate a text/template) for each raw byte offset (s.E). Only one pass through the buffer (s.Buf) is required to count lines and runes since the raw byte position (s.E) is frequently changed directly. Therefore, when multiple positions are wanted, consider caching the raw byte positions (s.E) and calling Positions() once for all of them.
Example ¶
package main
import (
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New("one line\nand another\r\nand yet another")
for _, p := range s.Positions(2, 12, 27) {
p.Print()
}
}
Output: U+006E 'n' 1,2-2 (2-2) U+0064 'd' 2,3-3 (12-12) U+0079 'y' 3,5-5 (27-27)
func (*S) Scan ¶
Scan decodes the next rune, setting it to R, and advances position (P) by the size of the rune (R) in bytes returning false then there is nothing left to scan. Only runes bigger than utf8.RuneSelf are decoded since most runes (ASCII) will usually be under this number.
Example ¶
package main
import (
"fmt"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New(`foo`)
s.Print() // equivalent of a "zero value"
fmt.Println(s.Scan())
s.Print()
fmt.Println(s.Scan())
s.Print()
fmt.Println(s.Scan())
s.Print()
fmt.Println(s.Scan()) // does not advance
s.Print() // same as before
}
Output: '\x00' 0-0 "foo" true 'f' 0-1 "oo" true 'o' 1-2 "o" true 'o' 2-3 "" false 'o' 2-3 ""
Example (Loop) ¶
package main
import (
"fmt"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New(`abcdefgh`)
for s.Scan() {
fmt.Print(string(s.Rune()))
if !s.Finished() {
fmt.Print("-")
}
}
}
Output: a-b-c-d-e-f-g-h
Example (Risky_Jump) ¶
package main
import (
"fmt"
"github.com/rwxrob/bonzai/scanner"
)
func main() {
s := scanner.New(`foo1234`)
fmt.Println(s.Scan())
s.Print()
s.E += 2 //😟 WARNING: s.R and s.B, not yet updated!
fmt.Println(s.Scan()) //😊 s.R and s.B now updated
s.Print()
}
Output: true 'f' 0-1 "oo1234" true '1' 3-4 "234"