BNF is a formal notation for defining exactly which strings count as valid syntax. The only real way to understand it is to build a string yourself, rule by rule, making genuine choices, not watching someone else's example play out. Every tool on this page is driven by your choices.
Angle brackets mark a non-terminal, something that still needs expanding. Quoted characters are terminals, actual characters that appear in the final string. ::= means "is defined as", | means "or".
Below is the current derivation string. Whenever it contains a non-terminal, you'll be offered every rule that could expand it. Your choices decide what gets built, try producing several different valid strings, not just one.
Same grammar, drawn as a diagram instead of written as rules. Click your way through it, left to right, the string builds as you go, exactly mirroring the choices you'd make in Section 2.
This time you're not exploring freely, you're aiming at a specific string. Every choice is checked immediately against the target, a wrong choice is flagged the instant you make it, not at the end.
Checked against the signed-integer grammar from Section 1 specifically, optional +/-, then one or more digits, nothing else. Not against "looks like a number" in general, and not against the identifier grammar coming up in Section 6, a string can satisfy one of these grammars and fail the other. Try something you built above, or something deliberately broken.
Numbers are a clean teaching example, but BNF's actual job is describing programming language syntax. Every language needs a rule for what counts as a valid variable name, here's a genuine one, built the same way.
This is precisely the rule the symbol table in the Translators lesson relies on: a name must start with a letter, then any mix of letters and digits is allowed, which is exactly why "1x" is never a legal variable name but "x1" is.
Watch how this feels different from Section 2's grammar as you use it below. <integer> ::= <digit> | <digit> <integer> is right-recursive (the recursive call comes last), so it immediately asks for a real digit at every step. This identifier rule is left-recursive (the recursive call comes first), so it asks you to decide the whole shape before any real letters or digits get filled in. Same underlying idea, recursion, genuinely different hands-on experience.
This diagram looks structurally different from Section 3's, and it should: a mandatory first box (you can't skip straight to the loop, a letter always comes first), then a loop that itself branches in two, letter or digit, every time round. That branch-inside-a-loop is something the signed-integer diagram never needed.
Real language grammars are built by combining smaller rules, not writing one giant rule from scratch. This one reuses both grammars already on this page, unchanged.
<identifier> and <signed-integer> are exactly the same non-terminals defined earlier on this page, nothing about them changes here, they're simply being reused as building blocks inside a bigger rule. This is precisely how real language specifications are actually organised.
The two large boxes aren't drawn out here, they're the exact diagrams from Sections 3 and 7, referenced rather than repeated, the same way a real language spec would say "see the identifier rule" instead of redrawing it every time.