SQLite Forum

Whole grammar railroad diagram
Login
After doing several grammar conversions to the kind of EBNF understood by https://www.bottlecaps.de/rr/ui to generate railroad diagrams see bellow the converted src/parser.y with some hand made changes to allow view it at https://www.bottlecaps.de/rr/ui the order of the rules could be changed to a better view of the railroad diagram. Copy and paste the EBNF bellow on https://www.bottlecaps.de/rr/ui tab Edit Grammar then switch to the tab View Diagram.

This script that uses Lua pattern matching to process src/parser.y:
====
auto fname = "sqlite3/src/parse.y";
auto txt = readfile(fname);

txt = txt.gsub("%%include%s*%b{}", "");
txt = txt.gsub("%%syntax_error%s*%b{}", "");
txt = txt.gsub("%%stack_overflow%s*%b{}", "");
txt = txt.gsub("%%default_type%s*%b{}", "");
txt = txt.gsub("%%extra_context%s*%b{}", "");
txt = txt.gsub("//[^\n]*", "");
txt = txt.gsub("/%*.-%*/", "");
txt = txt.gsub("%%token[^\n]+", "");
txt = txt.gsub("%%name[^\n]+", "");
txt = txt.gsub("%%left[^\n]+", "");
txt = txt.gsub("%%right[^\n]+", "");
txt = txt.gsub("%%nonassoc[^\n]+", "");
txt = txt.gsub("%%wildcard[^\n]+", "");
txt = txt.gsub("%%type%s+.-%b{}", "");
txt = txt.gsub("%%destructor%s+.-%b{}", "");
txt = txt.gsub("%.%s*%b{}", ".");
txt = txt.gsub("%b{}", " ");
txt = txt.gsub("%b()", " ");

print(txt);

auto rules = {};
auto rule_order = [];

txt.gmatch(
	"([%w_]+)%s*::=([^.]+)%.",
	function(k, v)
	{
		v = v.trim();
		if(v.len() == 0) v = "/* empty */";
		v = v.gsub("%s+", " ");
		if(v.indexOf("|") >= 0) v = "(" + v + ")";
		auto ev = table_rawget(rules, k, null);
		if(!ev) {
			rules[k] <- v;
			rule_order.append(k);
		}
		else
		{
			rules[k] = ev + "\n\t| " + v;
		}
		return true;
	}
);

foreach(idx, k in rule_order) print(/*idx,*/ k, "::=", rules[k]);

====

EBNF for https://www.bottlecaps.de/rr/ui :
====
/*
From src/parser.y
*/

input	::=	cmdlist
cmdlist	::=	cmdlist ecmd
	| ecmd
ecmd	::=	SEMI
	| cmdx SEMI
	| explain cmdx SEMI
explain	::=	EXPLAIN
	| EXPLAIN QUERY PLAN
cmdx	::=	cmd
cmd	::=	BEGIN transtype trans_opt
	| (COMMIT|END trans_opt)
	| ROLLBACK trans_opt
	| SAVEPOINT nm
	| RELEASE savepoint_opt nm
	| ROLLBACK trans_opt TO savepoint_opt nm
	| create_table create_table_args
	| DROP TABLE ifexists fullname
	| createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select
	| DROP VIEW ifexists fullname
	| select
	| with DELETE FROM xfullname indexed_opt where_opt_ret orderby_opt limit_opt
	| with DELETE FROM xfullname indexed_opt where_opt_ret
	| with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret orderby_opt limit_opt
	| with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret
	| with insert_cmd INTO xfullname idlist_opt select upsert
	| with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning
	| createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt
	| DROP INDEX ifexists fullname
	| VACUUM vinto
	| VACUUM nm vinto
	| PRAGMA nm dbnm
	| PRAGMA nm dbnm EQ nmnum
	| PRAGMA nm dbnm LP nmnum RP
	| PRAGMA nm dbnm EQ minus_num
	| PRAGMA nm dbnm LP minus_num RP
	| createkw trigger_decl BEGIN trigger_cmd_list END
	| DROP TRIGGER ifexists fullname
	| ATTACH database_kw_opt expr AS expr key_opt
	| DETACH database_kw_opt expr
	| REINDEX
	| REINDEX nm dbnm
	| ANALYZE
	| ANALYZE nm dbnm
	| ALTER TABLE fullname RENAME TO nm
	| ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist
	| ALTER TABLE fullname DROP kwcolumn_opt nm
	| ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm
	| create_vtab
	| create_vtab LP vtabarglist RP
trans_opt	::=	/* empty */
	| TRANSACTION
	| TRANSACTION nm
