...

Source file src/github.com/PaesslerAG/gval/parser.go

Documentation: github.com/PaesslerAG/gval

     1  package gval
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  	"text/scanner"
     8  	"unicode"
     9  )
    10  
    11  //Parser parses expressions in a Language into an Evaluable
    12  type Parser struct {
    13  	scanner scanner.Scanner
    14  	Language
    15  	lastScan   rune
    16  	camouflage error
    17  }
    18  
    19  func newParser(expression string, l Language) *Parser {
    20  	sc := scanner.Scanner{}
    21  	sc.Init(strings.NewReader(expression))
    22  	sc.Error = func(*scanner.Scanner, string) {}
    23  	sc.Filename = expression + "\t"
    24  	p := &Parser{scanner: sc, Language: l}
    25  	p.resetScannerProperties()
    26  	return p
    27  }
    28  
    29  func (p *Parser) resetScannerProperties() {
    30  	p.scanner.Whitespace = scanner.GoWhitespace
    31  	p.scanner.Mode = scanner.GoTokens
    32  	p.scanner.IsIdentRune = func(r rune, pos int) bool {
    33  		return unicode.IsLetter(r) || r == '_' || (pos > 0 && unicode.IsDigit(r))
    34  	}
    35  }
    36  
    37  // SetWhitespace sets the behavior of the whitespace matcher. The given
    38  // characters must be less than or equal to 0x20 (' ').
    39  func (p *Parser) SetWhitespace(chars ...rune) {
    40  	var mask uint64
    41  	for _, char := range chars {
    42  		mask |= 1 << char
    43  	}
    44  
    45  	p.scanner.Whitespace = mask
    46  }
    47  
    48  // SetMode sets the tokens that the underlying scanner will match.
    49  func (p *Parser) SetMode(mode uint) {
    50  	p.scanner.Mode = mode
    51  }
    52  
    53  // SetIsIdentRuneFunc sets the function that matches ident characters in the
    54  // underlying scanner.
    55  func (p *Parser) SetIsIdentRuneFunc(fn func(ch rune, i int) bool) {
    56  	p.scanner.IsIdentRune = fn
    57  }
    58  
    59  // Scan reads the next token or Unicode character from source and returns it.
    60  // It only recognizes tokens t for which the respective Mode bit (1<<-t) is set.
    61  // It returns scanner.EOF at the end of the source.
    62  func (p *Parser) Scan() rune {
    63  	if p.isCamouflaged() {
    64  		p.camouflage = nil
    65  		return p.lastScan
    66  	}
    67  	p.camouflage = nil
    68  	p.lastScan = p.scanner.Scan()
    69  	return p.lastScan
    70  }
    71  
    72  func (p *Parser) isCamouflaged() bool {
    73  	return p.camouflage != nil && p.camouflage != errCamouflageAfterNext
    74  }
    75  
    76  // Camouflage rewind the last Scan(). The Parser holds the camouflage error until
    77  // the next Scan()
    78  // Do not call Rewind() on a camouflaged Parser
    79  func (p *Parser) Camouflage(unit string, expected ...rune) {
    80  	if p.isCamouflaged() {
    81  		panic(fmt.Errorf("can only Camouflage() after Scan(): %w", p.camouflage))
    82  	}
    83  	p.camouflage = p.Expected(unit, expected...)
    84  }
    85  
    86  // Peek returns the next Unicode character in the source without advancing
    87  // the scanner. It returns EOF if the scanner's position is at the last
    88  // character of the source.
    89  // Do not call Peek() on a camouflaged Parser
    90  func (p *Parser) Peek() rune {
    91  	if p.isCamouflaged() {
    92  		panic("can not Peek() on camouflaged Parser")
    93  	}
    94  	return p.scanner.Peek()
    95  }
    96  
    97  var errCamouflageAfterNext = fmt.Errorf("Camouflage() after Next()")
    98  
    99  // Next reads and returns the next Unicode character.
   100  // It returns EOF at the end of the source.
   101  // Do not call Next() on a camouflaged Parser
   102  func (p *Parser) Next() rune {
   103  	if p.isCamouflaged() {
   104  		panic("can not Next() on camouflaged Parser")
   105  	}
   106  	p.camouflage = errCamouflageAfterNext
   107  	return p.scanner.Next()
   108  }
   109  
   110  // TokenText returns the string corresponding to the most recently scanned token.
   111  // Valid after calling Scan().
   112  func (p *Parser) TokenText() string {
   113  	return p.scanner.TokenText()
   114  }
   115  
   116  //Expected returns an error signaling an unexpected Scan() result
   117  func (p *Parser) Expected(unit string, expected ...rune) error {
   118  	return unexpectedRune{unit, expected, p.lastScan}
   119  }
   120  
   121  type unexpectedRune struct {
   122  	unit     string
   123  	expected []rune
   124  	got      rune
   125  }
   126  
   127  func (err unexpectedRune) Error() string {
   128  	exp := bytes.Buffer{}
   129  	runes := err.expected
   130  	switch len(runes) {
   131  	default:
   132  		for _, r := range runes[:len(runes)-2] {
   133  			exp.WriteString(scanner.TokenString(r))
   134  			exp.WriteString(", ")
   135  		}
   136  		fallthrough
   137  	case 2:
   138  		exp.WriteString(scanner.TokenString(runes[len(runes)-2]))
   139  		exp.WriteString(" or ")
   140  		fallthrough
   141  	case 1:
   142  		exp.WriteString(scanner.TokenString(runes[len(runes)-1]))
   143  	case 0:
   144  		return fmt.Sprintf("unexpected %s while scanning %s", scanner.TokenString(err.got), err.unit)
   145  	}
   146  	return fmt.Sprintf("unexpected %s while scanning %s expected %s", scanner.TokenString(err.got), err.unit, exp.String())
   147  }
   148  

View as plain text