Add first "is" Expect/Check package values

pull/53/head
rwxrob 2 years ago
parent 796adcd061
commit 12e6aa8585
No known key found for this signature in database
GPG Key ID: 2B9111F33082AE77

@ -5,6 +5,9 @@ import "github.com/rwxrob/bonzai/scanner"
func ExampleCur() {
m := new(scanner.Cur)
m.Print()
m.NewLine()
m.Print()
//Output:
// U+0000 '\x00' 0,0-0 (0-1)
// U+0000 '\x00' 1,1-1 (0-1)
}

@ -0,0 +1,40 @@
// Copyright 2022 Robert S. Muhlestein.
// SPDX-License-Identifier: Apache-2.0
/*
Package is contains the pseudo-grammar structs recognized by the scan.Expect and scan.Check methods. These structs are guaranteed never to have their structure order change in any way allowing them to be used in key-less, in-line composable notation (which, despite the many editor warnings, is entirely supported by Go and always will be).
*/
package is
// Not represents the logical inverse of whatever is passed. If This
// were a string, for example, Expect/Check would test that it was *not*
// at a given scan location.
type Not struct {
This interface{}
}
/*
type Min struct {
Match interface{}
Min int
}
type Count struct {
Match interface{}
Count int
}
type Seq []interface{}
type OneOf []interface{}
type MinMax struct {
Match interface{}
Min int
Max int
}
type Opt struct {
This interface{}
}
*/

