github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/config/expr_lex.go (about) 1 package config 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "unicode" 8 "unicode/utf8" 9 ) 10 11 // The parser expects the lexer to return 0 on EOF. 12 const lexEOF = 0 13 14 // The parser uses the type <prefix>Lex as a lexer. It must provide 15 // the methods Lex(*<prefix>SymType) int and Error(string). 16 type exprLex struct { 17 Err error 18 Input string 19 20 pos int 21 width int 22 } 23 24 // The parser calls this method to get each new token. 25 func (x *exprLex) Lex(yylval *exprSymType) int { 26 for { 27 c := x.next() 28 if c == lexEOF { 29 return lexEOF 30 } 31 32 // Ignore all whitespace 33 if unicode.IsSpace(c) { 34 continue 35 } 36 37 switch c { 38 case '"': 39 return x.lexString(yylval) 40 case ',': 41 return COMMA 42 case '(': 43 return LEFTPAREN 44 case ')': 45 return RIGHTPAREN 46 default: 47 x.backup() 48 return x.lexId(yylval) 49 } 50 } 51 } 52 53 func (x *exprLex) lexId(yylval *exprSymType) int { 54 var b bytes.Buffer 55 for { 56 c := x.next() 57 if c == lexEOF { 58 break 59 } 60 61 // If this isn't a character we want in an ID, return out. 62 // One day we should make this a regexp. 63 if c != '_' && 64 c != '-' && 65 c != '.' && 66 c != '*' && 67 !unicode.IsLetter(c) && 68 !unicode.IsNumber(c) { 69 x.backup() 70 break 71 } 72 73 if _, err := b.WriteRune(c); err != nil { 74 log.Printf("ERR: %s", err) 75 return lexEOF 76 } 77 } 78 79 yylval.str = b.String() 80 return IDENTIFIER 81 } 82 83 func (x *exprLex) lexString(yylval *exprSymType) int { 84 var b bytes.Buffer 85 for { 86 c := x.next() 87 if c == lexEOF { 88 break 89 } 90 91 // String end 92 if c == '"' { 93 break 94 } 95 96 if _, err := b.WriteRune(c); err != nil { 97 log.Printf("ERR: %s", err) 98 return lexEOF 99 } 100 } 101 102 yylval.str = b.String() 103 return STRING 104 } 105 106 // Return the next rune for the lexer. 107 func (x *exprLex) next() rune { 108 if int(x.pos) >= len(x.Input) { 109 x.width = 0 110 return lexEOF 111 } 112 113 r, w := utf8.DecodeRuneInString(x.Input[x.pos:]) 114 x.width = w 115 x.pos += x.width 116 return r 117 } 118 119 // peek returns but does not consume the next rune in the input 120 func (x *exprLex) peek() rune { 121 r := x.next() 122 x.backup() 123 return r 124 } 125 126 // backup steps back one rune. Can only be called once per next. 127 func (x *exprLex) backup() { 128 x.pos -= x.width 129 } 130 131 // The parser calls this method on a parse error. 132 func (x *exprLex) Error(s string) { 133 x.Err = fmt.Errorf("parse error: %s", s) 134 }