Skip to main content

ControlForge Data Structures Guide

James M. Belcher Founder, JMB Technical Services LLC April 2026 | ControlForge v1.0.535


1. Overview

ControlForge provides 8 data structure types with ~160 functions for managing collections of data from Structured Text. All handle-based structures (everything except ARRAY) persist across scan cycles and are thread-safe.

TypePatternAccessUse Case
ARRAYValue-basedIndexedFixed data, math operations, sorting
MAPHandle-basedKey-valueLookups, configuration, named data
LISTHandle-basedIndexed + linkedDynamic lists, insertion/removal at any position
QUEUEHandle-basedFIFOMessage buffers, work queues
STACKHandle-basedLIFOUndo history, depth-first traversal
DEQUEHandle-basedDouble-endedSliding windows, both-end access
SETHandle-basedUnique membersDeduplication, membership tests, set math
HEAP / PQUEUEHandle-basedPriority-orderedAlarm ranking, task scheduling

Handle Pattern

Handle-based structures return a string handle on creation. Pass the handle to all subsequent operations:

q := QUEUE_CREATE(); (* Returns "queue_1" *)
QUEUE_PUSH(q, 'message-1');
QUEUE_PUSH(q, 'message-2');
msg := QUEUE_POP(q); (* Returns "message-1" *)

2. ARRAY — Indexed Collections

Arrays are value-based — operations return new arrays rather than modifying in place.

Create

arr := ARRAY_CREATE(10, 20, 30, 40, 50);
arr := ARRAY_OF(0, 10); (* [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] — fill 10 elements with 0 *)

Access

val := ARRAY_GET(arr, 0); (* First element *)
arr := ARRAY_SET(arr, 2, 99); (* Set index 2 to 99 — returns new array *)
len := ARRAY_LENGTH(arr); (* 5 *)

Modify

arr := ARRAY_APPEND(arr, 60); (* Add to end *)
arr := ARRAY_INSERT(arr, 2, 25); (* Insert at index 2 *)
arr := ARRAY_REMOVE(arr, 0); (* Remove first element *)
arr := ARRAY_CONCAT(arr1, arr2); (* Join two arrays *)
arr := ARRAY_SLICE(arr, 1, 3); (* Elements [1..3) *)
found := ARRAY_CONTAINS(arr, 30); (* TRUE *)
idx := ARRAY_FIND(arr, 30); (* Index of first match, -1 if not found *)
count := ARRAY_COUNT(arr, 30); (* Number of occurrences *)

Transform

arr := ARRAY_SORT(arr); (* Ascending *)
arr := ARRAY_SORT_DESC(arr); (* Descending *)
arr := ARRAY_REVERSE(arr);
arr := ARRAY_UNIQUE(arr); (* Remove duplicates *)
arr := ARRAY_FILL(arr, 0); (* Set all elements to 0 *)

Aggregate

total := ARRAY_SUM(arr);
average := ARRAY_AVG(arr);
smallest := ARRAY_MIN(arr);
largest := ARRAY_MAX(arr);

Functional

(* Filter: keep elements matching condition *)
evens := ARRAY_FILTER(arr, 'x % 2 = 0');

(* Map: transform each element *)
doubled := ARRAY_MAP(arr, 'x * 2');

(* Reduce: accumulate to single value *)
sum := ARRAY_REDUCE(arr, 'acc + x', 0);

(* Any/All: test conditions *)
has_negative := ARRAY_ANY(arr, 'x < 0');
all_positive := ARRAY_ALL(arr, 'x > 0');

Quick Reference (50 functions)

FunctionReturnsDescription
ARRAY_CREATE(vals...)ARRAYCreate from values
ARRAY_OF(value, count)ARRAYCreate filled array
ARRAY_GET(arr, index)ANYRead element
ARRAY_SET(arr, index, val)ARRAYUpdate element (new array)
ARRAY_LENGTH(arr)INTElement count
ARRAY_APPEND(arr, val)ARRAYAdd to end
ARRAY_INSERT(arr, idx, val)ARRAYInsert at position
ARRAY_REMOVE(arr, idx)ARRAYRemove by index
ARRAY_REPLACE(arr, old, new)ARRAYReplace value
ARRAY_CONCAT(arr1, arr2)ARRAYJoin arrays
ARRAY_SLICE(arr, start, end)ARRAYSubarray
ARRAY_CONTAINS(arr, val)BOOLMembership test
ARRAY_FIND(arr, val)INTFirst index (-1 if missing)
ARRAY_FIND_INDEX(arr, expr)INTFirst matching index
ARRAY_COUNT(arr, val)INTCount occurrences
ARRAY_COUNT_IF(arr, expr)INTCount matching condition
ARRAY_SORT(arr)ARRAYSort ascending
ARRAY_SORT_DESC(arr)ARRAYSort descending
ARRAY_REVERSE(arr)ARRAYReverse order
ARRAY_UNIQUE(arr)ARRAYRemove duplicates
ARRAY_FILL(arr, val)ARRAYSet all to value
ARRAY_JOIN(arr, sep)STRINGJoin as string
ARRAY_COPY(arr)ARRAYDeep copy
ARRAY_SUM(arr)REALSum all
ARRAY_AVG(arr)REALAverage
ARRAY_MIN(arr)ANYMinimum
ARRAY_MAX(arr)ANYMaximum
ARRAY_FILTER(arr, expr)ARRAYKeep matching
ARRAY_MAP(arr, expr)ARRAYTransform each
ARRAY_REDUCE(arr, expr, init)ANYAccumulate
ARRAY_ANY(arr, expr)BOOLAny match?
ARRAY_ALL(arr, expr)BOOLAll match?
ARRAY_TAKE(arr, n)ARRAYFirst N elements
ARRAY_DROP(arr, n)ARRAYSkip first N
ARRAY_PARTITION(arr, expr)ARRAYSplit by condition
ARRAY_GROUP_BY(arr, expr)MAPGroup into map
ARRAY_ZIP_WITH(a, b, expr)ARRAYCombine two arrays
INDEX_OF(arr, val)INTAlias for ARRAY_FIND

3. MAP — Key-Value Store

String-keyed dictionaries for named data.

(* Create with initial data *)
config := MAP_CREATE('host', '10.0.0.50', 'port', 502, 'enabled', TRUE);

(* Or empty *)
m := MAP_CREATE();

(* Set and get *)
MAP_SET(m, 'temperature', 72.5);
MAP_SET(m, 'running', TRUE);
temp := MAP_GET(m, 'temperature'); (* 72.5 *)
val := MAP_GET(m, 'missing', 0.0); (* Default: 0.0 *)

(* Check and remove *)
IF MAP_HAS(m, 'temperature') THEN
MAP_DELETE(m, 'temperature');
END_IF;

(* Iterate *)
keys := MAP_KEYS(m); (* ["running"] *)
vals := MAP_VALUES(m);
entries := MAP_ENTRIES(m); (* [[key, val], ...] *)

(* Merge and build *)
MAP_MERGE(m, other_map); (* other overwrites on conflict *)
m := MAP_FROM_ARRAYS(key_array, val_array);

Quick Reference (19 functions)

FunctionReturnsDescription
MAP_CREATE([k,v,...])STRING (handle)Create map
MAP_SET(h, key, val)BOOLSet value
MAP_GET(h, key [, default])ANYGet value
MAP_DELETE(h, key)BOOLRemove key
MAP_HAS(h, key)BOOLKey exists?
MAP_SIZE(h)INTKey count
MAP_KEYS(h)ARRAYAll keys
MAP_VALUES(h)ARRAYAll values
MAP_ENTRIES(h)ARRAYKey-value pairs
MAP_MERGE(h1, h2)BOOLMerge (h2 overwrites)
MAP_FROM_ARRAYS(keys, vals)STRING (handle)Build from arrays
MAP_CLEAR(h)BOOLRemove all entries
MAP_EMPTY(h)BOOLCheck if empty

4. LIST — Dynamic Linked List

Random access plus efficient insertion/removal at any position.

lst := LIST_CREATE(10, 20, 30);

(* Add *)
LIST_PUSH_BACK(lst, 40);
LIST_PUSH_FRONT(lst, 5);
LIST_INSERT(lst, 2, 15);

(* Access *)
first := LIST_FRONT(lst);
last := LIST_BACK(lst);
val := LIST_GET(lst, 3);

(* Remove *)
LIST_POP_FRONT(lst);
LIST_POP_BACK(lst);
LIST_REMOVE(lst, 1);
LIST_REMOVE_VALUE(lst, 20);

(* Transform *)
LIST_SORT(lst);
LIST_REVERSE(lst);
LIST_ROTATE_LEFT(lst, 2);
arr := LIST_TO_ARRAY(lst);

Quick Reference (67 functions)

FunctionReturnsDescription
LIST_CREATE([vals...])STRING (handle)Create list
LIST_PUSH_FRONT(h, val)STRINGAdd to front
LIST_PUSH_BACK(h, val)STRINGAdd to end
LIST_POP_FRONT(h)ANYRemove and return first
LIST_POP_BACK(h)ANYRemove and return last
LIST_INSERT(h, idx, val)BOOLInsert at position
LIST_GET(h, idx)ANYRead by index
LIST_SET(h, idx, val)BOOLUpdate by index
LIST_REMOVE(h, idx)ANYRemove by index
LIST_REMOVE_VALUE(h, val)BOOLRemove first occurrence
LIST_REMOVE_ALL(h, val)BOOLRemove all occurrences
LIST_FRONT(h)ANYPeek first
LIST_BACK(h)ANYPeek last
LIST_SIZE(h)INTElement count
LIST_CONTAINS(h, val)BOOLMembership test
LIST_INDEX_OF(h, val)INTFirst index
LIST_SORT(h)STRINGSort ascending
LIST_SORT_DESC(h)STRINGSort descending
LIST_REVERSE(h)STRINGReverse in place
LIST_SLICE(h, start, end)STRINGSub-list
LIST_CONCAT(h1, h2)STRINGJoin lists
LIST_UNIQUE(h)STRINGRemove duplicates
LIST_FLATTEN(h)STRINGFlatten nested
LIST_ROTATE_LEFT(h, n)STRINGRotate left
LIST_ROTATE_RIGHT(h, n)STRINGRotate right
LIST_SWAP(h, i, j)BOOLSwap elements
LIST_ZIP(h1, h2)STRINGPair elements
LIST_FILL(h, val, count)STRINGFill with value
LIST_RANGE(start, end, step)STRINGGenerate sequence
LIST_CLEAR(h)BOOLRemove all
LIST_TO_ARRAY(h)ARRAYConvert to array

5. QUEUE — FIFO Buffer

First-in, first-out. Ideal for message buffers and work queues.

q := QUEUE_CREATE();

QUEUE_PUSH(q, 'job-1');
QUEUE_PUSH(q, 'job-2');
QUEUE_PUSH(q, 'job-3');

next := QUEUE_PEEK(q); (* "job-1" — peek without removing *)
job := QUEUE_POP(q); (* "job-1" — removes from front *)
size := QUEUE_SIZE(q); (* 2 *)
FunctionReturnsDescription
QUEUE_CREATE([vals...])STRING (handle)Create queue
QUEUE_PUSH(h, val)BOOLAdd to back
QUEUE_POP(h)ANYRemove from front
QUEUE_PEEK(h)ANYPeek front
QUEUE_BACK(h)ANYPeek back
QUEUE_SIZE(h)INTElement count
QUEUE_EMPTY(h)BOOLCheck if empty
QUEUE_CONTAINS(h, val)BOOLMembership test
QUEUE_CLEAR(h)BOOLRemove all
QUEUE_TO_ARRAY(h)ARRAYConvert to array

6. STACK — LIFO Buffer

Last-in, first-out. Ideal for undo history and recursive-like operations.

s := STACK_CREATE();

STACK_PUSH(s, 'action-1');
STACK_PUSH(s, 'action-2');
STACK_PUSH(s, 'action-3');

