Added September 2009
As the title suggests, these variables automatically re-size themselves to accommodate the data you assign to them.
Dynamic variables are allocated and maintained outside your user partition, so the only limit on how big they can become would be determined by available memory on the system. If unable to expand as needed, the program will get an error 3 - out of memory. The are understandably not as efficient as fixed-length variables, but can be extremely useful in situations where you don't have a good way of knowing the appropriate maximum size required at the time the program is written. (Letting the computer work a little harder is nearly always a better option than making the programmer work harder or risking bugs caused by inadvertent truncation!) .
To declare a dynamic variable, assign it an explicit size of zero, e.g.:
MAP1 STRING,S,0 ! dynamically sized string
MAP1 UNF,X,0 ! dynamically sized unformatted
MAP1 DARY(75),S,0 ! fixed-length array of dynamic strings
MAP1 XARY(24,9),X,0 ! fixed-length array of dynamic unformatted
Comments
Dynamic variables are impossible to align in memory with respect to other variables, so should normally only be declared at the MAP1 level. However, see Dynamic Variables Below MAP1 for a relaxation of the rule.
An array may be made up of dynamic elements, but the range of index values for the array remains fixed. However, you may use DIMX and REDIMX to dynamically allocate and resize such as array, e.g.
DIMX ZARY(A,B,C),S,0
REDIMX ZARY(A*2,B,C)
Re-dimensioning an array doesn't preserve the original contents unless only the first dimension changes, as in the example above.
Dynamic variables may also be used in function and procedure parameter lists, as well as for the return value of a function, i.e.:
Function Fn'Test$(a$ as s0) as s0
Dynamic variables (and array elements) initially take up 8 bytes of storage memory (used as a descriptor), so it won't make much sense to use them if they aren't going to expand beyond 8 bytes.
When an assignment is made to the variable, either via an assignment (e.g. STRING = "abc"), INPUT statement, or via an XCALL, Function, or Procedure, the variable will be expanded as needed to contain the data. If referenced in an expression before any value is assigned, they act like null strings.
It is not wise to use INPUT RAW with dynamic X variables, since the amount of data input depends on the current size of the variable, which would be 0 initially, but could be anything later (after some other kind of assignment).
As currently implemented, the internal memory allocated to a variable does not shrink, so first assigning a large amount of data and later assigning a small amount does not free memory. (This may change as the implementation evolves.)
The run-time system maintains several pools of dynamic variable memory, including one for global or persistent variables (which lasts for the duration of a RUN program), and one each for the automatic (aka local or stack) variables at each level of function/procedure nesting. All are allowed to grow without any particular limit during the execution, but are cleared as soon as the variables contained within go out of scope—i.e. at the end of a procedure, function, or program.
To compile programs with dynamic variables, you must use compiler edit 439+, and the /X:2 switch. Any legally compiled program containing dynamic variables will receive a new RUN header value of 0xF167 or 0xF1E7 (if /av), and will generate a "?RUN file incompatible" error if executed under any A-Shell runtime prior to 5.1.1159. Programs which do not contain dynamic variables should continue to compile and run exactly as before.
Example
To load an entire binary file into a dynamic X variable:
map1 bytes,f
map1 rcv,f
map1 ch,b,2,1
map1 blob,x,0 ! dynamic var to hold file
xcall SIZE, fspec$, bytes ! get size of file
open #ch, fspec$, input
xcall GET, blob, ch, bytes, rcv ! blob now contains file
close #ch
! create an exact copy of the original file...
open #ch, "copy.dat", output
print #ch, blob;
close #ch
See the SOSFUNC library module FNFILESTR.BSI for a set of related routines for copy files to memory variables and vice versa.
History
2014 September, A-Shell 6.1.1391: Eliminate the extra trailing null byte when initializing/expanding dynamic X variables. The extra null byte was added to dynamic strings to improve compatibility with external library routines expecting "C-style" (null-terminated) strings, but this doesn't apply to X variables, where the extra byte was just a nuisance. When loading a binary file into an X variable for example, it was throwing off the size and hash.
2009 September, A-Shell 5.1.1159: Feature added to A-Shell