Skip to content

Memory block

pyrung.core.memory_block

Block-based memory regions and indirect addressing.

Block provides factory methods for creating Tags from typed memory regions. IndirectRef enables pointer/indirect addressing resolved at runtime. BlockRange represents contiguous ranges for block operations.

SlotConfig dataclass

Effective runtime policy for one block slot.

Block dataclass

Factory for creating Tags from a typed memory region.

Block defines a named, 1-indexed memory region where every address shares the same TagType. Indexing a Block returns a cached LiveTag. The block holds no runtime values — all values live in SystemState.tags.

Address bounds are inclusive on both ends: Block("DS", INT, 1, 100) defines addresses 1–100 (100 tags). Indexing outside this range raises IndexError. Slice syntax (block[1:10]) is rejected — use .select(start, end) instead.

For sparse blocks (e.g. Click X/Y banks with non-contiguous valid addresses), pass valid_ranges to restrict which addresses within [start, end] are legal.

Parameters:

Name Type Description Default
name str

Block prefix used to generate tag names (e.g. "DS""DS1", "DS2" …).

required
type TagType

TagType shared by all tags in this block.

required
start int

Inclusive lower bound address (must be ≥ 0).

required
end int

Inclusive upper bound address (must be ≥ start).

required
retentive bool

Whether tags in this block survive power cycles. Default False.

False
valid_ranges tuple[tuple[int, int], ...] | None

Optional tuple of (lo, hi) inclusive segments that constrain which addresses within [start, end] are accessible. Addresses outside all segments raise IndexError.

None
address_formatter Callable[[str, int], str] | None

Optional callable (block_name, addr) → str that overrides default tag name generation. Used by dialects for canonical display names like "X001".

None
Example
DS = Block("DS", TagType.INT, 1, 100)
DS[1]          # → LiveTag("DS1", TagType.INT)
DS[101]        # → IndexError

# Range for block operations:
DS.select(1, 10)   # → BlockRange, tags DS1..DS10

# Indirect (pointer) addressing:
idx = Int("Idx")
DS[idx]        # → IndirectRef, resolved at scan time
DS[idx + 1]    # → IndirectExprRef

rename_slot

rename_slot(addr: int, name: str) -> None

Set the first-class logical name for one slot before materialization.

clear_slot_name

clear_slot_name(addr: int) -> None

Clear a first-class slot name override for one address.

configure_slot

configure_slot(
    addr: int,
    *,
    retentive: bool | None = None,
    default: object = UNSET,
) -> None

Set per-slot runtime policy before this slot is materialized.

configure_range

configure_range(
    start: int,
    end: int,
    *,
    retentive: bool | None = None,
    default: object = UNSET,
) -> None

Set per-slot policy for all valid addresses in the inclusive window.

clear_slot_config

clear_slot_config(addr: int) -> None

Clear per-slot policy overrides for one address.

clear_range_config

clear_range_config(start: int, end: int) -> None

Clear per-slot policy overrides for all valid addresses in a window.

slot_config

slot_config(addr: int) -> SlotConfig

Return the effective runtime slot policy without materializing a Tag.

select

select(start: int, end: int) -> BlockRange
select(
    start: Tag | Expression, end: int | Tag | Expression
) -> IndirectBlockRange
select(
    start: int, end: Tag | Expression
) -> IndirectBlockRange
select(
    start: int | Tag | Any, end: int | Tag | Any
) -> BlockRange | IndirectBlockRange

Select an inclusive range of addresses for block operations.

Both start and end are inclusive: DS.select(1, 10) yields 10 tags (1, 2, … 10). This mirrors the block constructor convention and avoids the off-by-one confusion of Python's half-open slices.

For sparse blocks (valid_ranges set), returns only the valid addresses within the window — gaps are silently skipped.

Parameters:

Name Type Description Default
start int | Tag | Any

Start address. int for a static range; Tag or Expression for a dynamically-resolved range.

required
end int | Tag | Any

End address. int for a static range; Tag or Expression for a dynamically-resolved range.

required

Returns:

Type Description
BlockRange | IndirectBlockRange

BlockRange when both arguments are int (resolved at

BlockRange | IndirectBlockRange

definition time). IndirectBlockRange when either argument is a

BlockRange | IndirectBlockRange

Tag or Expression (resolved each scan at execution time).

Raises:

Type Description
ValueError

If start > end.

IndexError

If either bound is outside the block's [start, end].

Example
# Static range
DS.select(1, 100)              # BlockRange, DS1..DS100

# Sparse window (Click X bank)
x.select(1, 21)               # valid tags only: X001..X016, X021

# Dynamic range (resolved each scan)
DS.select(start_tag, end_tag)  # IndirectBlockRange

# Use with bulk instructions:
fill(0, DS.select(1, 10))
blockcopy(DS.select(1, 10), DD.select(1, 10))
search(">=", 100, DS.select(1, 100), result=Found, found=FoundFlag)

map_to

map_to(target: BlockRange) -> MappingEntry

Create a logical-to-hardware mapping entry.

InputBlock dataclass

Bases: Block

Factory for creating InputTag instances from a physical input memory region.

InputBlock is identical to Block except:

  • Indexing returns LiveInputTag (not LiveTag), so elements have .immediate.
  • Always non-retentive — physical inputs do not survive power cycles.

Use InputBlock when the tags represent real hardware inputs (sensors, switches, etc.). In simulation, values are supplied via runner.patch() or runner.add_force() during the Read Inputs scan phase.

Example
X = InputBlock("X", TagType.BOOL, 1, 16)
X[1]           # → LiveInputTag("X1", BOOL)
X[1].immediate # → ImmediateRef — bypass image table
X.select(1, 8) # → BlockRange for bulk operations

OutputBlock dataclass

Bases: Block

Factory for creating OutputTag instances from a physical output memory region.

OutputBlock is identical to Block except:

  • Indexing returns LiveOutputTag (not LiveTag), so elements have .immediate.
  • Always non-retentive — physical outputs do not survive power cycles.

Writes to OutputTag elements are immediately visible to subsequent rungs within the same scan (standard PLC behavior). The actual hardware write happens at the Write Outputs scan phase (phase 6).

Example
Y = OutputBlock("Y", TagType.BOOL, 1, 16)
Y[1]           # → LiveOutputTag("Y1", BOOL)
Y[1].immediate # → ImmediateRef — bypass image table
Y.select(1, 8) # → BlockRange for bulk operations

BlockRange dataclass

Contiguous range of addresses for block operations.

Attributes:

Name Type Description
block Block

Source Block.

start int

Starting address (inclusive).

end int

Ending address (inclusive).

addresses property

addresses: range | tuple[int, ...]

Return addresses in this block window, filtered by block rules.

tags

tags() -> list[Tag]

Return list of Tag objects for all addresses in this block.

reverse

reverse() -> BlockRange

Return this same window with address iteration reversed.

as_value

as_value() -> Any

Wrap this range for TXT->numeric character-value conversion.

as_ascii

as_ascii() -> Any

Wrap this range for TXT->numeric ASCII-code conversion.

IndirectBlockRange dataclass

Memory block with runtime-resolved bounds.

Wraps a Block with start/end that may be Tags or Expressions, resolved at scan time.

Attributes:

Name Type Description
block Block

Source Block.

start_expr int | Tag | Any

Start address (int, Tag, or Expression).

end_expr int | Tag | Any

End address (int, Tag, or Expression).

resolve_ctx

resolve_ctx(ctx: ScanContext) -> BlockRange

Resolve expressions to concrete BlockRange using ScanContext.

reverse

reverse() -> IndirectBlockRange

Return this same dynamic window with address iteration reversed.

as_value

as_value() -> Any

Wrap this range for TXT->numeric character-value conversion.

as_ascii

as_ascii() -> Any

Wrap this range for TXT->numeric ASCII-code conversion.

IndirectRef dataclass

Tag with runtime-resolved address via pointer.

IndirectRef wraps a Block and pointer Tag. The actual address is resolved from the pointer's value at scan time.

Attributes:

Name Type Description
block Block

Block to index into.

pointer Tag

Tag whose value determines the address.

resolve

resolve(state: SystemState) -> Tag

Resolve pointer value to concrete Tag.

Parameters:

Name Type Description Default
state SystemState

Current system state to read pointer value from.

required

Returns:

Type Description
Tag

Concrete Tag at the resolved address.

Raises:

Type Description
IndexError

If resolved address is out of range.

resolve_ctx

resolve_ctx(ctx: ScanContext) -> Tag

Resolve pointer value to concrete Tag using ScanContext.

Parameters:

Name Type Description Default
ctx ScanContext

ScanContext to read pointer value from.

required

Returns:

Type Description
Tag

Concrete Tag at the resolved address.

Raises:

Type Description
IndexError

If resolved address is out of range.

IndirectExprRef dataclass

Tag with runtime-resolved address via expression.

IndirectExprRef wraps a Block and an Expression. The actual address is computed from the expression at scan time.

This enables pointer arithmetic like DS[idx + 1] where idx is a Tag.

Attributes:

Name Type Description
block Block

Block to index into.

expr Any

Expression whose value determines the address.

resolve_ctx

resolve_ctx(ctx: ScanContext) -> Tag

Resolve expression value to concrete Tag using ScanContext.

Parameters:

Name Type Description Default
ctx ScanContext

ScanContext to evaluate expression against.

required

Returns:

Type Description
Tag

Concrete Tag at the computed address.

Raises:

Type Description
IndexError

If resolved address is out of range.