String Views -- Zero-Copy, Live Slices of Strings
The VIEW statement creates a dynamic, zero-copy
reference into a string. Unlike MID$() or
BETWEEN$(), which return static copies, a
VIEW binds directly to the original source variable.
Lazy updating and caching:
Views update dynamically. When the source variable is assigned a new
value, a view does not immediately recalculate its start or
end positions. Instead, these are evaluated the next time the
view is referenced (a lazy update).
To avoid unnecessary work, a view also caches its resolved slice. If
the source has not changed since the last evaluation, the
cached start/end positions of the view are reused and no recalculation occurs.
This keeps repeated references fast and allocation-free.
These rules apply to any change in the source. For example, if the
source is set to the null string, then on the next reference every
view derived from it will also evaluate as a null string.
Views can be created using fixed positions and lengths, or using
start and end delimiters. All views support write-through changes to
the underlying string.
VIEW a Given Position and Length
The VIEW statement creates a live, dynamic slice of a
string variable. Unlike MID$(), which returns a copy, a
VIEW does not duplicate data — it binds the
view variable directly to the source string.
A view automatically tracks the current value of the source
variable. It updates instantly when characters change, or when the
entire source variable is reassigned to a new string.
Syntax
VIEW view_var$ INTO source_var$ position numeric_expr1, length numeric_expr2
All four parameters are required:
- view_var$ — the view variable
- source_var$ — the source string
- position — starting position (1-based, like MID$).
- length — number of characters in the view
Note:
-
A view is read-only except when using overlay operations
(
LSET, OVERLAY(), etc.), which write
directly into the underlying source string.
-
If the starting position is beyond the end of the source, the view
evaluates as an empty string.
- A negative starting position counts
backward from the end of the source.
-
If the view's requested length extends past the end of the source,
it is automatically truncated to the remaining characters.
Example
phone$ = "8085551212"
// Create a view into the first 3 characters
VIEW area$ INTO phone$: position 1, length 3
print area$ // Output: 808
// Create a view into the last 3 characters
VIEW area$ INTO phone$: position -3, length 3
print area$ // Output: 212
// Modify the source string
phone$ = "7022229999"
// area$ updates automatically
print area$ // Output: 702
// Overwriting a view updates the source
lset area$ = "abc"
print phone$ // Output: abc2229999
phone$ = ""
print area$ // Output: (empty string), since phone$ now has zero length.
The VIEW…MATCH Option
The MATCH option extends the fixed-position VIEW
syntax so that the source string is treated as a contiguous array of
fixed-width segments. Instead of selecting a single absolute start position,
you define:
- Base Position — where the first segment begins,
- Record Length — the width of every segment, and
- Index — which segment (occurrence) you want to view.
This is ideal for processing flat-file segments, binary headers,
fixed-layout structures, or tightly packed lists of codes—without doing
your own loop arithmetic.
Syntax for the MATCH Option
VIEW view_var$ INTO source_var$,
POSITION base,
LENGTH size,
MATCH index
base: The starting position of the first segment (typically 1).
size: The fixed width of each segment.
index: The segment number to view (1-based).
Multiple Views Inside a CLUSTER
You can also store multiple views inside a
CLUSTER, turning a single string into a lightweight,
structured record that automatically tracks the source variable.
Note: A CLUSTER is a named collection of variables.
cluster parts: area$, exch$
view parts->area$ into phone$ position 1, length 3
view parts->exch$ into phone$ position 5, length 3
phone$ = "702-997-1212"
print cluster parts // Outputs: 702, 997
phone$ = "213-555-1212"
print cluster parts // Outputs: 213, 555
Key Differences Between MID$() and VIEW
| Feature |
MID$() |
VIEW |
| Mechanism |
Creates a new string (copy) |
Creates a reference (pointer) |
| Memory |
Allocates new memory |
Points to the existing memory of the source |
| Behavior |
Static snapshot |
Dynamic: updates when the source variable changes |
| Use Case |
When you want a fixed value |
When you want to track changes |
VIEW Between -- Using Delimiters (Dynamic BETWEEN)
The extended VIEW syntax allows you to create a
dynamic slice of a string variable using start and
end delimiters, just like BETWEEN$() — except
the result is a live view instead of a copied string.
This makes it the dynamic counterpart to BETWEEN$(),
just as the original VIEW ... position/length is the
dynamic counterpart to MID$().
Syntax
VIEW view_var$ INTO base_var$ BETWEEN start_delim$, end_delim$
How it works:
-
The statement searches
base_var$ for
start_delim$ and end_delim$.
-
If
start_delim$ is empty, the beginning of
base_var$ is assumed.
-
If
end_delim$ is empty, the end of
base_var$ is assumed.
-
Instead of returning a copied substring, the view variable
points directly into the section of
base_var$
between the two delimiters.
-
If the delimiters are not found, the view becomes an empty string.
Note:
-
This form of
VIEW creates a read-only view
except for overlay operations (LSET,
OVERLAY()), which modify the corresponding characters
in base_var$.
It also treats delimited regions of a string as live fields.
Whenever the base string changes, the view re-locates its
delimited region the next time the view variable is referenced
(or becomes an empty string if the delimiters are not present).
Because the view points directly into the base string, overlaying the
view updates the underlying text without copying or rebuilding the string.
-
If
base_var$ is reassigned to a new value, the view
will re-evaluate its delimiters the next time the view variable
is referenced. This ensures the view always corresponds to the
current value of base_var$.
-
If the characters in
base_var$ shift — for example,
due to editing, concatenation, or overlay operations — the view
will locate the delimiters again when the view is next used.
This guarantees the view reflects the correct, live region of the
modified string.
-
The view statement is executable, so any of its expression arguments can be changed at runtime.
When evaluated, it updates the corresponding
view_var$ when the variable is next referenced.
VIEW_Between Options
-
MATCH — By default, the search uses the first
occurrence of the start delimiter paired with the next occurrence of
the end delimiter. With MATCH, the engine instead
returns the Nth such matching pair.
-
NOTRIM — By default, the substring between the
delimiters is trimmed of leading and trailing spaces before it is
returned or exposed via the view. Use NOTRIM to
preserve the exact characters between the delimiters, including
any surrounding whitespace.
-
BALANCED — By default, the search uses the first
occurrence of the start delimiter and the very next occurrence of
the end delimiter. With BALANCED, the engine instead
finds the outer delimited region: starting at the first
start delimiter and locating the matching closing delimiter that
encloses the full region, including any nested or inner delimiters.
VIEW Example
data$ = "phone=(702) 555-1212 age=42"
VIEW area$ INTO data$ BETWEEN "(", ")"
VIEW age$ INTO data$ BETWEEN "age=", ""
print area$ // Output: 702
print age$ // Output: 42
// Replace the entire base string
data$ = "My new phone number is (858) 555-1212"
// The view updates automatically
print area$ // Output: 858
print age$ // Output: (empty string)
// Overlay through the view modifies the source
lset area$ = "211"
print data$ // Output: My new phone number is (211) 555-1212
// Using MATCH to get a specific occurrence
data$ = "phone=(702) 555-(1212) age=42"
VIEW area2$ INTO data$ BETWEEN "(", ")" MATCH 2
print area2$ // Output: 1212
data$ = "phone=555-1212 age=42" // Missing parens
print area2$ // Output: (empty string)
// All VIEW parameters accept runtime expressions
list$ = "[apple][banana][cherry]"
for occurrence = 1 to 10
VIEW item$ INTO list$ BETWEEN "[", "]" MATCH occurrence
if item$ = "" then exit for // No more items
print item$
next occurrence
// Output (one per line):
// apple
// banana
// cherry
// Automatic zero-copy parsing into cluster fields upon reference
cluster info: name$, age$, weight$
view info->name$ into data$ between "name=" , ";"
view info->age$ into data$ between "age=" , ";"
view info->weight$ into data$ between "weight=", ";"
data$ = 'name=Fred; age=45; weight=160;'
print cluster info // outputs all field values in the cluster called info.
data$ = 'weight=120; age=25; name=Sally; '
print cluster info
// outputs:
// "Fred","45","160"
// "Sally","25","120"
Example: Using NOTRIM to Preserve Whitespace
// Base string with padded value
data$ = "value=[ 123 ] status=ok"
// Default: trims spaces between delimiters
VIEW num$ INTO data$ BETWEEN "[", "]"
// NOTRIM: keeps the exact characters between delimiters
VIEW num_raw$ INTO data$ BETWEEN "[", "]" NOTRIM
print "[" + num$ + "]" // Output: [123]
print "[" + num_raw$ + "]" // Output: [ 123 ]
Example: Using BALANCED for Outer Delimited Region
data$ = "outer[start [inner] tail] end"
// Without BALANCED: first "[" to first "]" after it
VIEW inner$ INTO data$ BETWEEN "[", "]"
// With BALANCED: first "[" to its matching outer "]"
VIEW outer$ INTO data$ BETWEEN "[", "]" BALANCED
print inner$ // Output: start [inner
print outer$ // Output: start [inner] tail
Behavior Summary
| Feature |
BETWEEN$() |
VIEW ... BETWEEN |
| Mechanism |
Returns a copy |
Creates a live view |
| Updates with base changes? |
No |
Yes, automatically |
| Memory |
Allocates new string |
No allocation; points to base |
| Write-through |
No |
Yes, using OVERLAY/LSET |