Skip to content

Commit 0a9ea05

Browse files
committed
Add END CONVERSATION statement parsing
- Create EndConversationStatement AST type with Conversation, WithCleanup, ErrorCode, and ErrorDescription fields - Add parseEndConversationStatement function to handle END CONVERSATION with optional WITH ERROR/WITH CLEANUP clauses - Update TryCatch, BeginEndBlock, and statement list parsing to properly distinguish END CONVERSATION from block-terminating END keyword - Add JSON marshalling for EndConversationStatement Enables 2 tests: EndConversationStatementTests, Baselines90_EndConversationStatementTests
1 parent 0f3199e commit 0a9ea05

6 files changed

Lines changed: 192 additions & 9 deletions

File tree

ast/end_conversation_statement.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ast
2+
3+
// EndConversationStatement represents END CONVERSATION statement
4+
type EndConversationStatement struct {
5+
Conversation ScalarExpression // The conversation handle
6+
WithCleanup bool // true if WITH CLEANUP specified
7+
ErrorCode ScalarExpression // optional error code with WITH ERROR
8+
ErrorDescription ScalarExpression // optional error description with WITH ERROR
9+
}
10+
11+
func (s *EndConversationStatement) statement() {}
12+
func (s *EndConversationStatement) node() {}

parser/marshal.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ func statementToJSON(stmt ast.Statement) jsonNode {
378378
return createTriggerStatementToJSON(s)
379379
case *ast.EnableDisableTriggerStatement:
380380
return enableDisableTriggerStatementToJSON(s)
381+
case *ast.EndConversationStatement:
382+
return endConversationStatementToJSON(s)
381383
case *ast.CreateDatabaseStatement:
382384
return createDatabaseStatementToJSON(s)
383385
case *ast.CreateDatabaseEncryptionKeyStatement:
@@ -9395,6 +9397,23 @@ func enableDisableTriggerStatementToJSON(s *ast.EnableDisableTriggerStatement) j
93959397
return node
93969398
}
93979399

