Skip to content

Rungs

For an introduction to the DSL vocabulary, see Core Concepts.

Rung comments

Use comment() before a rung to attach a comment:

comment("Initialize the light system.")
with Rung(Button):
    out(Light)

Multi-line comments use triple-quoted strings (automatically dedented and stripped):

comment("""
    This rung controls the main light.
    It activates when Button is pressed.
""")
with Rung(Button):
    out(Light)

Comments are limited to 1400 characters. Exceeding this raises ValueError.

Branching

branch() creates a parallel path within a rung. The branch condition is ANDed with the parent rung's condition.

with Rung(First):          # ① Evaluate: First
    out(Third)             # ③ Execute
    with branch(Second):   # ② Evaluate: First AND Second
        out(Fourth)        # ④ Execute
    out(Fifth)             # ⑤ Execute

Three rules:

  1. Conditions evaluate before instructions. ① and ② are resolved before ③ ④ ⑤ run. A branch ANDs its own condition with the parent rung's.
  2. Instructions execute in source order. ③ → ④ → ⑤, as written — not "all rung, then all branch."
  3. Each rung starts fresh. The next rung sees the state as it was left after the previous rung's instructions.

Nested branches

Branches can nest inside other branches. All conditions at every depth evaluate against the same rung-entry snapshot.

with Rung(A):
    out(X)
    with branch(B):
        out(Y)
        with branch(C):
            out(Z)  # Executes when A AND B AND C

This exists so codegen can faithfully represent imported ladder topologies. For hand-written logic, flat branches are clearer.

Continued rungs

Rung.continued() tells a rung to reuse the previous rung's condition snapshot instead of freezing a fresh one. All conditions in the continued rung evaluate against the pre-instruction state from the original rung.

with Rung(A):
    out(X)
with Rung(B).continued():
    out(Y)  # B evaluated against pre-X state (same snapshot as A)

This models the Click ladder editor pattern where a single visual rung has multiple independent wires to the right power rail. Without .continued(), splitting into separate Rung blocks would give each its own snapshot — changing behavior if the first rung's instructions mutate a tag that the second rung's conditions reference.

Multiple continued rungs chain — they all share the original snapshot:

with Rung(A):
    copy(10, Counter)
with Rung(Counter == 0).continued():
    out(X)  # True: Counter was 0 at snapshot time
with Rung(Counter == 10).continued():
    out(Y)  # False: Counter was 0 at snapshot time, not 10 yet

A normal (non-continued) rung breaks the chain and takes a fresh snapshot.

Constraints:

  • continued() cannot be the first rung in a program or subroutine.
  • Continued rungs cannot have their own .comment.

Like nested branches, this exists primarily for codegen round-tripping. For hand-written logic, separate rungs with fresh snapshots are easier to reason about.