Skip to content

fix(token): default associative "right" to match yacc/bison shift-first behavior#48

Merged
henrylee97 merged 2 commits into
masterfrom
fix/dangling-else-default-assoc
May 13, 2026
Merged

fix(token): default associative "right" to match yacc/bison shift-first behavior#48
henrylee97 merged 2 commits into
masterfrom
fix/dangling-else-default-assoc

Conversation

@henrylee97
Copy link
Copy Markdown
Member

Summary

  • Token.associative default changed from "left" to "right"
  • Without an explicit precedence declaration, S/R conflicts now resolve
    to shift (matching yacc/bison behavior) instead of reduce
  • Fixes the dangling-else problem: if c1 then if c2 then s1 else s2
    now correctly produces IfThenD(c1, IfThenElseD(c2, s1, s2))

Root Cause

The S/R conflict resolution condition in parser.py:

if item.precedence > symbol.precedence or (
    item.precedence == symbol.precedence
    and symbol.associative == "left"
):
    resolve to reduce

With the old "left" default, the second branch was always true at
precedence 0, so reduce always won — contradicting yacc/bison, which
defaults to shift when no declaration is present.

Migration
Grammars that relied on the implicit left-associativity of undeclared
tokens must now add an explicit associative = "left" to their token
class (as done for PLUS_SR in the test suite).

Test Plan

  • test_dangling_else_inner_if_gets_else — xfail removed, now passes
  • test_default_sr_conflict_is_left_associative — still passes with explicit associative = "left" on PLUS_SR
  • Full test suite: 61 passed
  • pyright: 0 errors

henrylee97 and others added 2 commits May 13, 2026 01:11
Without an explicit precedence declaration, S/R conflicts now resolve
to shift (matching yacc/bison behavior) instead of reduce. Previously
the "left" default caused reduce to always win at precedence 0, which
made the dangling-else attach to the outer if instead of the inner one.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Add explicit associative="left" to PLUS_SR so the left-associativity
  test continues to pass under the new "right" default
- Update Section 2 header and docstring to reflect that left-assoc now
  requires an explicit declaration rather than being the default
- Remove @pytest.mark.xfail from test_dangling_else_inner_if_gets_else;
  else now correctly binds to the inner if (shift wins at precedence 0)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@henrylee97 henrylee97 self-assigned this May 13, 2026
@henrylee97 henrylee97 added the bug Something isn't working label May 13, 2026
@henrylee97 henrylee97 enabled auto-merge (squash) May 13, 2026 01:14
@github-actions
Copy link
Copy Markdown

Pytest Report

Test Results

  • Tests: 61
  • Errors: 0
  • Failures: 0
  • Skipped: 0

Comment by ✨sambyeol/publish-pytest-action

@github-actions
Copy link
Copy Markdown

Pytest Report

Test Results

  • Tests: 61
  • Errors: 0
  • Failures: 0
  • Skipped: 0

Comment by ✨sambyeol/publish-pytest-action

@github-actions
Copy link
Copy Markdown

Super-linter summary

Language Validation result
PYTHON_BLACK Pass ✅
PYTHON_ISORT Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@henrylee97 henrylee97 merged commit bca4fdd into master May 13, 2026
7 checks passed
@henrylee97 henrylee97 deleted the fix/dangling-else-default-assoc branch May 13, 2026 01:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant