Skip to content

Ruby backend: module-level constants emitted as local variables, invisible inside class methods #328

@ldayton

Description

@ldayton

Summary

Python class-level constants (e.g., ParserStateFlags.PST_EOFTOKEN = 4096) are lowered to Ruby module-level local variables (e.g., parserstateflags_pst_eoftoken = 4096). These are invisible inside class method bodies in Ruby, causing NameError: undefined local variable at runtime.

Reproduction

Source Python:

class ParserStateFlags:
    PST_EOFTOKEN = 4096

class Lexer:
    def _read_word_internal(self):
        if self._parser_state & ParserStateFlags.PST_EOFTOKEN != 0:
            ...

Transpiled Ruby:

parserstateflags_pst_eoftoken = 4096  # module-level local variable

class Lexer
  def _read_word_internal
    if self._parser_state & parserstateflags_pst_eoftoken != 0  # NameError!

Expected Ruby:

PARSERSTATEFLAGS_PST_EOFTOKEN = 4096  # Ruby constant (uppercase)

class Lexer
  def _read_word_internal
    if self._parser_state & PARSERSTATEFLAGS_PST_EOFTOKEN != 0  # works

Impact

Fatal crash — the Parable Ruby backend can't parse anything. 80 module-level variables affected, 45+ references from inside class methods.

Affected variable families: parserstateflags_*, tokentype_*, dolbracestate_*, word_ctx_*, matchedpairflags_*.

Root cause

In Python, module-level variables are accessible from any scope. In Ruby, local variables are scoped to their enclosing block — class bodies create new scopes. The Ruby backend needs to emit these as Ruby constants (uppercase names) to make them accessible inside class methods.

Discovered via ldayton/Parable#413.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendbugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions