zclsyntax: recoverAfterBodyItem implementation

This recovery method attempts to place the peeker directly after the
newline indicating the end of the current body item. It does this by
counting open and close bracketing constructs and then returning when
a newline is encountered with no bracketing constructs open.

It's designed for use in the "header" part of a body item, with no
bracketing constructs open yet. It _might_ work in other situations, but
is likely to end up choosing the wrong end point if used in the middle
of a bracketed expression that itself contains newlines.
This commit is contained in:
Martin Atkins 2017-05-30 18:42:45 -07:00
parent 9348014b2d
commit db5167e3d8
2 changed files with 124 additions and 4 deletions

View File

@ -377,11 +377,45 @@ Token:
}
func (p *parser) recoverAfterBodyItem() {
// TODO: Seek forward until we find a newline that isn't inside any
// bracketer, and return with the peeker placed after it so we're
// ready to try to parse another body item.
p.recovery = true
var open []TokenType
Token:
for {
tok := p.Read()
switch tok.Type {
case TokenNewline:
if len(open) == 0 {
break Token
}
case TokenEOF:
break Token
case TokenOBrace, TokenOBrack, TokenOParen, TokenOQuote, TokenOHeredoc, TokenTemplateInterp, TokenTemplateControl:
open = append(open, tok.Type)
case TokenCBrace, TokenCBrack, TokenCParen, TokenCQuote, TokenCHeredoc:
opener := p.oppositeBracket(tok.Type)
for len(open) > 0 && open[len(open)-1] != opener {
open = open[:len(open)-1]
}
if len(open) > 0 {
open = open[:len(open)-1]
}
case TokenTemplateSeqEnd:
for len(open) > 0 && open[len(open)-1] != TokenTemplateInterp && open[len(open)-1] != TokenTemplateControl {
open = open[:len(open)-1]
}
if len(open) > 0 {
open = open[:len(open)-1]
}
}
}
}
// oppositeBracket finds the bracket that opposes the given bracketer, or

View File

@ -79,7 +79,6 @@ func TestParseConfig(t *testing.T) {
},
},
},
{
`block "foo" {}`,
0,
@ -133,6 +132,93 @@ func TestParseConfig(t *testing.T) {
},
},
},
{
`
block "invalid" 1.2 {}
block "valid" {}
`,
1,
&Body{
Attributes: Attributes{},
Blocks: Blocks{
&Block{
Type: "block",
Labels: []string{"invalid"},
Body: nil,
TypeRange: zcl.Range{
Start: zcl.Pos{Line: 2, Column: 1, Byte: 1},
End: zcl.Pos{Line: 2, Column: 6, Byte: 6},
},
LabelRanges: []zcl.Range{
{
Start: zcl.Pos{Line: 2, Column: 7, Byte: 7},
End: zcl.Pos{Line: 2, Column: 16, Byte: 16},
},
},
// Since we failed parsing before we got to the
// braces, the type range is used as a placeholder
// for these.
OpenBraceRange: zcl.Range{
Start: zcl.Pos{Line: 2, Column: 1, Byte: 1},
End: zcl.Pos{Line: 2, Column: 6, Byte: 6},
},
CloseBraceRange: zcl.Range{
Start: zcl.Pos{Line: 2, Column: 1, Byte: 1},
End: zcl.Pos{Line: 2, Column: 6, Byte: 6},
},
},
// Recovery behavior should allow us to still see this
// second block, even though the first was invalid.
&Block{
Type: "block",
Labels: []string{"valid"},
Body: &Body{
Attributes: Attributes{},
Blocks: Blocks{},
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 3, Column: 15, Byte: 38},
End: zcl.Pos{Line: 3, Column: 17, Byte: 40},
},
EndRange: zcl.Range{
Start: zcl.Pos{Line: 3, Column: 17, Byte: 40},
End: zcl.Pos{Line: 3, Column: 17, Byte: 40},
},
},
TypeRange: zcl.Range{
Start: zcl.Pos{Line: 3, Column: 1, Byte: 24},
End: zcl.Pos{Line: 3, Column: 6, Byte: 29},
},
LabelRanges: []zcl.Range{
{
Start: zcl.Pos{Line: 3, Column: 7, Byte: 30},
End: zcl.Pos{Line: 3, Column: 14, Byte: 37},
},
},
OpenBraceRange: zcl.Range{
Start: zcl.Pos{Line: 3, Column: 15, Byte: 38},
End: zcl.Pos{Line: 3, Column: 16, Byte: 39},
},
CloseBraceRange: zcl.Range{
Start: zcl.Pos{Line: 3, Column: 16, Byte: 39},
End: zcl.Pos{Line: 3, Column: 17, Byte: 40},
},
},
},
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 4, Column: 1, Byte: 41},
},
EndRange: zcl.Range{
Start: zcl.Pos{Line: 4, Column: 1, Byte: 41},
End: zcl.Pos{Line: 4, Column: 1, Byte: 41},
},
},
},
}
prettyConfig := &pretty.Config{