{"record":{"id":"f616966ff23fa9e9","repo":"oobabooga/textgen","slug":"expecting-at-remaining-src-f61696","errorCode":null,"errorMessage":"expecting ::= at {remaining_src}","messagePattern":"expecting ::= at (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"modules/grammar/grammar_utils.py","lineNumber":268,"sourceCode":"    outbuf = []\n    remaining_src = parse_sequence(state, src, rule_name, outbuf, is_nested)\n    while remaining_src and remaining_src[0] == \"|\":\n        remaining_src = remove_leading_white_space(remaining_src[1:], True)\n        remaining_src = parse_sequence(state, remaining_src, rule_name, outbuf, is_nested)\n\n    state.grammar_encoding.append(rule_id)\n    state.grammar_encoding.extend(outbuf)\n    state.grammar_encoding.append(0)\n    return remaining_src\n\n\ndef parse_rule(state, src):\n    name, remaining_src = parse_name(src)\n    remaining_src = remove_leading_white_space(remaining_src, False)\n    rule_id = get_symbol_id(state, name)\n\n    if remaining_src[:3] != \"::=\":\n        raise RuntimeError(\"expecting ::= at \" + remaining_src)\n    remaining_src = remove_leading_white_space(remaining_src[3:], True)\n\n    remaining_src = parse_alternates(state, remaining_src, name, rule_id, False)\n\n    if remaining_src and remaining_src[0] == \"\\r\":\n        remaining_src = remaining_src[2:] if remaining_src[1] == \"\\n\" else remaining_src[1:]\n    elif remaining_src and remaining_src[0] == \"\\n\":\n        remaining_src = remaining_src[1:]\n    elif remaining_src:\n        raise RuntimeError(\"expecting newline or end at \" + remaining_src)\n    return remove_leading_white_space(remaining_src, True)\n\n\ndef parse_ebnf(src):\n    try:\n        state = ParseState()\n        grammar_repr = remove_leading_white_space(src, True)\n        last_grammar_repr = \"\"","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/oobabooga/textgen/blob/ed888c71f221df552750e1834b3654abab8ae345/modules/grammar/grammar_utils.py#L250-L286","documentation":"Raised by parse_rule (modules/grammar/grammar_utils.py:268): after reading a rule name, the very next token must be the GBNF definition operator '::=' (within whitespace). A single '=', '==', ':=', or a missing operator entirely triggers this error, echoing the offending remainder.","triggerScenarios":"Grammar lines like 'root = \"a\"' (single '='), 'root := \"a\"', 'root ::= missing' handled elsewhere but 'root \"a\"' (operator omitted), or '::=' split by a stray comment/character.","commonSituations":"Writing BNF (which uses '::=' or '::='-variants like ':=='); mixing EBNF '=' style from tools like lodash-style generators or Lark grammars; typos and autocorrect mangling '::='; extra colons (':=:', '::==').","solutions":["Use exactly '::=' between every rule name and its body (two colons, equals).","Look at the echoed remaining_src to see the actual operator bytes — it exposes invisible characters or typos.","If converting from another grammar format (Lark, EBNF), script the conversion of '=' to '::='."],"exampleFix":"// before\nroot = \"a\" | \"b\"\n\n// after\nroot ::= \"a\" | \"b\"\n","handlingStrategy":"validation","validationCode":"import re\n\nDEF_OP = re.compile(r'^\\s*[A-Za-z0-9_-]+\\s*::=')\n\ndef all_rules_use_gbnf_op(grammar: str) -> list[str]:\n    bad = []\n    for line in grammar.splitlines():\n        s = line.strip()\n        if s and not s.startswith('#') and not DEF_OP.match(line):\n            bad.append(s.split('::=')[0].split('=')[0].strip() or s)\n    return bad\n","typeGuard":"def is_gbnf_rule(line: str) -> bool:\n    return bool(DEF_OP.match(line)) or line.strip().startswith('#') or not line.strip()\n","tryCatchPattern":"try:\n    parse_grammar(grammar_text)\nexcept RuntimeError as e:\n    if '::=' in str(e):\n        raise ValueError(\"Rules must use exactly '::=' (two colons then equals)\") from None\n    raise\n","preventionTips":["When porting EBNF/Lark grammars, script the '=' -> '::=' rewrite and lint the result.","Copy the operator, don't retype it — invisible Unicode look-alikes cause this exact error.","Keep a one-line canonical example ('root ::= \"a\"') at the top of your grammar files as a format anchor."],"tags":["grammar","gbnf","syntax-error","operator"],"backgroundTag":null,"analyzedSha":"ed888c71f221df552750e1834b3654abab8ae345","analyzedAt":"2026-08-15T05:24:21.000Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}