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  }