aboutsummaryrefslogtreecommitdiffstats
path: root/spec/grammar
diff options
context:
space:
mode:
authorAhmed <git@gumx.cc>2026-06-14 01:49:48 +0300
committerAhmed <git@gumx.cc>2026-06-14 01:49:48 +0300
commit41f8d3025f041aaab98fcc3511cb6ec2198f76a7 (patch)
treec147d5a547c3e2d7b49546157f324cbd80e88c48 /spec/grammar
init: vibed
Diffstat (limited to 'spec/grammar')
-rw-r--r--spec/grammar/UGI_GRAMMAR_v0.abnf148
-rw-r--r--spec/grammar/UGI_GRAMMAR_v0.ebnf167
2 files changed, 315 insertions, 0 deletions
diff --git a/spec/grammar/UGI_GRAMMAR_v0.abnf b/spec/grammar/UGI_GRAMMAR_v0.abnf
new file mode 100644
index 0000000..d0f7da1
--- /dev/null
+++ b/spec/grammar/UGI_GRAMMAR_v0.abnf
@@ -0,0 +1,148 @@
+; UGI Grammar v0 — RFC 5234 ABNF
+; Generated from ugi_registry_v0.json
+;
+; Compatible with abnf.dev/abnf2svg
+
+; === Top-level ===
+
+ugi-string = "ugi:" version "@" handle ":" field-list
+
+version = 1*DIGIT ["/" 1*DIGIT]
+
+; handle: alphanumeric, hyphens, underscores, dots
+handle = 1*(ALPHA / DIGIT / %x2D / %x5F / %x2E)
+
+field-list = field *("," field)
+
+; === Field dispatch ===
+
+field = field-a / field-b / field-c / field-d / field-e / field-f / field-g / field-h / field-i / field-j / field-k / field-l / field-m / field-o / field-p / field-q / field-r / field-s / field-t / field-v / field-w / field-x / field-y / field-z
+
+; === Common rules ===
+
+ODIGIT = %x30-37
+
+paid = "$"
+
+aspire = "+" ODIGIT
+
+paid-aspire = paid [aspire]
+
+modifier = paid-aspire / aspire
+
+alternative = "/" ODIGIT
+
+; custom sub-ID: ~ followed by 2+ alpha chars
+custom-sub = "~" 2*ALPHA
+
+; sub-ID: 1-4 alpha chars
+sub-id = 1*4ALPHA
+
+; === Per-field rules ===
+
+; a — Age (direct)
+field-a = %x61 ODIGIT
+
+; b — Build (special)
+field-b = %x62 ODIGIT [aspire] "/" ODIGIT [aspire]
+
+; c — Clothing (direct)
+field-c = %x63 ODIGIT [alternative / aspire]
+
+; d — Editor / IDE (multi)
+field-d-entry = (sub-id / custom-sub) ODIGIT [aspire]
+field-d = %x64 1*field-d-entry
+
+; e — Education (direct)
+field-e = %x65 ODIGIT [aspire]
+
+; f — Food / Cooking (direct)
+field-f = %x66 ODIGIT [modifier]
+
+; g — Geek Specialization (special)
+g-domain = 2*4ALPHA ["$"]
+g-custom = "~" 2*ALPHA
+field-g = %x67 (g-domain / g-custom) *("/" (g-domain / g-custom))
+
+; h — Hair (multi)
+field-h-entry = (sub-id / custom-sub) ODIGIT [aspire]
+field-h = %x68 1*field-h-entry
+
+; i — AI Stance (direct)
+field-i = %x69 ODIGIT
+
+; j — Gaming (multi)
+field-j-entry = (sub-id / custom-sub) ODIGIT [modifier]
+field-j = %x6A 1*field-j-entry
+
+; k — Books (direct)
+field-k = %x6B ODIGIT [aspire]
+
+; l — Spoken Languages (multi)
+field-l-entry = 2ALPHA ODIGIT [modifier]
+field-l = %x6C 1*field-l-entry
+
+; m — Music (multi)
+field-m-entry = (sub-id / custom-sub) ODIGIT
+field-m = %x6D 1*field-m-entry
+
+; o — Operating System (multi)
+field-o-entry = (sub-id / custom-sub) ODIGIT [modifier]
+field-o = %x6F 1*field-o-entry
+
+; p — Programming Languages (multi)
+field-p-entry = (sub-id / custom-sub) ODIGIT [modifier]
+field-p = %x70 1*field-p-entry
+
+; q — Security / Hacking (direct)
+field-q = %x71 ODIGIT [alternative / modifier]
+
+; r — Religion (single)
+field-r = %x72 (sub-id / custom-sub) ODIGIT
+
+; s — Sex / Relationships (direct)
+field-s = %x73 ODIGIT [alternative / aspire]
+
+; t — TV / Film (multi)
+field-t-entry = (sub-id / custom-sub) ODIGIT
+field-t = %x74 1*field-t-entry
+
+; v — Politics (special)
+field-v = %x76 ODIGIT "/" ODIGIT
+
+; w — Hardware (direct)
+field-w = %x77 ODIGIT [modifier]
+
+; x — Comics / Manga (multi)
+field-x-entry = (sub-id / custom-sub) ODIGIT
+field-x = %x78 1*field-x-entry
+
+; y — Fitness (direct)
+field-y = %x79 ODIGIT [modifier]
+
+; z — Maker / DIY (direct)
+field-z = %x7A ODIGIT [modifier]
+
+; === Block format ===
+
+ugi-block = block-header CRLF block-first-line CRLF 1*(block-field-line CRLF) block-footer
+
+block-header = "------- BEGIN UGI BLOCK -------"
+block-footer = "-------- END UGI BLOCK --------"
+
+block-first-line = "v:" version SP "@" handle [SP block-geek-field]
+
+; In block format, G field uses uppercase and appears on first line
+block-geek-field = "G" (g-domain / g-custom) *("/" (g-domain / g-custom))
+
+block-field-line = block-field *(SP block-field)
+
+; Block fields use uppercase code prefix repeated per sub-ID
+block-field = ALPHA 1*(ALPHA / DIGIT / %x24 / %x2B / %x2F / %x7E)
+
+; === Core rules (RFC 5234) ===
+
+ALPHA = %x41-5A / %x61-7A
+DIGIT = %x30-39
+SP = %x20
+CRLF = %x0D.0A / %x0A
diff --git a/spec/grammar/UGI_GRAMMAR_v0.ebnf b/spec/grammar/UGI_GRAMMAR_v0.ebnf
new file mode 100644
index 0000000..e37f87f
--- /dev/null
+++ b/spec/grammar/UGI_GRAMMAR_v0.ebnf
@@ -0,0 +1,167 @@
+/* UGI Grammar v0 — W3C EBNF */
+/* Generated from ugi_registry_v0.json */
+/* Compatible with bottlecaps.de/rr/ui */
+
+/* === Top-level === */
+
+ugi_string ::= 'ugi:' version '@' handle ':' field_list
+
+version ::= digit+ ( '/' digit+ )?
+
+/* handle: alphanumeric, hyphens, underscores, dots */
+handle ::= ( alpha | digit | '-' | '_' | '.' )+
+
+field_list ::= field ( ',' field )*
+
+/* === Field dispatch === */
+
+field ::= field_a
+ | field_b
+ | field_c
+ | field_d
+ | field_e
+ | field_f
+ | field_g
+ | field_h
+ | field_i
+ | field_j
+ | field_k
+ | field_l
+ | field_m
+ | field_o
+ | field_p
+ | field_q
+ | field_r
+ | field_s
+ | field_t
+ | field_v
+ | field_w
+ | field_x
+ | field_y
+ | field_z
+
+/* === Common rules === */
+
+odigit ::= [0-7]
+
+paid ::= '$'
+
+aspire ::= '+' odigit
+
+paid_aspire ::= paid aspire?
+
+modifier ::= paid_aspire | aspire
+
+alternative ::= '/' odigit
+
+/* custom sub-ID: ~ followed by 2+ alpha chars */
+custom_sub ::= '~' alpha alpha alpha*
+
+/* sub-ID: 1-4 alpha chars */
+sub_id ::= alpha alpha? alpha? alpha?
+
+/* === Per-field rules === */
+
+/* a — Age (direct) */
+field_a ::= 'a' odigit
+
+/* b — Build (special) */
+field_b ::= 'b' odigit aspire? '/' odigit aspire?
+
+/* c — Clothing (direct) */
+field_c ::= 'c' odigit ( alternative | aspire )?
+
+/* d — Editor / IDE (multi) */
+field_d_entry ::= ( sub_id | custom_sub ) odigit ( aspire )?
+field_d ::= 'd' field_d_entry+
+
+/* e — Education (direct) */
+field_e ::= 'e' odigit ( aspire )?
+
+/* f — Food / Cooking (direct) */
+field_f ::= 'f' odigit ( modifier )?
+
+/* g — Geek Specialization (special) */
+g_domain ::= alpha alpha alpha? alpha? paid?
+g_custom ::= '~' alpha alpha alpha*
+field_g ::= 'g' ( g_domain | g_custom ) ( '/' ( g_domain | g_custom ) )*
+
+/* h — Hair (multi) */
+field_h_entry ::= ( sub_id | custom_sub ) odigit ( aspire )?
+field_h ::= 'h' field_h_entry+
+
+/* i — AI Stance (direct) */
+field_i ::= 'i' odigit
+
+/* j — Gaming (multi) */
+field_j_entry ::= ( sub_id | custom_sub ) odigit ( modifier )?
+field_j ::= 'j' field_j_entry+
+
+/* k — Books (direct) */
+field_k ::= 'k' odigit ( aspire )?
+
+/* l — Spoken Languages (multi) */
+field_l_entry ::= alpha alpha odigit ( modifier )?
+field_l ::= 'l' field_l_entry+
+
+/* m — Music (multi) */
+field_m_entry ::= ( sub_id | custom_sub ) odigit
+field_m ::= 'm' field_m_entry+
+
+/* o — Operating System (multi) */
+field_o_entry ::= ( sub_id | custom_sub ) odigit ( modifier )?
+field_o ::= 'o' field_o_entry+
+
+/* p — Programming Languages (multi) */
+field_p_entry ::= ( sub_id | custom_sub ) odigit ( modifier )?
+field_p ::= 'p' field_p_entry+
+
+/* q — Security / Hacking (direct) */
+field_q ::= 'q' odigit ( alternative | modifier )?
+
+/* r — Religion (single) */
+field_r ::= 'r' ( sub_id | custom_sub ) odigit
+
+/* s — Sex / Relationships (direct) */
+field_s ::= 's' odigit ( alternative | aspire )?
+
+/* t — TV / Film (multi) */
+field_t_entry ::= ( sub_id | custom_sub ) odigit
+field_t ::= 't' field_t_entry+
+
+/* v — Politics (special) */
+field_v ::= 'v' odigit '/' odigit
+
+/* w — Hardware (direct) */
+field_w ::= 'w' odigit ( modifier )?
+
+/* x — Comics / Manga (multi) */
+field_x_entry ::= ( sub_id | custom_sub ) odigit
+field_x ::= 'x' field_x_entry+
+
+/* y — Fitness (direct) */
+field_y ::= 'y' odigit ( modifier )?
+
+/* z — Maker / DIY (direct) */
+field_z ::= 'z' odigit ( modifier )?
+
+/* === Block format === */
+
+ugi_block ::= block_header newline block_first_line newline block_field_line+ block_footer
+
+block_header ::= '------- BEGIN UGI BLOCK -------'
+block_footer ::= '-------- END UGI BLOCK --------'
+
+block_first_line ::= 'v:' version ' @' handle ( ' ' block_geek_field )?
+
+block_geek_field ::= 'G' ( g_domain | g_custom ) ( '/' ( g_domain | g_custom ) )*
+
+block_field_line ::= block_field ( ' ' block_field )* newline
+
+block_field ::= alpha ( alpha | digit | '$' | '+' | '/' | '~' )+
+
+/* === Terminals === */
+
+alpha ::= [a-zA-Z]
+digit ::= [0-9]
+newline ::= #xD #xA | #xA