transtype	::=	/* empty */
	| DEFERRED
	| IMMEDIATE
	| EXCLUSIVE
savepoint_opt	::=	SAVEPOINT
	| /* empty */
create_table	::=	createkw temp TABLE ifnotexists nm dbnm
createkw	::=	CREATE
ifnotexists	::=	/* empty */
	| IF NOT EXISTS
temp	::=	TEMP
	| /* empty */
create_table_args	::=	LP columnlist conslist_opt RP table_options
	| AS select
table_options	::=	/* empty */
	| WITHOUT nm
columnlist	::=	columnlist COMMA columnname carglist
	| columnname carglist
columnname	::=	nm typetoken
nm	::=	id
	| STRING
	| JOIN_KW
typetoken	::=	/* empty */
	| typename
	| typename LP signed RP
	| typename LP signed COMMA signed RP
typename	::=	ids
	| typename ids
signed	::=	plus_num
	| minus_num
scanpt	::=	/* empty */
scantok	::=	/* empty */
carglist	::=	carglist ccons
	| /* empty */
ccons	::=	CONSTRAINT nm
	| DEFAULT scantok term
	| DEFAULT LP expr RP
	| DEFAULT PLUS scantok term
	| DEFAULT MINUS scantok term
	| DEFAULT scantok id
	| NULL onconf
	| NOT NULL onconf
	| PRIMARY KEY sortorder onconf autoinc
	| UNIQUE onconf
	| CHECK LP expr RP
	| REFERENCES nm eidlist_opt refargs
	| defer_subclause
	| COLLATE ids
	| GENERATED ALWAYS AS generated
	| AS generated
generated	::=	LP expr RP
	| LP expr RP ID
autoinc	::=	/* empty */
	| AUTOINCR
refargs	::=	/* empty */
	| refargs refarg
refarg	::=	MATCH nm
	| ON INSERT refact
	| ON DELETE refact
	| ON UPDATE refact
refact	::=	SET NULL
	| SET DEFAULT
	| CASCADE
	| RESTRICT
	| NO ACTION
defer_subclause	::=	NOT DEFERRABLE init_deferred_pred_opt
	| DEFERRABLE init_deferred_pred_opt
init_deferred_pred_opt	::=	/* empty */
	| INITIALLY DEFERRED
	| INITIALLY IMMEDIATE
conslist_opt	::=	/* empty */
	| COMMA conslist
conslist	::=	conslist tconscomma tcons
	| tcons
tconscomma	::=	COMMA
	| /* empty */
tcons	::=	CONSTRAINT nm
	| PRIMARY KEY LP sortlist autoinc RP onconf
	| UNIQUE LP sortlist RP onconf
	| CHECK LP expr RP onconf
	| FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt
defer_subclause_opt	::=	/* empty */
	| defer_subclause
onconf	::=	/* empty */
	| ON CONFLICT resolvetype
orconf	::=	/* empty */
	| OR resolvetype
resolvetype	::=	raisetype
	| IGNORE
	| REPLACE
ifexists	::=	IF EXISTS
	| /* empty */
select	::=	WITH wqlist selectnowith
	| WITH RECURSIVE wqlist selectnowith
	| selectnowith
selectnowith	::=	oneselect
	| selectnowith multiselect_op oneselect
multiselect_op	::=	UNION
	| UNION ALL
	| (EXCEPT|INTERSECT)
oneselect	::=	SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt
	| SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt
	| values
values	::=	VALUES LP nexprlist RP
	| values COMMA LP nexprlist RP
distinct	::=	DISTINCT
	| ALL
	| /* empty */
sclp	::=	selcollist COMMA
	| /* empty */
selcollist	::=	sclp scanpt expr scanpt as
	| sclp scanpt STAR
	| sclp scanpt nm DOT STAR
as	::=	AS nm
	| ids
	| /* empty */
from	::=	/* empty */
	| FROM seltablist
stl_prefix	::=	seltablist joinop
	| /* empty */
seltablist	::=	stl_prefix nm dbnm as indexed_opt on_opt using_opt
	| stl_prefix nm dbnm LP exprlist RP as on_opt using_opt
	| stl_prefix LP select RP as on_opt using_opt
	| stl_prefix LP seltablist RP as on_opt using_opt
dbnm	::=	/* empty */
	| DOT nm
fullname	::=	nm
	| nm DOT nm
xfullname	::=	nm
	| nm DOT nm
	| nm DOT nm AS nm
	| nm AS nm
