From db5167e3d859a0aa9d1dce50e5c454bfc1806fa6 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 30 May 2017 18:42:45 -0700 Subject: [PATCH] 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. --- zcl/zclsyntax/parser.go | 40 ++++++++++++++-- zcl/zclsyntax/parser_test.go | 88 +++++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/zcl/zclsyntax/parser.go b/zcl/zclsyntax/parser.go index d362650..f8100dc 100644 --- a/zcl/zclsyntax/parser.go +++ b/zcl/zclsyntax/parser.go @@ -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 diff --git a/zcl/zclsyntax/parser_test.go b/zcl/zclsyntax/parser_test.go index e3bc1ac..050819d 100644 --- a/zcl/zclsyntax/parser_test.go +++ b/zcl/zclsyntax/parser_test.go @@ -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{