mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:22:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			1447 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			1447 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // Copyright 2022 The Gc Authors. All rights reserved. | ||
|  | // Use of this source code is governed by a BSD-style | ||
|  | // license that can be found in the LICENSE file. | ||
|  | 
 | ||
|  | package gc // import "modernc.org/gc/v3" | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"fmt" | ||
|  | 	"go/token" | ||
|  | 	"path/filepath" | ||
|  | 	"strings" | ||
|  | 	"unicode" | ||
|  | 	"unicode/utf8" | ||
|  | 
 | ||
|  | 	"modernc.org/mathutil" | ||
|  | 	mtoken "modernc.org/token" | ||
|  | ) | ||
|  | 
 | ||
|  | var ( | ||
|  | 	_ Node = (*Token)(nil) | ||
|  | 	_ Node = (*nonode)(nil) | ||
|  | 
 | ||
|  | 	keywords = map[string]token.Token{ | ||
|  | 		"break":       BREAK, | ||
|  | 		"case":        CASE, | ||
|  | 		"chan":        CHAN, | ||
|  | 		"const":       CONST, | ||
|  | 		"continue":    CONTINUE, | ||
|  | 		"default":     DEFAULT, | ||
|  | 		"defer":       DEFER, | ||
|  | 		"else":        ELSE, | ||
|  | 		"fallthrough": FALLTHROUGH, | ||
|  | 		"for":         FOR, | ||
|  | 		"func":        FUNC, | ||
|  | 		"go":          GO, | ||
|  | 		"goto":        GOTO, | ||
|  | 		"if":          IF, | ||
|  | 		"import":      IMPORT, | ||
|  | 		"interface":   INTERFACE, | ||
|  | 		"map":         MAP, | ||
|  | 		"package":     PACKAGE, | ||
|  | 		"range":       RANGE, | ||
|  | 		"return":      RETURN, | ||
|  | 		"select":      SELECT, | ||
|  | 		"struct":      STRUCT, | ||
|  | 		"switch":      SWITCH, | ||
|  | 		"type":        TYPE, | ||
|  | 		"var":         VAR, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	lineCommentTag = []byte("line ") | ||
|  | 	znode          = &nonode{} | ||
|  | ) | ||
|  | 
 | ||
|  | type nonode struct{} | ||
|  | 
 | ||
|  | func (*nonode) Position() (r token.Position) { return r } | ||
|  | func (*nonode) Source(full bool) string      { return "" } | ||
|  | 
 | ||
|  | // Token represents a lexeme, its position and its semantic value. | ||
|  | type Token struct { // 16 bytes on 64 bit arch | ||
|  | 	source *source | ||
|  | 
 | ||
|  | 	ch    int32 | ||
|  | 	index int32 | ||
|  | } | ||
|  | 
 | ||
|  | // Ch returns which token t represents | ||
|  | func (t Token) Ch() token.Token { return token.Token(t.ch) } | ||
|  | 
 | ||
|  | // Source implements Node. | ||
|  | func (t Token) Source(full bool) string { | ||
|  | 	// trc("%10s %v: #%v sep %v, src %v, buf %v", tokSource(t.Ch()), t.Position(), t.index, t.source.toks[t.index].sep, t.source.toks[t.index].src, len(t.source.buf)) | ||
|  | 	sep := t.Sep() | ||
|  | 	if !full && sep != "" { | ||
|  | 		sep = " " | ||
|  | 	} | ||
|  | 	src := t.Src() | ||
|  | 	if !full && strings.ContainsRune(src, '\n') { | ||
|  | 		src = " " | ||
|  | 	} | ||
|  | 	// trc("%q %q -> %q %q", t.Sep(), t.Src(), sep, src) | ||
|  | 	return sep + src | ||
|  | } | ||
|  | 
 | ||
|  | // Positions implements Node. | ||
|  | func (t Token) Position() (r token.Position) { | ||
|  | 	if t.source == nil { | ||
|  | 		return r | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s := t.source | ||
|  | 	off := mathutil.MinInt32(int32(len(s.buf)), s.toks[t.index].src) | ||
|  | 	return token.Position(s.file.PositionFor(mtoken.Pos(s.base+off), true)) | ||
|  | } | ||
|  | 
 | ||
|  | // Prev returns the token preceding t or a zero value if no such token exists. | ||
|  | func (t Token) Prev() (r Token) { | ||
|  | 	if index := t.index - 1; index >= 0 { | ||
|  | 		s := t.source | ||
|  | 		return Token{source: s, ch: s.toks[index].ch, index: index} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return r | ||
|  | } | ||
|  | 
 | ||
|  | // Next returns the token following t or a zero value if no such token exists. | ||
|  | func (t Token) Next() (r Token) { | ||
|  | 	if index := t.index + 1; index < int32(len(t.source.toks)) { | ||
|  | 		s := t.source | ||
|  | 		return Token{source: s, ch: s.toks[index].ch, index: index} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return r | ||
|  | } | ||
|  | 
 | ||
|  | // Sep returns any separators, combined, preceding t. | ||
|  | func (t Token) Sep() string { | ||
|  | 	s := t.source | ||
|  | 	if p, ok := s.sepPatches[t.index]; ok { | ||
|  | 		return p | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return string(s.buf[s.toks[t.index].sep:s.toks[t.index].src]) | ||
|  | } | ||
|  | 
 | ||
|  | // SetSep sets t's separator. | ||
|  | func (t Token) SetSep(s string) { | ||
|  | 	src := t.source | ||
|  | 	if src.sepPatches == nil { | ||
|  | 		src.sepPatches = map[int32]string{} | ||
|  | 	} | ||
|  | 	src.sepPatches[t.index] = s | ||
|  | } | ||
|  | 
 | ||
|  | // Src returns t's source form. | ||
|  | func (t Token) Src() string { | ||
|  | 	s := t.source | ||
|  | 	if p, ok := s.srcPatches[t.index]; ok { | ||
|  | 		return p | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if t.ch != int32(EOF) { | ||
|  | 		next := t.source.off | ||
|  | 		if t.index < int32(len(s.toks))-1 { | ||
|  | 			next = s.toks[t.index+1].sep | ||
|  | 		} | ||
|  | 		return string(s.buf[s.toks[t.index].src:next]) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return "" | ||
|  | } | ||
|  | 
 | ||
|  | // SetSrc sets t's source form. | ||
|  | func (t Token) SetSrc(s string) { | ||
|  | 	src := t.source | ||
|  | 	if src.srcPatches == nil { | ||
|  | 		src.srcPatches = map[int32]string{} | ||
|  | 	} | ||
|  | 	src.srcPatches[t.index] = s | ||
|  | } | ||
|  | 
 | ||
|  | // IsValid reports t is a valid token. Zero value reports false. | ||
|  | func (t Token) IsValid() bool { return t.source != nil } | ||
|  | 
 | ||
|  | type tok struct { // 12 bytes | ||
|  | 	ch  int32 | ||
|  | 	sep int32 | ||
|  | 	src int32 | ||
|  | } | ||
|  | 
 | ||
|  | func (t *tok) token() token.Token { return token.Token(t.ch) } | ||
|  | 
 | ||
|  | func (t *tok) position(s *source) (r token.Position) { | ||
|  | 	off := mathutil.MinInt32(int32(len(s.buf)), t.src) | ||
|  | 	return token.Position(s.file.PositionFor(mtoken.Pos(s.base+off), true)) | ||
|  | } | ||
|  | 
 | ||
|  | // source represents a single Go source file, editor text buffer etc. | ||
|  | type source struct { | ||
|  | 	buf        []byte | ||
|  | 	file       *mtoken.File | ||
|  | 	name       string | ||
|  | 	sepPatches map[int32]string | ||
|  | 	srcPatches map[int32]string | ||
|  | 	toks       []tok | ||
|  | 
 | ||
|  | 	base int32 | ||
|  | 	off  int32 | ||
|  | } | ||
|  | 
 | ||
|  | // 'buf' becomes owned by the result and must not be modified afterwards. | ||
|  | func newSource(name string, buf []byte) *source { | ||
|  | 	file := mtoken.NewFile(name, len(buf)) | ||
|  | 	return &source{ | ||
|  | 		buf:  buf, | ||
|  | 		file: file, | ||
|  | 		name: name, | ||
|  | 		base: int32(file.Base()), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | type ErrWithPosition struct { | ||
|  | 	pos token.Position | ||
|  | 	err error | ||
|  | } | ||
|  | 
 | ||
|  | func (e ErrWithPosition) String() string { | ||
|  | 	switch { | ||
|  | 	case e.pos.IsValid(): | ||
|  | 		return fmt.Sprintf("%v: %v", e.pos, e.err) | ||
|  | 	default: | ||
|  | 		return fmt.Sprintf("%v", e.err) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | type errList []ErrWithPosition | ||
|  | 
 | ||
|  | func (e errList) Err() (r error) { | ||
|  | 	if len(e) == 0 { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return e | ||
|  | } | ||
|  | 
 | ||
|  | func (e errList) Error() string { | ||
|  | 	w := 0 | ||
|  | 	prev := ErrWithPosition{pos: token.Position{Offset: -1}} | ||
|  | 	for _, v := range e { | ||
|  | 		if v.pos.Line == 0 || v.pos.Offset != prev.pos.Offset || v.err.Error() != prev.err.Error() { | ||
|  | 			e[w] = v | ||
|  | 			w++ | ||
|  | 			prev = v | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var a []string | ||
|  | 	for _, v := range e { | ||
|  | 		a = append(a, fmt.Sprint(v)) | ||
|  | 	} | ||
|  | 	return strings.Join(a, "\n") | ||
|  | } | ||
|  | 
 | ||
|  | func (e *errList) err(pos token.Position, msg string, args ...interface{}) { | ||
|  | 	if trcErrors { | ||
|  | 		trc("FAIL "+msg, args...) | ||
|  | 	} | ||
|  | 	switch { | ||
|  | 	case len(args) == 0: | ||
|  | 		*e = append(*e, ErrWithPosition{pos, fmt.Errorf("%s", msg)}) | ||
|  | 	default: | ||
|  | 		*e = append(*e, ErrWithPosition{pos, fmt.Errorf(msg, args...)}) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | type scanner struct { | ||
|  | 	*source | ||
|  | 	dir  string | ||
|  | 	errs errList | ||
|  | 	tok  tok | ||
|  | 
 | ||
|  | 	last int32 | ||
|  | 
 | ||
|  | 	errBudget int | ||
|  | 
 | ||
|  | 	c byte // Lookahead byte. | ||
|  | 
 | ||
|  | 	eof      bool | ||
|  | 	isClosed bool | ||
|  | } | ||
|  | 
 | ||
|  | func newScanner(name string, buf []byte) *scanner { | ||
|  | 	dir, _ := filepath.Split(name) | ||
|  | 	r := &scanner{source: newSource(name, buf), errBudget: 10, dir: dir} | ||
|  | 	switch { | ||
|  | 	case len(buf) == 0: | ||
|  | 		r.eof = true | ||
|  | 	default: | ||
|  | 		r.c = buf[0] | ||
|  | 		if r.c == '\n' { | ||
|  | 			r.file.AddLine(int(r.base + r.off)) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return r | ||
|  | } | ||
|  | 
 | ||
|  | func isDigit(c byte) bool      { return c >= '0' && c <= '9' } | ||
|  | func isHexDigit(c byte) bool   { return isDigit(c) || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F' } | ||
|  | func isIDNext(c byte) bool     { return isIDFirst(c) || isDigit(c) } | ||
|  | func isOctalDigit(c byte) bool { return c >= '0' && c <= '7' } | ||
|  | 
 | ||
|  | func isIDFirst(c byte) bool { | ||
|  | 	return c >= 'a' && c <= 'z' || | ||
|  | 		c >= 'A' && c <= 'Z' || | ||
|  | 		c == '_' | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) position() token.Position { | ||
|  | 	return token.Position(s.source.file.PositionFor(mtoken.Pos(s.base+s.off), true)) | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) pos(off int32) token.Position { | ||
|  | 	return token.Position(s.file.PositionFor(mtoken.Pos(s.base+off), true)) | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) token() Token { | ||
|  | 	return Token{source: s.source, ch: s.tok.ch, index: int32(len(s.toks) - 1)} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) err(off int32, msg string, args ...interface{}) { | ||
|  | 	if s.errBudget <= 0 { | ||
|  | 		s.close() | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s.errBudget-- | ||
|  | 	if n := int32(len(s.buf)); off >= n { | ||
|  | 		off = n | ||
|  | 	} | ||
|  | 	s.errs.err(s.pos(off), msg, args...) | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) close() { | ||
|  | 	if s.isClosed { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s.tok.ch = int32(ILLEGAL) | ||
|  | 	s.eof = true | ||
|  | 	s.isClosed = true | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) next() { | ||
|  | 	if s.eof { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s.off++ | ||
|  | 	if int(s.off) == len(s.buf) { | ||
|  | 		s.c = 0 | ||
|  | 		s.eof = true | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s.c = s.buf[s.off] | ||
|  | 	if s.c == '\n' { | ||
|  | 		s.file.AddLine(int(s.base + s.off)) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) nextN(n int) { | ||
|  | 	if int(s.off) == len(s.buf)-n { | ||
|  | 		s.c = 0 | ||
|  | 		s.eof = true | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s.off += int32(n) | ||
|  | 	s.c = s.buf[s.off] | ||
|  | 	if s.c == '\n' { | ||
|  | 		s.file.AddLine(int(s.base + s.off)) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) scan() (r bool) { | ||
|  | 	if s.isClosed { | ||
|  | 		return false | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s.last = s.tok.ch | ||
|  | 	s.tok.sep = s.off | ||
|  | 	s.tok.ch = -1 | ||
|  | 	for { | ||
|  | 		if r = s.scan0(); !r || s.tok.ch >= 0 { | ||
|  | 			s.toks = append(s.toks, s.tok) | ||
|  | 			// trc("", dump(s.token())) | ||
|  | 			return r | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) scan0() (r bool) { | ||
|  | 	s.tok.src = mathutil.MinInt32(s.off, int32(len(s.buf))) | ||
|  | 	switch s.c { | ||
|  | 	case ' ', '\t', '\r', '\n': | ||
|  | 		// White space, formed from spaces (U+0020), horizontal tabs (U+0009), carriage | ||
|  | 		// returns (U+000D), and newlines (U+000A), is ignored except as it separates | ||
|  | 		// tokens that would otherwise combine into a single token. | ||
|  | 		if s.c == '\n' && s.injectSemi() { | ||
|  | 			return true | ||
|  | 		} | ||
|  | 
 | ||
|  | 		s.next() | ||
|  | 		return true | ||
|  | 	case '/': | ||
|  | 		off := s.off | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(QUO_ASSIGN) | ||
|  | 		case '/': | ||
|  | 			// Line comments start with the character sequence // and stop at the end of | ||
|  | 			// the line. | ||
|  | 			s.next() | ||
|  | 			s.lineComment(off) | ||
|  | 			return true | ||
|  | 		case '*': | ||
|  | 			// General comments start with the character sequence /* and stop with the | ||
|  | 			// first subsequent character sequence */. | ||
|  | 			s.next() | ||
|  | 			s.generalComment(off) | ||
|  | 			return true | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(QUO) | ||
|  | 		} | ||
|  | 	case '(': | ||
|  | 		s.tok.ch = int32(LPAREN) | ||
|  | 		s.next() | ||
|  | 	case ')': | ||
|  | 		s.tok.ch = int32(RPAREN) | ||
|  | 		s.next() | ||
|  | 	case '[': | ||
|  | 		s.tok.ch = int32(LBRACK) | ||
|  | 		s.next() | ||
|  | 	case ']': | ||
|  | 		s.tok.ch = int32(RBRACK) | ||
|  | 		s.next() | ||
|  | 	case '{': | ||
|  | 		s.tok.ch = int32(LBRACE) | ||
|  | 		s.next() | ||
|  | 	case '}': | ||
|  | 		s.tok.ch = int32(RBRACE) | ||
|  | 		s.next() | ||
|  | 	case ',': | ||
|  | 		s.tok.ch = int32(COMMA) | ||
|  | 		s.next() | ||
|  | 	case ';': | ||
|  | 		s.tok.ch = int32(SEMICOLON) | ||
|  | 		s.next() | ||
|  | 	case '~': | ||
|  | 		s.tok.ch = int32(TILDE) | ||
|  | 		s.next() | ||
|  | 	case '"': | ||
|  | 		off := s.off | ||
|  | 		s.next() | ||
|  | 		s.stringLiteral(off) | ||
|  | 	case '\'': | ||
|  | 		off := s.off | ||
|  | 		s.next() | ||
|  | 		s.runeLiteral(off) | ||
|  | 	case '`': | ||
|  | 		s.next() | ||
|  | 		for { | ||
|  | 			switch { | ||
|  | 			case s.c == '`': | ||
|  | 				s.next() | ||
|  | 				s.tok.ch = int32(STRING) | ||
|  | 				return true | ||
|  | 			case s.eof: | ||
|  | 				s.err(s.off, "raw string literal not terminated") | ||
|  | 				s.tok.ch = int32(STRING) | ||
|  | 				return true | ||
|  | 			case s.c == 0: | ||
|  | 				panic(todo("%v: %#U", s.position(), s.c)) | ||
|  | 			default: | ||
|  | 				s.next() | ||
|  | 			} | ||
|  | 		} | ||
|  | 	case '.': | ||
|  | 		s.next() | ||
|  | 		off := s.off | ||
|  | 		if isDigit(s.c) { | ||
|  | 			s.dot(false, true) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if s.c != '.' { | ||
|  | 			s.tok.ch = int32(PERIOD) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 
 | ||
|  | 		s.next() | ||
|  | 		if s.c != '.' { | ||
|  | 			s.off = off | ||
|  | 			s.c = '.' | ||
|  | 			s.tok.ch = int32(PERIOD) | ||
|  | 			return true | ||
|  | 		} | ||
|  | 
 | ||
|  | 		s.next() | ||
|  | 		s.tok.ch = int32(ELLIPSIS) | ||
|  | 		return true | ||
|  | 	case '%': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(REM_ASSIGN) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(REM) | ||
|  | 		} | ||
|  | 	case '*': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(MUL_ASSIGN) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(MUL) | ||
|  | 		} | ||
|  | 	case '^': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(XOR_ASSIGN) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(XOR) | ||
|  | 		} | ||
|  | 	case '+': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '+': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(INC) | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(ADD_ASSIGN) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(ADD) | ||
|  | 		} | ||
|  | 	case '-': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '-': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(DEC) | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(SUB_ASSIGN) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(SUB) | ||
|  | 		} | ||
|  | 	case ':': | ||
|  | 		s.next() | ||
|  | 		switch { | ||
|  | 		case s.c == '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(DEFINE) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(COLON) | ||
|  | 		} | ||
|  | 	case '=': | ||
|  | 		s.next() | ||
|  | 		switch { | ||
|  | 		case s.c == '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(EQL) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(ASSIGN) | ||
|  | 		} | ||
|  | 	case '!': | ||
|  | 		s.next() | ||
|  | 		switch { | ||
|  | 		case s.c == '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(NEQ) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(NOT) | ||
|  | 		} | ||
|  | 	case '>': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(GEQ) | ||
|  | 		case '>': | ||
|  | 			s.next() | ||
|  | 			switch s.c { | ||
|  | 			case '=': | ||
|  | 				s.next() | ||
|  | 				s.tok.ch = int32(SHR_ASSIGN) | ||
|  | 			default: | ||
|  | 				s.tok.ch = int32(SHR) | ||
|  | 			} | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(GTR) | ||
|  | 		} | ||
|  | 	case '<': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(LEQ) | ||
|  | 		case '<': | ||
|  | 			s.next() | ||
|  | 			switch s.c { | ||
|  | 			case '=': | ||
|  | 				s.next() | ||
|  | 				s.tok.ch = int32(SHL_ASSIGN) | ||
|  | 			default: | ||
|  | 				s.tok.ch = int32(SHL) | ||
|  | 			} | ||
|  | 		case '-': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(ARROW) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(LSS) | ||
|  | 		} | ||
|  | 	case '|': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '|': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(LOR) | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(OR_ASSIGN) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(OR) | ||
|  | 		} | ||
|  | 	case '&': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '&': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(LAND) | ||
|  | 		case '^': | ||
|  | 			s.next() | ||
|  | 			switch s.c { | ||
|  | 			case '=': | ||
|  | 				s.next() | ||
|  | 				s.tok.ch = int32(AND_NOT_ASSIGN) | ||
|  | 			default: | ||
|  | 				s.tok.ch = int32(AND_NOT) | ||
|  | 			} | ||
|  | 		case '=': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(AND_ASSIGN) | ||
|  | 		default: | ||
|  | 			s.tok.ch = int32(AND) | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		switch { | ||
|  | 		case isIDFirst(s.c): | ||
|  | 			s.next() | ||
|  | 			s.identifierOrKeyword() | ||
|  | 		case isDigit(s.c): | ||
|  | 			s.numericLiteral() | ||
|  | 		case s.c >= 0x80: | ||
|  | 			off := s.off | ||
|  | 			switch r := s.rune(); { | ||
|  | 			case unicode.IsLetter(r): | ||
|  | 				s.identifierOrKeyword() | ||
|  | 			case r == 0xfeff: | ||
|  | 				if off == 0 { // Ignore BOM, but only at buffer start. | ||
|  | 					return true | ||
|  | 				} | ||
|  | 
 | ||
|  | 				s.err(off, "illegal byte order mark") | ||
|  | 				s.tok.ch = int32(ILLEGAL) | ||
|  | 			default: | ||
|  | 				s.err(s.off, "illegal character %#U", r) | ||
|  | 				s.tok.ch = int32(ILLEGAL) | ||
|  | 			} | ||
|  | 		case s.eof: | ||
|  | 			if s.injectSemi() { | ||
|  | 				return true | ||
|  | 			} | ||
|  | 
 | ||
|  | 			s.close() | ||
|  | 			s.tok.ch = int32(EOF) | ||
|  | 			s.tok.sep = mathutil.MinInt32(s.tok.sep, s.tok.src) | ||
|  | 			return false | ||
|  | 		// case s.c == 0: | ||
|  | 		// 	panic(todo("%v: %#U", s.position(), s.c)) | ||
|  | 		default: | ||
|  | 			s.err(s.off, "illegal character %#U", s.c) | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(ILLEGAL) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return true | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) runeLiteral(off int32) { | ||
|  | 	// Leading ' consumed. | ||
|  | 	ok := 0 | ||
|  | 	s.tok.ch = int32(CHAR) | ||
|  | 	expOff := int32(-1) | ||
|  | 	if s.eof { | ||
|  | 		s.err(off, "rune literal not terminated") | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for { | ||
|  | 		switch s.c { | ||
|  | 		case '\\': | ||
|  | 			ok++ | ||
|  | 			s.next() | ||
|  | 			switch s.c { | ||
|  | 			case '\'', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v': | ||
|  | 				s.next() | ||
|  | 			case 'x', 'X': | ||
|  | 				s.next() | ||
|  | 				for i := 0; i < 2; i++ { | ||
|  | 					if s.c == '\'' { | ||
|  | 						if i != 2 { | ||
|  | 							s.err(s.off, "illegal character %#U in escape sequence", s.c) | ||
|  | 						} | ||
|  | 						s.next() | ||
|  | 						return | ||
|  | 					} | ||
|  | 
 | ||
|  | 					if !isHexDigit(s.c) { | ||
|  | 						s.err(s.off, "illegal character %#U in escape sequence", s.c) | ||
|  | 						break | ||
|  | 					} | ||
|  | 					s.next() | ||
|  | 				} | ||
|  | 			case 'u': | ||
|  | 				s.u(4) | ||
|  | 			case 'U': | ||
|  | 				s.u(8) | ||
|  | 			default: | ||
|  | 				switch { | ||
|  | 				case s.eof: | ||
|  | 					s.err(s.base+s.off, "escape sequence not terminated") | ||
|  | 					return | ||
|  | 				case isOctalDigit(s.c): | ||
|  | 					for i := 0; i < 3; i++ { | ||
|  | 						s.next() | ||
|  | 						if s.c == '\'' { | ||
|  | 							if i != 2 { | ||
|  | 								s.err(s.off, "illegal character %#U in escape sequence", s.c) | ||
|  | 							} | ||
|  | 							s.next() | ||
|  | 							return | ||
|  | 						} | ||
|  | 
 | ||
|  | 						if !isOctalDigit(s.c) { | ||
|  | 							s.err(s.off, "illegal character %#U in escape sequence", s.c) | ||
|  | 							break | ||
|  | 						} | ||
|  | 					} | ||
|  | 				default: | ||
|  | 					s.err(s.off, "unknown escape sequence") | ||
|  | 				} | ||
|  | 			} | ||
|  | 		case '\'': | ||
|  | 			s.next() | ||
|  | 			if ok != 1 { | ||
|  | 				s.err(off, "illegal rune literal") | ||
|  | 			} | ||
|  | 			return | ||
|  | 		case '\t': | ||
|  | 			s.next() | ||
|  | 			ok++ | ||
|  | 		default: | ||
|  | 			switch { | ||
|  | 			case s.eof: | ||
|  | 				switch { | ||
|  | 				case ok != 0: | ||
|  | 					s.err(expOff, "rune literal not terminated") | ||
|  | 				default: | ||
|  | 					s.err(s.base+s.off, "rune literal not terminated") | ||
|  | 				} | ||
|  | 				return | ||
|  | 			case s.c == 0: | ||
|  | 				panic(todo("%v: %#U", s.position(), s.c)) | ||
|  | 			case s.c < ' ': | ||
|  | 				ok++ | ||
|  | 				s.err(s.off, "non-printable character: %#U", s.c) | ||
|  | 				s.next() | ||
|  | 			case s.c >= 0x80: | ||
|  | 				ok++ | ||
|  | 				off := s.off | ||
|  | 				if c := s.rune(); c == 0xfeff { | ||
|  | 					s.err(off, "illegal byte order mark") | ||
|  | 				} | ||
|  | 			default: | ||
|  | 				ok++ | ||
|  | 				s.next() | ||
|  | 			} | ||
|  | 		} | ||
|  | 		if ok != 0 && expOff < 0 { | ||
|  | 			expOff = s.off | ||
|  | 			if s.eof { | ||
|  | 				expOff++ | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) stringLiteral(off int32) { | ||
|  | 	// Leadind " consumed. | ||
|  | 	s.tok.ch = int32(STRING) | ||
|  | 	for { | ||
|  | 		switch { | ||
|  | 		case s.c == '"': | ||
|  | 			s.next() | ||
|  | 			return | ||
|  | 		case s.c == '\\': | ||
|  | 			s.next() | ||
|  | 			switch s.c { | ||
|  | 			case '"', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v': | ||
|  | 				s.next() | ||
|  | 				continue | ||
|  | 			case 'x', 'X': | ||
|  | 				s.next() | ||
|  | 				if !isHexDigit(s.c) { | ||
|  | 					panic(todo("%v: %#U", s.position(), s.c)) | ||
|  | 				} | ||
|  | 
 | ||
|  | 				s.next() | ||
|  | 				if !isHexDigit(s.c) { | ||
|  | 					panic(todo("%v: %#U", s.position(), s.c)) | ||
|  | 				} | ||
|  | 
 | ||
|  | 				s.next() | ||
|  | 				continue | ||
|  | 			case 'u': | ||
|  | 				s.u(4) | ||
|  | 				continue | ||
|  | 			case 'U': | ||
|  | 				s.u(8) | ||
|  | 				continue | ||
|  | 			default: | ||
|  | 				switch { | ||
|  | 				case isOctalDigit(s.c): | ||
|  | 					s.next() | ||
|  | 					if isOctalDigit(s.c) { | ||
|  | 						s.next() | ||
|  | 					} | ||
|  | 					if isOctalDigit(s.c) { | ||
|  | 						s.next() | ||
|  | 					} | ||
|  | 					continue | ||
|  | 				default: | ||
|  | 					s.err(off-1, "unknown escape sequence") | ||
|  | 				} | ||
|  | 			} | ||
|  | 		case s.c == '\n': | ||
|  | 			fallthrough | ||
|  | 		case s.eof: | ||
|  | 			s.err(off, "string literal not terminated") | ||
|  | 			return | ||
|  | 		case s.c == 0: | ||
|  | 			s.err(s.off, "illegal character NUL") | ||
|  | 		} | ||
|  | 
 | ||
|  | 		switch { | ||
|  | 		case s.c >= 0x80: | ||
|  | 			off := s.off | ||
|  | 			if s.rune() == 0xfeff { | ||
|  | 				s.err(off, "illegal byte order mark") | ||
|  | 			} | ||
|  | 			continue | ||
|  | 		} | ||
|  | 
 | ||
|  | 		s.next() | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) u(n int) (r rune) { | ||
|  | 	// Leading u/U not consumed. | ||
|  | 	s.next() | ||
|  | 	off := s.off | ||
|  | 	for i := 0; i < n; i++ { | ||
|  | 		switch { | ||
|  | 		case isHexDigit(s.c): | ||
|  | 			var n rune | ||
|  | 			switch { | ||
|  | 			case s.c >= '0' && s.c <= '9': | ||
|  | 				n = rune(s.c) - '0' | ||
|  | 			case s.c >= 'a' && s.c <= 'f': | ||
|  | 				n = rune(s.c) - 'a' + 10 | ||
|  | 			case s.c >= 'A' && s.c <= 'F': | ||
|  | 				n = rune(s.c) - 'A' + 10 | ||
|  | 			} | ||
|  | 			r = 16*r + n | ||
|  | 		default: | ||
|  | 			switch { | ||
|  | 			case s.eof: | ||
|  | 				s.err(s.base+s.off, "escape sequence not terminated") | ||
|  | 			default: | ||
|  | 				s.err(s.off, "illegal character %#U in escape sequence", s.c) | ||
|  | 			} | ||
|  | 			return r | ||
|  | 		} | ||
|  | 
 | ||
|  | 		s.next() | ||
|  | 	} | ||
|  | 	if r < 0 || r > unicode.MaxRune || r >= 0xd800 && r <= 0xdfff { | ||
|  | 		s.err(off-1, "escape sequence is invalid Unicode code point") | ||
|  | 	} | ||
|  | 	return r | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) identifierOrKeyword() { | ||
|  | out: | ||
|  | 	for { | ||
|  | 		switch { | ||
|  | 		case isIDNext(s.c): | ||
|  | 			s.next() | ||
|  | 		case s.c >= 0x80: | ||
|  | 			off := s.off | ||
|  | 			c := s.c | ||
|  | 			switch r := s.rune(); { | ||
|  | 			case unicode.IsLetter(r) || unicode.IsDigit(r): | ||
|  | 				// already consumed | ||
|  | 			default: | ||
|  | 				s.off = off | ||
|  | 				s.c = c | ||
|  | 				break out | ||
|  | 			} | ||
|  | 		case s.eof: | ||
|  | 			break out | ||
|  | 		case s.c == 0: | ||
|  | 			s.err(s.off, "illegal character NUL") | ||
|  | 			break out | ||
|  | 		default: | ||
|  | 			break out | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if s.tok.ch = int32(keywords[string(s.buf[s.tok.src:s.off])]); s.tok.ch == 0 { | ||
|  | 		s.tok.ch = int32(IDENT) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) numericLiteral() { | ||
|  | 	// Leading decimal digit not consumed. | ||
|  | 	var hasHexMantissa, needFrac bool | ||
|  | more: | ||
|  | 	switch s.c { | ||
|  | 	case '0': | ||
|  | 		s.next() | ||
|  | 		switch s.c { | ||
|  | 		case '.': | ||
|  | 			// nop | ||
|  | 		case 'b', 'B': | ||
|  | 			s.next() | ||
|  | 			s.binaryLiteral() | ||
|  | 			return | ||
|  | 		case 'e', 'E': | ||
|  | 			s.exponent() | ||
|  | 			s.tok.ch = int32(FLOAT) | ||
|  | 			return | ||
|  | 		case 'p', 'P': | ||
|  | 			s.err(s.off, "'%c' exponent requires hexadecimal mantissa", s.c) | ||
|  | 			s.exponent() | ||
|  | 			s.tok.ch = int32(FLOAT) | ||
|  | 			return | ||
|  | 		case 'o', 'O': | ||
|  | 			s.next() | ||
|  | 			s.octalLiteral() | ||
|  | 			return | ||
|  | 		case 'x', 'X': | ||
|  | 			hasHexMantissa = true | ||
|  | 			needFrac = true | ||
|  | 			s.tok.ch = int32(INT) | ||
|  | 			s.next() | ||
|  | 			if s.c == '.' { | ||
|  | 				s.next() | ||
|  | 				s.dot(hasHexMantissa, needFrac) | ||
|  | 				return | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if s.hexadecimals() == 0 { | ||
|  | 				s.err(s.base+s.off, "hexadecimal literal has no digits") | ||
|  | 				return | ||
|  | 			} | ||
|  | 
 | ||
|  | 			needFrac = false | ||
|  | 		case 'i': | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(IMAG) | ||
|  | 			return | ||
|  | 		default: | ||
|  | 			invalidOff := int32(-1) | ||
|  | 			var invalidDigit byte | ||
|  | 			for { | ||
|  | 				if s.c == '_' { | ||
|  | 					for n := 0; s.c == '_'; n++ { | ||
|  | 						if n == 1 { | ||
|  | 							s.err(s.off, "'_' must separate successive digits") | ||
|  | 						} | ||
|  | 						s.next() | ||
|  | 					} | ||
|  | 					if !isDigit(s.c) { | ||
|  | 						s.err(s.off-1, "'_' must separate successive digits") | ||
|  | 					} | ||
|  | 				} | ||
|  | 				if isOctalDigit(s.c) { | ||
|  | 					s.next() | ||
|  | 					continue | ||
|  | 				} | ||
|  | 
 | ||
|  | 				if isDigit(s.c) { | ||
|  | 					if invalidOff < 0 { | ||
|  | 						invalidOff = s.off | ||
|  | 						invalidDigit = s.c | ||
|  | 					} | ||
|  | 					s.next() | ||
|  | 					continue | ||
|  | 				} | ||
|  | 
 | ||
|  | 				break | ||
|  | 			} | ||
|  | 			switch s.c { | ||
|  | 			case '.', 'e', 'E', 'i': | ||
|  | 				break more | ||
|  | 			} | ||
|  | 			if isDigit(s.c) { | ||
|  | 				break more | ||
|  | 			} | ||
|  | 			if invalidOff > 0 { | ||
|  | 				s.err(invalidOff, "invalid digit '%c' in octal literal", invalidDigit) | ||
|  | 			} | ||
|  | 			s.tok.ch = int32(INT) | ||
|  | 			return | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		s.decimals() | ||
|  | 	} | ||
|  | 	switch s.c { | ||
|  | 	case '.': | ||
|  | 		s.next() | ||
|  | 		s.dot(hasHexMantissa, needFrac) | ||
|  | 	case 'p', 'P': | ||
|  | 		if !hasHexMantissa { | ||
|  | 			s.err(s.off, "'%c' exponent requires hexadecimal mantissa", s.c) | ||
|  | 		} | ||
|  | 		fallthrough | ||
|  | 	case 'e', 'E': | ||
|  | 		s.exponent() | ||
|  | 		if s.c == 'i' { | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(IMAG) | ||
|  | 			return | ||
|  | 		} | ||
|  | 
 | ||
|  | 		s.tok.ch = int32(FLOAT) | ||
|  | 	case 'i': | ||
|  | 		s.next() | ||
|  | 		s.tok.ch = int32(IMAG) | ||
|  | 	default: | ||
|  | 		s.tok.ch = int32(INT) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) octalLiteral() { | ||
|  | 	// Leading 0o consumed. | ||
|  | 	ok := false | ||
|  | 	invalidOff := int32(-1) | ||
|  | 	var invalidDigit byte | ||
|  | 	s.tok.ch = int32(INT) | ||
|  | 	for { | ||
|  | 		for n := 0; s.c == '_'; n++ { | ||
|  | 			if n == 1 { | ||
|  | 				s.err(s.off, "'_' must separate successive digits") | ||
|  | 			} | ||
|  | 			s.next() | ||
|  | 		} | ||
|  | 		switch s.c { | ||
|  | 		case '0', '1', '2', '3', '4', '5', '6', '7': | ||
|  | 			s.next() | ||
|  | 			ok = true | ||
|  | 		case '8', '9': | ||
|  | 			if invalidOff < 0 { | ||
|  | 				invalidOff = s.off | ||
|  | 				invalidDigit = s.c | ||
|  | 			} | ||
|  | 			s.next() | ||
|  | 		case '.': | ||
|  | 			s.tok.ch = int32(FLOAT) | ||
|  | 			s.err(s.off, "invalid radix point in octal literal") | ||
|  | 			s.next() | ||
|  | 		case 'e', 'E': | ||
|  | 			s.tok.ch = int32(FLOAT) | ||
|  | 			s.err(s.off, "'%c' exponent requires decimal mantissa", s.c) | ||
|  | 			s.exponent() | ||
|  | 		case 'p', 'P': | ||
|  | 			s.tok.ch = int32(FLOAT) | ||
|  | 			s.err(s.off, "'%c' exponent requires hexadecimal mantissa", s.c) | ||
|  | 			s.exponent() | ||
|  | 		default: | ||
|  | 			switch { | ||
|  | 			case !ok: | ||
|  | 				s.err(s.base+s.off, "octal literal has no digits") | ||
|  | 			case invalidOff > 0: | ||
|  | 				s.err(invalidOff, "invalid digit '%c' in octal literal", invalidDigit) | ||
|  | 			} | ||
|  | 			if s.c == 'i' { | ||
|  | 				s.next() | ||
|  | 				s.tok.ch = int32(IMAG) | ||
|  | 			} | ||
|  | 			return | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) binaryLiteral() { | ||
|  | 	// Leading 0b consumed. | ||
|  | 	ok := false | ||
|  | 	invalidOff := int32(-1) | ||
|  | 	var invalidDigit byte | ||
|  | 	s.tok.ch = int32(INT) | ||
|  | 	for { | ||
|  | 		for n := 0; s.c == '_'; n++ { | ||
|  | 			if n == 1 { | ||
|  | 				s.err(s.off, "'_' must separate successive digits") | ||
|  | 			} | ||
|  | 			s.next() | ||
|  | 		} | ||
|  | 		switch s.c { | ||
|  | 		case '0', '1': | ||
|  | 			s.next() | ||
|  | 			ok = true | ||
|  | 		case '.': | ||
|  | 			s.tok.ch = int32(FLOAT) | ||
|  | 			s.err(s.off, "invalid radix point in binary literal") | ||
|  | 			s.next() | ||
|  | 		case 'e', 'E': | ||
|  | 			s.tok.ch = int32(FLOAT) | ||
|  | 			s.err(s.off, "'%c' exponent requires decimal mantissa", s.c) | ||
|  | 			s.exponent() | ||
|  | 		case 'p', 'P': | ||
|  | 			s.tok.ch = int32(FLOAT) | ||
|  | 			s.err(s.off, "'%c' exponent requires hexadecimal mantissa", s.c) | ||
|  | 			s.exponent() | ||
|  | 		default: | ||
|  | 			if isDigit(s.c) { | ||
|  | 				if invalidOff < 0 { | ||
|  | 					invalidOff = s.off | ||
|  | 					invalidDigit = s.c | ||
|  | 				} | ||
|  | 				s.next() | ||
|  | 				continue | ||
|  | 			} | ||
|  | 
 | ||
|  | 			switch { | ||
|  | 			case !ok: | ||
|  | 				s.err(s.base+s.off, "binary literal has no digits") | ||
|  | 			case invalidOff > 0: | ||
|  | 				s.err(invalidOff, "invalid digit '%c' in binary literal", invalidDigit) | ||
|  | 			} | ||
|  | 			if s.c == 'i' { | ||
|  | 				s.next() | ||
|  | 				s.tok.ch = int32(IMAG) | ||
|  | 			} | ||
|  | 			return | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) generalComment(off int32) (injectSemi bool) { | ||
|  | 	// Leading /* consumed | ||
|  | 	off0 := s.off - 2 | ||
|  | 	var nl bool | ||
|  | 	for { | ||
|  | 		switch { | ||
|  | 		case s.c == '*': | ||
|  | 			s.next() | ||
|  | 			switch s.c { | ||
|  | 			case '/': | ||
|  | 				s.lineInfo(off0, s.off+1) | ||
|  | 				s.next() | ||
|  | 				if nl { | ||
|  | 					return s.injectSemi() | ||
|  | 				} | ||
|  | 
 | ||
|  | 				return false | ||
|  | 			} | ||
|  | 		case s.c == '\n': | ||
|  | 			nl = true | ||
|  | 			s.next() | ||
|  | 		case s.eof: | ||
|  | 			s.tok.ch = 0 | ||
|  | 			s.err(off, "comment not terminated") | ||
|  | 			return true | ||
|  | 		case s.c == 0: | ||
|  | 			panic(todo("%v: %#U", s.position(), s.c)) | ||
|  | 		default: | ||
|  | 			s.next() | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) lineComment(off int32) (injectSemi bool) { | ||
|  | 	// Leading // consumed | ||
|  | 	off0 := s.off - 2 | ||
|  | 	for { | ||
|  | 		switch { | ||
|  | 		case s.c == '\n': | ||
|  | 			s.lineInfo(off0, s.off+1) | ||
|  | 			if s.injectSemi() { | ||
|  | 				return true | ||
|  | 			} | ||
|  | 
 | ||
|  | 			s.next() | ||
|  | 			return false | ||
|  | 		case s.c >= 0x80: | ||
|  | 			if c := s.rune(); c == 0xfeff { | ||
|  | 				s.err(off+2, "illegal byte order mark") | ||
|  | 			} | ||
|  | 		case s.eof: | ||
|  | 			s.off++ | ||
|  | 			if s.injectSemi() { | ||
|  | 				return true | ||
|  | 			} | ||
|  | 
 | ||
|  | 			return false | ||
|  | 		case s.c == 0: | ||
|  | 			return false | ||
|  | 		default: | ||
|  | 			s.next() | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) lineInfo(off, next int32) { | ||
|  | 	if off != 0 && s.buf[off+1] != '*' && s.buf[off-1] != '\n' && s.buf[off-1] != '\r' { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	str := s.buf[off:next] | ||
|  | 	if !bytes.HasPrefix(str[len("//"):], lineCommentTag) { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	switch { | ||
|  | 	case str[1] == '*': | ||
|  | 		str = str[:len(str)-len("*/")] | ||
|  | 	default: | ||
|  | 		str = str[:len(str)-len("\n")] | ||
|  | 	} | ||
|  | 	str = str[len("//"):] | ||
|  | 
 | ||
|  | 	str, ln, ok := s.lineInfoNum(str[len("line "):]) | ||
|  | 	col := 0 | ||
|  | 	if ok == liBadNum || ok == liNoNum { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	hasCol := false | ||
|  | 	var n int | ||
|  | 	if str, n, ok = s.lineInfoNum(str); ok == liBadNum { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if ok != liNoNum { | ||
|  | 		col = ln | ||
|  | 		ln = n | ||
|  | 		hasCol = true | ||
|  | 	} | ||
|  | 
 | ||
|  | 	fn := strings.TrimSpace(string(str)) | ||
|  | 	switch { | ||
|  | 	case fn == "" && hasCol: | ||
|  | 		fn = s.pos(off).Filename | ||
|  | 	case fn != "": | ||
|  | 		fn = filepath.Clean(fn) | ||
|  | 		if !filepath.IsAbs(fn) { | ||
|  | 			fn = filepath.Join(s.dir, fn) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	// trc("set %v %q %v %v", next, fn, ln, col) | ||
|  | 	s.file.AddLineColumnInfo(int(next), fn, ln, col) | ||
|  | } | ||
|  | 
 | ||
|  | const ( | ||
|  | 	liNoNum = iota | ||
|  | 	liBadNum | ||
|  | 	liOK | ||
|  | ) | ||
|  | 
 | ||
|  | func (s *scanner) lineInfoNum(str []byte) (_ []byte, n, r int) { | ||
|  | 	// trc("==== %q", str) | ||
|  | 	x := len(str) - 1 | ||
|  | 	if x < 0 || !isDigit(str[x]) { | ||
|  | 		return str, 0, liNoNum | ||
|  | 	} | ||
|  | 
 | ||
|  | 	mul := 1 | ||
|  | 	for x > 0 && isDigit(str[x]) { | ||
|  | 		n += mul * (int(str[x]) - '0') | ||
|  | 		mul *= 10 | ||
|  | 		x-- | ||
|  | 		if n < 0 { | ||
|  | 			return str, 0, liBadNum | ||
|  | 		} | ||
|  | 	} | ||
|  | 	if x < 0 || str[x] != ':' { | ||
|  | 		return str, 0, liBadNum | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// trc("---- %q %v %v", str[:x], n, liOK) | ||
|  | 	return str[:x], n, liOK | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) rune() rune { | ||
|  | 	switch r, sz := utf8.DecodeRune(s.buf[s.off:]); { | ||
|  | 	case r == utf8.RuneError && sz == 0: | ||
|  | 		panic(todo("%v: %#U", s.position(), s.c)) | ||
|  | 	case r == utf8.RuneError && sz == 1: | ||
|  | 		s.err(s.off, "illegal UTF-8 encoding") | ||
|  | 		s.next() | ||
|  | 		return r | ||
|  | 	default: | ||
|  | 		s.nextN(sz) | ||
|  | 		return r | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) dot(hasHexMantissa, needFrac bool) { | ||
|  | 	// '.' already consumed | ||
|  | 	switch { | ||
|  | 	case hasHexMantissa: | ||
|  | 		if s.hexadecimals() == 0 && needFrac { | ||
|  | 			s.err(s.off, "hexadecimal literal has no digits") | ||
|  | 		} | ||
|  | 		switch s.c { | ||
|  | 		case 'p', 'P': | ||
|  | 			// ok | ||
|  | 		default: | ||
|  | 			s.err(s.off, "hexadecimal mantissa requires a 'p' exponent") | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		if s.decimals() == 0 && needFrac { | ||
|  | 			panic(todo("%v: %#U", s.position(), s.c)) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	switch s.c { | ||
|  | 	case 'p', 'P': | ||
|  | 		if !hasHexMantissa { | ||
|  | 			s.err(s.off, "'%c' exponent requires hexadecimal mantissa", s.c) | ||
|  | 		} | ||
|  | 		fallthrough | ||
|  | 	case 'e', 'E': | ||
|  | 		s.exponent() | ||
|  | 		if s.c == 'i' { | ||
|  | 			s.next() | ||
|  | 			s.tok.ch = int32(IMAG) | ||
|  | 			return | ||
|  | 		} | ||
|  | 
 | ||
|  | 		s.tok.ch = int32(FLOAT) | ||
|  | 	case 'i': | ||
|  | 		s.next() | ||
|  | 		s.tok.ch = int32(IMAG) | ||
|  | 	default: | ||
|  | 		s.tok.ch = int32(FLOAT) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) exponent() { | ||
|  | 	// Leanding e or E not consumed. | ||
|  | 	s.next() | ||
|  | 	switch s.c { | ||
|  | 	case '+', '-': | ||
|  | 		s.next() | ||
|  | 	} | ||
|  | 	if !isDigit(s.c) { | ||
|  | 		s.err(s.base+s.off, "exponent has no digits") | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s.decimals() | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) decimals() (r int) { | ||
|  | 	first := true | ||
|  | 	for { | ||
|  | 		switch { | ||
|  | 		case isDigit(s.c): | ||
|  | 			first = false | ||
|  | 			s.next() | ||
|  | 			r++ | ||
|  | 		case s.c == '_': | ||
|  | 			for n := 0; s.c == '_'; n++ { | ||
|  | 				if first || n == 1 { | ||
|  | 					s.err(s.off, "'_' must separate successive digits") | ||
|  | 				} | ||
|  | 				s.next() | ||
|  | 			} | ||
|  | 			if !isDigit(s.c) { | ||
|  | 				s.err(s.off-1, "'_' must separate successive digits") | ||
|  | 			} | ||
|  | 		default: | ||
|  | 			return r | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *scanner) hexadecimals() (r int) { | ||
|  | 	for { | ||
|  | 		switch { | ||
|  | 		case isHexDigit(s.c): | ||
|  | 			s.next() | ||
|  | 			r++ | ||
|  | 		case s.c == '_': | ||
|  | 			for n := 0; s.c == '_'; n++ { | ||
|  | 				if n == 1 { | ||
|  | 					s.err(s.off, "'_' must separate successive digits") | ||
|  | 				} | ||
|  | 				s.next() | ||
|  | 			} | ||
|  | 			if !isHexDigit(s.c) { | ||
|  | 				s.err(s.off-1, "'_' must separate successive digits") | ||
|  | 			} | ||
|  | 		default: | ||
|  | 			return r | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // When the input is broken into tokens, a semicolon is automatically inserted | ||
|  | // into the token stream immediately after a line's final token if that token | ||
|  | // is | ||
|  | // | ||
|  | //   - an identifier | ||
|  | //   - an integer, floating-point, imaginary, rune, or string literal | ||
|  | //   - one of the keywords break, continue, fallthrough, or return | ||
|  | //   - one of the operators and punctuation ++, --, ), ], or } | ||
|  | func (s *scanner) injectSemi() bool { | ||
|  | 	switch token.Token(s.last) { | ||
|  | 	case | ||
|  | 		IDENT, INT, FLOAT, IMAG, CHAR, STRING, | ||
|  | 		BREAK, CONTINUE, FALLTHROUGH, RETURN, | ||
|  | 		INC, DEC, RPAREN, RBRACK, RBRACE: | ||
|  | 
 | ||
|  | 		s.tok.ch = int32(SEMICOLON) | ||
|  | 		s.last = 0 | ||
|  | 		if s.c == '\n' { | ||
|  | 			s.next() | ||
|  | 		} | ||
|  | 		return true | ||
|  | 	} | ||
|  | 
 | ||
|  | 	s.last = 0 | ||
|  | 	return false | ||
|  | } |