top := STACK_PEEK(s); (* "action-3" *)
undo := STACK_POP(s); (* "action-3" — removes from top *)
FunctionReturnsDescription
STACK_CREATE([vals...])STRING (handle)Create stack
STACK_PUSH(h, val)BOOLPush to top
STACK_POP(h)ANYPop from top
STACK_PEEK(h)ANYPeek top
STACK_BOTTOM(h)ANYPeek bottom
STACK_SIZE(h)INTElement count
STACK_EMPTY(h)BOOLCheck if empty
STACK_CONTAINS(h, val)BOOLMembership test
STACK_REVERSE(h)BOOLReverse order
STACK_CLEAR(h)BOOLRemove all
STACK_TO_ARRAY(h)ARRAYConvert to array

7. DEQUE — Double-Ended Queue

Push and pop from both ends.

d := DEQUE_CREATE();

DEQUE_PUSH_FRONT(d, 'A');
DEQUE_PUSH_BACK(d, 'B');
DEQUE_PUSH_FRONT(d, 'C');
(* Contents: C, A, B *)

front := DEQUE_POP_FRONT(d); (* "C" *)
back := DEQUE_POP_BACK(d); (* "B" *)
FunctionReturnsDescription
DEQUE_CREATE()STRING (handle)Create deque
DEQUE_PUSH_FRONT(h, val)BOOLAdd to front
DEQUE_PUSH_BACK(h, val)BOOLAdd to back
DEQUE_POP_FRONT(h)ANYRemove from front
DEQUE_POP_BACK(h)ANYRemove from back
DEQUE_FRONT(h)ANYPeek front
DEQUE_BACK(h)ANYPeek back
DEQUE_SIZE(h)INTElement count
DEQUE_EMPTY(h)BOOLCheck if empty

8. SET — Unique Collection

Stores unique values. Supports set algebra (union, intersection, difference).

s := SET_CREATE('A', 'B', 'C');

SET_ADD(s, 'D');
SET_ADD(s, 'A'); (* No effect — already present *)
SET_REMOVE(s, 'B');

IF SET_CONTAINS(s, 'C') THEN
(* ... *)
END_IF;

(* Set operations *)
s2 := SET_CREATE('C', 'D', 'E');
SET_UNION(s, s2); (* s = {A, C, D, E} *)
SET_INTERSECTION(s, s2); (* s = {C, D, E} *)
SET_DIFFERENCE(s, s2); (* s = elements in s but not s2 *)

is_sub := SET_IS_SUBSET(s, s2);
FunctionReturnsDescription
SET_CREATE([vals...])STRING (handle)Create set
SET_ADD(h, val)BOOLAdd value
SET_REMOVE(h, val)BOOLRemove value
SET_CONTAINS(h, val)BOOLMembership test
SET_SIZE(h)INTElement count
SET_UNION(h1, h2)INTUnion (modifies h1)
SET_INTERSECTION(h1, h2)INTIntersect (modifies h1)
SET_DIFFERENCE(h1, h2)INTDifference (modifies h1)
SET_SYMMETRIC_DIFFERENCE(h1, h2)INTXOR (modifies h1)
SET_IS_SUBSET(h1, h2)BOOLh1 subset of h2?
SET_IS_SUPERSET(h1, h2)BOOLh1 superset of h2?
SET_EMPTY(h)BOOLCheck if empty
SET_CLEAR(h)BOOLRemove all
SET_TO_ARRAY(h)ARRAYConvert to array

9. HEAP / PQUEUE — Priority Queue

Items are ordered by priority. Min-heap (default) returns lowest priority first; max-heap returns highest first.

(* Min-heap: lowest priority comes out first *)
h := HEAP_CREATE();

HEAP_PUSH(h, 'low-alarm', 3.0);
HEAP_PUSH(h, 'critical', 1.0);
HEAP_PUSH(h, 'warning', 2.0);

next := HEAP_PEEK(h); (* "critical" — priority 1.0 *)
pri := HEAP_PEEK_PRIORITY(h); (* 1.0 *)
item := HEAP_POP(h); (* "critical" — removed *)

(* Max-heap: highest priority comes out first *)
mh := HEAP_CREATE_MAX();
HEAP_PUSH(mh, 'VIP', 100.0);
HEAP_PUSH(mh, 'normal', 10.0);
top := HEAP_POP(mh); (* "VIP" *)

