meyer

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 31, 2026 License: MIT

README

meyer

A pure Go parser for the SQLite SQL dialect. Zero dependencies, hand-written recursive descent — no parser generators.

(a Meyer lemon, for SQLite's Lemon parser generator)

meyer is being built to replace the ANTLR-generated SQLite parser in sqlc. See PLAN.md for the architecture and CLAUDE.md for the development workflow.

Go Reference

Usage

go get github.com/sqlc-dev/meyer
package main

import (
	"errors"
	"fmt"

	"github.com/sqlc-dev/meyer/ast"
	"github.com/sqlc-dev/meyer/parser"
)

func main() {
	const src = `SELECT u.name, count(o.id) FROM users u
	  JOIN orders o ON o.user_id = u.id
	 WHERE u.created_at > :since`

	stmts, err := parser.ParseString(src)
	if err != nil {
		var perr *parser.Error
		if errors.As(err, &perr) {
			fmt.Printf("%s at byte %d\n", perr.Message, perr.Offset)
		}
		return
	}

	for _, stmt := range stmts {
		ast.Walk(stmt, func(n ast.Node) bool {
			switch n := n.(type) {
			case *ast.TableRef:
				if n.Name != nil {
					fmt.Println("table:", n.Name.Name.Name)
				}
			case *ast.BindParam:
				fmt.Printf("param: %s at %d:%d\n", n.Raw, n.Pos(), n.End())
			}
			return true
		})
	}
}
table: users
table: orders
param: :since at 100:106

ParseString and ParseStatement take a string, Parse takes an io.Reader, and ParseExpr parses a single expression. A rejected input returns a *parser.Error carrying SQLite's exact message and the byte offset of the fault; its Error() renders as line:column: message.

_, err := parser.ParseString("SELECT FROM t")
fmt.Println(err) // 1:8: near "FROM": syntax error

Every node embeds ast.Span, so Pos() and End() give byte offsets into the original input — sqlc slices the source with them to find -- name: comments and to report errors, so they are load-bearing rather than diagnostic. ast.String and ast.Statements render a tree back to SQL, which is a re-parseable rendering rather than a formatter.

The full API is on pkg.go.dev: parser for the entry points, ast for the node set, and lexer and token if you want the token stream on its own.

To see what the parser did with something:

go run ./cmd/debug-parse 'SELECT sum(x) OVER w FROM t'   # the tree
go run ./cmd/debug-parse -tokens 'SELECT 1'              # the token stream
go run ./cmd/debug-parse -render -f query.sql            # re-rendered SQL

Status

The parser covers the whole grammar of the pinned SQLite release and passes the full corpus: 21,326 of 21,326 cases, extracted from every test script in SQLite's own test suite.

Still to come, from PLAN.md: the sqlc integration itself, and the optional sqllogictest smoke gate.

Conformance

There is no upstream parse-tree oracle — SQLite cannot dump its parse tree — so conformance is defined three ways, in decreasing order of authority:

  1. Accept/reject against SQLite itself. SQL is extracted from SQLite's test suite and run through a pinned SQLite build. meyer must accept exactly what SQLite's parser accepts, and on rejection produce the identical message and byte offset. See parser/testdata/README.md for provenance and the pinned version.
  2. A round-trip property. Every accepting case is rendered back to SQL, re-parsed, and the two trees compared. This catches dropped clauses and mis-associated operators, which accept/reject cannot see.
  3. AST snapshots. A hand-written tour of the node set under parser/testdata/ast, reviewed in diffs. These are meyer's own goldens and never outrank the oracle.

Plus two searches for cases the corpus does not contain: a fuzz target asserting that arbitrary bytes terminate without panicking, that a rejection is always a *parser.Error, and that anything accepted survives the round trip; and cmd/difftest, which checks meyer against a live SQLite build, message and byte offset included, over corpus SQL and the ~36k inputs of SQLite's own fuzzdata databases — each one directly and then mutated a token at a time.

Acknowledgments

SQLite and its test suite are public domain — https://sqlite.org/copyright.html. meyer's grammar and test corpus derive from them; internal/reference/ keeps the upstream parse.y and tokenize.c alongside the port, as documentation.

License

MIT

Directories

Path Synopsis
Package ast declares the syntax tree for the SQLite SQL dialect.
Package ast declares the syntax tree for the SQLite SQL dialect.
cmd
debug-parse command
Command debug-parse parses SQL and prints the result, for working on the parser by hand.
Command debug-parse parses SQL and prints the result, for working on the parser by hand.
difftest command
Command difftest looks for places where meyer and SQLite disagree.
Command difftest looks for places where meyer and SQLite disagree.
next-test command
Command next-test picks the next corpus case to implement.
Command next-test picks the next corpus case to implement.
regenerate-parse command
Command regenerate-parse builds meyer's test corpus from the SQLite source tree, using a real SQLite build as the oracle.
Command regenerate-parse builds meyer's test corpus from the SQLite source tree, using a real SQLite build as the oracle.
internal
dump
Package dump renders an ast.Node as a stable, human-reviewable tree.
Package dump renders an ast.Node as a stable, human-reviewable tree.
roundtrip
Package roundtrip states meyer's round-trip property in one place.
Package roundtrip states meyer's round-trip property in one place.
sqlitesrc
Package sqlitesrc manages the pinned SQLite release that meyer is tested against.
Package sqlitesrc manages the pinned SQLite release that meyer is tested against.
tclextract
Package tclextract pulls literal SQL test cases out of SQLite's TCL test scripts (test/*.test in the SQLite source tree).
Package tclextract pulls literal SQL test cases out of SQLite's TCL test scripts (test/*.test in the SQLite source tree).
testfile
Package testfile reads and writes meyer's consolidated corpus files.
Package testfile reads and writes meyer's consolidated corpus files.
Package lexer implements SQLite's tokenizer.
Package lexer implements SQLite's tokenizer.
Package parser implements a hand-written recursive descent parser for the SQLite SQL dialect.
Package parser implements a hand-written recursive descent parser for the SQLite SQL dialect.
Package token defines the lexical tokens of the SQLite SQL dialect.
Package token defines the lexical tokens of the SQLite SQL dialect.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL