config: parser
This commit is contained in:
parent
db160a0249
commit
1dcefba5c4
|
@ -4,4 +4,6 @@ example.tf
|
||||||
terraform.tfplan
|
terraform.tfplan
|
||||||
terraform.tfstate
|
terraform.tfstate
|
||||||
bin/
|
bin/
|
||||||
|
config/y.go
|
||||||
|
config/y.output
|
||||||
vendor/
|
vendor/
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
// This is the yacc input for creating the parser for interpolation
|
||||||
|
// expressions in Go.
|
||||||
|
|
||||||
|
// To build it:
|
||||||
|
//
|
||||||
|
// go tool yacc -p "expr" expr.y (produces y.go)
|
||||||
|
//
|
||||||
|
|
||||||
|
%{
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
%union {
|
||||||
|
expr Interpolation
|
||||||
|
str string
|
||||||
|
variable InterpolatedVariable
|
||||||
|
args []Interpolation
|
||||||
|
}
|
||||||
|
|
||||||
|
%type <args> args
|
||||||
|
%type <expr> expr
|
||||||
|
%type <str> string
|
||||||
|
%type <variable> variable
|
||||||
|
|
||||||
|
%token <str> STRING IDENTIFIER
|
||||||
|
%token <str> COMMA LEFTPAREN RIGHTPAREN
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
top:
|
||||||
|
expr
|
||||||
|
{
|
||||||
|
fmt.Printf("%#v", $1)
|
||||||
|
}
|
||||||
|
|
||||||
|
expr:
|
||||||
|
string
|
||||||
|
{
|
||||||
|
$$ = &LiteralInterpolation{Literal: $1}
|
||||||
|
}
|
||||||
|
| variable
|
||||||
|
{
|
||||||
|
$$ = &VariableInterpolation{Variable: $1}
|
||||||
|
}
|
||||||
|
| IDENTIFIER LEFTPAREN args RIGHTPAREN
|
||||||
|
{
|
||||||
|
$$ = &FunctionInterpolation{Func: $1, Args: $3}
|
||||||
|
}
|
||||||
|
|
||||||
|
args:
|
||||||
|
{
|
||||||
|
$$ = nil
|
||||||
|
}
|
||||||
|
| expr COMMA expr
|
||||||
|
{
|
||||||
|
$$ = append($$, $1, $3)
|
||||||
|
}
|
||||||
|
| expr
|
||||||
|
{
|
||||||
|
$$ = append($$, $1)
|
||||||
|
}
|
||||||
|
|
||||||
|
string:
|
||||||
|
STRING
|
||||||
|
{
|
||||||
|
$$ = $1
|
||||||
|
}
|
||||||
|
|
||||||
|
variable:
|
||||||
|
IDENTIFIER
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
$$, err = NewInterpolatedVariable($1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%%
|
|
@ -0,0 +1,130 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The parser expects the lexer to return 0 on EOF.
|
||||||
|
const lexEOF = 0
|
||||||
|
|
||||||
|
// The parser uses the type <prefix>Lex as a lexer. It must provide
|
||||||
|
// the methods Lex(*<prefix>SymType) int and Error(string).
|
||||||
|
type exprLex struct {
|
||||||
|
input string
|
||||||
|
pos int
|
||||||
|
width int
|
||||||
|
}
|
||||||
|
|
||||||
|
// The parser calls this method to get each new token.
|
||||||
|
func (x *exprLex) Lex(yylval *exprSymType) int {
|
||||||
|
for {
|
||||||
|
c := x.next()
|
||||||
|
if c == lexEOF {
|
||||||
|
return lexEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore all whitespace
|
||||||
|
if unicode.IsSpace(c) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case '"':
|
||||||
|
return x.lexString(yylval)
|
||||||
|
case ',':
|
||||||
|
return COMMA
|
||||||
|
case '(':
|
||||||
|
return LEFTPAREN
|
||||||
|
case ')':
|
||||||
|
return RIGHTPAREN
|
||||||
|
default:
|
||||||
|
x.backup()
|
||||||
|
return x.lexId(yylval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *exprLex) lexId(yylval *exprSymType) int {
|
||||||
|
var b bytes.Buffer
|
||||||
|
for {
|
||||||
|
c := x.next()
|
||||||
|
if c == lexEOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this isn't a character we want in an ID, return out.
|
||||||
|
// One day we should make this a regexp.
|
||||||
|
if c != '_' &&
|
||||||
|
c != '.' &&
|
||||||
|
c != '*' &&
|
||||||
|
!unicode.IsLetter(c) &&
|
||||||
|
!unicode.IsNumber(c) {
|
||||||
|
x.backup()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := b.WriteRune(c); err != nil {
|
||||||
|
log.Printf("ERR: %s", err)
|
||||||
|
return lexEOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yylval.str = b.String()
|
||||||
|
return IDENTIFIER
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *exprLex) lexString(yylval *exprSymType) int {
|
||||||
|
var b bytes.Buffer
|
||||||
|
for {
|
||||||
|
c := x.next()
|
||||||
|
if c == lexEOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// String end
|
||||||
|
if c == '"' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := b.WriteRune(c); err != nil {
|
||||||
|
log.Printf("ERR: %s", err)
|
||||||
|
return lexEOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yylval.str = b.String()
|
||||||
|
return STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the next rune for the lexer.
|
||||||
|
func (x *exprLex) next() rune {
|
||||||
|
if int(x.pos) >= len(x.input) {
|
||||||
|
x.width = 0
|
||||||
|
return lexEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
r, w := utf8.DecodeRuneInString(x.input[x.pos:])
|
||||||
|
x.width = w
|
||||||
|
x.pos += x.width
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// peek returns but does not consume the next rune in the input
|
||||||
|
func (x *exprLex) peek() rune {
|
||||||
|
r := x.next()
|
||||||
|
x.backup()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// backup steps back one rune. Can only be called once per next.
|
||||||
|
func (x *exprLex) backup() {
|
||||||
|
x.pos -= x.width
|
||||||
|
}
|
||||||
|
|
||||||
|
// The parser calls this method on a parse error.
|
||||||
|
func (x *exprLex) Error(s string) {
|
||||||
|
log.Printf("parse error: %s", s)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExprParse(t *testing.T) {
|
||||||
|
exprParse(&exprLex{input: `lookup(var.foo)`})
|
||||||
|
}
|
|
@ -37,8 +37,8 @@ type InterpolatedVariable interface {
|
||||||
// FunctionInterpolation is an Interpolation that executes a function
|
// FunctionInterpolation is an Interpolation that executes a function
|
||||||
// with some variable number of arguments to generate a value.
|
// with some variable number of arguments to generate a value.
|
||||||
type FunctionInterpolation struct {
|
type FunctionInterpolation struct {
|
||||||
Func InterpolationFunc
|
Func string
|
||||||
Args []InterpolatedVariable
|
Args []Interpolation
|
||||||
|
|
||||||
key string
|
key string
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue