# Root: Handles empty arrays, loose lists, or coordinate blocks root ::= (block-sequence | empty-scene)? ws? empty-scene ::= "[]" block-sequence ::= block (ws-or-nl+ block)* # A block is a raw tag name or a fully positioned layout primitive block ::= primitive (anchor? vector)? primitive ::= "[" [a-zA-Z0-9_-]+ "]" anchor ::= "[abs]" | "[@" digits ("," ws? "@" digits)* "]" # Vector strings support flat numbers, expressions, nested matrices, or config parameters vector ::= "[" value ("," ws? value)* "]" value ::= nested-array | parameter-assignment | expression | attribute | signed-num | boolean-flag | variable # Captures matrices like [1,1,1.5] or [-0.6,0.6,-0.4] cleanly nested-array ::= "[" value ("," ws? value)* "]" # Layout modifier keywords or attributes attribute ::= "noise=" signed-num | "mesh" | "surf" | "-%" # Fixed: Explicitly wrapped multi-line assignment alternates in parentheses parameter-assignment ::= ( tag-name "=(" ws? [xyz] ws? "," ws? signed-num ws? ")" | tag-name "(" ws? [xyz] ws? "," ws? num ws? ")" | tag-name "(" num ")" ) # Expressions handle explicit signs directly adjacent to implicit multi-variables expression ::= term (op term)* # Fix: Explicitly wrapped the multi-line term rule alternates in parentheses to resolve the parsing error term ::= ( variable multiplication-suffix? | signed-num variable? | sign? variable | "sym(" ws? expression ws? ")" ) multiplication-suffix ::= "*" (variable | signed-num) variable ::= "fw" | "hw" | "fd" | "hd" | "fh" | "hh" | "bd" | "bh" | "@idx" | "@" digits boolean-flag ::= "surf" | "-%" # STRICT number definition - only one sign character allowed total positive-num ::= digits ("." digits)? negative-num ::= "-" digits ("." digits)? num ::= positive-num | negative-num # Allows things like +5 or -5 without doubling up a negative_num's minus sign signed-num ::= sign? positive-num | negative-num # SINGLE sign only - completely kills any '--' variants instantly sign ::= "+" | "-" op ::= "+" | "-" | "*" | "/" # Explicit named primitives to ensure perfect alignment with the GBNF compiler loop tag-name ::= [a-zA-Z_-]+ digits ::= [0-9]+ ws ::= [ \t]+ ws-or-nl ::= [ \t\r\n]+