hcl: support heredocs [GH-6]
This commit is contained in:
parent
8a779f6e41
commit
26239b8eab
@ -37,7 +37,7 @@ func TestDecode_interface(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"multiline_bad.hcl",
|
"multiline_bad.hcl",
|
||||||
false,
|
false,
|
||||||
map[string]interface{}{"foo": "bar\nbaz"},
|
map[string]interface{}{"foo": "bar\nbaz\n"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"multiline.json",
|
"multiline.json",
|
||||||
|
88
hcl/lex.go
88
hcl/lex.go
@ -94,6 +94,8 @@ func (x *hclLex) Lex(yylval *hclSymType) int {
|
|||||||
return RIGHTBRACE
|
return RIGHTBRACE
|
||||||
case '"':
|
case '"':
|
||||||
return x.lexString(yylval)
|
return x.lexString(yylval)
|
||||||
|
case '<':
|
||||||
|
return x.lexHeredoc(yylval)
|
||||||
default:
|
default:
|
||||||
x.backup()
|
x.backup()
|
||||||
return x.lexId(yylval)
|
return x.lexId(yylval)
|
||||||
@ -191,6 +193,86 @@ func (x *hclLex) lexId(yylval *hclSymType) int {
|
|||||||
return IDENTIFIER
|
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
|
// lexNumber lexes out a number
|
||||||
func (x *hclLex) lexNumber(yylval *hclSymType) int {
|
func (x *hclLex) lexNumber(yylval *hclSymType) int {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
@ -238,6 +320,12 @@ func (x *hclLex) lexString(yylval *hclSymType) int {
|
|||||||
break
|
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 we're starting into variable, mark it
|
||||||
if braces == 0 && c == '$' && x.peek() == '{' {
|
if braces == 0 && c == '$' && x.peek() == '{' {
|
||||||
braces += 1
|
braces += 1
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
foo = "bar
|
foo = <<EOF
|
||||||
baz"
|
bar
|
||||||
|
baz
|
||||||
|
EOF
|
||||||
|
Loading…
Reference in New Issue
Block a user