joinop	::=	(COMMA|JOIN)
	| JOIN_KW JOIN
	| JOIN_KW nm JOIN
	| JOIN_KW nm nm JOIN
on_opt	::=	ON expr
	| /* empty */
indexed_opt	::=	/* empty */
	| INDEXED BY nm
	| NOT INDEXED
using_opt	::=	USING LP idlist RP
	| /* empty */
orderby_opt	::=	/* empty */
	| ORDER BY sortlist
sortlist	::=	sortlist COMMA expr sortorder nulls
	| expr sortorder nulls
sortorder	::=	ASC
	| DESC
	| /* empty */
nulls	::=	NULLS FIRST
	| NULLS LAST
	| /* empty */
groupby_opt	::=	/* empty */
	| GROUP BY nexprlist
having_opt	::=	/* empty */
	| HAVING expr
limit_opt	::=	/* empty */
	| LIMIT expr
	| LIMIT expr OFFSET expr
	| LIMIT expr COMMA expr
where_opt	::=	/* empty */
	| WHERE expr
where_opt_ret	::=	/* empty */
	| WHERE expr
	| RETURNING selcollist
	| WHERE expr RETURNING selcollist
setlist	::=	setlist COMMA nm EQ expr
	| setlist COMMA LP idlist RP EQ expr
	| nm EQ expr
	| LP idlist RP EQ expr
upsert	::=	/* empty */
	| RETURNING selcollist
	| ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert
	| ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert
	| ON CONFLICT DO NOTHING returning
	| ON CONFLICT DO UPDATE SET setlist where_opt returning
returning	::=	RETURNING selcollist
	| /* empty */
insert_cmd	::=	INSERT orconf
	| REPLACE
idlist_opt	::=	/* empty */
	| LP idlist RP
idlist	::=	idlist COMMA nm
	| nm
expr	::=	term
	| LP expr RP
	| id
	| JOIN_KW
	| nm DOT nm
	| nm DOT nm DOT nm
	| VARIABLE
	| expr COLLATE ids
	| CAST LP expr AS typetoken RP
	| id LP distinct exprlist RP
	| id LP STAR RP
	| id LP distinct exprlist RP filter_over
	| id LP STAR RP filter_over
	| LP nexprlist COMMA expr RP
	| expr AND expr
	| expr OR expr
	| (expr LT|GT|GE|LE expr)
	| (expr EQ|NE expr)
	| (expr BITAND|BITOR|LSHIFT|RSHIFT expr)
	| (expr PLUS|MINUS expr)
	| (expr STAR|SLASH|REM expr)
	| expr CONCAT expr
	| expr likeop expr
	| expr likeop expr ESCAPE expr
	| (expr ISNULL|NOTNULL)
	| expr NOT NULL
	| expr IS expr
	| expr IS NOT expr
	| NOT expr
	| BITNOT expr
	| (PLUS|MINUS expr)
	| expr between_op expr AND expr
	| expr in_op LP exprlist RP
	| LP select RP
	| expr in_op LP select RP
	| expr in_op nm dbnm paren_exprlist
	| EXISTS LP select RP
	| CASE case_operand case_exprlist case_else END
	| RAISE LP IGNORE RP
	| RAISE LP raisetype COMMA nm RP
term	::=	(NULL|FLOAT|BLOB)
	| STRING
	| INTEGER
	| CTIME_KW
likeop	::=	(LIKE_KW|MATCH)
	| (NOT LIKE_KW|MATCH)
between_op	::=	BETWEEN
	| NOT BETWEEN
in_op	::=	IN
	| NOT IN
case_exprlist	::=	case_exprlist WHEN expr THEN expr
	| WHEN expr THEN expr
case_else	::=	ELSE expr
	| /* empty */
case_operand	::=	expr
	| /* empty */
exprlist	::=	nexprlist
	| /* empty */
nexprlist	::=	nexprlist COMMA expr
	| expr
paren_exprlist	::=	/* empty */
	| LP exprlist RP
uniqueflag	::=	UNIQUE
	| /* empty */
eidlist_opt	::=	/* empty */
	| LP eidlist RP
eidlist	::=	eidlist COMMA nm collate sortorder
	| nm collate sortorder
collate	::=	/* empty */
	| COLLATE ids
vinto	::=	INTO expr
	| /* empty */
nmnum	::=	plus_num
	| nm
	| ON
	| DELETE
	| DEFAULT
plus_num	::=	PLUS number
	| number
minus_num	::=	MINUS number
trigger_decl	::=	temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause
trigger_time	::=	(BEFORE|AFTER)
	| INSTEAD OF
	| /* empty */
trigger_event	::=	(DELETE|INSERT)
	| UPDATE
	| UPDATE OF idlist
foreach_clause	::=	/* empty */
	| FOR EACH ROW
when_clause	::=	/* empty */
	| WHEN expr
trigger_cmd_list	::=	trigger_cmd_list trigger_cmd SEMI
	| trigger_cmd SEMI
trnm	::=	nm
	| nm DOT nm
tridxby	::=	/* empty */
	| INDEXED BY nm
	| NOT INDEXED
trigger_cmd	::=	UPDATE orconf trnm tridxby SET setlist from where_opt scanpt
	| scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt
	| DELETE FROM trnm tridxby where_opt scanpt
	| scanpt select scanpt
raisetype	::=	ROLLBACK
	| ABORT
	| FAIL
key_opt	::=	/* empty */
	| KEY expr
database_kw_opt	::=	DATABASE
	| /* empty */
add_column_fullname	::=	fullname
kwcolumn_opt	::=	/* empty */
	| COLUMNKW
create_vtab	::=	createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm
vtabarglist	::=	vtabarg
	| vtabarglist COMMA vtabarg
vtabarg	::=	/* empty */
	| vtabarg vtabargtoken
vtabargtoken	::=	ANY
	| lp anylist RP
lp	::=	LP
anylist	::=	/* empty */
	| anylist LP anylist RP
	| anylist ANY
with	::=	/* empty */
	| WITH wqlist
	| WITH RECURSIVE wqlist
wqas	::=	AS
	| AS MATERIALIZED
	| AS NOT MATERIALIZED
wqitem	::=	nm eidlist_opt wqas LP select RP
wqlist	::=	wqitem
	| wqlist COMMA wqitem
windowdefn_list	::=	windowdefn
	| windowdefn_list COMMA windowdefn
windowdefn	::=	nm AS LP window RP
window	::=	PARTITION BY nexprlist orderby_opt frame_opt
	| nm PARTITION BY nexprlist orderby_opt frame_opt
	| ORDER BY sortlist frame_opt
	| nm ORDER BY sortlist frame_opt
	| frame_opt
	| nm frame_opt
frame_opt	::=	/* empty */
	| range_or_rows frame_bound_s frame_exclude_opt
	| range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt
range_or_rows	::=	(RANGE|ROWS|GROUPS)
frame_bound_s	::=	frame_bound
	| UNBOUNDED PRECEDING
frame_bound_e	::=	frame_bound
	| UNBOUNDED FOLLOWING
frame_bound	::=	(expr PRECEDING|FOLLOWING)
	| CURRENT ROW
frame_exclude_opt	::=	/* empty */
	| EXCLUDE frame_exclude
frame_exclude	::=	NO OTHERS
	| CURRENT ROW
	| (GROUP|TIES)
window_clause	::=	WINDOW windowdefn_list
filter_over	::=	filter_clause over_clause
	| over_clause
	| filter_clause
over_clause	::=	OVER LP window RP
	| OVER nm
filter_clause	::=	FILTER LP WHERE expr RP

// Tokens

