func (s *Scanner) scanIdent() *Token {
	pos := s.pos()
	v := s.scanName()
	if strings.ToLower(v) == "url" {
		if ch := s.read(); ch == '(' {
			return s.scanURL(pos)
		}
		s.unread(1)
	} else if ch := s.read(); ch == '(' {
		return &Token{Tok: FunctionToken, Value: v, Pos: pos}
	}
	s.unread(1)
	return &Token{Tok: IdentToken, Value: v, Pos: pos}
}
scanIdent consumes a ident-like token.
This function can return an ident, function, url, or bad-url.
package css
import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strconv"
	"strings"
)
var eof rune = -1
type Scanner struct {
	Errors []*Error
	rd io.RuneReader
	tokbuf  *Token  
	tokbufn bool    
	buf    [4]rune  
	bufpos [4]Pos   
	bufi   int      
	bufn   int      
}
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{rd: bufio.NewReader(r)}
}
func (s *Scanner) Scan() *Token {
	if s.tokbufn {
		s.tokbufn = false
		return s.tokbuf
	}
	tok := s.scan()
	s.tokbuf = tok
	return tok
}
func (s *Scanner) scan() *Token {
	for {
		ch := s.read()
		pos := s.pos()
		if isWhitespace(ch) {
			return s.scanWhitespace()
		}
		if isDigit(ch) {
			s.unread(1)
			return s.scanNumeric(pos)
		}
		if ch == 'u' || ch == 'U' {
			ch1, ch2 := s.read(), s.read()
			if ch1 == '+' && (isHexDigit(ch2) || ch2 == '?') {
				s.unread(1)
				return s.scanUnicodeRange()
			}
			s.unread(2)
			return s.scanIdent()
		} else if isNameStart(ch) {
			return s.scanIdent()
		}
		switch ch {
		case eof:
			return &Token{Tok: EOFToken, Pos: pos}
		case '"', '\'':
			return s.scanString()
		case '#':
			return s.scanHash()
		case '$':
			if next := s.read(); next == '=' {
				return &Token{Tok: SuffixMatchToken, Pos: pos}
			}
			s.unread(1)
			return &Token{Tok: DelimToken, Value: string(ch), Pos: pos}
		case '*':
			if next := s.read(); next == '=' {
				return &Token{Tok: SubstringMatchToken, Pos: pos}
			}
			s.unread(1)
			return &Token{Tok: DelimToken, Value: string(ch), Pos: pos}
		case '^':
			if next := s.read(); next == '=' {
				return &Token{Tok: PrefixMatchToken, Pos: pos}
			}
			s.unread(1)
			return &Token{Tok: DelimToken, Value: string(ch), Pos: pos}
		case '~':
			if next := s.read(); next == '=' {
				return &Token{Tok: IncludeMatchToken, Pos: pos}
			}
			s.unread(1)
			return &Token{Tok: DelimToken, Value: string(ch), Pos: pos}
		case ',':
			return &Token{Tok: CommaToken, Pos: pos}
		case '-':
			if s.peekNumber() {
				s.unread(1)
				return s.scanNumeric(pos)
			} else if s.peekIdent() {
				return s.scanIdent()
			}
			ch1, ch2 := s.read(), s.read()
			if ch1 == '-' && ch2 == '>' {
				return &Token{Tok: CDCToken, Pos: pos}
			}
			s.unread(2)
			return &Token{Tok: DelimToken, Value: "-", Pos: pos}
		case '/':
			if ch1 := s.read(); ch1 == '*' {
				s.scanComment()
				continue
			}
			s.unread(1)
			return &Token{Tok: DelimToken, Value: "/", Pos: pos}
		case ':':
			return &Token{Tok: ColonToken, Pos: pos}
		case ';':
			return &Token{Tok: SemicolonToken, Pos: pos}
		case '<':
			if ch0 := s.read(); ch0 == '!' {
				if ch1 := s.read(); ch1 == '-' {
					if ch2 := s.read(); ch2 == '-' {
						return &Token{Tok: CDOToken, Pos: pos}
					}
					s.unread(1)
				}
				s.unread(1)
			}
			s.unread(1)
			return &Token{Tok: DelimToken, Value: "<", Pos: pos}
		case '@':
			if s.read(); s.peekIdent() {
				return &Token{Tok: AtKeywordToken, Value: s.scanName(), Pos: pos}
			}
			return &Token{Tok: DelimToken, Value: "@", Pos: pos}
		case '(':
			return &Token{Tok: LParenToken, Pos: pos}
		case ')':
			return &Token{Tok: RParenToken, Pos: pos}
		case '[':
			return &Token{Tok: LBrackToken, Pos: pos}
		case ']':
			return &Token{Tok: RBrackToken, Pos: pos}
		case '{':
			return &Token{Tok: LBraceToken, Pos: pos}
		case '}':
			return &Token{Tok: RBraceToken, Pos: pos}
		case '\\':
			if s.peekEscape() {
				return s.scanIdent()
			}
			s.Errors = append(s.Errors, &Error{Message: "unescaped \\", Pos: s.pos()})
			return &Token{Tok: DelimToken, Value: "\\", Pos: pos}
		case '+', '.':
			if s.peekNumber() {
				s.unread(1)
				return s.scanNumeric(pos)
			}
			return &Token{Tok: DelimToken, Value: string(ch), Pos: pos}
		case '|':
			if ch1 := s.read(); ch1 == '=' {
				return &Token{Tok: DashMatchToken, Pos: pos}
			} else if ch1 == '|' {
				return &Token{Tok: ColumnToken, Pos: pos}
			}
			s.unread(1)
			return &Token{Tok: DelimToken, Value: string(ch), Pos: pos}
		default:
			return &Token{Tok: DelimToken, Value: string(ch), Pos: pos}
		}
	}
}
func (s *Scanner) unscan() {
	s.tokbufn = true
}
func (s *Scanner) current() *Token {
	return s.tokbuf
}
func (s *Scanner) scanWhitespace() *Token {
	pos := s.pos()
	var buf bytes.Buffer
	_, _ = buf.WriteRune(s.curr())
	for {
		ch := s.read()
		if ch == eof {
			break
		} else if !isWhitespace(ch) {
			s.unread(1)
			break
		}
		_, _ = buf.WriteRune(ch)
	}
	return &Token{Tok: WhitespaceToken, Value: buf.String(), Pos: pos}
}
func (s *Scanner) scanString() *Token {
	pos, ending := s.pos(), s.curr()
	var buf bytes.Buffer
	for {
		ch := s.read()
		if ch == eof || ch == ending {
			return &Token{Tok: StringToken, Value: buf.String(), Ending: ending, Pos: pos}
		} else if ch == '\n' {
			s.unread(1)
			return &Token{Tok: BadStringToken, Pos: pos}
		} else if ch == '\\' {
			if next := s.read(); next == eof {
				continue
			} else if next == '\n' {
				_, _ = buf.WriteRune(next)
				continue
			}
			s.unread(1)
			if s.peekEscape() {
				_, _ = buf.WriteRune(s.scanEscape())
				continue
			}
		}
		_, _ = buf.WriteRune(ch)
	}
}
func (s *Scanner) scanNumeric(pos Pos) *Token {
	num, typ, repr := s.scanNumber()
	if s.read(); s.peekIdent() {
		unit := s.scanName()
		return &Token{Tok: DimensionToken, Type: typ, Value: repr + unit, Number: num, Unit: unit, Pos: pos}
	} else {
		s.unread(1)
	}
	if ch := s.read(); ch == '%' {
		return &Token{Tok: PercentageToken, Type: typ, Value: repr + "%", Number: num, Pos: pos}
	} else {
		s.unread(1)
	}
	return &Token{Tok: NumberToken, Type: typ, Value: repr, Number: num, Pos: pos}
}
func (s *Scanner) scanNumber() (num float64, typ, repr string) {
	var buf bytes.Buffer
	typ = "integer"
	if ch := s.read(); ch == '+' || ch == '-' {
		_, _ = buf.WriteRune(ch)
	} else {
		s.unread(1)
	}
	_, _ = buf.WriteString(s.scanDigits())
	if ch0 := s.read(); ch0 == '.' {
		if ch1 := s.read(); isDigit(ch1) {
			typ = "number"
			_, _ = buf.WriteRune(ch0)
			_, _ = buf.WriteRune(ch1)
			_, _ = buf.WriteString(s.scanDigits())
		} else {
			s.unread(2)
		}
	} else {
		s.unread(1)
	}
	if ch0 := s.read(); ch0 == 'e' || ch0 == 'E' {
		if ch1 := s.read(); ch1 == '+' || ch1 == '-' {
			if ch2 := s.read(); isDigit(ch2) {
				typ = "number"
				_, _ = buf.WriteRune(ch0)
				_, _ = buf.WriteRune(ch1)
				_, _ = buf.WriteRune(ch2)
			} else {
				s.unread(3)
			}
		} else if isDigit(ch1) {
			typ = "number"
			_, _ = buf.WriteRune(ch0)
			_, _ = buf.WriteRune(ch1)
		} else {
			s.unread(2)
		}
	} else {
		s.unread(1)
	}
	num, _ = strconv.ParseFloat(buf.String(), 64)
	repr = buf.String()
	return
}
func (s *Scanner) scanDigits() string {
	var buf bytes.Buffer
	for {
		if ch := s.read(); isDigit(ch) {
			_, _ = buf.WriteRune(ch)
		} else {
			s.unread(1)
			break
		}
	}
	return buf.String()
}
func (s *Scanner) scanComment() {
	for {
		ch0 := s.read()
		if ch0 == eof {
			break
		} else if ch0 == '*' {
			if ch1 := s.read(); ch1 == '/' {
				break
			} else {
				s.unread(1)
			}
		}
	}
}
func (s *Scanner) scanHash() *Token {
	pos := s.pos()
	if ch := s.read(); isName(ch) || s.peekEscape() {
		typ := "unrestricted"
		if s.peekIdent() {
			typ = "id"
		}
		return &Token{Tok: HashToken, Value: s.scanName(), Type: typ, Pos: pos}
	}
	s.unread(1)
	return &Token{Tok: DelimToken, Value: "#", Pos: pos}
}
func (s *Scanner) scanName() string {
	var buf bytes.Buffer
	s.unread(1)
	for {
		if ch := s.read(); isName(ch) {
			_, _ = buf.WriteRune(ch)
		} else if s.peekEscape() {
			_, _ = buf.WriteRune(s.scanEscape())
		} else {
			s.unread(1)
			return buf.String()
		}
	}
}