9400+
func endConversationStatementToJSON(s *ast.EndConversationStatement) jsonNode {
9401+
node := jsonNode{
9402+
"$type": "EndConversationStatement",
9403+
"WithCleanup": s.WithCleanup,
9404+
}
9405+
if s.Conversation != nil {
9406+
node["Conversation"] = scalarExpressionToJSON(s.Conversation)
9407+
}
9408+
if s.ErrorCode != nil {
9409+
node["ErrorCode"] = scalarExpressionToJSON(s.ErrorCode)
9410+
}
9411+
if s.ErrorDescription != nil {
9412+
node["ErrorDescription"] = scalarExpressionToJSON(s.ErrorDescription)
9413+
}
9414+
return node
9415+
}
9416+
93989417
func alterIndexStatementToJSON(s *ast.AlterIndexStatement) jsonNode {
93999418
node := jsonNode{
94009419
"$type": "AlterIndexStatement",

parser/parse_statements.go

Lines changed: 153 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,12 +1518,28 @@ func (p *Parser) parseTryCatchStatement() (*ast.TryCatchStatement, error) {
15181518
}
15191519

15201520
// Parse statements until END TRY
1521-
for p.curTok.Type != TokenEnd && p.curTok.Type != TokenEOF {
1521+
for p.curTok.Type != TokenEOF {
15221522
// Skip semicolons
15231523
if p.curTok.Type == TokenSemicolon {
15241524
p.nextToken()
15251525
continue
15261526
}
1527+
// Check for END TRY (not END CONVERSATION)
1528+
if p.curTok.Type == TokenEnd {
1529+
if p.peekTok.Type == TokenConversation {
1530+
// It's END CONVERSATION, parse it
1531+
endConvStmt, err := p.parseEndConversationStatement()
1532+
if err != nil {
1533+
return nil, err
1534+
}
1535+
if endConvStmt != nil {
1536+
stmt.TryStatements.Statements = append(stmt.TryStatements.Statements, endConvStmt)
1537+
}
1538+
continue
1539+
}
1540+
// It's END TRY, break
1541+
break
1542+
}
15271543
s, err := p.parseStatement()
15281544
if err != nil {
15291545
return nil, err
@@ -1552,12 +1568,28 @@ func (p *Parser) parseTryCatchStatement() (*ast.TryCatchStatement, error) {
15521568
stmt.CatchStatements = &ast.StatementList{}
15531569

15541570
// Parse catch statements until END CATCH
1555-
for p.curTok.Type != TokenEnd && p.curTok.Type != TokenEOF {
1571+
for p.curTok.Type != TokenEOF {
15561572
// Skip semicolons
15571573
if p.curTok.Type == TokenSemicolon {
15581574
p.nextToken()
15591575
continue
15601576
}
1577+
// Check for END CATCH (not END CONVERSATION)
1578+
if p.curTok.Type == TokenEnd {
1579+
if p.peekTok.Type == TokenConversation {
1580+
// It's END CONVERSATION, parse it
1581+
endConvStmt, err := p.parseEndConversationStatement()
1582+
if err != nil {
1583+
return nil, err
1584+
}
1585+
if endConvStmt != nil {
1586+
stmt.CatchStatements.Statements = append(stmt.CatchStatements.Statements, endConvStmt)
1587+
}
1588+
continue
1589+
}
1590+
// It's END CATCH, break
1591+
break
1592+
}
15611593
s, err := p.parseStatement()
15621594
if err != nil {
15631595
return nil, err
@@ -1588,8 +1620,24 @@ func (p *Parser) parseBeginEndBlockStatementContinued() (*ast.BeginEndBlockState
15881620
StatementList: &ast.StatementList{},
15891621
}
15901622

1591-
// Parse statements until END
1592-
for p.curTok.Type != TokenEnd && p.curTok.Type != TokenEOF {
1623+
// Parse statements until END (but not END CONVERSATION)
1624+
for p.curTok.Type != TokenEOF {
1625+
// Check for END (not END CONVERSATION)
1626+
if p.curTok.Type == TokenEnd {
1627+
if p.peekTok.Type == TokenConversation {
1628+
// It's END CONVERSATION, parse it
1629+
endConvStmt, err := p.parseEndConversationStatement()
1630+
if err != nil {
1631+
return nil, err
1632+
}
1633+
if endConvStmt != nil {
1634+
stmt.StatementList.Statements = append(stmt.StatementList.Statements, endConvStmt)
1635+
}
1636+
continue
1637+
}
1638+
// It's END (block terminator), break
1639+
break
1640+
}
15931641
s, err := p.parseStatement()
15941642
if err != nil {
15951643
return nil, err
@@ -1620,8 +1668,24 @@ func (p *Parser) parseBeginEndBlockStatement() (*ast.BeginEndBlockStatement, err
16201668
StatementList: &ast.StatementList{},
16211669
}
16221670

1623-
// Parse statements until END
1624-
for p.curTok.Type != TokenEnd && p.curTok.Type != TokenEOF {
1671+
// Parse statements until END (but not END CONVERSATION)
1672+
for p.curTok.Type != TokenEOF {
1673+
// Check for END (not END CONVERSATION)
1674+
if p.curTok.Type == TokenEnd {
1675+
if p.peekTok.Type == TokenConversation {
1676+
// It's END CONVERSATION, parse it
1677+
endConvStmt, err := p.parseEndConversationStatement()
1678+
if err != nil {
1679+
return nil, err
1680+
}
1681+
if endConvStmt != nil {
1682+
stmt.StatementList.Statements = append(stmt.StatementList.Statements, endConvStmt)
1683+
}
1684+
continue
1685+
}
1686+
// It's END (block terminator), break
1687+
break
1688+
}
16251689
s, err := p.parseStatement()
16261690
if err != nil {
16271691
return nil, err
@@ -2845,8 +2909,21 @@ func (p *Parser) parseStatementList() (*ast.StatementList, error) {
28452909
continue
28462910
}
28472911

2848-
// Check for END (end of BEGIN block or TRY/CATCH)
2912+
// Check for END (end of BEGIN block or TRY/CATCH, or END CONVERSATION statement)
28492913
if p.curTok.Type == TokenEnd {
2914+
// Look ahead to check if it's END CONVERSATION (a statement)
2915+
if p.peekTok.Type == TokenConversation {
2916+
// It's END CONVERSATION statement, parse it
2917+
stmt, err := p.parseEndConversationStatement()
2918+
if err != nil {
2919+
return nil, err
2920+
}
2921+
if stmt != nil {
2922+
sl.Statements = append(sl.Statements, stmt)
2923+
}
2924+
continue
2925+
}
2926+
// Otherwise it's the end of a BEGIN block
28502927
break
28512928
}
28522929

@@ -9840,6 +9917,75 @@ func (p *Parser) parseEnableDisableTriggerStatement(enforcement string) (*ast.En
98409917
return stmt, nil
98419918
}
98429919

9920+
// parseEndConversationStatement parses END CONVERSATION statements
9921+
func (p *Parser) parseEndConversationStatement() (*ast.EndConversationStatement, error) {
9922+
// Consume END
9923+
p.nextToken()
9924+
9925+
// Expect CONVERSATION
9926+
if strings.ToUpper(p.curTok.Literal) != "CONVERSATION" {
9927+
return nil, fmt.Errorf("expected CONVERSATION after END, got %s", p.curTok.Literal)
9928+
}
9929+
p.nextToken()
9930+
9931+
stmt := &ast.EndConversationStatement{}
9932+
9933+
// Parse the conversation handle expression
9934+
expr, err := p.parseScalarExpression()
9935+
if err != nil {
9936+
return nil, err
9937+
}
9938+
stmt.Conversation = expr
9939+
9940+
// Check for WITH clause
9941+
if p.curTok.Type == TokenWith {
9942+
p.nextToken()
9943+
9944+
if strings.ToUpper(p.curTok.Literal) == "CLEANUP" {
9945+
stmt.WithCleanup = true
9946+
p.nextToken()
9947+
} else if strings.ToUpper(p.curTok.Literal) == "ERROR" {
9948+
p.nextToken()
9949+
9950+
// Expect =
9951+
if p.curTok.Type == TokenEquals {
9952+
p.nextToken()
9953+
}
9954+
9955+
// Parse error code
9956+
errCode, err := p.parseScalarExpression()
9957+
if err != nil {
9958+
return nil, err
9959+
}
9960+
stmt.ErrorCode = errCode
9961+
9962+
// Expect DESCRIPTION
9963+
if strings.ToUpper(p.curTok.Literal) == "DESCRIPTION" {
9964+
p.nextToken()
9965+
9966+
// Expect =
9967+
if p.curTok.Type == TokenEquals {
9968+
p.nextToken()
9969+
}
9970+
9971+
// Parse error description
9972+
errDesc, err := p.parseScalarExpression()
9973+
if err != nil {
9974+
return nil, err
9975+
}
9976+
stmt.ErrorDescription = errDesc
9977+
}
9978+
}
9979+
}
9980+
9981+
// Skip optional semicolon
9982+
if p.curTok.Type == TokenSemicolon {
9983+
p.nextToken()
9984+
}
9985+
9986+
return stmt, nil
9987+
}
9988+
98439989
// parseCreateWorkloadGroupStatement parses CREATE WORKLOAD GROUP statement.
98449990
func (p *Parser) parseCreateWorkloadGroupStatement() (*ast.CreateWorkloadGroupStatement, error) {
98459991
// Consume WORKLOAD

parser/parser.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ func (p *Parser) parseStatement() (ast.Statement, error) {
181181
return p.parseBackupStatement()
182182
case TokenClose:
183183
return p.parseCloseStatement()
184+
case TokenEnd:
185+
// Check for END CONVERSATION
186+
if p.peekTok.Type == TokenConversation {
187+
return p.parseEndConversationStatement()
188+
}
189+
return nil, fmt.Errorf("unexpected token: END")
184190
case TokenOpen:
185191
return p.parseOpenStatement()
186192
case TokenDbcc:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}

0 commit comments

Comments
 (0)