ASF — API Reference
This document describes the runtime API exposed by ASF scripts and the VM builtins/methods available to scripts. It summarizes semantics, signatures, error behavior, and examples.
Table of contents
- Runtime model & conventions
- Globals and program entry
- Value model
- Builtin global functions
- Array and String methods (exposed as properties)
- Regex Object
- Object methods / member behavior
- VBA Expressions integration
- Error & truthiness rules
- Examples & usage patterns
Runtime model & conventions
- Program: a compiled AST that can be executed by the VM. The ASF host exposes
.Compile(script)→ programIndex and.Run(programIndex)to run. - Scope / closures: closures capture the environment by reference (shared-write semantics). That means nested functions can mutate outer-scope variables and see changes across closures.
- Indexing base: arrays honor
__option_baseset in runtime globals (commonly 0 or 1). All array helpers and methods handle this consistently. - Call signature for array callbacks:
- Callback receives
(value, index, array)whereindexrespects__option_base. thiscan be supplied when callingCallClosureor via bound method objects (property-style access returns bound method withbaseValasthis).
- Callback receives
Value model
- Core types:
Number,String,Boolean,Array(Variant arrays),Object(ASF_Map),Null(nullliteral),Closure(map representing a function),Empty. - Arrays are Variant arrays with explicit LBound/UBound.
- Objects are
ASF_Mapinstances (Map-like behavior with.GetValue/.SetValue).
Truthiness
IsTruthy(value)is used internally:Falsey:False,0(numeric zero),""(empty string),Empty,Null.- Everything else is truthy.
- Beware: some builtins use numeric semantics when needed.
Builtin global functions
These are callable as top-level functions or via the helper bridge; some are also available as named builtins in the VM.
print(...)— pretty-prints arguments and appends to runtime log (used by test suite).range(limit)orrange(start, end)orrange(start, end, step)
Returns an array of numbers.- Example:
range(3) // [0,1,2]
- Example:
flatten(array, depth?)— returns a flattened array todepth(fully flattened if depth omitted).clone(value)— deep clone for arrays and objects.IsArray(x)— returnstrueifxis an array.IsNumeric(x)— returnstrueifxis numeric.forEach(arr, fn)— executes the provided function once for each array element. This method returns the original array, doesn’t behave like the javascript alternative.Regex(pattern?, ignoreCase?, multiline?, dotAll?)— regex Object constructor. If all arguments are omitted, the regex Object will not initialized. If one argument is given, this is considered thepattern; if two arguments are given,patternandignoreCaseflag will be set; if three arguments are given,pattern,ignoreCaseandmultilineflags will be set; when all arguments are givem, also thedotAllis set.
Those are the current named builtins surfaced for scripts.
Array methods (exposed as properties — e.g. arr.map)
All array methods are available as properties on arrays. Accessing a.map returns a bound builtin object you can call immediately: a.map(fn) or assign to a variable and call later m = a.map; m(fn).
Callback signature for methods that accept callbacks: callback(value, index, array)
this: For method-style calls, the base array is bound as the default this when the method is obtained via property access; CallClosure may also accept a thisArg.
Below is a compact table (name — signature — brief behavior — example):
map(callback)- Returns new array with
callbackapplied to every element (does not mutate original). Supports nested arrays and closures. - Example:
[1,2].map(fun(x){ return x*2 }) -> [2,4]
- Returns new array with
filter(callback)- Returns new array containing elements where
callbackis truthy. - Example:
[1,2,3].filter(fun(x){ return x%2==1 }) -> [1,3]
- Returns new array containing elements where
reduce(callback, initial?)- Accumulates values using
callback(acc, value); ifinitialis omitted the first array element is used as initial (like JS). - Example:
[1,2,3].reduce(fun(acc,x){ return acc+x }, 0) -> 6
- Accumulates values using
forEach(callback)- Calls
callbackfor each element; returnsEmpty(or behavior consistent with print/log). No return collection.
- Calls
slice(start?, end?)- Non-mutating subarray selection. Respects negative indexes and
__option_base.
- Non-mutating subarray selection. Respects negative indexes and
push(...items)- Mutates array by appending items; returns new length. Writes back to LValue container.
pop()- Mutates array removing last element; returns popped element. Writes back to LValue.
shift()- Mutates array removing first element; returns popped element. Writes back to LValue.
unshift(...items)- Mutates array prepending items; returns new length. Writes back to LValue.
concat(...itemsOrArrays)- Returns a new array concatenating the base and provided items/arrays.
unique()- Returns new array containing unique elements; deep-aware (structural equality for arrays/objects).
flatten(depth?)- Returns new flattened array up to
depth(fully flattened if not specified).
- Returns new flattened array up to
clone()- Deep clone of array/object/value.
toString()/join(separator?)- Joins elements to produce a string; arrays/objects are pretty-printed for complex elements.
delete(index)- Remove element at user-facing index, mutate array and write back. Returns
true/false.
- Remove element at user-facing index, mutate array and write back. Returns
splice(start, deleteCount?, ...items)- Mutating splice: removes items, inserts new ones, returns array of removed elements.
toSpliced(start, deleteCount?, ...items)- Non-mutating splice; returns new array with the changes applied to a copy.
at(index)- Returns element at index (supports negative indexing relative to end). Index respects
__option_base.
- Returns element at index (supports negative indexing relative to end). Index respects
copyWithin(target, start=0, end=len)- Mutates array by copying a slice to another location. Writes back to LValue.
entries()- Returns array of
[index, value]pairs starting at__option_base.
- Returns array of
every(callback)- Returns true if callback is truthy for all elements.
some(callback)- Returns true if callback is truthy for any element.
find(callback)/findIndex(callback)/findLast(callback)/findLastIndex(callback)- Search operations. Indices follow
__option_base.findreturns element orEmpty,findIndexreturns index or -1.
- Search operations. Indices follow
includes(value)/indexOf(value)/lastIndexOf(value)- Search using deep equality for complex values.
of(...items)-
Create array from items (also exposed as global named builtin).
-
from(source, mapFn?, thisArg?)— produce an array from an array/string/single value; ifmapFnis provided and is a closure it’s applied to each element (signature(value, index, source)).
-
reverse()- Mutates array in place (writes back). Returns mutated array.
toReversed()- Non-mutating reverse; returns a new array.
sort(comparator?)/toSorted(comparator?)sortmutates the array in-place and writes back.toSortedreturns a new sorted array.- Comparator is a closure
fun(a,b)returning negative/0/positive (or numeric) similar to JS. - Implementation uses in-place QuickSort with comparator hook.
with(index, value)- Returns a shallow copy with
valueset atindex(non-mutating).
- Returns a shallow copy with
string methods (exposed as properties — e.g. str.toLowercase)
All string indexing in ASF is zero-based. String methods that accept negative indexes treat them as offset from the end (like JavaScript .slice() / .at()).
Templates and regex patterns must be enclosed by the backtick character “`”.
Instance methods (call on a string value, e.g. ‘hello’.method(…))
-
length—str.length— Number of UTF-16 code units in the string (like JSlength). —'abc'.length -> 3 -
at(index)—str.at(index)— Return character atindex. Supports negative indexes (e.g.-1is last char). Returns''for out-of-range. —'abc'.at(1) -> 'b','abc'.at(-1) -> 'c' -
charAt(index)—str.charAt(index)— Same asat(index)(returns single-character string or''). —'abc'.charAt(2) -> 'c' -
charCodeAt(index)—str.charCodeAt(index)— Returns numeric UTF-16 code of character atindex. If out-of-range returnsEmpty(or implementation-defined numeric error). —'A'.charCodeAt(0) -> 65 -
concat(...items)—str.concat(item1, item2, ...)— Return new string by concatenatingstrwith provided values (non-strings are coerced). Does not mutate. —'a'.concat('b', 3) -> 'ab3' -
endsWith(search, pos?)—str.endsWith(search[, length])— Tests whether string ends withsearch(optionally considering only up tolengthcharacters). Returns boolean. —'abc'.endsWith('c') -> True -
includes(sub)—str.includes(sub)—Trueifsubappears anywhere instr. —'abc'.includes('b') -> True -
indexOf(search, pos?)—str.indexOf(search[, fromIndex])— Index of first occurrence; returns-1when not found. NegativefromIndextreated as0. —'banana'.indexOf('na') -> 2 -
lastIndexOf(search, pos?)—str.lastIndexOf(search[, fromIndex])— Index of last occurrence <=fromIndex. Returns-1if none. —'banana'.lastIndexOf('na') -> 4 -
localeCompare(other)—str.localeCompare(other)— simple VBA based implementation, does not behave like javascript: returns negative, 0, or positive number like JS. —'a'.localeCompare('b') -> -1 match(patternOrSlashRegex)—str.match(pattern)— Works two ways:- If
patternis a slash-regex string'/pat/flags'or a regex object, behaves like JSmatch.- If
gflag present (global), returns an array of strings (just the matches; groups ignored). - If
gflag absent, returns an array-like collection:[fullMatch, group1, group2, ...](orEmptyif no match).
- If
- If
patternis a plain string, the first occurrence is returned in the array as full match (no groups). - Example:
js 'a1b2'.match(`/\\d/`) /*-> ['1', ...groups? none]*/; 'a1b2'.match(`/\\d/g`) //-> ['1','2']
- If
matchAll(patternOrSlashRegex)—str.matchAll(pattern)— JS-like behavior:- Requires the
g(global) flag on regex (throw/raise error at runtime if omitted). - Returns an array of matches (strings) for global matches (no groups). If you need groups, use
ExecAllorregexobject methods. - Example:
js 'a1b2'.matchAll(`/\\d/g`) /*-> ['1','2']*/; 'a1b2'.matchAll(`/\\d/`) // -> **error**
- Requires the
-
padEnd(targetLength, padString?)—str.padEnd(len[, pad])— Pads on the right tolenusingpad(defaults to space). Returns new string. —'a'.padEnd(3,'-') -> 'a--' -
padStart(targetLength, padString?)—str.padStart(len[, pad])— Pads on the left. —'a'.padStart(3,'0') -> '00a' -
repeat(count)—str.repeat(count)— Returns repeated stringcounttimes.countmust be non-negative integer. —'ab'.repeat(2) -> 'abab' replace(searchValue, replaceValue)—str.replace(search, repl)— Replaces first match whensearchis a string, or behavior depends on regex flags:searchmay be a plain string (literal) or a slash-regex string'/pattern/flags'or a regex object.replaceValuemay be a string or a function/closure. If string, supports$-expansions:$&— full match$n— numbered capture$1..$99$`— left context (text before match)$'— right context (text after match)$$— literal$
- If
replaceValueis a closure/map, it is invoked ascallback(match, p1, p2, ..., offset, originalString)and its return coerced to string (complex values pretty-printed). offsetis zero-based (consistent with JavaScript).- Example:
js 'a1b'.replace(`/(\\d)/`, fun(m,p,off,s){ return '[' & p & ']'}) // -> 'a[1]b'
-
replaceAll(searchValue, replaceValue)—str.replaceAll(search, repl)— Same asreplacebut replaces all matches. Accepts slash-regexgor will do global if called asreplaceAll(or if regex flaggpresent). Function replacement,$expansions supported. Safeguards for zero-length matches (engine advances one char to avoid infinite loops). -
slice(start?, end?)—str.slice(start?, end?)— Extracts substring fromstartup to (but not including)end. Supports negative indices counted from end. Non-mutating. —'abcdef'.slice(1,4) -> 'bcd' -
split(separator?, limit?)—str.split(sep, limit?)— Ifsepis a slash-regex string or regex object, uses the regex engine (honorsg/flags). Ifsepis'', split into characters. Returns array.limitoptional. —'a,b;c'.split('/[;,]/') -> ['a','b','c'] -
startsWith(search, pos?)—str.startsWith(search[, pos])— Tests whetherstrstarts withsearchat optionalpos. Returns boolean. —'abc'.startsWith('b',1) -> True -
substring(start?, end?)—str.substring(start?, end?)— Returns substring betweenstartandend. Negative values are treated as0. Ifstart> endthey are swapped (JS behavior). —'abcd'.substring(2,1) -> 'bc' -
toLowercase()—str.toLowercase()— Return lowercased string. (Name:toLowercaseper ASF naming.) —'ABC'.toLowercase() -> 'abc' -
toUppercase()—str.toUppercase()— Return uppercased string. —'abc'.toUppercase() -> 'ABC' -
trim()—str.trim()— Removes whitespace from both ends. —' hi '.trim() -> 'hi' -
trimStart()—str.trimStart()— Removes leading whitespace. —' hi'.trimStart() -> 'hi' -
trimEnd()—str.trimEnd()— Removes trailing whitespace. —'hi '.trimEnd() -> 'hi' -
toString()—str.toString()— Identity/primitive string representation. —('x').toString() -> 'x' valueOf()—str.valueOf()— Returns primitive string value (alias oftoString). —'x'.valueOf() -> 'x'
Regex Object API
ASF provides a native regex engine exposed through the regex() constructor and slash-regex syntax.
Regex objects are stateful, configurable, and reusable, and integrate seamlessly with string methods like match, replace, split, etc.
Execution & Matching
exec(subject)regex.exec(subject)- Executes the regex on
subjectand returns full match + capture groups as an array. - Returns
Emptyif no match. - Equivalent to JS
RegExp.prototype.exec(non-global). - Example:
regex(`(a)(b)`).exec('xab') // -> ['ab', 'a', 'b']
execAt(subject, position)regex.execAt(subject, pos)- Attempts a match starting exactly at
pos(1-based index). - Returns full match + groups or
Empty. - Useful for scanners and parsers.
- Example:
r = regex(`\\d+`) r.execAt('a123', 2) // -> ['123']
execAll(subject)regex.execAll(subject)- Returns all matches as an array.
- Each element is the full match string only (groups ignored).
- Equivalent to JS global matching.
- Example:
regex(`\\d+`).execAll('a1b22c333') // -> ['1', '22', '333']
test(subject)replace(subject, replacement)regex.replace(subject, replacement)- Replaces all matches in
subject. replacementis a string.- Supports
$expansions:$0full match$1..$99capture groups
- Example:
regex(`(\d+)`).replace('a12b', '[$1]') // -> 'a[12]b'
split(subject)escape(text)
Pattern
getPattern()- Returns the current regex pattern string.
- Example:
r.getPattern()
setPattern(pattern)- Sets a new pattern (does not reset flags).
- Example:
r.setPattern(`\d+`)Flags
getIgnoreCase()/setIgnoreCase(value)- Controls case-insensitive matching (
iflag). - Example:
r.setIgnoreCase(true)
- Controls case-insensitive matching (
getMultiline()/setMultiline(value)- Enables multiline mode (
mflag). ^and$match line boundaries.- Example:
r.setMultiline(true)
- Enables multiline mode (
getDotAll()/setDotAll(value)- Enables dotAll mode (
sflag). .matches newline characters.- Example:
r.setDotAll(true)Execution Control
- Enables dotAll mode (
getMaxMatchSteps()/setMaxMatchSteps(value)- Limits internal backtracking steps.
- Prevents catastrophic regex behavior.
- Example:
r.setMaxMatchSteps(50000)Initialization Helper
init(pattern, ignoreCase?, multiline?, dotAll?)- Reinitializes the regex object safely.
- Returns
Trueon success,Falseon error. - Example:
r.init(`\w+`, true, true, false)
String Templates
- Templates are an special type of strings holding expressions and literals:
<literal>${<expression>}<literal>?. - Each
${...}block is captured as an expression exactly as typed respecting escaping rules inside. - The tokenizer allows escape this chars for string Templates: “`”, “/”, “\”, “$”, “{“, “}”
Objects & members
- Objects are maps:
{ k: 1, nested: { x: 2 } }. - Member read:
o.xresolves the property on the object (if base isASF_Map) or if the base is an array and property matches a builtin array method name, it returns a bound builtin method (see “method/property semantics” above). - Member write semantics use
ResolveProphelper to ensure property writes mutate the real container (works with nestedparentObjchains created by the compiler when producing LValue metadata).
Builtin method dispatch rules
- When evaluating a
Membernode, the VM:- Evaluates the
baseexpression. - If
baseevaluates to anASF_Map(object), property lookup returns stored value (function/map/primitive). - If
baseevaluates to an array andpropis the name of an array-method, the VM returns aBuiltinMethodmap:{ type: "BuiltinMethod", method: "<name>", baseVal: <array> }. - When a
Callhas a callee that isBuiltinMethod, the VM routes the call into the unified array-method dispatch block and executes the method withbaseValLocalpre-bound.
- Evaluates the
This design allows a.map(fn) and f = a.map; f(fn) to behave consistently.
VBA Expressions integration
@( ... )syntax embeds raw VBAexpressions. The string inside is evaluated by the VBA Expressions evaluator at runtime and its result is returned to the script.- Use-case: matrix operations, calling VBA functions/worksheet UDFs, or invoking existing code via the
gExprEvaluatorbridge.
Errors & failure modes
- Many method builtins validate their arguments and return
Emptyif arguments are invalid (for example calling an array method on a non-array base will typically produceEmpty). - Parser/compile errors raise exceptions during
.Compile(); runtime errors in expressions raise errors inside.Run()(try/catch inside ASF scripts can be used to handle runtime exceptions). - Tests exercise many edge cases — consult
TestRunner.basfor the canonical expected behavior.
Examples
// map/filter/reduce chain
a = [1,2,3,4,5];
sum = a.filter(fun(x){ return x > 2 }).reduce(fun(acc,x){ return acc + x }, 0);
return(sum); // 12
// using bound method as first-class
m = a.map;
b = m(fun(x){ return x * 10 });
// from with mapper
print(from([1,2,3], fun(x,i,arr){ return x + i })); // -> [1,3,5]
// sort with comparator
a = [3,1,2];
a.sort(fun(a,b){ return a - b });
// Advanced replacements
fun replacer(match, p1, p2, p3, offset, string)
{return [p1, p2, p3].join(' - ');};
newString = 'abc12345#$*%'.replace(`/(\D*)(\d*)(\W*)/`, replacer);
print(newString);