Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Claude Development Guide

## Next Steps

To continue implementing parser support for skipped tests, consult:

```
skipped_tests_by_size.txt
```

This file lists all skipped tests ordered by query file size (smallest first). Smaller tests are generally simpler to implement.

## Workflow

1. Pick tests from `skipped_tests_by_size.txt` starting from the top
2. Check the test's `query.sql` to understand what SQL needs parsing
3. Check the test's `ast.json` to understand the expected output format
4. Implement the necessary AST types in `ast/`
5. Add parser logic in `parser/parser.go`
6. Add JSON marshaling functions in `parser/parser.go`
7. Enable the test by setting `{"skip": false}` in its `metadata.json`
8. Run `go test ./parser/...` to verify
9. **Update `skipped_tests_by_size.txt`** after enabling tests

## Updating skipped_tests_by_size.txt

After enabling tests, regenerate the file:

```bash
cd parser/testdata
for dir in */; do
if [ -f "$dir/metadata.json" ] && grep -q '"skip": true' "$dir/metadata.json" 2>/dev/null; then
if [ -f "$dir/query.sql" ]; then
size=$(wc -c < "$dir/query.sql")
name="${dir%/}"
echo "$size $name"
fi
fi
done | sort -n > ../../skipped_tests_by_size.txt
```

## Test Structure

Each test in `parser/testdata/` contains:
- `metadata.json` - `{"skip": true}` or `{"skip": false}`
- `query.sql` - T-SQL to parse
- `ast.json` - Expected AST output
12 changes: 12 additions & 0 deletions ast/begin_transaction_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ast

// BeginTransactionStatement represents a BEGIN [DISTRIBUTED] [TRAN|TRANSACTION] statement.
type BeginTransactionStatement struct {
Name *IdentifierOrValueExpression `json:"Name,omitempty"`
Distributed bool `json:"Distributed"`
MarkDefined bool `json:"MarkDefined"`
MarkDescription ScalarExpression `json:"MarkDescription,omitempty"`
}

func (b *BeginTransactionStatement) node() {}
func (b *BeginTransactionStatement) statement() {}
10 changes: 10 additions & 0 deletions ast/commit_transaction_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ast

// CommitTransactionStatement represents a COMMIT [TRAN|TRANSACTION] statement.
type CommitTransactionStatement struct {
Name *IdentifierOrValueExpression `json:"Name,omitempty"`
DelayedDurabilityOption string `json:"DelayedDurabilityOption,omitempty"`
}

func (c *CommitTransactionStatement) node() {}
func (c *CommitTransactionStatement) statement() {}
10 changes: 10 additions & 0 deletions ast/create_default_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ast

// CreateDefaultStatement represents a CREATE DEFAULT statement.
type CreateDefaultStatement struct {
Name *SchemaObjectName `json:"Name"`
Expression ScalarExpression `json:"Expression"`
}

func (c *CreateDefaultStatement) node() {}
func (c *CreateDefaultStatement) statement() {}
9 changes: 9 additions & 0 deletions ast/create_master_key_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ast

// CreateMasterKeyStatement represents a CREATE MASTER KEY ENCRYPTION BY PASSWORD statement.
type CreateMasterKeyStatement struct {
Password ScalarExpression `json:"Password"`
}

func (c *CreateMasterKeyStatement) node() {}
func (c *CreateMasterKeyStatement) statement() {}
9 changes: 9 additions & 0 deletions ast/goto_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ast

// GoToStatement represents a GOTO label statement.
type GoToStatement struct {
LabelName *Identifier `json:"LabelName"`
}

func (g *GoToStatement) node() {}
func (g *GoToStatement) statement() {}
5 changes: 3 additions & 2 deletions ast/identifier_or_value_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package ast

// IdentifierOrValueExpression represents either an identifier or a value expression.
type IdentifierOrValueExpression struct {
Value string `json:"Value,omitempty"`
Identifier *Identifier `json:"Identifier,omitempty"`
Value string `json:"Value,omitempty"`
Identifier *Identifier `json:"Identifier,omitempty"`
ValueExpression ScalarExpression `json:"ValueExpression,omitempty"`
}