ABORT ::= "ABORT"
ACTION ::= "ACTION"
ADD ::= "ADD"
AFTER ::= "AFTER"
ALL ::= "ALL"
ALTER ::= "ALTER"
ALWAYS ::= "ALWAYS"
ANALYZE ::= "ANALYZE"
AND ::= "AND"
AS ::= "AS"
ASC ::= "ASC"
ATTACH ::= "ATTACH"
AUTOINCREMENT ::= "AUTOINCREMENT"
BEFORE ::= "BEFORE"
BEGIN ::= "BEGIN"
BETWEEN ::= "BETWEEN"
BY ::= "BY"
CASCADE ::= "CASCADE"
CASE ::= "CASE"
CAST ::= "CAST"
CHECK ::= "CHECK"
COLLATE ::= "COLLATE"
COLUMN ::= "COLUMN"
COMMIT ::= "COMMIT"
CONFLICT ::= "CONFLICT"
CONSTRAINT ::= "CONSTRAINT"
CREATE ::= "CREATE"
CROSS ::= "CROSS"
CURRENT ::= "CURRENT"
CURRENT_DATE ::= "CURRENT_DATE"
CURRENT_TIME ::= "CURRENT_TIME"
CURRENT_TIMESTAMP ::= "CURRENT_TIMESTAMP"
DATABASE ::= "DATABASE"
DEFAULT ::= "DEFAULT"
DEFERRED ::= "DEFERRED"
DEFERRABLE ::= "DEFERRABLE"
DELETE ::= "DELETE"
DESC ::= "DESC"
DETACH ::= "DETACH"
DISTINCT ::= "DISTINCT"
DO ::= "DO"
DROP ::= "DROP"
END ::= "END"
EACH ::= "EACH"
ELSE ::= "ELSE"
ESCAPE ::= "ESCAPE"
EXCEPT ::= "EXCEPT"
EXCLUSIVE ::= "EXCLUSIVE"
EXCLUDE ::= "EXCLUDE"
EXISTS ::= "EXISTS"
EXPLAIN ::= "EXPLAIN"
FAIL ::= "FAIL"
FILTER ::= "FILTER"
FIRST ::= "FIRST"
FOLLOWING ::= "FOLLOWING"
FOR ::= "FOR"
FOREIGN ::= "FOREIGN"
FROM ::= "FROM"
FULL ::= "FULL"
GENERATED ::= "GENERATED"
GLOB ::= "GLOB"
GROUP ::= "GROUP"
GROUPS ::= "GROUPS"
HAVING ::= "HAVING"
IF ::= "IF"
IGNORE ::= "IGNORE"
IMMEDIATE ::= "IMMEDIATE"
IN ::= "IN"
INDEX ::= "INDEX"
INDEXED ::= "INDEXED"
INITIALLY ::= "INITIALLY"
INNER ::= "INNER"
INSERT ::= "INSERT"
INSTEAD ::= "INSTEAD"
INTERSECT ::= "INTERSECT"
INTO ::= "INTO"
IS ::= "IS"
ISNULL ::= "ISNULL"
JOIN ::= "JOIN"
KEY ::= "KEY"
LAST ::= "LAST"
LEFT ::= "LEFT"
LIKE ::= "LIKE"
LIMIT ::= "LIMIT"
MATCH ::= "MATCH"
MATERIALIZED ::= "MATERIALIZED"
NATURAL ::= "NATURAL"
NO ::= "NO"
NOT ::= "NOT"
NOTHING ::= "NOTHING"
NOTNULL ::= "NOTNULL"
NULL ::= "NULL"
NULLS ::= "NULLS"
OF ::= "OF"
OFFSET ::= "OFFSET"
ON ::= "ON"
OR ::= "OR"
ORDER ::= "ORDER"
OTHERS ::= "OTHERS"
OUTER ::= "OUTER"
OVER ::= "OVER"
PARTITION ::= "PARTITION"
PLAN ::= "PLAN"
PRAGMA ::= "PRAGMA"
PRECEDING ::= "PRECEDING"
PRIMARY ::= "PRIMARY"
QUERY ::= "QUERY"
RAISE ::= "RAISE"
RANGE ::= "RANGE"
RECURSIVE ::= "RECURSIVE"
REFERENCES ::= "REFERENCES"
REGEXP ::= "REGEXP"
REINDEX ::= "REINDEX"
RELEASE ::= "RELEASE"
RENAME ::= "RENAME"
REPLACE ::= "REPLACE"
RESTRICT ::= "RESTRICT"
RETURNING ::= "RETURNING"
RIGHT ::= "RIGHT"
ROLLBACK ::= "ROLLBACK"
ROW ::= "ROW"
ROWS ::= "ROWS"
SAVEPOINT ::= "SAVEPOINT"
SELECT ::= "SELECT"
SET ::= "SET"
TABLE ::= "TABLE"
TEMP ::= "TEMP"
TEMPORARY ::= "TEMPORARY"
THEN ::= "THEN"
TIES ::= "TIES"
TO ::= "TO"
TRANSACTION ::= "TRANSACTION"
TRIGGER ::= "TRIGGER"
UNBOUNDED ::= "UNBOUNDED"
UNION ::= "UNION"
UNIQUE ::= "UNIQUE"
UPDATE ::= "UPDATE"
USING ::= "USING"
VACUUM ::= "VACUUM"
VALUES ::= "VALUES"
VIEW ::= "VIEW"
VIRTUAL ::= "VIRTUAL"
WHEN ::= "WHEN"
WHERE ::= "WHERE"
WINDOW ::= "WINDOW"
WITH ::= "WITH"
WITHOUT ::= "WITHOUT"

PIPE ::= '|'
MINUS ::= '-'
LT ::= '<'
GT ::= '>'
EQ ::= '='
BANG ::= '!'
SLASH ::= '/'
LP ::= '('
RP ::= ')'
SEMI ::= ';'
PLUS ::= '+'
STAR ::= '*'
PERCENT ::= '%'
COMMA ::= ','
AND ::= '&'
TILDA ::= '~'
DOT ::= '.'
====