func (s *Scanner) scanEscape() rune {
	ch := s.next()  
	switch ch {
	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"':
	case '0', '1', '2', '3', '4', '5', '6', '7':
		ch = s.scanDigits(ch, 8, 3)
	case 'x':
		ch = s.scanDigits(s.next(), 16, 2)
	case 'u':
		ch = s.scanDigits(s.next(), 16, 4)
	case 'U':
		ch = s.scanDigits(s.next(), 16, 8)
	default:
		s.err("illegal char escape")
	}
	return ch
}
scanEscape scans an escape sequence
package scanner
import (
	"bytes"
	"fmt"
	"os"
	"regexp"
	"unicode"
	"unicode/utf8"
	"github.com/hashicorp/hcl/hcl/token"
)
const eof = rune(0)
type Scanner struct {
	buf *bytes.Buffer  
	src []byte         
	srcPos  token.Pos  
	prevPos token.Pos  
	lastCharLen int  
	lastLineLen int  
	tokStart int  
	tokEnd   int  
	Error func(pos token.Pos, msg string)
	ErrorCount int
	tokPos token.Pos
}
func New(src []byte) *Scanner {
	b := bytes.NewBuffer(src)
	s := &Scanner{
		buf: b,
		src: src,
	}
	s.srcPos.Line = 1
	return s
}
func (s *Scanner) next() rune {
	ch, size, err := s.buf.ReadRune()
	if err != nil {
		s.srcPos.Column++
		s.srcPos.Offset += size
		s.lastCharLen = size
		return eof
	}
	if ch == utf8.RuneError && size == 1 {
		s.srcPos.Column++
		s.srcPos.Offset += size
		s.lastCharLen = size
		s.err("illegal UTF-8 encoding")
		return ch
	}
	s.prevPos = s.srcPos
	s.srcPos.Column++
	s.lastCharLen = size
	s.srcPos.Offset += size
	if ch == '\n' {
		s.srcPos.Line++
		s.lastLineLen = s.srcPos.Column
		s.srcPos.Column = 0
	}
	if ch == '\x00' && s.buf.Len() > 0 {
		s.err("unexpected null character (0x00)")
		return eof
	}
	return ch
}
func (s *Scanner) unread() {
	if err := s.buf.UnreadRune(); err != nil {
		panic(err)  
	}
	s.srcPos = s.prevPos  
}
func (s *Scanner) peek() rune {
	peek, _, err := s.buf.ReadRune()
	if err != nil {
		return eof
	}
	s.buf.UnreadRune()
	return peek
}
func (s *Scanner) Scan() token.Token {
	ch := s.next()
	for isWhitespace(ch) {
		ch = s.next()
	}
	var tok token.Type
	s.tokStart = s.srcPos.Offset - s.lastCharLen
	s.tokPos.Offset = s.srcPos.Offset - s.lastCharLen
	if s.srcPos.Column > 0 {
		s.tokPos.Line = s.srcPos.Line
		s.tokPos.Column = s.srcPos.Column
	} else {
		s.tokPos.Line = s.srcPos.Line - 1
		s.tokPos.Column = s.lastLineLen
	}
	switch {
	case isLetter(ch):
		tok = token.IDENT
		lit := s.scanIdentifier()
		if lit == "true" || lit == "false" {
			tok = token.BOOL
		}
	case isDecimal(ch):
		tok = s.scanNumber(ch)
	default:
		switch ch {
		case eof:
			tok = token.EOF
		case '"':
			tok = token.STRING
			s.scanString()
		case '#', '/':
			tok = token.COMMENT
			s.scanComment(ch)
		case '.':
			tok = token.PERIOD
			ch = s.peek()
			if isDecimal(ch) {
				tok = token.FLOAT
				ch = s.scanMantissa(ch)
				ch = s.scanExponent(ch)
			}
		case '<':
			tok = token.HEREDOC
			s.scanHeredoc()
		case '[':
			tok = token.LBRACK
		case ']':
			tok = token.RBRACK
		case '{':
			tok = token.LBRACE
		case '}':
			tok = token.RBRACE
		case ',':
			tok = token.COMMA
		case '=':
			tok = token.ASSIGN
		case '+':
			tok = token.ADD
		case '-':
			if isDecimal(s.peek()) {
				ch := s.next()
				tok = s.scanNumber(ch)
			} else {
				tok = token.SUB
			}
		default:
			s.err("illegal char")
		}
	}
	s.tokEnd = s.srcPos.Offset
	var tokenText string
	if s.tokStart >= 0 {
		tokenText = string(s.src[s.tokStart:s.tokEnd])
	}
	s.tokStart = s.tokEnd  
	return token.Token{
		Type: tok,
		Pos:  s.tokPos,
		Text: tokenText,
	}
}
func (s *Scanner) scanComment(ch rune) {
	if ch == '#' || (ch == '/' && s.peek() != '*') {
		if ch == '/' && s.peek() != '/' {
			s.err("expected '/' for comment")
			return
		}
		ch = s.next()
		for ch != '\n' && ch >= 0 && ch != eof {
			ch = s.next()
		}
		if ch != eof && ch >= 0 {
			s.unread()
		}
		return
	}
	if ch == '/' {
		s.next()
		ch = s.next()  
	}
	for {
		if ch < 0 || ch == eof {
			s.err("comment not terminated")
			break
		}
		ch0 := ch
		ch = s.next()
		if ch0 == '*' && ch == '/' {
			break
		}
	}
}
func (s *Scanner) scanNumber(ch rune) token.Type {
	if ch == '0' {
		ch = s.next()
		if ch == 'x' || ch == 'X' {
			ch = s.next()
			found := false
			for isHexadecimal(ch) {
				ch = s.next()
				found = true
			}
			if !found {
				s.err("illegal hexadecimal number")
			}
			if ch != eof {
				s.unread()
			}
			return token.NUMBER
		}
		illegalOctal := false
		for isDecimal(ch) {
			ch = s.next()
			if ch == '8' || ch == '9' {
				illegalOctal = true
			}
		}
		if ch == 'e' || ch == 'E' {
			ch = s.scanExponent(ch)
			return token.FLOAT
		}
		if ch == '.' {
			ch = s.scanFraction(ch)
			if ch == 'e' || ch == 'E' {
				ch = s.next()
				ch = s.scanExponent(ch)
			}
			return token.FLOAT
		}
		if illegalOctal {
			s.err("illegal octal number")
		}
		if ch != eof {
			s.unread()
		}
		return token.NUMBER
	}
	s.scanMantissa(ch)
	ch = s.next()  
	if ch == 'e' || ch == 'E' {
		ch = s.scanExponent(ch)
		return token.FLOAT
	}
	if ch == '.' {
		ch = s.scanFraction(ch)
		if ch == 'e' || ch == 'E' {
			ch = s.next()
			ch = s.scanExponent(ch)
		}
		return token.FLOAT
	}
	if ch != eof {
		s.unread()
	}
	return token.NUMBER
}
func (s *Scanner) scanMantissa(ch rune) rune {
	scanned := false
	for isDecimal(ch) {
		ch = s.next()
		scanned = true
	}
	if scanned && ch != eof {
		s.unread()
	}
	return ch
}
func (s *Scanner) scanFraction(ch rune) rune {
	if ch == '.' {
		ch = s.peek()  
		ch = s.scanMantissa(ch)
	}
	return ch
}
func (s *Scanner) scanExponent(ch rune) rune {
	if ch == 'e' || ch == 'E' {
		ch = s.next()
		if ch == '-' || ch == '+' {
			ch = s.next()
		}
		ch = s.scanMantissa(ch)
	}
	return ch
}
func (s *Scanner) scanHeredoc() {
	if s.next() != '<' {
		s.err("heredoc expected second '<', didn't see it")
		return
	}
	offs := s.srcPos.Offset
	ch := s.next()
	if ch == '-' {
		ch = s.next()
	}
	for isLetter(ch) || isDigit(ch) {
		ch = s.next()
	}
	if ch == eof {
		s.err("heredoc not terminated")
		return
	}
	if ch == '\r' {
		if s.peek() == '\n' {
			ch = s.next()
		}
	}
	if ch != '\n' {
		s.err("invalid characters in heredoc anchor")
		return
	}
	identBytes := s.src[offs : s.srcPos.Offset-s.lastCharLen]
	if len(identBytes) == 0 {
		s.err("zero-length heredoc anchor")
		return
	}
	var identRegexp *regexp.Regexp
	if identBytes[0] == '-' {
		identRegexp = regexp.MustCompile(fmt.Sprintf(`[[:space:]]*%s\z`, identBytes[1:]))
	} else {
		identRegexp = regexp.MustCompile(fmt.Sprintf(`[[:space:]]*%s\z`, identBytes))
	}
	lineStart := s.srcPos.Offset
	for {
		ch := s.next()
		if ch == '\n' {
			lineBytesLen := s.srcPos.Offset - s.lastCharLen - lineStart
			if lineBytesLen >= len(identBytes) && identRegexp.Match(s.src[lineStart:s.srcPos.Offset-s.lastCharLen]) {
				break
			}
			lineStart = s.srcPos.Offset
		}
		if ch == eof {
			s.err("heredoc not terminated")
			return
		}
	}
	return
}
func (s *Scanner) scanString() {
	braces := 0
	for {
		ch := s.next()
		if (ch == '\n' && braces == 0) || ch < 0 || ch == eof {
			s.err("literal not terminated")
			return
		}
		if ch == '"' && braces == 0 {
			break
		}
		if braces == 0 && ch == '$' && s.peek() == '{' {
			braces++
			s.next()
		} else if braces > 0 && ch == '{' {
			braces++
		}
		if braces > 0 && ch == '}' {
			braces--
		}
		if ch == '\\' {
			s.scanEscape()
		}
	}
	return
}