{"record":{"id":"d6ef1091a5234c97","repo":"gleam-lang/gleam","slug":"expression-not-fully-reduced","errorCode":null,"errorMessage":"Expression not fully reduced.","messagePattern":"Expression not fully reduced\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler-core/src/parse.rs","lineNumber":4926,"sourceCode":"// Higher number means higher precedence.\n// All operators are left associative.\n\n/// Simple-Precedence-Parser, handle seeing an operator or end\nfn handle_operator<A>(\n    next_operator: Option<(Spanned, u8)>,\n    operator_stack: &mut Vec<(Spanned, u8)>,\n    expression_stack: &mut Vec<A>,\n    do_reduce: &impl Fn(Spanned, &mut Vec<A>),\n) -> Option<A> {\n    let mut next_operator = next_operator;\n    loop {\n        match (operator_stack.pop(), next_operator.take()) {\n            (None, None) => match expression_stack.pop() {\n                Some(fin) => {\n                    if expression_stack.is_empty() {\n                        return Some(fin);\n                    } else {\n                        panic!(\"Expression not fully reduced.\")\n                    }\n                }\n                _ => {\n                    return None;\n                }\n            },\n\n            (None, Some(operator)) => {\n                operator_stack.push(operator);\n                break;\n            }\n\n            (Some((operator, _)), None) => do_reduce(operator, expression_stack),\n\n            (Some((left_operator, left_precedence)), Some((right_operator, right_precedence))) => {\n                match left_precedence.cmp(&right_precedence) {\n                    // all operators are left associative\n                    Ordering::Greater | Ordering::Equal => {","sourceCodeStart":4908,"sourceCodeEnd":4944,"githubUrl":"https://github.com/gleam-lang/gleam/blob/15b07c783065c92bb016b1b74ed0995a5259850f/compiler-core/src/parse.rs#L4908-L4944","documentation":"At the end of the expression precedence-climbing loop, the parser expects exactly one fully reduced expression on the stack. If more than one expression remains after popping the final one, the reduction invariants were violated, so it panics. The `_ => return None` arm handles genuine parse failure (returns `None` so a proper parse error is reported).","triggerScenarios":"The infix expression parser's operator/operand stack bookkeeping goes out of sync — e.g. an operator handler pushes an expression without consuming one, or a bug in a new operator's precedence handling leaves two unreduced expressions stacked when the input ends.","commonSituations":"Gleam parser development/regressions; extremely rare for end users, since malformed input normally returns None and produces a normal parse error instead.","solutions":["Reduce the input to a minimal snippet that triggers the panic and file a Gleam compiler bug with it.","If you are modifying the parser, audit operator handlers so every `push_operand` is balanced by `do_reduce_expression` calls.","As a workaround, reformulate the expression (add explicit parentheses) — though a panic here usually indicates a compiler bug, not user error."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let parsed = std::panic::catch_unwind(|| parse_expression(tokens));\nmatch parsed {\n    Ok(Some(expr)) => expr,\n    _ => report_compiler_bug(\"Expression not fully reduced\"),\n}","preventionTips":["Treat this panic as a compiler bug: reduce input to a minimal snippet and report it.","Keep the Gleam compiler up to date; parser stack bugs get fixed in patches.","When modifying parse.rs, keep every operand push balanced with operator reductions."],"tags":["parser","panic","precedence","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"15b07c783065c92bb016b1b74ed0995a5259850f","analyzedAt":"2026-09-14T11:14:59.388Z","contentChangedAt":"2026-09-14T11:14:59.388Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}