Skip to content

Commit

Permalink
hcl: support heredocs [GH-6]
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Aug 21, 2014
1 parent 8a779f6 commit 26239b8
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 3 deletions.
2 changes: 1 addition & 1 deletion decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestDecode_interface(t *testing.T) {
{
"multiline_bad.hcl",
false,
map[string]interface{}{"foo": "bar\nbaz"},
map[string]interface{}{"foo": "bar\nbaz\n"},
},
{
"multiline.json",
Expand Down
88 changes: 88 additions & 0 deletions hcl/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ func (x *hclLex) Lex(yylval *hclSymType) int {
return RIGHTBRACE
case '"':
return x.lexString(yylval)
case '<':
return x.lexHeredoc(yylval)
default:
x.backup()
return x.lexId(yylval)
Expand Down Expand Up @@ -191,6 +193,86 @@ func (x *hclLex) lexId(yylval *hclSymType) int {
return IDENTIFIER
}

// lexHeredoc extracts a string from the input in heredoc format
func (x *hclLex) lexHeredoc(yylval *hclSymType) int {
if x.next() != '<' {
x.createErr("Heredoc must start with <<")
return lexEOF
}

// Now determine the marker
var buf bytes.Buffer
for {
c := x.next()
if c == lexEOF {
return lexEOF
}

// Newline signals the end of the marker
if c == '\n' {
break
}

if _, err := buf.WriteRune(c); err != nil {
return lexEOF
}
}

marker := buf.String()
if marker == "" {
x.createErr("Heredoc must have a marker, e.g. <<FOO")
return lexEOF
}

check := true
buf.Reset()
for {
c := x.next()

// If we're checking, then check to see if we see the marker
if check {
check = false

var cs []rune
for _, r := range marker {
if r != c {
break
}

cs = append(cs, c)
c = x.next()
}
if len(cs) == len(marker) {
break
}

if len(cs) > 0 {
for _, c := range cs {
if _, err := buf.WriteRune(c); err != nil {
return lexEOF
}
}
}
}

if c == lexEOF {
return lexEOF
}

// If we hit a newline, then reset to check
if c == '\n' {
check = true
}

if _, err := buf.WriteRune(c); err != nil {
return lexEOF
}
}

yylval.str = buf.String()
return STRING
}

// lexNumber lexes out a number
func (x *hclLex) lexNumber(yylval *hclSymType) int {
var b bytes.Buffer
Expand Down Expand Up @@ -238,6 +320,12 @@ func (x *hclLex) lexString(yylval *hclSymType) int {
break
}

// If we hit a newline, then its an error
if c == '\n' {
x.createErr(fmt.Sprintf("Newline before string closed"))
return lexEOF
}

// If we're starting into variable, mark it
if braces == 0 && c == '$' && x.peek() == '{' {
braces += 1
Expand Down
6 changes: 4 additions & 2 deletions test-fixtures/multiline_bad.hcl
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
foo = "bar
baz"
foo = <<EOF
bar
baz
EOF

0 comments on commit 26239b8

Please sign in to comment.