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 ::= '.' ====