(* Top-N queries *)
top3 := HEAP_N_LARGEST(h, 3);
bottom3 := HEAP_N_SMALLEST(h, 3);

(* Update priority *)
HEAP_UPDATE_PRIORITY(h, 'warning', 0.5); (* Promote to higher priority *)

PQUEUE functions are aliases with identical behavior (e.g., PQUEUE_CREATE = HEAP_CREATE, PQUEUE_PUSH = HEAP_PUSH).

FunctionReturnsDescription
HEAP_CREATE()STRING (handle)Create min-heap
HEAP_CREATE_MAX()STRING (handle)Create max-heap
HEAP_PUSH(h, val, priority)BOOLAdd with priority
HEAP_POP(h)ANYRemove highest-priority item
HEAP_PEEK(h)ANYPeek highest-priority item
HEAP_PEEK_PRIORITY(h)REALPeek its priority value
HEAP_UPDATE_PRIORITY(h, val, pri)BOOLChange priority
HEAP_SIZE(h)INTElement count
HEAP_CONTAINS(h, val)BOOLMembership test
HEAP_N_SMALLEST(h, n)ARRAYN lowest-priority items
HEAP_N_LARGEST(h, n)ARRAYN highest-priority items
HEAP_MERGE(h1, h2)BOOLMerge heaps
HEAP_FROM_ARRAY(arr)STRING (handle)Build from array
HEAP_EMPTY(h)BOOLCheck if empty
HEAP_CLEAR(h)BOOLRemove all
HEAP_TO_ARRAY(h)ARRAYConvert to array

10. Complete Example: Alarm Priority System

PROGRAM POU_AlarmManager
VAR
alarms : STRING; (* HEAP handle *)
active_set : STRING; (* SET handle — track active alarm IDs *)
history : STRING; (* QUEUE handle — last 50 acknowledged *)
initialized : BOOL := FALSE;

(* Inputs *)
high_temp : BOOL;
low_pressure : BOOL;
door_open : BOOL;
END_VAR

IF NOT initialized THEN
alarms := HEAP_CREATE(); (* Min-heap: priority 1 = most critical *)
active_set := SET_CREATE();
history := QUEUE_CREATE();
initialized := TRUE;
END_IF;

(* Raise alarms on conditions *)
IF high_temp AND NOT SET_CONTAINS(active_set, 'HIGH_TEMP') THEN
HEAP_PUSH(alarms, 'HIGH_TEMP', 1.0); (* Critical *)
SET_ADD(active_set, 'HIGH_TEMP');
END_IF;

IF low_pressure AND NOT SET_CONTAINS(active_set, 'LOW_PRESS') THEN
HEAP_PUSH(alarms, 'LOW_PRESS', 2.0); (* Warning *)
SET_ADD(active_set, 'LOW_PRESS');
END_IF;

IF door_open AND NOT SET_CONTAINS(active_set, 'DOOR_OPEN') THEN
HEAP_PUSH(alarms, 'DOOR_OPEN', 3.0); (* Advisory *)
SET_ADD(active_set, 'DOOR_OPEN');
END_IF;

(* Most critical alarm is always at the top *)
IF NOT HEAP_EMPTY(alarms) THEN
top_alarm := HEAP_PEEK(alarms);
top_priority := HEAP_PEEK_PRIORITY(alarms);
END_IF;

(* Acknowledge: move to history queue *)
IF ack_requested AND NOT HEAP_EMPTY(alarms) THEN
acked := HEAP_POP(alarms);
SET_REMOVE(active_set, acked);
QUEUE_PUSH(history, acked);

(* Keep history at 50 max *)
IF QUEUE_SIZE(history) > 50 THEN
QUEUE_POP(history);
END_IF;
END_IF;

END_PROGRAM

ControlForge v1.0.535 | ~160 Data Structure Functions | ARRAY, MAP, LIST, QUEUE, STACK, DEQUE, SET, HEAP/PQUEUE

© 2026 JMB Technical Services LLC. All rights reserved. Back to All Guides