func (*IdentifierOrValueExpression) node() {}
9 changes: 9 additions & 0 deletions ast/label_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ast

// LabelStatement represents a label definition (e.g., "start:").
type LabelStatement struct {
Value string `json:"Value"`
}

func (l *LabelStatement) node() {}
func (l *LabelStatement) statement() {}
9 changes: 9 additions & 0 deletions ast/parenthesis_expression.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ast

// ParenthesisExpression represents a parenthesized scalar expression.
type ParenthesisExpression struct {
Expression ScalarExpression `json:"Expression,omitempty"`
}

func (p *ParenthesisExpression) node() {}
func (p *ParenthesisExpression) scalarExpression() {}
9 changes: 9 additions & 0 deletions ast/rollback_transaction_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ast

// RollbackTransactionStatement represents a ROLLBACK [TRAN|TRANSACTION] statement.
type RollbackTransactionStatement struct {
Name *IdentifierOrValueExpression `json:"Name,omitempty"`
}

func (r *RollbackTransactionStatement) node() {}
func (r *RollbackTransactionStatement) statement() {}
9 changes: 9 additions & 0 deletions ast/save_transaction_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ast

// SaveTransactionStatement represents a SAVE [TRAN|TRANSACTION] statement.
type SaveTransactionStatement struct {
Name *IdentifierOrValueExpression `json:"Name,omitempty"`
}

func (s *SaveTransactionStatement) node() {}
func (s *SaveTransactionStatement) statement() {}
10 changes: 10 additions & 0 deletions ast/try_catch_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ast

// TryCatchStatement represents a BEGIN TRY...END TRY BEGIN CATCH...END CATCH block.
type TryCatchStatement struct {
TryStatements *StatementList `json:"TryStatements,omitempty"`
CatchStatements *StatementList `json:"CatchStatements,omitempty"`
}

func (t *TryCatchStatement) node() {}
func (t *TryCatchStatement) statement() {}
12 changes: 12 additions & 0 deletions ast/waitfor_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ast

// WaitForStatement represents a WAITFOR [DELAY|TIME] statement.
type WaitForStatement struct {
WaitForOption string `json:"WaitForOption"`
Parameter ScalarExpression `json:"Parameter,omitempty"`
Timeout ScalarExpression `json:"Timeout,omitempty"`
Statement Statement `json:"Statement,omitempty"`
}

func (w *WaitForStatement) node() {}
func (w *WaitForStatement) statement() {}
40 changes: 40 additions & 0 deletions parser/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,27 @@ const (
TokenRevoke
TokenTo
TokenPublic

// Transaction keywords
TokenCommit
TokenRollback
TokenSave
TokenTransaction
TokenTran
TokenWork

// Additional keywords
TokenWaitfor
TokenDelay
TokenTime
TokenMaster
TokenKey
TokenEncryption
TokenPassword
TokenLabel
TokenRaiserror
TokenTruncate
TokenColon
)

// Token represents a lexical token.
Expand Down Expand Up @@ -211,6 +232,10 @@ func (l *Lexer) NextToken() Token {
tok.Type = TokenSemicolon
tok.Literal = ";"
l.readChar()
case ':':
tok.Type = TokenColon
tok.Literal = ":"
l.readChar()
case '=':
tok.Type = TokenEquals
tok.Literal = "="
Expand Down Expand Up @@ -478,6 +503,21 @@ var keywords = map[string]TokenType{
"REVOKE": TokenRevoke,
"TO": TokenTo,
"PUBLIC": TokenPublic,
"COMMIT": TokenCommit,
"ROLLBACK": TokenRollback,
"SAVE": TokenSave,
"TRANSACTION": TokenTransaction,
"TRAN": TokenTran,
"WORK": TokenWork,
"WAITFOR": TokenWaitfor,
"DELAY": TokenDelay,
"TIME": TokenTime,
"MASTER": TokenMaster,
"KEY": TokenKey,
"ENCRYPTION": TokenEncryption,
"PASSWORD": TokenPassword,
"RAISERROR": TokenRaiserror,
"TRUNCATE": TokenTruncate,
}

func lookupKeyword(ident string) TokenType {
Expand Down
Loading
Loading