|
|
|
package scanner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"unicode/utf8"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// EOF is a dummy rune representing End-Of-File
|
|
|
|
EOF = -1
|
|
|
|
)
|
|
|
|
|
|
|
|
// A Position in the input string and in the line-based document
|
|
|
|
type Position struct {
|
|
|
|
Offset uint
|
|
|
|
Line, Column uint
|
|
|
|
}
|
|
|
|
|
|
|
|
// An Scanner represent the low level layer for text parsers
|
|
|
|
type Scanner struct {
|
|
|
|
name string
|
|
|
|
input string
|
|
|
|
|
|
|
|
base Position
|
|
|
|
cursor Position
|
|
|
|
runes uint
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewScannerFromString instantiates a new Scanner to
|
|
|
|
// parse a given string
|
|
|
|
func NewScannerFromString(name, input string) *Scanner {
|
|
|
|
return &Scanner{
|
|
|
|
name: name,
|
|
|
|
input: input,
|
|
|
|
base: Position{0, 1, 1},
|
|
|
|
cursor: Position{0, 1, 1},
|
|
|
|
runes: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Length returns the number of bytes and runes in the Terminal that is been detected
|
|
|
|
func (l *Scanner) Length() (uint, uint) {
|
|
|
|
return l.cursor.Offset - l.base.Offset, l.runes
|
|
|
|
}
|
|
|
|
|
|
|
|
// Empty tells if there are no runes accounted for the next Terminal yet
|
|
|
|
func (l *Scanner) Empty() bool {
|
|
|
|
return l.runes == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// StepForth moves the cursor forward
|
|
|
|
func (l *Scanner) StepForth(runes, bytes uint) {
|
|
|
|
l.cursor.Offset += bytes
|
|
|
|
l.cursor.Column += runes
|
|
|
|
l.runes += runes
|
|
|
|
}
|
|
|
|
|
|
|
|
// StepBack moves the cursor backward
|
|
|
|
func (l *Scanner) StepBack(runes, bytes uint) {
|
|
|
|
l.cursor.Offset -= bytes
|
|
|
|
// FIXME: what if column goes < 1?
|
|
|
|
l.cursor.Column -= runes
|
|
|
|
l.runes -= runes
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset moves the cursor back to the base
|
|
|
|
func (l *Scanner) Reset() {
|
|
|
|
l.cursor = l.base
|
|
|
|
l.runes = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip trashes everything up to the cursor
|
|
|
|
func (l *Scanner) Skip() {
|
|
|
|
l.base = l.cursor
|
|
|
|
l.runes = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewLine accounts a line break in the position of the cursor
|
|
|
|
func (l *Scanner) NewLine() {
|
|
|
|
l.cursor.Line++
|
|
|
|
l.cursor.Column = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Peek returns the next rune but not moving the cursor
|
|
|
|
func (l *Scanner) Peek() (rune, uint) {
|
|
|
|
if l.cursor.Offset == uint(len(l.input)) {
|
|
|
|
return EOF, 0
|
|
|
|
}
|
|
|
|
r, bytes := utf8.DecodeRuneInString(l.input[l.cursor.Offset:])
|
|
|
|
return r, uint(bytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next returns the next rune but moving the cursor
|
|
|
|
func (l *Scanner) Next() (rune, uint) {
|
|
|
|
r, bytes := l.Peek()
|
|
|
|
if bytes > 0 {
|
|
|
|
l.StepForth(1, bytes)
|
|
|
|
}
|
|
|
|
return r, bytes
|
|
|
|
}
|