2015-02-13 14:40:36 -05:00
|
|
|
// Copyright 2009 The Go 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 (
|
2016-03-11 13:39:20 -05:00
|
|
|
"bufio"
|
2016-04-05 14:20:04 -07:00
|
|
|
"bytes"
|
2016-08-30 16:31:53 -07:00
|
|
|
"cmd/compile/internal/syntax"
|
2015-02-13 14:40:36 -05:00
|
|
|
"cmd/internal/obj"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"unicode"
|
|
|
|
|
"unicode/utf8"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
EOF = -1
|
2016-03-11 14:28:16 -08:00
|
|
|
BOM = 0xFEFF
|
2015-02-13 14:40:36 -05:00
|
|
|
)
|
|
|
|
|
|
2016-04-05 14:20:04 -07:00
|
|
|
// lexlineno is the line number _after_ the most recently read rune.
|
|
|
|
|
// In particular, it's advanced (or rewound) as newlines are read (or unread).
|
|
|
|
|
var lexlineno int32
|
|
|
|
|
|
|
|
|
|
// lineno is the line number at the start of the most recently lexed token.
|
|
|
|
|
var lineno int32
|
|
|
|
|
|
|
|
|
|
var lexbuf bytes.Buffer
|
|
|
|
|
var strbuf bytes.Buffer
|
|
|
|
|
var litbuf string // LLITERAL value for use in syntax error messages
|
|
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
func isSpace(c rune) bool {
|
2015-09-24 15:41:05 +02:00
|
|
|
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
func isLetter(c rune) bool {
|
|
|
|
|
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_'
|
2015-09-24 15:41:05 +02:00
|
|
|
}
|
|
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
func isDigit(c rune) bool {
|
2015-09-24 15:41:05 +02:00
|
|
|
return '0' <= c && c <= '9'
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-07 08:01:47 +02:00
|
|
|
func isQuoted(s string) bool {
|
|
|
|
|
return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-24 15:41:05 +02:00
|
|
|
func plan9quote(s string) string {
|
|
|
|
|
if s == "" {
|
|
|
|
|
return "''"
|
|
|
|
|
}
|
|
|
|
|
for _, c := range s {
|
|
|
|
|
if c <= ' ' || c == '\'' {
|
|
|
|
|
return "'" + strings.Replace(s, "'", "''", -1) + "'"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 16:31:53 -07:00
|
|
|
type Pragma syntax.Pragma
|
2016-02-26 13:32:28 -08:00
|
|
|
|
|
|
|
|
const (
|
2016-10-10 16:46:28 -04:00
|
|
|
Nointerface Pragma = 1 << iota
|
|
|
|
|
Noescape // func parameters don't escape
|
|
|
|
|
Norace // func must not have race detector annotations
|
|
|
|
|
Nosplit // func should not execute on separate stack
|
|
|
|
|
Noinline // func should not be inlined
|
|
|
|
|
CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all
|
|
|
|
|
UintptrEscapes // pointers converted to uintptr escape
|
|
|
|
|
|
|
|
|
|
// Runtime-only pragmas.
|
|
|
|
|
// See ../../../../runtime/README.md for detailed descriptions.
|
|
|
|
|
|
|
|
|
|
Systemstack // func must run on system stack
|
|
|
|
|
Nowritebarrier // emit compiler error instead of write barrier
|
|
|
|
|
Nowritebarrierrec // error on write barrier in this or recursive callees
|
|
|
|
|
Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees
|
2016-02-26 13:32:28 -08:00
|
|
|
)
|
|
|
|
|
|
2016-09-15 15:45:10 +10:00
|
|
|
func pragmaValue(verb string) Pragma {
|
2016-08-30 14:48:01 -07:00
|
|
|
switch verb {
|
|
|
|
|
case "go:nointerface":
|
|
|
|
|
if obj.Fieldtrack_enabled != 0 {
|
|
|
|
|
return Nointerface
|
|
|
|
|
}
|
|
|
|
|
case "go:noescape":
|
|
|
|
|
return Noescape
|
|
|
|
|
case "go:norace":
|
|
|
|
|
return Norace
|
|
|
|
|
case "go:nosplit":
|
|
|
|
|
return Nosplit
|
|
|
|
|
case "go:noinline":
|
|
|
|
|
return Noinline
|
|
|
|
|
case "go:systemstack":
|
|
|
|
|
if !compiling_runtime {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("//go:systemstack only allowed in runtime")
|
2016-08-30 14:48:01 -07:00
|
|
|
}
|
|
|
|
|
return Systemstack
|
|
|
|
|
case "go:nowritebarrier":
|
|
|
|
|
if !compiling_runtime {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("//go:nowritebarrier only allowed in runtime")
|
2016-08-30 14:48:01 -07:00
|
|
|
}
|
|
|
|
|
return Nowritebarrier
|
|
|
|
|
case "go:nowritebarrierrec":
|
|
|
|
|
if !compiling_runtime {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("//go:nowritebarrierrec only allowed in runtime")
|
2016-08-30 14:48:01 -07:00
|
|
|
}
|
|
|
|
|
return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier
|
2016-10-10 16:46:28 -04:00
|
|
|
case "go:yeswritebarrierrec":
|
|
|
|
|
if !compiling_runtime {
|
|
|
|
|
yyerror("//go:yeswritebarrierrec only allowed in runtime")
|
|
|
|
|
}
|
|
|
|
|
return Yeswritebarrierrec
|
2016-08-30 14:48:01 -07:00
|
|
|
case "go:cgo_unsafe_args":
|
|
|
|
|
return CgoUnsafeArgs
|
|
|
|
|
case "go:uintptrescapes":
|
|
|
|
|
// For the next function declared in the file
|
|
|
|
|
// any uintptr arguments may be pointer values
|
|
|
|
|
// converted to uintptr. This directive
|
|
|
|
|
// ensures that the referenced allocated
|
|
|
|
|
// object, if any, is retained and not moved
|
|
|
|
|
// until the call completes, even though from
|
|
|
|
|
// the types alone it would appear that the
|
|
|
|
|
// object is no longer needed during the
|
|
|
|
|
// call. The conversion to uintptr must appear
|
|
|
|
|
// in the argument list.
|
|
|
|
|
// Used in syscall/dll_windows.go.
|
|
|
|
|
return UintptrEscapes
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-20 11:06:35 -08:00
|
|
|
type lexer struct {
|
2016-02-22 11:53:20 -08:00
|
|
|
// source
|
2016-03-11 13:55:53 -08:00
|
|
|
bin *bufio.Reader
|
|
|
|
|
prevlineno int32 // line no. of most recently read character
|
2016-02-22 11:53:20 -08:00
|
|
|
|
2016-02-20 12:53:34 -08:00
|
|
|
nlsemi bool // if set, '\n' and EOF translate to ';'
|
2016-02-20 11:06:35 -08:00
|
|
|
|
2016-02-26 13:32:28 -08:00
|
|
|
// pragma flags
|
|
|
|
|
// accumulated by lexer; reset by parser
|
|
|
|
|
pragma Pragma
|
|
|
|
|
|
2016-02-20 11:06:35 -08:00
|
|
|
// current token
|
|
|
|
|
tok int32
|
2016-02-25 17:27:10 -08:00
|
|
|
sym_ *Sym // valid if tok == LNAME
|
|
|
|
|
val Val // valid if tok == LLITERAL
|
2016-03-01 14:47:26 -08:00
|
|
|
op Op // valid if tok == LOPER, LASOP, or LINCOP, or prec > 0
|
2016-02-25 17:27:10 -08:00
|
|
|
prec OpPrec // operator precedence; 0 if not a binary operator
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-25 17:27:10 -08:00
|
|
|
type OpPrec int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// Precedences of binary operators (must be > 0).
|
|
|
|
|
PCOMM OpPrec = 1 + iota
|
|
|
|
|
POROR
|
|
|
|
|
PANDAND
|
|
|
|
|
PCMP
|
|
|
|
|
PADD
|
|
|
|
|
PMUL
|
|
|
|
|
)
|
|
|
|
|
|
2015-11-23 14:11:15 -08:00
|
|
|
const (
|
2016-02-24 16:17:49 -08:00
|
|
|
// The value of single-char tokens is just their character's Unicode value.
|
|
|
|
|
// They are all below utf8.RuneSelf. Shift other tokens up to avoid conflicts.
|
2016-03-01 14:47:26 -08:00
|
|
|
|
|
|
|
|
// names and literals
|
|
|
|
|
LNAME = utf8.RuneSelf + iota
|
|
|
|
|
LLITERAL
|
|
|
|
|
|
|
|
|
|
// operator-based operations
|
|
|
|
|
LOPER
|
2015-11-23 14:11:15 -08:00
|
|
|
LASOP
|
2016-03-01 14:47:26 -08:00
|
|
|
LINCOP
|
|
|
|
|
|
|
|
|
|
// miscellaneous
|
2015-11-23 14:11:15 -08:00
|
|
|
LCOLAS
|
2016-03-01 14:47:26 -08:00
|
|
|
LCOMM
|
|
|
|
|
LDDD
|
|
|
|
|
|
|
|
|
|
// keywords
|
2015-11-23 14:11:15 -08:00
|
|
|
LBREAK
|
|
|
|
|
LCASE
|
|
|
|
|
LCHAN
|
|
|
|
|
LCONST
|
|
|
|
|
LCONTINUE
|
|
|
|
|
LDEFAULT
|
|
|
|
|
LDEFER
|
|
|
|
|
LELSE
|
|
|
|
|
LFALL
|
|
|
|
|
LFOR
|
|
|
|
|
LFUNC
|
|
|
|
|
LGO
|
|
|
|
|
LGOTO
|
|
|
|
|
LIF
|
|
|
|
|
LIMPORT
|
|
|
|
|
LINTERFACE
|
|
|
|
|
LMAP
|
|
|
|
|
LPACKAGE
|
|
|
|
|
LRANGE
|
|
|
|
|
LRETURN
|
|
|
|
|
LSELECT
|
|
|
|
|
LSTRUCT
|
|
|
|
|
LSWITCH
|
|
|
|
|
LTYPE
|
|
|
|
|
LVAR
|
2016-03-01 14:47:26 -08:00
|
|
|
|
2015-11-23 14:11:15 -08:00
|
|
|
LIGNORE
|
|
|
|
|
)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-03-11 15:22:24 -08:00
|
|
|
var lexn = map[rune]string{
|
|
|
|
|
LNAME: "NAME",
|
|
|
|
|
LLITERAL: "LITERAL",
|
|
|
|
|
|
|
|
|
|
LOPER: "OPER",
|
|
|
|
|
LASOP: "ASOP",
|
|
|
|
|
LINCOP: "INCOP",
|
|
|
|
|
|
|
|
|
|
LCOLAS: "COLAS",
|
|
|
|
|
LCOMM: "COMM",
|
|
|
|
|
LDDD: "DDD",
|
|
|
|
|
|
|
|
|
|
LBREAK: "BREAK",
|
|
|
|
|
LCASE: "CASE",
|
|
|
|
|
LCHAN: "CHAN",
|
|
|
|
|
LCONST: "CONST",
|
|
|
|
|
LCONTINUE: "CONTINUE",
|
|
|
|
|
LDEFAULT: "DEFAULT",
|
|
|
|
|
LDEFER: "DEFER",
|
|
|
|
|
LELSE: "ELSE",
|
|
|
|
|
LFALL: "FALL",
|
|
|
|
|
LFOR: "FOR",
|
|
|
|
|
LFUNC: "FUNC",
|
|
|
|
|
LGO: "GO",
|
|
|
|
|
LGOTO: "GOTO",
|
|
|
|
|
LIF: "IF",
|
|
|
|
|
LIMPORT: "IMPORT",
|
|
|
|
|
LINTERFACE: "INTERFACE",
|
|
|
|
|
LMAP: "MAP",
|
|
|
|
|
LPACKAGE: "PACKAGE",
|
|
|
|
|
LRANGE: "RANGE",
|
|
|
|
|
LRETURN: "RETURN",
|
|
|
|
|
LSELECT: "SELECT",
|
|
|
|
|
LSTRUCT: "STRUCT",
|
|
|
|
|
LSWITCH: "SWITCH",
|
|
|
|
|
LTYPE: "TYPE",
|
|
|
|
|
LVAR: "VAR",
|
|
|
|
|
|
|
|
|
|
// LIGNORE is never escaping lexer.next
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func lexname(lex rune) string {
|
|
|
|
|
if s, ok := lexn[lex]; ok {
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("LEX-%d", lex)
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-20 12:53:34 -08:00
|
|
|
func (l *lexer) next() {
|
|
|
|
|
nlsemi := l.nlsemi
|
|
|
|
|
l.nlsemi = false
|
2016-02-25 17:27:10 -08:00
|
|
|
l.prec = 0
|
2016-02-20 12:53:34 -08:00
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
l0:
|
2016-02-20 12:53:34 -08:00
|
|
|
// skip white space
|
2016-02-22 23:07:30 -08:00
|
|
|
c := l.getr()
|
2016-02-20 12:53:34 -08:00
|
|
|
for isSpace(c) {
|
|
|
|
|
if c == '\n' && nlsemi {
|
2015-02-23 17:34:49 -05:00
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: implicit semi\n")
|
|
|
|
|
}
|
cmd/compile: rationalize (lex)?lineno handling
Previously, many error messages inconsistantly used either lexlineno
and lineno. In general this works out okay because they're almost
always the same. The only exceptional case is after lexing a
multi-line raw string literal, where lineno will be the line number of
the opening quote and lexlineno is the line number of the closing
quote.
This CL makes the compiler's error message more consistent:
- Lexer error messages related to invalid byte sequences (i.e., NUL
bytes, bad UTF-8 sequences, and non-initial BOMs) are emitted at
lexlineno (i.e., the source line that contains the invalid byte
sequence).
- All other error messages (notably the parser's "syntax errors") now
use lineno. The minor change from this is that bogus input like:
package `
bogus`
will emit "syntax error: unexpected string literal, expecting name"
error at line 1, instead of line 2.
- Instead of maintaining prevlineno all the time, just record it
when/where actually needed and not already available elsewhere (which
turns out to be just one function).
- Lastly, we remove the legacy "syntax error near ..." fallback in
Yerror, now that the parser always emits more detailed syntax error
messages.
Change-Id: Iaf5f784223d0385fa3a5b09ef2b2ad447feab02f
Reviewed-on: https://go-review.googlesource.com/19925
Reviewed-by: Robert Griesemer <gri@golang.org>
2016-02-25 16:07:04 -08:00
|
|
|
// Insert implicit semicolon on previous line,
|
|
|
|
|
// before the newline character.
|
|
|
|
|
lineno = lexlineno - 1
|
2016-02-20 12:53:34 -08:00
|
|
|
l.tok = ';'
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-02-22 23:07:30 -08:00
|
|
|
c = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-20 12:53:34 -08:00
|
|
|
// start of token
|
|
|
|
|
lineno = lexlineno
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
// identifiers and keywords
|
|
|
|
|
// (for better error messages consume all chars >= utf8.RuneSelf for identifiers)
|
|
|
|
|
if isLetter(c) || c >= utf8.RuneSelf {
|
2016-02-23 22:04:20 -08:00
|
|
|
l.ident(c)
|
|
|
|
|
if l.tok == LIGNORE {
|
2016-02-22 23:07:30 -08:00
|
|
|
goto l0
|
|
|
|
|
}
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-02-24 16:17:49 -08:00
|
|
|
// c < utf8.RuneSelf
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-23 22:04:20 -08:00
|
|
|
var c1 rune
|
|
|
|
|
var op Op
|
2016-02-25 17:27:10 -08:00
|
|
|
var prec OpPrec
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
switch c {
|
|
|
|
|
case EOF:
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2016-02-20 12:53:34 -08:00
|
|
|
// Treat EOF as "end of line" for the purposes
|
|
|
|
|
// of inserting a semicolon.
|
|
|
|
|
if nlsemi {
|
|
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: implicit semi\n")
|
|
|
|
|
}
|
|
|
|
|
l.tok = ';'
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
l.tok = -1
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-23 22:04:20 -08:00
|
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
|
|
|
l.number(c)
|
|
|
|
|
return
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
case '.':
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-09-11 00:03:19 +02:00
|
|
|
if isDigit(c1) {
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2016-02-23 22:04:20 -08:00
|
|
|
l.number('.')
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c1 == '.' {
|
2016-03-11 13:39:20 -05:00
|
|
|
p, err := l.bin.Peek(1)
|
|
|
|
|
if err == nil && p[0] == '.' {
|
|
|
|
|
l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
c = LDDD
|
|
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2015-02-13 14:40:36 -05:00
|
|
|
c1 = '.'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '"':
|
2016-02-23 22:04:20 -08:00
|
|
|
l.stdString()
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '`':
|
2016-02-23 22:04:20 -08:00
|
|
|
l.rawString()
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '\'':
|
2016-02-23 22:04:20 -08:00
|
|
|
l.rune()
|
2016-02-20 12:53:34 -08:00
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '/':
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '*' {
|
2016-02-25 22:10:48 -08:00
|
|
|
c = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
for {
|
2016-02-25 22:10:48 -08:00
|
|
|
if c == '*' {
|
2016-02-22 23:07:30 -08:00
|
|
|
c = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c == '/' {
|
2016-02-25 22:10:48 -08:00
|
|
|
break
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-02-25 22:10:48 -08:00
|
|
|
continue
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
if c == EOF {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("eof in comment")
|
2015-02-13 14:40:36 -05:00
|
|
|
errorexit()
|
|
|
|
|
}
|
2016-02-25 22:10:48 -08:00
|
|
|
c = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-02-25 22:10:48 -08:00
|
|
|
|
|
|
|
|
// A comment containing newlines acts like a newline.
|
|
|
|
|
if lexlineno > lineno && nlsemi {
|
|
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: implicit semi\n")
|
|
|
|
|
}
|
|
|
|
|
l.tok = ';'
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
goto l0
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c1 == '/' {
|
2016-02-22 11:53:20 -08:00
|
|
|
c = l.getlinepragma()
|
2015-02-13 14:40:36 -05:00
|
|
|
for {
|
|
|
|
|
if c == '\n' || c == EOF {
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2015-02-13 14:40:36 -05:00
|
|
|
goto l0
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
c = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-25 17:27:10 -08:00
|
|
|
op = ODIV
|
|
|
|
|
prec = PMUL
|
|
|
|
|
goto binop1
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case ':':
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '=' {
|
2016-02-22 23:07:30 -08:00
|
|
|
c = LCOLAS
|
2015-02-13 14:40:36 -05:00
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '*':
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OMUL
|
|
|
|
|
prec = PMUL
|
|
|
|
|
goto binop
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '%':
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OMOD
|
|
|
|
|
prec = PMUL
|
|
|
|
|
goto binop
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '+':
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OADD
|
|
|
|
|
goto incop
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '-':
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OSUB
|
|
|
|
|
goto incop
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '>':
|
2016-03-01 14:47:26 -08:00
|
|
|
c = LOPER
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '>' {
|
2016-02-25 17:27:10 -08:00
|
|
|
op = ORSH
|
|
|
|
|
prec = PMUL
|
|
|
|
|
goto binop
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-25 17:27:10 -08:00
|
|
|
l.prec = PCMP
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '=' {
|
2016-02-25 17:27:10 -08:00
|
|
|
l.op = OGE
|
2015-02-13 14:40:36 -05:00
|
|
|
goto lx
|
|
|
|
|
}
|
2016-02-25 17:27:10 -08:00
|
|
|
l.op = OGT
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '<':
|
2016-03-01 14:47:26 -08:00
|
|
|
c = LOPER
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '<' {
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OLSH
|
|
|
|
|
prec = PMUL
|
|
|
|
|
goto binop
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c1 == '-' {
|
2016-02-22 23:07:30 -08:00
|
|
|
c = LCOMM
|
2016-02-25 17:27:10 -08:00
|
|
|
// Not a binary operator, but parsed as one
|
|
|
|
|
// so we can give a good error message when used
|
|
|
|
|
// in an expression context.
|
|
|
|
|
l.prec = PCOMM
|
|
|
|
|
l.op = OSEND
|
2015-02-13 14:40:36 -05:00
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-25 17:27:10 -08:00
|
|
|
l.prec = PCMP
|
|
|
|
|
if c1 == '=' {
|
|
|
|
|
l.op = OLE
|
|
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
l.op = OLT
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '=':
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '=' {
|
2016-03-01 14:47:26 -08:00
|
|
|
c = LOPER
|
2016-02-25 17:27:10 -08:00
|
|
|
l.prec = PCMP
|
|
|
|
|
l.op = OEQ
|
2015-02-13 14:40:36 -05:00
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '!':
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '=' {
|
2016-03-01 14:47:26 -08:00
|
|
|
c = LOPER
|
2016-02-25 17:27:10 -08:00
|
|
|
l.prec = PCMP
|
|
|
|
|
l.op = ONE
|
2015-02-13 14:40:36 -05:00
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case '&':
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '&' {
|
2016-03-01 14:47:26 -08:00
|
|
|
c = LOPER
|
2016-02-25 17:27:10 -08:00
|
|
|
l.prec = PANDAND
|
|
|
|
|
l.op = OANDAND
|
2015-02-13 14:40:36 -05:00
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c1 == '^' {
|
2016-03-01 14:47:26 -08:00
|
|
|
c = LOPER
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OANDNOT
|
|
|
|
|
prec = PMUL
|
|
|
|
|
goto binop
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OAND
|
|
|
|
|
prec = PMUL
|
|
|
|
|
goto binop1
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '|':
|
2016-02-22 23:07:30 -08:00
|
|
|
c1 = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c1 == '|' {
|
2016-03-01 14:47:26 -08:00
|
|
|
c = LOPER
|
2016-02-25 17:27:10 -08:00
|
|
|
l.prec = POROR
|
|
|
|
|
l.op = OOROR
|
2015-02-13 14:40:36 -05:00
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OOR
|
|
|
|
|
prec = PADD
|
|
|
|
|
goto binop1
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '^':
|
2016-02-25 17:27:10 -08:00
|
|
|
op = OXOR
|
|
|
|
|
prec = PADD
|
|
|
|
|
goto binop
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
case '(', '[', '{', ',', ';':
|
|
|
|
|
goto lx
|
|
|
|
|
|
2016-02-20 12:53:34 -08:00
|
|
|
case ')', ']', '}':
|
|
|
|
|
l.nlsemi = true
|
|
|
|
|
goto lx
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
default:
|
2016-02-22 23:07:30 -08:00
|
|
|
// anything else is illegal
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("syntax error: illegal character %#U", c)
|
2016-02-22 23:07:30 -08:00
|
|
|
goto l0
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
lx:
|
2015-11-27 16:11:05 -08:00
|
|
|
if Debug['x'] != 0 {
|
2016-02-25 17:27:10 -08:00
|
|
|
if c >= utf8.RuneSelf {
|
2016-03-02 11:30:29 -08:00
|
|
|
fmt.Printf("%v lex: TOKEN %s\n", linestr(lineno), lexname(c))
|
2015-11-27 16:11:05 -08:00
|
|
|
} else {
|
2016-03-02 11:30:29 -08:00
|
|
|
fmt.Printf("%v lex: TOKEN '%c'\n", linestr(lineno), c)
|
2015-02-23 17:34:49 -05:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
l.tok = c
|
2016-02-20 12:53:34 -08:00
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-25 17:27:10 -08:00
|
|
|
incop:
|
|
|
|
|
c1 = l.getr()
|
|
|
|
|
if c1 == c {
|
|
|
|
|
l.nlsemi = true
|
|
|
|
|
l.op = op
|
|
|
|
|
c = LINCOP
|
|
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
prec = PADD
|
|
|
|
|
goto binop1
|
|
|
|
|
|
|
|
|
|
binop:
|
|
|
|
|
c1 = l.getr()
|
|
|
|
|
binop1:
|
|
|
|
|
if c1 != '=' {
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2016-02-25 17:27:10 -08:00
|
|
|
l.op = op
|
|
|
|
|
l.prec = prec
|
|
|
|
|
goto lx
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-20 12:53:34 -08:00
|
|
|
l.op = op
|
2015-02-23 17:34:49 -05:00
|
|
|
if Debug['x'] != 0 {
|
2015-11-27 16:11:05 -08:00
|
|
|
fmt.Printf("lex: TOKEN ASOP %s=\n", goopnames[op])
|
2015-02-23 17:34:49 -05:00
|
|
|
}
|
2016-02-20 12:53:34 -08:00
|
|
|
l.tok = LASOP
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *lexer) ident(c rune) {
|
|
|
|
|
cp := &lexbuf
|
|
|
|
|
cp.Reset()
|
|
|
|
|
|
|
|
|
|
// accelerate common case (7bit ASCII)
|
|
|
|
|
for isLetter(c) || isDigit(c) {
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// general case
|
|
|
|
|
for {
|
|
|
|
|
if c >= utf8.RuneSelf {
|
2016-08-16 12:55:17 -07:00
|
|
|
if unicode.IsLetter(c) || c == '_' || unicode.IsDigit(c) {
|
2016-02-23 22:04:20 -08:00
|
|
|
if cp.Len() == 0 && unicode.IsDigit(c) {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("identifier cannot begin with digit %#U", c)
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
} else {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("invalid identifier character %#U", c)
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
cp.WriteRune(c)
|
|
|
|
|
} else if isLetter(c) || isDigit(c) {
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
c = l.getr()
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
cp = nil
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-26 01:37:28 -08:00
|
|
|
name := lexbuf.Bytes()
|
|
|
|
|
|
|
|
|
|
if len(name) >= 2 {
|
|
|
|
|
if tok, ok := keywords[string(name)]; ok {
|
|
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: %s\n", lexname(tok))
|
|
|
|
|
}
|
|
|
|
|
switch tok {
|
|
|
|
|
case LBREAK, LCONTINUE, LFALL, LRETURN:
|
|
|
|
|
l.nlsemi = true
|
|
|
|
|
}
|
|
|
|
|
l.tok = tok
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 15:45:10 +10:00
|
|
|
s := lookupBytes(name)
|
2016-02-23 22:04:20 -08:00
|
|
|
if Debug['x'] != 0 {
|
2016-09-09 21:08:46 -07:00
|
|
|
fmt.Printf("lex: ident %v\n", s)
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
l.sym_ = s
|
2016-02-26 01:37:28 -08:00
|
|
|
l.nlsemi = true
|
|
|
|
|
l.tok = LNAME
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var keywords = map[string]int32{
|
|
|
|
|
"break": LBREAK,
|
|
|
|
|
"case": LCASE,
|
|
|
|
|
"chan": LCHAN,
|
|
|
|
|
"const": LCONST,
|
|
|
|
|
"continue": LCONTINUE,
|
|
|
|
|
"default": LDEFAULT,
|
|
|
|
|
"defer": LDEFER,
|
|
|
|
|
"else": LELSE,
|
|
|
|
|
"fallthrough": LFALL,
|
|
|
|
|
"for": LFOR,
|
|
|
|
|
"func": LFUNC,
|
|
|
|
|
"go": LGO,
|
|
|
|
|
"goto": LGOTO,
|
|
|
|
|
"if": LIF,
|
|
|
|
|
"import": LIMPORT,
|
|
|
|
|
"interface": LINTERFACE,
|
|
|
|
|
"map": LMAP,
|
|
|
|
|
"package": LPACKAGE,
|
|
|
|
|
"range": LRANGE,
|
|
|
|
|
"return": LRETURN,
|
|
|
|
|
"select": LSELECT,
|
|
|
|
|
"struct": LSTRUCT,
|
|
|
|
|
"switch": LSWITCH,
|
|
|
|
|
"type": LTYPE,
|
|
|
|
|
"var": LVAR,
|
|
|
|
|
|
|
|
|
|
// 💩
|
|
|
|
|
"notwithstanding": LIGNORE,
|
|
|
|
|
"thetruthofthematter": LIGNORE,
|
|
|
|
|
"despiteallobjections": LIGNORE,
|
|
|
|
|
"whereas": LIGNORE,
|
|
|
|
|
"insofaras": LIGNORE,
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-23 22:04:20 -08:00
|
|
|
func (l *lexer) number(c rune) {
|
|
|
|
|
cp := &lexbuf
|
|
|
|
|
cp.Reset()
|
|
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
// parse mantissa before decimal point or exponent
|
|
|
|
|
isInt := false
|
|
|
|
|
malformedOctal := false
|
2016-02-23 22:04:20 -08:00
|
|
|
if c != '.' {
|
|
|
|
|
if c != '0' {
|
2016-02-24 12:53:28 -08:00
|
|
|
// decimal or float
|
2016-02-23 22:04:20 -08:00
|
|
|
for isDigit(c) {
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
|
|
|
|
}
|
2016-02-24 12:53:28 -08:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// c == 0
|
|
|
|
|
cp.WriteByte('0')
|
|
|
|
|
c = l.getr()
|
|
|
|
|
if c == 'x' || c == 'X' {
|
|
|
|
|
isInt = true // must be int
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
|
|
|
|
for isDigit(c) || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
|
|
|
|
}
|
|
|
|
|
if lexbuf.Len() == 2 {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("malformed hex constant")
|
2016-02-24 12:53:28 -08:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// decimal 0, octal, or float
|
|
|
|
|
for isDigit(c) {
|
|
|
|
|
if c > '7' {
|
|
|
|
|
malformedOctal = true
|
|
|
|
|
}
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
|
|
|
|
}
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-02-24 12:53:28 -08:00
|
|
|
}
|
2016-02-23 22:04:20 -08:00
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
// unless we have a hex number, parse fractional part or exponent, if any
|
2016-03-24 20:57:53 +11:00
|
|
|
var str string
|
2016-02-24 12:53:28 -08:00
|
|
|
if !isInt {
|
|
|
|
|
isInt = true // assume int unless proven otherwise
|
|
|
|
|
|
|
|
|
|
// fraction
|
|
|
|
|
if c == '.' {
|
|
|
|
|
isInt = false
|
|
|
|
|
cp.WriteByte('.')
|
|
|
|
|
c = l.getr()
|
|
|
|
|
for isDigit(c) {
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// exponent
|
2016-08-16 12:55:17 -07:00
|
|
|
if c == 'e' || c == 'E' {
|
2016-02-24 12:53:28 -08:00
|
|
|
isInt = false
|
2016-02-23 22:04:20 -08:00
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
2016-02-24 12:53:28 -08:00
|
|
|
if c == '+' || c == '-' {
|
2016-02-23 22:04:20 -08:00
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
|
|
|
|
}
|
2016-02-24 12:53:28 -08:00
|
|
|
if !isDigit(c) {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("malformed floating point constant exponent")
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
2016-02-24 12:53:28 -08:00
|
|
|
for isDigit(c) {
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
c = l.getr()
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
// imaginary constant
|
|
|
|
|
if c == 'i' {
|
|
|
|
|
str = lexbuf.String()
|
|
|
|
|
x := new(Mpcplx)
|
2016-03-20 13:55:42 -07:00
|
|
|
x.Real.SetFloat64(0.0)
|
|
|
|
|
x.Imag.SetString(str)
|
2016-02-24 12:53:28 -08:00
|
|
|
if x.Imag.Val.IsInf() {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("overflow in imaginary constant")
|
2016-03-20 13:55:42 -07:00
|
|
|
x.Imag.SetFloat64(0.0)
|
2016-02-24 12:53:28 -08:00
|
|
|
}
|
|
|
|
|
l.val.U = x
|
2016-02-23 22:04:20 -08:00
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: imaginary literal\n")
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
2016-02-24 12:53:28 -08:00
|
|
|
goto done
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
2015-02-23 17:34:49 -05:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
if isInt {
|
|
|
|
|
if malformedOctal {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("malformed octal constant")
|
2016-02-24 12:53:28 -08:00
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
str = lexbuf.String()
|
|
|
|
|
x := new(Mpint)
|
2016-03-20 13:55:42 -07:00
|
|
|
x.SetString(str)
|
2016-02-24 12:53:28 -08:00
|
|
|
if x.Ovf {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("overflow in constant")
|
2016-03-20 13:55:42 -07:00
|
|
|
x.SetInt64(0)
|
2016-02-24 12:53:28 -08:00
|
|
|
}
|
|
|
|
|
l.val.U = x
|
2016-02-23 22:04:20 -08:00
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: integer literal\n")
|
|
|
|
|
}
|
2016-02-23 22:04:20 -08:00
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
} else { // float
|
2016-02-23 22:04:20 -08:00
|
|
|
|
2016-02-24 12:53:28 -08:00
|
|
|
str = lexbuf.String()
|
|
|
|
|
x := newMpflt()
|
2016-03-20 13:55:42 -07:00
|
|
|
x.SetString(str)
|
2016-02-24 12:53:28 -08:00
|
|
|
if x.Val.IsInf() {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("overflow in float constant")
|
2016-03-20 13:55:42 -07:00
|
|
|
x.SetFloat64(0.0)
|
2016-02-24 12:53:28 -08:00
|
|
|
}
|
|
|
|
|
l.val.U = x
|
|
|
|
|
|
|
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: floating literal\n")
|
|
|
|
|
}
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done:
|
2016-04-26 10:55:32 -07:00
|
|
|
litbuf = "" // lazily initialized in (*parser).syntax_error
|
2016-02-20 12:53:34 -08:00
|
|
|
l.nlsemi = true
|
|
|
|
|
l.tok = LLITERAL
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *lexer) stdString() {
|
|
|
|
|
lexbuf.Reset()
|
|
|
|
|
lexbuf.WriteString(`"<string>"`)
|
|
|
|
|
|
|
|
|
|
cp := &strbuf
|
|
|
|
|
cp.Reset()
|
|
|
|
|
|
2016-02-24 11:49:31 -08:00
|
|
|
for {
|
|
|
|
|
r, b, ok := l.onechar('"')
|
|
|
|
|
if !ok {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if r == 0 {
|
|
|
|
|
cp.WriteByte(b)
|
2016-02-23 22:04:20 -08:00
|
|
|
} else {
|
2016-02-24 11:49:31 -08:00
|
|
|
cp.WriteRune(r)
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l.val.U = internString(cp.Bytes())
|
|
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: string literal\n")
|
|
|
|
|
}
|
|
|
|
|
litbuf = "string literal"
|
|
|
|
|
l.nlsemi = true
|
|
|
|
|
l.tok = LLITERAL
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *lexer) rawString() {
|
|
|
|
|
lexbuf.Reset()
|
|
|
|
|
lexbuf.WriteString("`<string>`")
|
|
|
|
|
|
|
|
|
|
cp := &strbuf
|
|
|
|
|
cp.Reset()
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
c := l.getr()
|
|
|
|
|
if c == '\r' {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if c == EOF {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("eof in string")
|
2016-02-23 22:04:20 -08:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if c == '`' {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
cp.WriteRune(c)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-20 12:53:34 -08:00
|
|
|
l.val.U = internString(cp.Bytes())
|
2015-02-23 17:34:49 -05:00
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: string literal\n")
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
litbuf = "string literal"
|
2016-02-20 12:53:34 -08:00
|
|
|
l.nlsemi = true
|
|
|
|
|
l.tok = LLITERAL
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-23 22:04:20 -08:00
|
|
|
func (l *lexer) rune() {
|
2016-02-24 11:49:31 -08:00
|
|
|
r, b, ok := l.onechar('\'')
|
|
|
|
|
if !ok {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("empty character literal or unescaped ' in character literal")
|
2016-02-24 11:49:31 -08:00
|
|
|
r = '\''
|
|
|
|
|
}
|
|
|
|
|
if r == 0 {
|
|
|
|
|
r = rune(b)
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-24 11:49:31 -08:00
|
|
|
if c := l.getr(); c != '\'' {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("missing '")
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2016-02-23 22:04:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x := new(Mpint)
|
|
|
|
|
l.val.U = x
|
2016-03-20 13:55:42 -07:00
|
|
|
x.SetInt64(int64(r))
|
2016-02-23 22:04:20 -08:00
|
|
|
x.Rune = true
|
|
|
|
|
if Debug['x'] != 0 {
|
|
|
|
|
fmt.Printf("lex: codepoint literal\n")
|
|
|
|
|
}
|
|
|
|
|
litbuf = "rune literal"
|
|
|
|
|
l.nlsemi = true
|
|
|
|
|
l.tok = LLITERAL
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 16:03:26 -05:00
|
|
|
var internedStrings = map[string]string{}
|
|
|
|
|
|
|
|
|
|
func internString(b []byte) string {
|
|
|
|
|
s, ok := internedStrings[string(b)] // string(b) here doesn't allocate
|
2016-02-22 11:53:20 -08:00
|
|
|
if !ok {
|
|
|
|
|
s = string(b)
|
|
|
|
|
internedStrings[s] = s
|
2015-03-02 16:03:26 -05:00
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-22 09:51:12 +09:00
|
|
|
// read and interpret syntax that looks like
|
|
|
|
|
// //line parse.y:15
|
|
|
|
|
// as a discontinuity in sequential line numbers.
|
|
|
|
|
// the next line of input comes from parse.y:15
|
2016-02-22 23:07:30 -08:00
|
|
|
func (l *lexer) getlinepragma() rune {
|
|
|
|
|
c := l.getr()
|
2015-06-08 10:17:38 -07:00
|
|
|
if c == 'g' { // check for //go: directive
|
2015-03-02 12:35:15 -05:00
|
|
|
cp := &lexbuf
|
|
|
|
|
cp.Reset()
|
|
|
|
|
cp.WriteByte('g') // already read
|
|
|
|
|
for {
|
2016-02-22 23:07:30 -08:00
|
|
|
c = l.getr()
|
2015-03-02 12:35:15 -05:00
|
|
|
if c == EOF || c >= utf8.RuneSelf {
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
if c == '\n' {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
}
|
|
|
|
|
cp = nil
|
|
|
|
|
|
2015-07-17 22:29:44 -07:00
|
|
|
text := strings.TrimSuffix(lexbuf.String(), "\r")
|
2015-03-08 22:41:48 -04:00
|
|
|
|
|
|
|
|
if strings.HasPrefix(text, "go:cgo_") {
|
2016-04-07 08:01:47 +02:00
|
|
|
pragcgobuf += pragcgo(text)
|
2015-03-02 12:35:15 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-08 10:17:38 -07:00
|
|
|
verb := text
|
|
|
|
|
if i := strings.Index(text, " "); i >= 0 {
|
2015-03-02 12:35:15 -05:00
|
|
|
verb = verb[:i]
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-08 10:17:38 -07:00
|
|
|
switch verb {
|
|
|
|
|
case "go:linkname":
|
2015-09-11 00:03:19 +02:00
|
|
|
if !imported_unsafe {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("//go:linkname only allowed in Go files that import \"unsafe\"")
|
2015-03-02 12:35:15 -05:00
|
|
|
}
|
2015-06-08 10:17:38 -07:00
|
|
|
f := strings.Fields(text)
|
2015-03-02 12:35:15 -05:00
|
|
|
if len(f) != 3 {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("usage: //go:linkname localname linkname")
|
2015-06-08 10:17:38 -07:00
|
|
|
break
|
2015-03-02 12:35:15 -05:00
|
|
|
}
|
2016-09-15 15:45:10 +10:00
|
|
|
lookup(f[1]).Linkname = f[2]
|
2016-08-30 14:48:01 -07:00
|
|
|
default:
|
2016-09-15 15:45:10 +10:00
|
|
|
l.pragma |= pragmaValue(verb)
|
2015-11-02 16:45:07 -05:00
|
|
|
}
|
2015-03-02 12:35:15 -05:00
|
|
|
return c
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-06-08 10:17:38 -07:00
|
|
|
|
|
|
|
|
// check for //line directive
|
2015-02-13 14:40:36 -05:00
|
|
|
if c != 'l' {
|
2015-03-02 12:35:15 -05:00
|
|
|
return c
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
for i := 1; i < 5; i++ {
|
2016-02-22 23:07:30 -08:00
|
|
|
c = l.getr()
|
|
|
|
|
if c != rune("line "[i]) {
|
2015-03-02 12:35:15 -05:00
|
|
|
return c
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 12:35:15 -05:00
|
|
|
cp := &lexbuf
|
2015-02-13 14:40:36 -05:00
|
|
|
cp.Reset()
|
2015-03-02 12:35:15 -05:00
|
|
|
linep := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
for {
|
2016-02-22 23:07:30 -08:00
|
|
|
c = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
if c == EOF {
|
2015-03-02 12:35:15 -05:00
|
|
|
return c
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
if c == '\n' {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if c == ' ' {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if c == ':' {
|
|
|
|
|
linep = cp.Len() + 1
|
|
|
|
|
}
|
|
|
|
|
cp.WriteByte(byte(c))
|
|
|
|
|
}
|
|
|
|
|
cp = nil
|
|
|
|
|
|
|
|
|
|
if linep == 0 {
|
2015-03-02 12:35:15 -05:00
|
|
|
return c
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-07-17 22:29:44 -07:00
|
|
|
text := strings.TrimSuffix(lexbuf.String(), "\r")
|
2015-06-08 10:17:38 -07:00
|
|
|
n, err := strconv.Atoi(text[linep:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c // todo: make this an error instead? it is almost certainly a bug.
|
|
|
|
|
}
|
|
|
|
|
if n > 1e8 {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("line number out of range")
|
2015-06-08 10:17:38 -07:00
|
|
|
errorexit()
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
if n <= 0 {
|
2015-03-02 12:35:15 -05:00
|
|
|
return c
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-06-08 10:17:38 -07:00
|
|
|
linehistupdate(text[:linep-1], n)
|
2015-02-13 14:40:36 -05:00
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-07 08:01:47 +02:00
|
|
|
func pragcgo(text string) string {
|
|
|
|
|
f := pragmaFields(text)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-07 08:01:47 +02:00
|
|
|
verb := f[0][3:] // skip "go:"
|
|
|
|
|
switch verb {
|
|
|
|
|
case "cgo_export_static", "cgo_export_dynamic":
|
|
|
|
|
switch {
|
|
|
|
|
case len(f) == 2 && !isQuoted(f[1]):
|
|
|
|
|
local := plan9quote(f[1])
|
|
|
|
|
return fmt.Sprintln(verb, local)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-07 08:01:47 +02:00
|
|
|
case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
|
|
|
|
|
local := plan9quote(f[1])
|
|
|
|
|
remote := plan9quote(f[2])
|
|
|
|
|
return fmt.Sprintln(verb, local, remote)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-07 08:01:47 +02:00
|
|
|
default:
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror(`usage: //go:%s local [remote]`, verb)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-04-07 08:01:47 +02:00
|
|
|
case "cgo_import_dynamic":
|
|
|
|
|
switch {
|
|
|
|
|
case len(f) == 2 && !isQuoted(f[1]):
|
|
|
|
|
local := plan9quote(f[1])
|
|
|
|
|
return fmt.Sprintln(verb, local)
|
|
|
|
|
|
|
|
|
|
case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]):
|
|
|
|
|
local := plan9quote(f[1])
|
|
|
|
|
remote := plan9quote(f[2])
|
|
|
|
|
return fmt.Sprintln(verb, local, remote)
|
|
|
|
|
|
|
|
|
|
case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]):
|
|
|
|
|
local := plan9quote(f[1])
|
|
|
|
|
remote := plan9quote(f[2])
|
|
|
|
|
library := plan9quote(strings.Trim(f[3], `"`))
|
|
|
|
|
return fmt.Sprintln(verb, local, remote, library)
|
|
|
|
|
|
|
|
|
|
default:
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror(`usage: //go:cgo_import_dynamic local [remote ["library"]]`)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-04-07 08:01:47 +02:00
|
|
|
case "cgo_import_static":
|
|
|
|
|
switch {
|
|
|
|
|
case len(f) == 2 && !isQuoted(f[1]):
|
|
|
|
|
local := plan9quote(f[1])
|
|
|
|
|
return fmt.Sprintln(verb, local)
|
|
|
|
|
|
|
|
|
|
default:
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror(`usage: //go:cgo_import_static local`)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-04-07 08:01:47 +02:00
|
|
|
case "cgo_dynamic_linker":
|
|
|
|
|
switch {
|
|
|
|
|
case len(f) == 2 && isQuoted(f[1]):
|
|
|
|
|
path := plan9quote(strings.Trim(f[1], `"`))
|
|
|
|
|
return fmt.Sprintln(verb, path)
|
|
|
|
|
|
|
|
|
|
default:
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror(`usage: //go:cgo_dynamic_linker "path"`)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-04-07 08:01:47 +02:00
|
|
|
case "cgo_ldflag":
|
|
|
|
|
switch {
|
|
|
|
|
case len(f) == 2 && isQuoted(f[1]):
|
|
|
|
|
arg := plan9quote(strings.Trim(f[1], `"`))
|
|
|
|
|
return fmt.Sprintln(verb, arg)
|
|
|
|
|
|
|
|
|
|
default:
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror(`usage: //go:cgo_ldflag "arg"`)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2016-04-07 08:01:47 +02:00
|
|
|
return ""
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-04-07 08:01:47 +02:00
|
|
|
// pragmaFields is similar to strings.FieldsFunc(s, isSpace)
|
|
|
|
|
// but does not split when inside double quoted regions and always
|
|
|
|
|
// splits before the start and after the end of a double quoted region.
|
|
|
|
|
// pragmaFields does not recognize escaped quotes. If a quote in s is not
|
|
|
|
|
// closed the part after the opening quote will not be returned as a field.
|
|
|
|
|
func pragmaFields(s string) []string {
|
|
|
|
|
var a []string
|
|
|
|
|
inQuote := false
|
|
|
|
|
fieldStart := -1 // Set to -1 when looking for start of field.
|
|
|
|
|
for i, c := range s {
|
|
|
|
|
switch {
|
|
|
|
|
case c == '"':
|
|
|
|
|
if inQuote {
|
|
|
|
|
inQuote = false
|
|
|
|
|
a = append(a, s[fieldStart:i+1])
|
|
|
|
|
fieldStart = -1
|
|
|
|
|
} else {
|
|
|
|
|
inQuote = true
|
|
|
|
|
if fieldStart >= 0 {
|
|
|
|
|
a = append(a, s[fieldStart:i])
|
|
|
|
|
}
|
|
|
|
|
fieldStart = i
|
|
|
|
|
}
|
|
|
|
|
case !inQuote && isSpace(c):
|
|
|
|
|
if fieldStart >= 0 {
|
|
|
|
|
a = append(a, s[fieldStart:i])
|
|
|
|
|
fieldStart = -1
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
if fieldStart == -1 {
|
|
|
|
|
fieldStart = i
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2016-04-07 08:01:47 +02:00
|
|
|
if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string.
|
|
|
|
|
a = append(a, s[fieldStart:])
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-04-07 08:01:47 +02:00
|
|
|
return a
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
func (l *lexer) getr() rune {
|
2016-03-11 13:39:20 -05:00
|
|
|
redo:
|
2016-03-11 13:55:53 -08:00
|
|
|
l.prevlineno = lexlineno
|
2016-03-11 13:39:20 -05:00
|
|
|
r, w, err := l.bin.ReadRune()
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err != io.EOF {
|
|
|
|
|
Fatalf("io error: %v", err)
|
2016-02-22 23:07:30 -08:00
|
|
|
}
|
2016-03-11 13:39:20 -05:00
|
|
|
return -1
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-03-11 13:39:20 -05:00
|
|
|
switch r {
|
|
|
|
|
case 0:
|
|
|
|
|
yyerrorl(lexlineno, "illegal NUL byte")
|
|
|
|
|
case '\n':
|
2016-08-16 12:55:17 -07:00
|
|
|
lexlineno++
|
2016-03-11 13:39:20 -05:00
|
|
|
case utf8.RuneError:
|
|
|
|
|
if w == 1 {
|
|
|
|
|
yyerrorl(lexlineno, "illegal UTF-8 sequence")
|
|
|
|
|
}
|
|
|
|
|
case BOM:
|
2016-03-02 11:01:25 -08:00
|
|
|
yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file")
|
2016-02-22 23:07:30 -08:00
|
|
|
goto redo
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
return r
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-03-11 13:55:53 -08:00
|
|
|
func (l *lexer) ungetr() {
|
2016-03-11 13:39:20 -05:00
|
|
|
l.bin.UnreadRune()
|
2016-03-11 13:55:53 -08:00
|
|
|
lexlineno = l.prevlineno
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-24 11:49:31 -08:00
|
|
|
// onechar lexes a single character within a rune or interpreted string literal,
|
|
|
|
|
// handling escape sequences as necessary.
|
|
|
|
|
func (l *lexer) onechar(quote rune) (r rune, b byte, ok bool) {
|
2016-02-22 23:07:30 -08:00
|
|
|
c := l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
switch c {
|
|
|
|
|
case EOF:
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("eof in string")
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2016-02-24 11:49:31 -08:00
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '\n':
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("newline in string")
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2016-02-24 11:49:31 -08:00
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
|
break
|
|
|
|
|
|
2016-02-24 11:49:31 -08:00
|
|
|
case quote:
|
|
|
|
|
return
|
|
|
|
|
|
2015-02-13 14:40:36 -05:00
|
|
|
default:
|
2016-02-24 11:49:31 -08:00
|
|
|
return c, 0, true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-22 23:07:30 -08:00
|
|
|
c = l.getr()
|
2015-02-13 14:40:36 -05:00
|
|
|
switch c {
|
|
|
|
|
case 'x':
|
2016-02-24 11:49:31 -08:00
|
|
|
return 0, byte(l.hexchar(2)), true
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case 'u':
|
2016-02-24 11:49:31 -08:00
|
|
|
return l.unichar(4), 0, true
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case 'U':
|
2016-02-24 11:49:31 -08:00
|
|
|
return l.unichar(8), 0, true
|
|
|
|
|
|
|
|
|
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
|
|
|
|
x := c - '0'
|
2015-03-02 12:35:15 -05:00
|
|
|
for i := 2; i > 0; i-- {
|
2016-02-22 23:07:30 -08:00
|
|
|
c = l.getr()
|
2015-03-02 12:35:15 -05:00
|
|
|
if c >= '0' && c <= '7' {
|
2016-02-24 11:49:31 -08:00
|
|
|
x = x*8 + c - '0'
|
2015-03-02 12:35:15 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("non-octal character in escape sequence: %c", c)
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2015-03-02 12:35:15 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-22 11:53:20 -08:00
|
|
|
if x > 255 {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("octal escape value > 255: %d", x)
|
2015-03-02 12:35:15 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-24 11:49:31 -08:00
|
|
|
return 0, byte(x), true
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case 'a':
|
|
|
|
|
c = '\a'
|
|
|
|
|
case 'b':
|
|
|
|
|
c = '\b'
|
|
|
|
|
case 'f':
|
|
|
|
|
c = '\f'
|
|
|
|
|
case 'n':
|
|
|
|
|
c = '\n'
|
|
|
|
|
case 'r':
|
|
|
|
|
c = '\r'
|
|
|
|
|
case 't':
|
|
|
|
|
c = '\t'
|
|
|
|
|
case 'v':
|
|
|
|
|
c = '\v'
|
|
|
|
|
case '\\':
|
|
|
|
|
c = '\\'
|
|
|
|
|
|
|
|
|
|
default:
|
2016-02-24 11:49:31 -08:00
|
|
|
if c != quote {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("unknown escape sequence: %c", c)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 11:49:31 -08:00
|
|
|
return c, 0, true
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-24 11:49:31 -08:00
|
|
|
func (l *lexer) unichar(n int) rune {
|
|
|
|
|
x := l.hexchar(n)
|
|
|
|
|
if x > utf8.MaxRune || 0xd800 <= x && x < 0xe000 {
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("invalid Unicode code point in escape sequence: %#x", x)
|
2016-02-22 11:53:20 -08:00
|
|
|
x = utf8.RuneError
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2016-02-24 11:49:31 -08:00
|
|
|
return rune(x)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *lexer) hexchar(n int) uint32 {
|
|
|
|
|
var x uint32
|
|
|
|
|
|
|
|
|
|
for ; n > 0; n-- {
|
|
|
|
|
var d uint32
|
|
|
|
|
switch c := l.getr(); {
|
|
|
|
|
case isDigit(c):
|
|
|
|
|
d = uint32(c - '0')
|
|
|
|
|
case 'a' <= c && c <= 'f':
|
|
|
|
|
d = uint32(c - 'a' + 10)
|
|
|
|
|
case 'A' <= c && c <= 'F':
|
|
|
|
|
d = uint32(c - 'A' + 10)
|
|
|
|
|
default:
|
2016-09-15 15:45:10 +10:00
|
|
|
yyerror("non-hex character in escape sequence: %c", c)
|
2016-03-11 13:55:53 -08:00
|
|
|
l.ungetr()
|
2016-02-24 11:49:31 -08:00
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
x = x*16 + d
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2016-02-24 11:49:31 -08:00
|
|
|
return x
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|