mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-03-22 01:54:12 -05:00
191 lines
3.9 KiB
Go
191 lines
3.9 KiB
Go
// Modified from github.com/zdebeer99/goexpression
|
|
package filter
|
|
|
|
import (
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// Pos represents a byte position in the original input text from which
|
|
// this template was parsed.
|
|
type Pos int
|
|
|
|
func (p Pos) Position() Pos {
|
|
return p
|
|
}
|
|
|
|
const eof = -1
|
|
|
|
// Scanner, Iterates through a string.
|
|
type Scanner struct {
|
|
input string
|
|
start Pos
|
|
pos Pos
|
|
width Pos
|
|
curr rune
|
|
prevState ScannerState
|
|
safebackup bool //insure backup is called only once after next.
|
|
}
|
|
|
|
// NewScanner Creates a New Scanner pointer.
|
|
func NewScanner(template string) *Scanner {
|
|
return &Scanner{input: template}
|
|
}
|
|
|
|
func (this *Scanner) StartPosition() int {
|
|
return int(this.start)
|
|
}
|
|
|
|
func (this *Scanner) SetPosition(pos int) {
|
|
this.pos = Pos(pos)
|
|
}
|
|
|
|
func (this *Scanner) SetStartPosition(pos int) {
|
|
this.start = Pos(pos)
|
|
}
|
|
|
|
// Token return the current selected text and move the start position to the current position
|
|
func (this *Scanner) Commit() string {
|
|
r1 := this.input[this.start:this.pos]
|
|
this.start = this.pos
|
|
this.prevState = this.SaveState()
|
|
return r1
|
|
}
|
|
|
|
// IsEOF check if the end of the current string has been reached.
|
|
func (this *Scanner) IsEOF() bool {
|
|
return int(this.pos) >= len(this.input)
|
|
}
|
|
|
|
func (this *Scanner) Size() int {
|
|
return len(this.input)
|
|
}
|
|
|
|
func (this *Scanner) MoveStart(pos int) {
|
|
this.start = this.start + Pos(pos)
|
|
}
|
|
|
|
// Next returns the next rune in the input.
|
|
func (this *Scanner) Next() rune {
|
|
this.safebackup = true
|
|
if this.IsEOF() {
|
|
this.width = 0
|
|
return eof
|
|
}
|
|
r, w := utf8.DecodeRuneInString(this.input[this.pos:])
|
|
this.width = Pos(w)
|
|
this.pos += this.width
|
|
this.curr = r
|
|
return r
|
|
}
|
|
|
|
func (this *Scanner) Skip() {
|
|
this.Next()
|
|
this.Commit()
|
|
}
|
|
|
|
// Peek returns but does not consume the next rune in the input.
|
|
func (this *Scanner) Peek() rune {
|
|
r := this.Next()
|
|
this.Backup()
|
|
return r
|
|
}
|
|
|
|
// Backup steps back one rune. Can only be called once per call of next.
|
|
func (this *Scanner) Backup() {
|
|
this.pos -= this.width
|
|
}
|
|
|
|
// Rollback move the curr pos back to the start pos.
|
|
func (this *Scanner) Rollback() {
|
|
this.LoadState(this.prevState)
|
|
}
|
|
|
|
// Ignore skips over the pending input before this point.
|
|
func (this *Scanner) Ignore() {
|
|
this.start = this.pos
|
|
}
|
|
|
|
// accept consumes the next rune if it's from the valid set.
|
|
func (this *Scanner) Accept(valid string) bool {
|
|
if strings.IndexRune(valid, this.Next()) >= 0 {
|
|
return true
|
|
}
|
|
this.Backup()
|
|
return false
|
|
}
|
|
|
|
// acceptRun consumes a run of runes from the valid set.
|
|
func (this *Scanner) AcceptRun(valid string) (found int) {
|
|
for strings.IndexRune(valid, this.Next()) >= 0 {
|
|
found++
|
|
}
|
|
this.Backup()
|
|
return found
|
|
}
|
|
|
|
// runTo consumes a run of runes until an item in the valid set is found.
|
|
func (this *Scanner) RunTo(valid string) rune {
|
|
for {
|
|
r := this.Next()
|
|
if r == eof {
|
|
return r
|
|
}
|
|
if strings.IndexRune(valid, r) >= 0 {
|
|
return r
|
|
}
|
|
}
|
|
this.Backup()
|
|
return eof
|
|
}
|
|
|
|
func (this *Scanner) Prefix(pre string) bool {
|
|
if strings.HasPrefix(this.input[this.pos:], pre) {
|
|
this.pos += Pos(len(pre))
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (this *Scanner) SkipSpaces() {
|
|
for IsSpace(this.Next()) {
|
|
}
|
|
this.Backup()
|
|
this.Ignore()
|
|
}
|
|
|
|
func (this *Scanner) SkipToNewLine() {
|
|
for {
|
|
r := this.Next()
|
|
if this.IsEOF() {
|
|
break
|
|
}
|
|
if r == '\n' {
|
|
break
|
|
}
|
|
}
|
|
this.Ignore()
|
|
return
|
|
}
|
|
|
|
// lineNumber reports which line we're on, based on the position of
|
|
// the previous item returned by nextItem. Doing it this way
|
|
// means we don't have to worry about peek double counting.
|
|
func (this *Scanner) LineNumber() int {
|
|
return 1 + strings.Count(this.input[:this.pos], "\n")
|
|
}
|
|
|
|
type ScannerState struct {
|
|
start Pos
|
|
pos Pos
|
|
width Pos
|
|
}
|
|
|
|
func (this *Scanner) SaveState() ScannerState {
|
|
return ScannerState{start: this.start, pos: this.pos, width: this.width}
|
|
}
|
|
|
|
func (this *Scanner) LoadState(state ScannerState) {
|
|
this.start, this.pos, this.width = state.start, state.pos, state.width
|
|
}
|