~john-koepi/ubuntu/trusty/golang/default

« back to all changes in this revision

Viewing changes to src/pkg/exp/template/lex.go

  • Committer: Bazaar Package Importer
  • Author(s): Ondřej Surý
  • Date: 2011-08-03 17:04:59 UTC
  • mfrom: (14.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20110803170459-wzd99m3567y80ila
Tags: 1:59-1
* Imported Upstream version 59
* Refresh patches to a new release
* Fix FTBFS on ARM (Closes: #634270)
* Update version.bash to work with Debian packaging and not hg
  repository

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2011 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 template
 
6
 
 
7
import (
 
8
        "fmt"
 
9
        "strings"
 
10
        "unicode"
 
11
        "utf8"
 
12
)
 
13
 
 
14
// item represents a token or text string returned from the scanner.
 
15
type item struct {
 
16
        typ itemType
 
17
        val string
 
18
}
 
19
 
 
20
func (i item) String() string {
 
21
        switch {
 
22
        case i.typ == itemEOF:
 
23
                return "EOF"
 
24
        case i.typ == itemError:
 
25
                return i.val
 
26
        case i.typ > itemKeyword:
 
27
                return fmt.Sprintf("<%s>", i.val)
 
28
        case len(i.val) > 10:
 
29
                return fmt.Sprintf("%.10q...", i.val)
 
30
        }
 
31
        return fmt.Sprintf("%q", i.val)
 
32
}
 
33
 
 
34
// itemType identifies the type of lex items.
 
35
type itemType int
 
36
 
 
37
const (
 
38
        itemError   itemType = iota // error occurred; value is text of error
 
39
        itemBool                    // boolean constant
 
40
        itemComplex                 // complex constant (1+2i); imaginary is just a number
 
41
        itemEOF
 
42
        itemField      // alphanumeric identifier, starting with '.', possibly chained ('.x.y')
 
43
        itemIdentifier // alphanumeric identifier
 
44
        itemLeftDelim  // left action delimiter
 
45
        itemNumber     // simple number, including imaginary
 
46
        itemPipe       // pipe symbol
 
47
        itemRawString  // raw quoted string (includes quotes)
 
48
        itemRightDelim // right action delimiter
 
49
        itemString     // quoted string (includes quotes)
 
50
        itemText       // plain text
 
51
        // Keywords appear after all the rest.
 
52
        itemKeyword  // used only to delimit the keywords
 
53
        itemDot      // the cursor, spelled '.'.
 
54
        itemDefine   // define keyword
 
55
        itemElse     // else keyword
 
56
        itemEnd      // end keyword
 
57
        itemIf       // if keyword
 
58
        itemRange    // range keyword
 
59
        itemTemplate // template keyword
 
60
        itemWith     // with keyword
 
61
)
 
62
 
 
63
// Make the types prettyprint.
 
64
var itemName = map[itemType]string{
 
65
        itemError:      "error",
 
66
        itemBool:       "bool",
 
67
        itemComplex:    "complex",
 
68
        itemEOF:        "EOF",
 
69
        itemField:      "field",
 
70
        itemIdentifier: "identifier",
 
71
        itemLeftDelim:  "left delim",
 
72
        itemNumber:     "number",
 
73
        itemPipe:       "pipe",
 
74
        itemRawString:  "raw string",
 
75
        itemRightDelim: "right delim",
 
76
        itemString:     "string",
 
77
        // keywords
 
78
        itemDot:      ".",
 
79
        itemDefine:   "define",
 
80
        itemElse:     "else",
 
81
        itemIf:       "if",
 
82
        itemEnd:      "end",
 
83
        itemRange:    "range",
 
84
        itemTemplate: "template",
 
85
        itemWith:     "with",
 
86
}
 
87
 
 
88
func (i itemType) String() string {
 
89
        s := itemName[i]
 
90
        if s == "" {
 
91
                return fmt.Sprintf("item%d", int(i))
 
92
        }
 
93
        return s
 
94
}
 
95
 
 
96
var key = map[string]itemType{
 
97
        ".":        itemDot,
 
98
        "define":   itemDefine,
 
99
        "else":     itemElse,
 
100
        "end":      itemEnd,
 
101
        "if":       itemIf,
 
102
        "range":    itemRange,
 
103
        "template": itemTemplate,
 
104
        "with":     itemWith,
 
105
}
 
106
 
 
107
const eof = -1
 
108
 
 
109
// stateFn represents the state of the scanner as a function that returns the next state.
 
110
type stateFn func(*lexer) stateFn
 
111
 
 
112
// lexer holds the state of the scanner.
 
113
type lexer struct {
 
114
        name  string    // the name of the input; used only for error reports.
 
115
        input string    // the string being scanned.
 
116
        state stateFn   // the next lexing function to enter
 
117
        pos   int       // current position in the input.
 
118
        start int       // start position of this item.
 
119
        width int       // width of last rune read from input.
 
120
        items chan item // channel of scanned items.
 
121
}
 
122
 
 
123
// next returns the next rune in the input.
 
124
func (l *lexer) next() (rune int) {
 
125
        if l.pos >= len(l.input) {
 
126
                l.width = 0
 
127
                return eof
 
128
        }
 
129
        rune, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
 
130
        l.pos += l.width
 
131
        return rune
 
132
}
 
133
 
 
134
// peek returns but does not consume the next rune in the input.
 
135
func (l *lexer) peek() int {
 
136
        rune := l.next()
 
137
        l.backup()
 
138
        return rune
 
139
}
 
140
 
 
141
// backup steps back one rune. Can only be called once per call of next.
 
142
func (l *lexer) backup() {
 
143
        l.pos -= l.width
 
144
}
 
145
 
 
146
// emit passes an item back to the client.
 
147
func (l *lexer) emit(t itemType) {
 
148
        l.items <- item{t, l.input[l.start:l.pos]}
 
149
        l.start = l.pos
 
150
}
 
151
 
 
152
// ignore skips over the pending input before this point.
 
153
func (l *lexer) ignore() {
 
154
        l.start = l.pos
 
155
}
 
156
 
 
157
// accept consumes the next rune if it's from the valid set.
 
158
func (l *lexer) accept(valid string) bool {
 
159
        if strings.IndexRune(valid, l.next()) >= 0 {
 
160
                return true
 
161
        }
 
162
        l.backup()
 
163
        return false
 
164
}
 
165
 
 
166
// acceptRun consumes a run of runes from the valid set.
 
167
func (l *lexer) acceptRun(valid string) {
 
168
        for strings.IndexRune(valid, l.next()) >= 0 {
 
169
        }
 
170
        l.backup()
 
171
}
 
172
 
 
173
// lineNumber reports which line we're on. Doing it this way
 
174
// means we don't have to worry about peek double counting.
 
175
func (l *lexer) lineNumber() int {
 
176
        return 1 + strings.Count(l.input[:l.pos], "\n")
 
177
}
 
178
 
 
179
// error returns an error token and terminates the scan by passing
 
180
// back a nil pointer that will be the next state, terminating l.run.
 
181
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
 
182
        l.items <- item{itemError, fmt.Sprintf(format, args...)}
 
183
        return nil
 
184
}
 
185
 
 
186
// nextItem returns the next item from the input.
 
187
func (l *lexer) nextItem() item {
 
188
        for {
 
189
                select {
 
190
                case item := <-l.items:
 
191
                        return item
 
192
                default:
 
193
                        l.state = l.state(l)
 
194
                }
 
195
        }
 
196
        panic("not reached")
 
197
}
 
198
 
 
199
// lex creates a new scanner for the input string.
 
200
func lex(name, input string) *lexer {
 
201
        l := &lexer{
 
202
                name:  name,
 
203
                input: input,
 
204
                state: lexText,
 
205
                items: make(chan item, 2), // Two items of buffering is sufficient for all state functions
 
206
        }
 
207
        return l
 
208
}
 
209
 
 
210
// state functions
 
211
 
 
212
const (
 
213
        leftDelim    = "{{"
 
214
        rightDelim   = "}}"
 
215
        leftComment  = "{{/*"
 
216
        rightComment = "*/}}"
 
217
)
 
218
 
 
219
// lexText scans until an opening action delimiter, "{{".
 
220
func lexText(l *lexer) stateFn {
 
221
        for {
 
222
                if strings.HasPrefix(l.input[l.pos:], leftDelim) {
 
223
                        if l.pos > l.start {
 
224
                                l.emit(itemText)
 
225
                        }
 
226
                        return lexLeftDelim
 
227
                }
 
228
                if l.next() == eof {
 
229
                        break
 
230
                }
 
231
        }
 
232
        // Correctly reached EOF.
 
233
        if l.pos > l.start {
 
234
                l.emit(itemText)
 
235
        }
 
236
        l.emit(itemEOF)
 
237
        return nil
 
238
}
 
239
 
 
240
// lexLeftDelim scans the left delimiter, which is known to be present.
 
241
func lexLeftDelim(l *lexer) stateFn {
 
242
        if strings.HasPrefix(l.input[l.pos:], leftComment) {
 
243
                return lexComment
 
244
        }
 
245
        l.pos += len(leftDelim)
 
246
        l.emit(itemLeftDelim)
 
247
        return lexInsideAction
 
248
}
 
249
 
 
250
// lexComment scans a comment. The left comment marker is known to be present.
 
251
func lexComment(l *lexer) stateFn {
 
252
        i := strings.Index(l.input[l.pos:], rightComment)
 
253
        if i < 0 {
 
254
                return l.errorf("unclosed comment")
 
255
        }
 
256
        l.pos += i + len(rightComment)
 
257
        l.ignore()
 
258
        return lexText
 
259
}
 
260
 
 
261
// lexRightDelim scans the right delimiter, which is known to be present.
 
262
func lexRightDelim(l *lexer) stateFn {
 
263
        l.pos += len(rightDelim)
 
264
        l.emit(itemRightDelim)
 
265
        return lexText
 
266
}
 
267
 
 
268
// lexInsideAction scans the elements inside action delimiters.
 
269
func lexInsideAction(l *lexer) stateFn {
 
270
        // Either number, quoted string, or identifier.
 
271
        // Spaces separate and are ignored.
 
272
        // Pipe symbols separate and are emitted.
 
273
        for {
 
274
                if strings.HasPrefix(l.input[l.pos:], rightDelim) {
 
275
                        return lexRightDelim
 
276
                }
 
277
                switch r := l.next(); {
 
278
                case r == eof || r == '\n':
 
279
                        return l.errorf("unclosed action")
 
280
                case isSpace(r):
 
281
                        l.ignore()
 
282
                case r == '|':
 
283
                        l.emit(itemPipe)
 
284
                case r == '"':
 
285
                        return lexQuote
 
286
                case r == '`':
 
287
                        return lexRawQuote
 
288
                case r == '.':
 
289
                        // special look-ahead for ".field" so we don't break l.backup().
 
290
                        if l.pos < len(l.input) {
 
291
                                r := l.input[l.pos]
 
292
                                if r < '0' || '9' < r {
 
293
                                        return lexIdentifier // itemDot comes from the keyword table.
 
294
                                }
 
295
                        }
 
296
                        fallthrough // '.' can start a number.
 
297
                case r == '+' || r == '-' || ('0' <= r && r <= '9'):
 
298
                        l.backup()
 
299
                        return lexNumber
 
300
                case isAlphaNumeric(r):
 
301
                        l.backup()
 
302
                        return lexIdentifier
 
303
                default:
 
304
                        return l.errorf("unrecognized character in action: %#U", r)
 
305
                }
 
306
        }
 
307
        return nil
 
308
}
 
309
 
 
310
// lexIdentifier scans an alphanumeric or field.
 
311
func lexIdentifier(l *lexer) stateFn {
 
312
Loop:
 
313
        for {
 
314
                switch r := l.next(); {
 
315
                case isAlphaNumeric(r):
 
316
                        // absorb.
 
317
                case r == '.' && l.input[l.start] == '.':
 
318
                        // field chaining; absorb into one token.
 
319
                default:
 
320
                        l.backup()
 
321
                        word := l.input[l.start:l.pos]
 
322
                        switch {
 
323
                        case key[word] > itemKeyword:
 
324
                                l.emit(key[word])
 
325
                        case word[0] == '.':
 
326
                                l.emit(itemField)
 
327
                        case word == "true", word == "false":
 
328
                                l.emit(itemBool)
 
329
                        default:
 
330
                                l.emit(itemIdentifier)
 
331
                        }
 
332
                        break Loop
 
333
                }
 
334
        }
 
335
        return lexInsideAction
 
336
}
 
337
 
 
338
// lexNumber scans a number: decimal, octal, hex, float, or imaginary.  This
 
339
// isn't a perfect number scanner - for instance it accepts "." and "0x0.2"
 
340
// and "089" - but when it's wrong the input is invalid and the parser (via
 
341
// strconv) will notice.
 
342
func lexNumber(l *lexer) stateFn {
 
343
        if !l.scanNumber() {
 
344
                return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
 
345
        }
 
346
        if sign := l.peek(); sign == '+' || sign == '-' {
 
347
                // Complex: 1+2i.  No spaces, must end in 'i'.
 
348
                if !l.scanNumber() || l.input[l.pos-1] != 'i' {
 
349
                        return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
 
350
                }
 
351
                l.emit(itemComplex)
 
352
        } else {
 
353
                l.emit(itemNumber)
 
354
        }
 
355
        return lexInsideAction
 
356
}
 
357
 
 
358
func (l *lexer) scanNumber() bool {
 
359
        // Optional leading sign.
 
360
        l.accept("+-")
 
361
        // Is it hex?
 
362
        digits := "0123456789"
 
363
        if l.accept("0") && l.accept("xX") {
 
364
                digits = "0123456789abcdefABCDEF"
 
365
        }
 
366
        l.acceptRun(digits)
 
367
        if l.accept(".") {
 
368
                l.acceptRun(digits)
 
369
        }
 
370
        if l.accept("eE") {
 
371
                l.accept("+-")
 
372
                l.acceptRun("0123456789")
 
373
        }
 
374
        // Is it imaginary?
 
375
        l.accept("i")
 
376
        // Next thing mustn't be alphanumeric.
 
377
        if isAlphaNumeric(l.peek()) {
 
378
                l.next()
 
379
                return false
 
380
        }
 
381
        return true
 
382
}
 
383
 
 
384
// lexQuote scans a quoted string.
 
385
func lexQuote(l *lexer) stateFn {
 
386
Loop:
 
387
        for {
 
388
                switch l.next() {
 
389
                case '\\':
 
390
                        if r := l.next(); r != eof && r != '\n' {
 
391
                                break
 
392
                        }
 
393
                        fallthrough
 
394
                case eof, '\n':
 
395
                        return l.errorf("unterminated quoted string")
 
396
                case '"':
 
397
                        break Loop
 
398
                }
 
399
        }
 
400
        l.emit(itemString)
 
401
        return lexInsideAction
 
402
}
 
403
 
 
404
// lexRawQuote scans a raw quoted string.
 
405
func lexRawQuote(l *lexer) stateFn {
 
406
Loop:
 
407
        for {
 
408
                switch l.next() {
 
409
                case eof, '\n':
 
410
                        return l.errorf("unterminated raw quoted string")
 
411
                case '`':
 
412
                        break Loop
 
413
                }
 
414
        }
 
415
        l.emit(itemRawString)
 
416
        return lexInsideAction
 
417
}
 
418
 
 
419
// isSpace reports whether r is a space character.
 
420
func isSpace(r int) bool {
 
421
        switch r {
 
422
        case ' ', '\t', '\n', '\r':
 
423
                return true
 
424
        }
 
425
        return false
 
426
}
 
427
 
 
428
// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
 
429
func isAlphaNumeric(r int) bool {
 
430
        return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
 
431
}