{"record":{"id":"d2ea48b24485870e","repo":"antlr/antlr4","slug":"precedence-predicates-are-not-supported-in-lexers-d2ea48","errorCode":null,"errorMessage":"Precedence predicates are not supported in lexers.","messagePattern":"Precedence predicates are not supported in lexers\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"runtime/CSharp/src/Atn/LexerATNSimulator.cs","lineNumber":499,"sourceCode":"\n\t\tprotected LexerATNConfig GetEpsilonTarget(ICharStream input,\n\t\t\t\t\t\t\t\t\t\t\t   LexerATNConfig config,\n\t\t\t\t\t\t\t\t\t\t\t   Transition t,\n\t\t\t\t\t\t\t\t\t\t\t   ATNConfigSet configs,\n\t\t\t\t\t\t\t\t\t\t\t   bool speculative,\n\t\t\t\t\t\t\t\t\t\t\t   bool treatEofAsEpsilon)\n\t\t{\n\t\t\tLexerATNConfig c = null;\n\t\t\tswitch (t.TransitionType)\n\t\t\t{\n\t\t\t\tcase TransitionType.RULE:\n\t\t\t\t\tRuleTransition ruleTransition = (RuleTransition)t;\n\t\t\t\t\tPredictionContext newContext = new SingletonPredictionContext(config.context, ruleTransition.followState.stateNumber);\n\t\t\t\t\tc = new LexerATNConfig(config, t.target, newContext);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase TransitionType.PRECEDENCE:\n\t\t\t\t\tthrow new Exception(\"Precedence predicates are not supported in lexers.\");\n\n\t\t\t\tcase TransitionType.PREDICATE:\n\t\t\t\t\t/*  Track traversing semantic predicates. If we traverse,\n\t\t\t\t\t we cannot add a DFA state for this \"reach\" computation\n\t\t\t\t\t because the DFA would not test the predicate again in the\n\t\t\t\t\t future. Rather than creating collections of semantic predicates\n\t\t\t\t\t like v3 and testing them on prediction, v4 will test them on the\n\t\t\t\t\t fly all the time using the ATN not the DFA. This is slower but\n\t\t\t\t\t semantically it's not used that often. One of the key elements to\n\t\t\t\t\t this predicate mechanism is not adding DFA states that see\n\t\t\t\t\t predicates immediately afterwards in the ATN. For example,\n\n\t\t\t\t\t a : ID {p1}? | ID {p2}? ;\n\n\t\t\t\t\t should create the start state for rule 'a' (to save start state\n\t\t\t\t\t competition), but should not create target of ID state. The\n\t\t\t\t\t collection of ATN states the following ID references includes\n\t\t\t\t\t states reached by traversing predicates. Since this is when we","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/CSharp/src/Atn/LexerATNSimulator.cs#L481-L517","documentation":"LexerATNSimulator.AdaptivePredict/closure path throws when it encounters a PRECEDENCE transition while running a lexer ATN. Precedence predicates ({p>N}?) are a parser-only feature: they resolve left-recursive rule precedence, which has no meaning during tokenization. If a serialized lexer ATN contains a precedence transition, the ATN itself is malformed for lexing purposes and the simulator refuses to continue.","triggerScenarios":"A lexer ATN containing a TransitionType.PRECEDENCE edge is executed by LexerATNSimulator (the closure loop hits the PRECEDENCE case and throws). Typically caused by building a LexerInterpreter or LexerATNSimulator over an ATN that was actually serialized from a parser grammar, or by a grammar/tool bug that embedded precedence predicates in lexer rules.","commonSituations":"Passing a parser ATN into a lexer interpreter (mixing up the two serialized ATNs); using a left-recursive-rule grammar file as both lexer and parser source; tool/runtime version skew producing malformed serialized lexer ATNs.","solutions":["Check atn.grammarType == ATNType.Lexer before constructing LexerInterpreter/LexerATNSimulator, and use the lexer's own serialized ATN","Remove precedence predicates ({p>...}?) from lexer rules; if you need precedence-dependent lexing, restructure so the parser handles it","Regenerate the grammar with the ANTLR tool version matching your runtime to rule out malformed serialization"],"exampleFix":"// before\n// atn actually came from MyParser (grammar MyParser; parser rules...)\nvar lexInterp = new LexerInterpreter(\"MyLexer\", vocab, ruleNames, modeNames, atn, input); // throws at runtime\n\n// after\nif (atn.grammarType != ATNType.Lexer)\n    throw new InvalidOperationException(\"Expected a lexer ATN, got \" + atn.grammarType);\nvar lexInterp = new LexerInterpreter(\"MyLexer\", vocab, ruleNames, modeNames, atn, input);","handlingStrategy":"validation","validationCode":"if (atn.grammarType != ATNType.Lexer)\n    throw new InvalidOperationException($\"Expected lexer ATN but got {atn.grammarType}; check which serialized ATN you passed.\");","typeGuard":"static bool IsLexerAtn(ATN atn) => atn != null && atn.grammarType == ATNType.Lexer;","tryCatchPattern":null,"preventionTips":["Keep lexer and parser serialized ATNs in separately named members","Never use precedence predicates in lexer rules; run grammar through the tool with warnings-as-errors"],"tags":["antlr","csharp","lexer","precedence","atn","api-misuse"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}