github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/cmd/compile/internal/gc/lex.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gc 6 7 import ( 8 "cmd/compile/internal/syntax" 9 "cmd/internal/obj" 10 "cmd/internal/src" 11 "fmt" 12 "strings" 13 ) 14 15 // lineno is the source position at the start of the most recently lexed token. 16 // TODO(gri) rename and eventually remove 17 var lineno src.XPos 18 19 func makePos(base *src.PosBase, line, col uint) src.XPos { 20 return Ctxt.PosTable.XPos(src.MakePos(base, line, col)) 21 } 22 23 func isSpace(c rune) bool { 24 return c == ' ' || c == '\t' || c == '\n' || c == '\r' 25 } 26 27 func isQuoted(s string) bool { 28 return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' 29 } 30 31 func plan9quote(s string) string { 32 if s == "" { 33 return "''" 34 } 35 for _, c := range s { 36 if c <= ' ' || c == '\'' { 37 return "'" + strings.Replace(s, "'", "''", -1) + "'" 38 } 39 } 40 return s 41 } 42 43 const ( 44 // Func pragmas. 45 Nointerface syntax.Pragma = 1 << iota 46 Noescape // func parameters don't escape 47 Norace // func must not have race detector annotations 48 Nosplit // func should not execute on separate stack 49 Noinline // func should not be inlined 50 CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all 51 UintptrEscapes // pointers converted to uintptr escape 52 53 // Runtime-only func pragmas. 54 // See ../../../../runtime/README.md for detailed descriptions. 55 Systemstack // func must run on system stack 56 Nowritebarrier // emit compiler error instead of write barrier 57 Nowritebarrierrec // error on write barrier in this or recursive callees 58 Yeswritebarrierrec // cancels Nowritebarrierrec in this function and callees 59 60 // Runtime-only type pragmas 61 NotInHeap // values of this type must not be heap allocated 62 ) 63 64 func pragmaValue(verb string) syntax.Pragma { 65 switch verb { 66 case "go:nointerface": 67 if obj.Fieldtrack_enabled != 0 { 68 return Nointerface 69 } 70 case "go:noescape": 71 return Noescape 72 case "go:norace": 73 return Norace 74 case "go:nosplit": 75 return Nosplit 76 case "go:noinline": 77 return Noinline 78 case "go:systemstack": 79 return Systemstack 80 case "go:nowritebarrier": 81 return Nowritebarrier 82 case "go:nowritebarrierrec": 83 return Nowritebarrierrec | Nowritebarrier // implies Nowritebarrier 84 case "go:yeswritebarrierrec": 85 return Yeswritebarrierrec 86 case "go:cgo_unsafe_args": 87 return CgoUnsafeArgs 88 case "go:uintptrescapes": 89 // For the next function declared in the file 90 // any uintptr arguments may be pointer values 91 // converted to uintptr. This directive 92 // ensures that the referenced allocated 93 // object, if any, is retained and not moved 94 // until the call completes, even though from 95 // the types alone it would appear that the 96 // object is no longer needed during the 97 // call. The conversion to uintptr must appear 98 // in the argument list. 99 // Used in syscall/dll_windows.go. 100 return UintptrEscapes 101 case "go:notinheap": 102 return NotInHeap 103 } 104 return 0 105 } 106 107 var internedStrings = map[string]string{} 108 109 func internString(b []byte) string { 110 s, ok := internedStrings[string(b)] // string(b) here doesn't allocate 111 if !ok { 112 s = string(b) 113 internedStrings[s] = s 114 } 115 return s 116 } 117 118 // pragcgo is called concurrently if files are parsed concurrently. 119 func (p *noder) pragcgo(pos src.Pos, text string) string { 120 f := pragmaFields(text) 121 122 verb := f[0][3:] // skip "go:" 123 switch verb { 124 case "cgo_export_static", "cgo_export_dynamic": 125 switch { 126 case len(f) == 2 && !isQuoted(f[1]): 127 local := plan9quote(f[1]) 128 return fmt.Sprintln(verb, local) 129 130 case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): 131 local := plan9quote(f[1]) 132 remote := plan9quote(f[2]) 133 return fmt.Sprintln(verb, local, remote) 134 135 default: 136 p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf(`usage: //go:%s local [remote]`, verb)}) 137 } 138 case "cgo_import_dynamic": 139 switch { 140 case len(f) == 2 && !isQuoted(f[1]): 141 local := plan9quote(f[1]) 142 return fmt.Sprintln(verb, local) 143 144 case len(f) == 3 && !isQuoted(f[1]) && !isQuoted(f[2]): 145 local := plan9quote(f[1]) 146 remote := plan9quote(f[2]) 147 return fmt.Sprintln(verb, local, remote) 148 149 case len(f) == 4 && !isQuoted(f[1]) && !isQuoted(f[2]) && isQuoted(f[3]): 150 local := plan9quote(f[1]) 151 remote := plan9quote(f[2]) 152 library := plan9quote(strings.Trim(f[3], `"`)) 153 return fmt.Sprintln(verb, local, remote, library) 154 155 default: 156 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_dynamic local [remote ["library"]]`}) 157 } 158 case "cgo_import_static": 159 switch { 160 case len(f) == 2 && !isQuoted(f[1]): 161 local := plan9quote(f[1]) 162 return fmt.Sprintln(verb, local) 163 164 default: 165 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_import_static local`}) 166 } 167 case "cgo_dynamic_linker": 168 switch { 169 case len(f) == 2 && isQuoted(f[1]): 170 path := plan9quote(strings.Trim(f[1], `"`)) 171 return fmt.Sprintln(verb, path) 172 173 default: 174 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_dynamic_linker "path"`}) 175 } 176 case "cgo_ldflag": 177 switch { 178 case len(f) == 2 && isQuoted(f[1]): 179 arg := plan9quote(strings.Trim(f[1], `"`)) 180 return fmt.Sprintln(verb, arg) 181 182 default: 183 p.error(syntax.Error{Pos: pos, Msg: `usage: //go:cgo_ldflag "arg"`}) 184 } 185 } 186 return "" 187 } 188 189 // pragmaFields is similar to strings.FieldsFunc(s, isSpace) 190 // but does not split when inside double quoted regions and always 191 // splits before the start and after the end of a double quoted region. 192 // pragmaFields does not recognize escaped quotes. If a quote in s is not 193 // closed the part after the opening quote will not be returned as a field. 194 func pragmaFields(s string) []string { 195 var a []string 196 inQuote := false 197 fieldStart := -1 // Set to -1 when looking for start of field. 198 for i, c := range s { 199 switch { 200 case c == '"': 201 if inQuote { 202 inQuote = false 203 a = append(a, s[fieldStart:i+1]) 204 fieldStart = -1 205 } else { 206 inQuote = true 207 if fieldStart >= 0 { 208 a = append(a, s[fieldStart:i]) 209 } 210 fieldStart = i 211 } 212 case !inQuote && isSpace(c): 213 if fieldStart >= 0 { 214 a = append(a, s[fieldStart:i]) 215 fieldStart = -1 216 } 217 default: 218 if fieldStart == -1 { 219 fieldStart = i 220 } 221 } 222 } 223 if !inQuote && fieldStart >= 0 { // Last field might end at the end of the string. 224 a = append(a, s[fieldStart:]) 225 } 226 return a 227 }