@ -1,3 +1,6 @@
// Copyright 2022 Robert S. Muhlestein.
// SPDX-License-Identifier: Apache-2.0
package scanner
import (
@ -6,6 +9,7 @@ import (
"io"
"unicode/utf8"
"github.com/rwxrob/bonzai/scanner/is"
"github.com/rwxrob/bonzai/scanner/tk"
)
@ -20,13 +24,14 @@ type Scanner struct {
// New returns a newly initialized non-linear, rune-centric, buffered
// data scanner with support for parsing data from io.Reader, string,
// and []byte types. Also see the Init method.
func New(i interface{}) *Scanner {
// and []byte types. Returns nil and the error if any encountered during
// initialization. Also see the Init method.
func New(i interface{}) (*Scanner, error) {
p := new(Scanner)
if err := p.Init(i); err != nil {
return p
return nil, err
}
return nil
return p, nil
}
// Init reads all of passed parsable data (io.Reader, string, []byte)
@ -114,8 +119,9 @@ func (p *Scanner) String() string { return p.Cur.String() }
// Print delegates to internal cursor Print.
func (p *Scanner) Print() { p.Cur.Print() }
// CopyCur returns a copy of the current scanner cursor. See Cur.
func (p *Scanner) CopyCur() *Cur {
// Mark returns a copy of the current scanner cursor to preserve like
// a bookmark into the buffer data. See Cur, Look, LookSlice.
func (p *Scanner) Mark() *Cur {
if p.Cur == nil {
return nil
}
@ -126,13 +132,33 @@ func (p *Scanner) CopyCur() *Cur {
// Jump replaces the internal cursor with a copy of the one passed
// effectively repositioning the scanner's current position in the
// buffered data.
// buffered data. Beware, however, that the new cursor must originate
// from the same (or identical) data buffer or the values will be out of
// sync.
func (p *Scanner) Jump(c *Cur) { nc := *c; p.Cur = &nc }
// Peek returns a string containing all the runes from the current
// scanner cursor position forward to the number of runes passed.
// If end of data is countered will everything up until that point.
// Also so Look and LookSlice.
func (p *Scanner) Peek(n uint) string {
buf := ""
pos := p.Cur.Byte
for c := uint(0); c < n; c++ {
r, ln := utf8.DecodeRune(p.Buf[pos:])
if ln == 0 {
break
}
buf += string(r)
pos += ln
}
return buf
}
// Look returns a string containing all the bytes from the current
// scanner cursor position up to the passed cursor position, forward or
// backward. Neither the internal nor the passed cursor position is
// changed.
// 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 (p *Scanner) Look(to *Cur) string {
if to.Byte < p.Cur.Byte {
return string(p.Buf[to.Byte:p.Cur.Next])
@ -147,23 +173,18 @@ func (p *Scanner) LookSlice(beg *Cur, end *Cur) string {
}
// Expect takes a variable list of parsable types including rune,
// string, Class, Check, Opt, Not, Seq, One, Min, MinMax, Count. This
// allows grammars to be represented simply and parsed easily without
// exceptional overhead from additional function calls and indirection.
// string, is.In, is.Opt, is.Not, is.Seq, is.Min, is.MinMax, is.Count,
// and all strings from the tk subpackage. This 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 (p *Scanner) Expect(scannable ...interface{}) (*Cur, error) {
var beg, end *Cur
beg = p.Cur
for _, m := range scannable {
switch v := m.(type) {
case rune:
if p.Cur.Rune != v {
err := p.ErrorExpected(m)
p.Jump(beg)
return nil, err
}
end = p.CopyCur()
p.Scan()
// please keep the most common at the top
switch v := m.(type) {
case string:
if v == "" {
@ -175,9 +196,27 @@ func (p *Scanner) Expect(scannable ...interface{}) (*Cur, error) {
p.Jump(beg)
return nil, err
}
end = p.CopyCur()
end = p.Mark()
p.Scan()
}
case rune:
if p.Cur.Rune != v {
err := p.ErrorExpected(m)
p.Jump(beg)
return nil, err
}
end = p.Mark()
p.Scan()
case is.Not:
if _, e := p.Check(v.This); e == nil {
err := p.ErrorExpected(v)
p.Jump(beg)
return nil, err
}
end = p.Mark()
/*
case Class:
if !v.Check(p.Cur.Rune) {
@ -185,7 +224,7 @@ func (p *Scanner) Expect(scannable ...interface{}) (*Cur, error) {
p.Jump(beg)
return nil, err
}
end = p.CopyCur()
end = p.Mark()
p.Scan()
case Check:
@ -206,17 +245,9 @@ func (p *Scanner) Expect(scannable ...interface{}) (*Cur, error) {
}
end = m
case is.Not:
if _, e := p.Check(v.This); e == nil {
err := p.ErrorExpected(v)
p.Jump(beg)
return nil, err
}
end = p.CopyCur()
case is.Min:
c := 0
last := p.CopyCur()
last := p.Mark()
var err error
var m *Cur
for {
@ -243,7 +274,7 @@ func (p *Scanner) Expect(scannable ...interface{}) (*Cur, error) {
case is.MinMax:
c := 0
last := p.CopyCur()
last := p.Mark()
var err error
var m *Cur
for {
@ -278,7 +309,7 @@ func (p *Scanner) Expect(scannable ...interface{}) (*Cur, error) {
var m *Cur
var err error
for _, i := range v {
last := p.CopyCur()
last := p.Mark()
m, err = p.Expect(i)
if err == nil {
break
@ -292,34 +323,33 @@ func (p *Scanner) Expect(scannable ...interface{}) (*Cur, error) {
*/
default:
return nil, fmt.Errorf("expect: unsupported argument type (%T)", m)
return nil, fmt.Errorf("expect: unscannable type (%T)", m)
}
}
return end, nil
}
// 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.
func (p *Scanner) ErrorExpected(this interface{}) error {
var msg string
but := fmt.Sprintf(` but got %v`, p)
but := fmt.Sprintf(` at %v`, p)
if p.Done() {
runes := `runes`
if p.Cur.Pos.Rune == 1 {
runes = `rune`
}
but = fmt.Sprintf(` but exceeded data length (%v %v)`, p.Cur.Pos.Rune, runes)
but = fmt.Sprintf(`, exceeded data length (%v %v)`,
p.Cur.Pos.Rune, runes)
}
// TODO add verbose errors for *all* types in Grammar
switch v := this.(type) {
case string:
msg = fmt.Sprintf(`expected string %q`, v)
case rune:
case rune: // otherwise will use uint32
msg = fmt.Sprintf(`expected rune %q`, v)
/*
case Class:
msg = fmt.Sprintf(`expected class %v (%v)`, v.Ident(), v.Desc())
default:
msg = fmt.Sprintf(`expected %T %q`, v, v)
*/
case is.Not:
msg = fmt.Sprintf(`not expecting %q`, v.This)
default:
msg = fmt.Sprintf(`expected %T %q`, v, v)
}
return errors.New(msg + but)
}
@ -327,7 +357,9 @@ func (p *Scanner) ErrorExpected(this interface{}) error {
// NewLine delegates to interval Curs.NewLine.
func (p *Scanner) NewLine() { p.Cur.NewLine() }
func (p *Scanner) Check(ms ...interface{}) (*Cur, error) {
defer p.Jump(p.CopyCur())
return p.Expect(ms...)
// Check behaves exactly like Expect but jumps back to the original
// cursor position after scanning for expected scannable values.
func (p *Scanner) Check(scannable ...interface{}) (*Cur, error) {
defer p.Jump(p.Mark())
return p.Expect(scannable...)
}

@ -0,0 +1,218 @@
package scanner_test
import (
"fmt"
"strings"
"github.com/rwxrob/bonzai/scanner"
"github.com/rwxrob/bonzai/scanner/is"
)
func ExampleNew_string() {
s, err := scanner.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 := scanner.New([]byte{'s', 'o', 'm'})
if err != nil {
fmt.Println(err)
}
s.Print()
s.ScanN(3)
s.Print()
fmt.Println(s.Done())
//Output:
// U+0073 's' 1,1-1 (1-1)
// <EOD>
// true
}
func ExampleNew_reader() {
r := strings.NewReader("some thing")
s, err := scanner.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 := scanner.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 := scanner.New("some thing")
if err != nil {
fmt.Println(err)
}
m := s.Mark()
//log.Printf("%p", s.Cur)
//log.Printf("%p", m)
fmt.Println(s.Cur != m)
// Output:
// true
}
func ExampleJump() {
s1, _ := scanner.New("some thing")
s1.ScanN(5)
s1.Print() // t
s2, _ := scanner.New("other thing")
s2.ScanN(6)
s2.Print() // t
s1.Jump(s2.Cur) // WRONG, must be same source buffer
s1.Print()
s3, _ := scanner.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 ExamplePeek() {
s, _ := scanner.New("some thing")
s.ScanN(6)
fmt.Println(s.Peek(3))
// Output:
// hin
}
func ExampleLook() {
s, _ := scanner.New("some thing")
s.Scan()
m1 := s.Mark()
m1.Print()
s.ScanN(3)
fmt.Printf("%q\n", s.Look(m1)) // look behind
s.ScanN(4)
m2 := s.Mark()
m2.Print()
s.Jump(m1)
fmt.Printf("%q\n", s.Look(m2)) // look ahead
// Output:
// U+006F 'o' 1,2-2 (2-2)
// "ome "
// U+006E 'n' 1,9-9 (9-9)
// "ome thin"
}
func ExampleLookSlice() {
s, _ := scanner.New("some thing")
s.Scan()
m1 := s.Mark()
m1.Print()
s.ScanN(7)
m2 := s.Mark()
m2.Print()
fmt.Println(s.LookSlice(m1, m2))
// Output:
// U+006F 'o' 1,2-2 (2-2)
// U+006E 'n' 1,9-9 (9-9)
// ome thin
}
func ExampleNewLine() {
s, _ := scanner.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 ExampleErrorExpected() {
s, _ := scanner.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_basic() {
s, _ := scanner.New("some thing")
c, _ := s.Expect("some", ' ', "thin")
c.Print()
fmt.Println(s.Done())
s.Print()
s.Scan()
s.Print()
fmt.Println(s.Done())
// Output:
// U+006E 'n' 1,9-9 (9-9)
// false
// U+0067 'g' 1,10-10 (10-10)
// <EOD>
// true
}
func ExampleCheck() {
s, _ := scanner.New("some thing")
c, _ := s.Check("some", ' ', "thin") // same as Expect ...
c.Print() // ... with cur return ...
s.Print() // ... just doesn't advance
// Output:
// U+006E 'n' 1,9-9 (9-9)
// U+0073 's' 1,1-1 (1-1)
}
func ExampleExpect_not() {
s, _ := scanner.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)
// Output:
// U+0073 's' 1,1-1 (1-1)
// <nil>
// <nil>
// not expecting "some" at U+0073 's' 1,1-1 (1-1)
}
Loading…
Cancel
Save