Releases: ECP-Solutions/ASF
ASFv3.1.1
Summary
ASF v3.1.1 enhances Office application integration by enabling seamless data exchange with Excel, Word, PowerPoint and all MS Office applications for comprehensive debugging visibility for automation workflows.
Highlights
-
Added
-
Extended Call Trace for Office Objects — Full execution tracing with safe formatting for host containers and Office objects.
-
Bidirectional Array Conversion — Automatic conversion between VBA 2D arrays and ASF jagged arrays.
Dim arr As Variant arr = Array(Array("id", "name", "email"), _ Array(1, "John", "[email protected]"), _ Array(2, "Jane", "[email protected]")) engine.InjectVariable "arr", arr pid = engine.Compile("$1.Sheets(1).Range('A1:C3').Value2 = arr") engine.Run pid, ThisWorkbook ' ASF converts jagged --> 2D for Range.Value2 assignment ' ASF --> VBA: Excel ranges returned as properly formatted jagged arrays pid = engine.Compile("return $1.Sheets(1).Range('A1:C3').Value2") result = engine.Run(pid, ThisWorkbook) ' ASF converts 2D --> jagged for internal processing
-
Safe VBA Parameter Marshaling — Proper array conversion for
CallByNameoperations:' Internal: CastVBAparam() ensures proper 2D array format ' ASF jagged arrays --> VBA 2D arrays for method calls pid = engine.Compile("$1.Sheets(1).Range('A1:F11').Value2 = arr; return $1.Sheets(1).Range('A1:F11').Value2") result = engine.Run(pid, ThisWorkbook) ' Internally: ' 1. arr (jagged) --> converted to 2D for Range.Value2 setter ' 2. Range.Value2 getter returns 2D --> converted to jagged for ASF ' 3. Final return value converted back as needed
-
-
Improved
- Call Trace Formatting — Enhanced console output for complex Office objects:
- Safe string representation for Worksheets, Ranges, and other Office objects
- Proper formatting of nested arrays in trace output
- Type indicators for Office object types (
<Sheets>,<Worksheet>,<Range>)
- Call Trace Formatting — Enhanced console output for complex Office objects:
-
Internal core changes:
- VM (
ASF_VM.cls):- Added
CastVBAparam()function for safe VBA parameter conversion - Modified
CallObjectMethod()to wrap all arguments withCastVBAparam() - Enhanced
ArrayToASF2()for bidirectional array conversion - Added safe object formatting in
PushCallTrace()for Office objects - Improved return value handling to convert 2D arrays to jagged format
- Added type checking to preserve object types during conversions
- Added
- VM (
-
Technical Details:
-
Array Conversion Flow:
VBA 2D Array → ASF Jagged Array: Input: Dim arr(1 To 3, 1 To 2) As Variant arr(1,1) = "A1" arr(1,2) = "B1" arr(2,1) = "A2" arr(2,2) = "B2" arr(3,1) = "A3" arr(3,2) = "B3" Output: [ ["A1", "B1"], ["A2", "B2"], ["A3", "B3"] ] ASF Jagged Array → VBA 2D Array: Input: [ ["A1", "B1"], ["A2", "B2"], ["A3", "B3"] ] Output: arr(1 To 3, 1 To 2) arr(1,1) = "A1" arr(1,2) = "B1" arr(2,1) = "A2" arr(2,2) = "B2" arr(3,1) = "A3" arr(3,2) = "B3" -
CastVBAparam() Function:
Purpose: Convert ASF jagged arrays to VBA 2D arrays for CallByName Input Handling: - Jagged Array → Converted to 2D array - 2D Array → Passed through unchanged - Scalar Values → Passed through unchanged - Objects → Passed through unchanged Usage in CallObjectMethod(): Before: CallByName(obj, method, VbGet, .item(1), .item(2)) After: CallByName(obj, method, VbGet, CastVBAparam(.item(1)), CastVBAparam(.item(2))) Ensures: Range.Value2 = jaggedArray works correctly -
Call Trace Enhancement:
Object Type Detection: - TypeName() used to identify Office objects - Special formatting for: Sheets, Worksheet, Range, Document, Presentation - Generic <Object> for unrecognized types Array Formatting: - Small arrays: Full inline display - Large arrays: Formatted with indentation and newlines - Nested structures: Recursive pretty-printing Example Output: CALL: Sheets() -> <Sheets> CALL: range('A1:F11') -> <Range> CALL: Value2() -> [ [ 'id', 'name', 'email' ] [ 1, 'John', '[email protected]' ] [ 2, 'Jane', '[email protected]' ] ] -
Compatibility Notes:
- Works with all Office applications supporting VBA
- Handles native Office applications properties correctly (Excel, Word, PowerPoint, Access)
- Maintains backward compatibility with non-Office ASF scripts
-
Breaking Changes
None. This release is fully backward compatible.
Full Changelog: v3.1.0...v3.1.1
ASFv3.1.0
Summary
ASF v3.1.0 introduces native Office Object support, enabling seamless interaction with Excel, Word, PowerPoint, and other Office applications directly from ASF scripts. This release adds optional host application access control, fixes VBAexpressions integration bugs, resolves property chaining issues, and includes significant internal refactoring for improved code maintainability.
Highlights
-
Added
-
Native Office Object Support — Direct access to Office application objects with full method chaining:
Dim engine As ASF: Set engine = New ASF ' Access Excel ranges and properties engine.AppAccess = True pid = engine.Compile("let a = $1.sheets(1).range('A1').value + $1.sheets(1).range('B1').value; return a.slice(21)") result = engine.Run(pid, Application.Workbooks(1)) ' If A1 = "Hello from range A1." and B1 = " Good to see you in B1!" ' => "Good to see you in B1!" ' Word document manipulation pid = engine.Compile("return $1.paragraphs(1).range.text") text = engine.Run(pid, ActiveDocument) ' PowerPoint slide access pid = engine.Compile("return $1.slides(1).shapes.count") count = engine.Run(pid, ActivePresentation)
-
AppAccessProperty — Optional host application exposure with security control:Dim engine As New ASF ' Grant script access to host application engine.AppAccess = True pid = engine.Compile("return $1.name") twbName = engine.Run(pid, ThisWorkbook) ' Secure mode (default) engine.AppAccess = False pid = engine.Compile("return $1.name") ' Error: Object required
-
-
Fixed
-
Property Chaining Resolution — Proper segregation of dotted properties:
' Before v3.1.0: Treated "sheets.count" as single property pid = engine.Compile("return $1.sheets.count") ' Parser error: Cannot find property "sheets.count" ' After v3.1.0: Properly segregates each property pid = engine.Compile("return $1.sheets.count") count = engine.Run(pid, ThisWorkbook) ' => 3 (works correctly)
-
VBAexpressionsType Safety — Fixed scope creation bug.
-
-
Improved
ParsePrimaryNoderefactoring — Enhanced code readability for maintainability:- Clearer control flow and logic structure
- Better separation of concerns for different token types
- More maintainable for future enhancements
- No functional changes (internal only)
-
Internal core changes:
-
ASF (
ASF.cls):- Added
AppAccessproperty (Boolean) to control host application exposure - Added
APP_ACCESS_private member (default: False) - Modified
Run()to checkAppAccessbefore exposing host application object - Enhanced security model for controlled object access
- Added
-
Compiler (
ASF_Compiler.cls):- Refactored
ParsePrimaryNode()for improved code clarity - Fixed property chaining to properly parse dotted notation
- Enhanced tokenization of member access chains
- Improved handling of collapsed identifiers with multiple dots
- Better separation between simple properties and complex chains
- Refactored
-
VM (
ASF_VM.cls):- Added Office object support in member access evaluation
- Modified
EvalMemberNode()to handle native VBA objects - Enhanced method call handling for Office object models
- Added type checking in
Eval()for VBAexpressions integration - Improved
CreateEvaluator()to validate object types before evaluation - Added safeguards against passing objects to VBAexpressions
-
-
Technical Details:
-
Office Object Integration: Users can pass any object from the base
ApplicationExpression: $1.sheets(1).range('A1').value Parsing Flow: 1. $1 → Placeholder variable (Workbook object) 2. .sheets(1) → Member "sheets" + Call with arg 1 3. .range('A1') → Member "range" + Call with arg 'A1' 4. .value → Member "value" AST Structure: Member { base: Member { base: Call { callee: Member { base: Call { callee: Member { base: Variable("$1"), prop: "sheets" }, args: [1] }, prop: "range" }, args: ['A1'] }, prop: "value" } } VM Execution: - Evaluates each member/call in sequence - Uses CallByName for VBA object methods - Returns final property value -
AppAccess Security Model:
AppAccess = False (default): - Application objects NOT injected into scope - Scripts cannot access host application - Maximum security, sandboxed execution AppAccess = True (opt-in): - Scripts can access Application properties and methods - Useful for trusted automation scripts - Objects can be passed explicitly via placeholders or using a custom scopes. - Requires explicit enable by caller engine.AppAccess = False ' Sandbox pid = engine.Compile("return $1.name") result = engine.Run(pid, ThisWorkbook) ' Explicit object ✓ -
Property Chain Parsing:
Input: "$1.sheets.count" Before v3.1.0: Tokenizer: IDENT("$1"), SYM("."), IDENT("sheets.count") Parser: Member{base: $1, prop: "sheets.count"} ✗ Error: No property "sheets.count" on Workbook After v3.1.0: Tokenizer: IDENT("$1"), SYM("."), IDENT("sheets"), SYM("."), IDENT("count") Parser: Member{ base: Member{ base: Variable("$1"), prop: "sheets" }, prop: "count" } ✓ Result: Correct property access chain -
ParsePrimaryNodeRefactoring:Improvements: - Extracted common patterns into helper functions - Clearer variable naming conventions - Consistent code style -
Compatibility Notes:
- Works with Excel, Word, PowerPoint, Access, Outlook, and other VBA-enabled apps
CallByNameused for maximum compatibility across Office versionsAppAccessdefaults toFalsefor backward compatibility- Existing scripts without Office objects continue to work unchanged
-
Breaking Changes
None. This release is fully backward compatible.
Full Changelog: v3.0.2...v3.1.0
ASFv3.0.2
Summary
ASF v3.0.2 introduces runtime placeholders ($1, $2, $3, ...) enabling JavaScript-like compact lambda expressions with optimal performance, inspired by stdLambda. This release also adds optional type casting control for injected variables, VBAexpressions evaluator integration and a new VarToASF to cast variables and enforce runtime sandboxing.
Highlights
-
Added
-
Runtime Placeholders — Concise parameter syntax for inline expressions:
'// Compact lambda expressions with $1, $2, ... pid = engine.Compile("return $1 * $2") result = engine.Run(pid, 5, 10) '// => 50 '// Array operations pid = engine.Compile("return $1.filter(fun(x) {return x % 2 == 0;})") evens = engine.Run(pid, Array(1, 2, 3, 4, 5)) '// => [2, 4] '// Expressions calling built-in functions pid = engine.Compile("return Math.sin($1) + Math.cos($2)") result = engine.Run(pid, 0, Math.PI) '// => 1
-
CastInjectedVars Property — Optional automatic type conversion control:
Dim engine As New ASF ' Disable automatic casting for raw performance engine.CastInjectedVars = False pid = engine.Compile("return $1.filter(fun(x){return x > 10;})") result = engine.Run(pid, myArray) ' Enable casting for VBA interop (default: True) engine.CastInjectedVars = True pid = engine.Compile("return $1 + $2") result = engine.Run(pid, vbArray1, vbArray2)
-
VarToASFmethod — ASF secure variable casting:Dim engine As New ASF ' Casting outside the runtime engine.CastInjectedVars = False myArray = engine.VarToASF(myCollection) pid = engine.Compile("return $1.filter(fun(x){return x > 10;} )") result = engine.Run(pid, myArray)
-
VBAexpressions Integration — Direct evaluator access:
' Create evaluator for VBA expressions engine.CreateEvaluator "y+2*x" result = engine.Eval("x=2; y=5") '// => 9 ' Access evaluator directly Set ev = engine.Evaluator
-
PredeclaredId Support — ASF class can now be used as predeclared:
' Static methods emulation Set evaluator = ASF.CreateEvaluator "y+2*x"
-
-
Internal core changes:
-
ASF (
ASF.cls):- Changed
VB_PredeclaredIdtoTruefor predeclared usage - Added
CastInjectedVarsproperty (Boolean) to control type conversion - Added
CreateEvaluator()method for VBAexpressions integration - Added
Eval()method for direct expression evaluation - Added
VarToASF()method for safe variables casting to ASF - Modified
Run()to acceptParamArray params()for runtime placeholders - Added
EVALUATOR_private member for VBAexpressions instance - Added
CAST_INJECTED_VARIABLES_flag (default: True)
- Changed
-
Parser (
ASF_Parser.cls):- Added tokenization for placeholder variables (
$1,$2,$3, ...) - Placeholder detection:
$followed by one or more digits - Placeholders tokenized as
IDENTtype for seamless integration
- Added tokenization for placeholder variables (
-
VM (
ASF_VM.cls):- Added
CastInjectedVarsproperty to control automatic type conversion - Modified
InjectVariable()to respectCAST_INJECTED_VARIABLES_flag - Modified
RunProgramByIndex()to accept optionalparamsparameter - Automatic placeholder mapping: parameters map to
$1,$2,$3, ... in scope - Added
VarToASF()method for safe variables casting to ASF - Added
tmpVarfor non-cast variable assignment - Enhanced injected variable handling with
On Error Resume Nextand.Remove()
- Added
-
-
Technical Details:
-
Runtime Placeholder Syntax:
$1, $2, $3, ..., $N → Access positional parameters -
Parameter Mapping:
' Single parameter engine.Run(pid, value) → $1 = value ' Multiple parameters (array) engine.Run(pid, v1, v2, v3) → $1 = v1, $2 = v2, $3 = v3 ' Array parameter engine.Run(pid, arrayVar) → $1 = arrayVar (entire array)
-
Type Casting Behavior:
' CastInjectedVars = True (default) ' Automatically converts VBA arrays/collections to ASF format ' Ensures compatibility with ASF array methods ' CastInjectedVars = False ' Passes variables as-is without conversion ' Maximum performance for pre-formatted data ' 5-10x faster when casting is not needed
-
Placeholder Tokenization:
Input: "$1 + $2 * $10" Tokens: [IDENT:"$1"], [OP:"+"], [IDENT:"$2"], [OP:"*"], [IDENT:"$10"] Scope: $1, $2, $10 looked up as regular identifiers in program scope -
Implementation Strategy:
1. Parser tokenizes $N as IDENT tokens 2. VM receives params via ParamArray 3. VM injects params into progScope as "$1", "$2", etc. 4. ASF code references $1, $2 like any other variable 5. Type casting applied if CastInjectedVars = True -
Compatibility Notes:
- Placeholders are standard identifiers (no special handling in AST)
- Can be used anywhere a variable is valid
- Cannot be assigned to (read-only parameters)
Dim engine As ASF: Set engine = New ASF With engine .Run .Compile("print(`placeholders operation = ${$2/$1}`); $1 = 5; print($1)"), 2, 10 End With ' The $1 placeholder holds 2 as value
- Support unlimited parameter count (limited only by VBA ParamArray)
- Fully compatible with existing ASF features (closures, classes, modules)
-
Special Cases:
' Missing placeholders default to undefined pid = engine.Compile("return $3") result = engine.Run(pid, 1, 2) ' $3 is undefined ' Parameters beyond passed values are undefined pid = engine.Compile("return [$1, $2, $3]") result = engine.Run(pid, 10, 20) ' => [10, 20, undefined] ' Single array parameter pid = engine.Compile("return $1.length") result = engine.Run(pid, Array(1, 2, 3)) ' => 3 ' Multiple array parameters pid = engine.Compile("return $1.concat($2)") result = engine.Run(pid, arr1, arr2)
-
Performance Benchmarks
Runtime Placeholders vs stdLambda
Test Setup: 5000 iterations, expressions with Math functions
Expression: "sin($1) + 2*5 - cos($2)"
stdLambda:
Set lambda = stdLambda.Create("sin($1)+2*5-cos($2)")
result = lambda.Run(x, y)
Performance: 8719 ms (1743.8µs per operation)
ASF (v3.0.2):
pid = engine.Compile("return Math.sin($1)+2*5-Math.cos($2)")
result = engine.Run(pid, x, y)
Performance: 2312 ms (462.4µs per operation)
Result: ASF is 3.77x faster with identical API
Array Transformations with Functions
Test Setup: 100 iterations, filter + reduce with Math functions
Operation: Filter evens, reduce with sin(acc) + sin(x)
stdLambda:
Performance: 8719 ms (87190µs per operation)
ASF (v3.0.2):
Performance: 3453 ms (34530µs per operation)
Result: ASF is 2.52x faster
Breaking Changes
None. This release is fully backward compatible.
- Existing code without placeholders works unchanged
Run()method signature extended but maintains compatibilityCastInjectedVarsdefaults toTrue(existing behavior)- All previous features remain functional
Comparison: ASF vs stdLambda (Updated)
Performance Profile
stdLambda ASF v3.0.2 Winner
─────────────────────────────────────────
Pure Arithmetic 6.2µs 306.2µs stdLambda (49x)
Math Functions 1743.8µs 462.4µs ASF (3.77x) ⭐
Transformations 87190µs 34530µs ASF (2.52x) ⭐
Real-world weighted average: ASF is 2.5x faster
Credits
Special thanks to the VBA community for performance testing and feedback that drove the runtime placeholder implementation.
Full Changelog: v3.0.1...v3.0.2
ASFv3.0.1
Summary
ASF v3.0.1 adds a comprehensive Math object providing JavaScript-compatible mathematical constants and functions. The Math object exposes 28 functions and 2 constants accessible through property syntax (e.g., Math.sin(x), Math.PI), enabling scientific computing and advanced numerical operations within VBA Advanced Scripting.
Highlights
-
Added
-
Math object — JavaScript-compatible mathematical namespace with constants and methods:
// ── Mathematical constants ───────────────────────── Math.E; // Euler's number ≈ 2.718281828 Math.PI; // Pi ≈ 3.141592654 // ── Trigonometric functions ──────────────────────── Math.sin(Math.PI / 2); // => 1 Math.cos(Math.PI); // => -1 Math.tan(Math.PI / 4); // => 1 Math.asin(1); // => 1.5708 (π/2) Math.acos(0); // => 1.5708 (π/2) Math.atan(1); // => 0.7854 (π/4) Math.atan2(1, 1); // => 0.7854 (π/4) // ── Hyperbolic functions ─────────────────────────── Math.sinh(1); // => 1.1752 Math.cosh(1); // => 1.5431 Math.tanh(1); // => 0.7616 Math.asinh(1); // => 0.8814 Math.acosh(2); // => 1.3170 Math.atanh(0.5); // => 0.5493 // ── Exponential & logarithmic ────────────────────── Math.exp(2); // => 7.389 (e²) Math.expm1(0.1); // => 0.1052 (e^x - 1) Math.log(Math.E); // => 1 (natural log) Math.log10(100); // => 2 (base-10 log) Math.log2(8); // => 3 (base-2 log) Math.log1p(0.1); // => 0.0953 (ln(1 + x)) // ── Power & roots ────────────────────────────────── Math.pow(2, 8); // => 256 Math.sqrt(16); // => 4 Math.cbrt(27); // => 3 (cube root) Math.hypot(3, 4); // => 5 (√(3² + 4²)) Math.hypot(1, 2, 2); // => 3 (√(1² + 2² + 2²)) // ── Rounding ─────────────────────────────────────── Math.floor(4.7); // => 4 Math.ceil(4.1); // => 5 Math.round(4.5); // => 4 (rounds to even) Math.round(5.5); // => 6 // ── Sign & absolute value ────────────────────────── Math.abs(-5); // => 5 Math.sign(-10); // => -1 Math.sign(0); // => 0 Math.sign(42); // => 1 // ── Min/max (variadic) ───────────────────────────── Math.max(1, 5, 3, 9, 2); // => 9 Math.min(1, 5, 3, 9, 2); // => 1
-
Practical examples:
// ── Distance calculation ─────────────────────────── fun distance(x1, y1, x2, y2) { return Math.hypot(x2 - x1, y2 - y1); }; d = distance(0, 0, 3, 4); // => 5 // ── Degree/radian conversion ─────────────────────── fun toRadians(degrees) { return degrees * Math.PI / 180; }; fun toDegrees(radians) { return radians * 180 / Math.PI; }; angle = toRadians(90); // => 1.5708 degrees = toDegrees(Math.PI); // => 180 // ── Circle area ──────────────────────────────────── fun circleArea(radius) { return Math.PI * Math.pow(radius, 2); }; area = circleArea(5); // => 78.5398 // ── Statistical operations ───────────────────────── fun mean(numbers) { sum = numbers.reduce(fun(acc, x) { return acc + x; }, 0); return sum / numbers.length; }; fun variance(numbers) { avg = mean(numbers); return mean(numbers.map(fun(x) { return Math.pow(x - avg, 2); })); }; fun stddev(numbers) { return Math.sqrt(variance(numbers)); }; data = [2, 4, 4, 4, 5, 5, 7, 9]; avg = mean(data); // => 5 std = stddev(data); // => 2 // ── Sigmoid function (ML) ────────────────────────── fun sigmoid(x) { return 1 / (1 + Math.exp(-x)); }; s = sigmoid(0); // => 0.5 // ── Clamp value to range ─────────────────────────── fun clamp(value, min, max) { return Math.max(min, Math.min(max, value)); }; clamped = clamp(150, 0, 100); // => 100
-
-
Internal core changes:
- VM (
ASF_VM.cls):- Math object implemented as a property-dispatch builtin in
EvalMemberNode - Check for
baseLocal.GetValue("name") = "Math"after builtin method lookup - Math constants (
E,PI) evaluated immediately and returned - Math methods route through existing Call evaluation with
propNmatching function name - All 28 functions implemented using VBA native math functions (
Atn,Exp,Log,Sqr, etc.) - Variadic support for
hypot,max,minusingFor Eachloops overevaluatedcollection
- Math object implemented as a property-dispatch builtin in
- VM (
-
Technical Details:
-
Math Constants:
Math.E → Exp(1) ≈ 2.718281828459045 Math.PI → 4 * Atn(1) ≈ 3.141592653589793 -
Trigonometric Functions (argument in radians):
Math.sin(x) → Sine Math.cos(x) → Cosine Math.tan(x) → Tangent Math.asin(x) → Arcsine (returns NaN if |x| > 1) Math.acos(x) → Arccosine (returns NaN if |x| > 1) Math.atan(x) → Arctangent Math.atan2(y,x) → Two-argument arctangent (quadrant-aware) -
Hyperbolic Functions:
Math.sinh(x) → Hyperbolic sine Math.cosh(x) → Hyperbolic cosine Math.tanh(x) → Hyperbolic tangent Math.asinh(x) → Inverse hyperbolic sine Math.acosh(x) → Inverse hyperbolic cosine (returns NaN if x < 1) Math.atanh(x) → Inverse hyperbolic tangent (returns NaN if |x| >= 1) -
Exponential & Logarithmic Functions:
Math.exp(x) → e^x Math.expm1(x) → e^x - 1 (more accurate for small x) Math.log(x) → Natural logarithm (returns NaN if x <= 0) Math.log10(x) → Base-10 logarithm (returns NaN if x <= 0) Math.log2(x) → Base-2 logarithm (returns NaN if x <= 0) Math.log1p(x) → ln(1 + x) (returns NaN if x <= 0) -
Power & Root Functions:
Math.pow(x, y) → x^y Math.sqrt(x) → Square root Math.cbrt(x) → Cube root (handles negative values) Math.hypot(...) → √(x₁² + x₂² + ... + xₙ²) (Euclidean norm) -
Rounding Functions:
Math.floor(x) → Largest integer ≤ x Math.ceil(x) → Smallest integer ≥ x Math.round(x) → Rounds to nearest integer (banker's rounding) -
Other Functions:
Math.abs(x) → Absolute value Math.sign(x) → -1, 0, or 1 depending on sign Math.max(...) → Maximum value (variadic) Math.min(...) → Minimum value (variadic) -
Implementation Notes:
- All trigonometric functions expect radians (not degrees)
Math.round()uses VBA'sRound()(round-to-even)- Invalid inputs (e.g.,
Math.asin(2)) return the string"NaN" Math.atan2(y, x)follows JavaScript convention (y-coordinate first)Math.cbrt(x)correctly handles negative values:Math.cbrt(-8) => -2- Variadic functions (
hypot,max,min) accept any number of arguments
-
Special Cases:
Math.acos(1) => 0 Math.acos(-1) => π (3.14159...) Math.asin(1) => π/2 (1.5708...) Math.asin(-1) => -π/2 (-1.5708...) Math.acosh(1) => 0 Math.atan2(0, 0) => 0 Math.atan2(0, -1) => π Math.atanh(0) => 0 Math.ceil(-4.7) => -4 Math.floor(-4.7) => -5
-
Usage Examples
// Scientific calculator
fun quadraticFormula(a, b, c) {
discriminant = Math.pow(b, 2) - 4 * a * c;
if (discriminant < 0) {
return null; // No real solutions
};
sqrtDisc = Math.sqrt(discriminant);
x1 = (-b + sqrtDisc) / (2 * a);
x2 = (-b - sqrtDisc) / (2 * a);
return [x1, x2];
};
// Solve x² - 5x + 6 = 0
solutions = quadraticFormula(1, -5, 6);
// => [3, 2]
// Trigonometric calculations
fun polarToCartesian(r, theta) {
x = r * Math.cos(theta);
y = r * Math.sin(theta);
return { x: x, y: y };
};Full Changelog: v3.0.0...v3.0.1
ASFv3.0.0
Summary
ASF v3.0.0 is a major-version release that introduces a full ECMAScript-style module system (import/export) and adds working-directory builtins (cwd, scwd) together with a single-call Execute entry point on the ASF UI. Modules are cached on first load, circular dependencies are detected at load time, and relative paths are resolved against the current working directory. Many of the new features are still in beta, awaiting community support and feedback, or under continuous development.
Breaking Changes
- File extension adopted:
.vas.
.vasstands for VBA Advanced Scripting.
ReadModuleSourceresolves.vasautomatically; bare module names without an extension are tried first as-is, then with.vasappended.
Highlights
-
Added
-
.vasfile extension — the official source-file extension for all VBA Advanced Scripting files: -
Module system —
import/exportstatements with ECMAScript semantics:// ── Named exports (math.vas) ────────────────────── fun add(a, b) { return a + b; }; fun multiply(a, b) { return a * b; }; PI = 3.14159; export { add, multiply, PI }; // ── Named imports (main_math.vas) ───────────────── scwd(wd); import { add, multiply, PI } from './math.vas'; result = add(5, 3); area = PI * multiply(5, 5); return `5 + 3 = ${result}, Circle area: ${area}`; // => "5 + 3 = 8, Circle area: 78.53975"
// ── Default export (calculator.vas) ─────────────── fun Calculator() { return { add: fun(a, b) { return a + b; }, subtract: fun(a, b) { return a - b; } }; }; export default Calculator; // ── Default import (main_calculator.vas) ────────── scwd(wd); import calc from './calculator.vas'; calculator = calc(); return(calculator.add(10, 5)); // => "15"
// ── Namespace import (utils.vas) ────────────────── fun formatName(first, last) { return first + ' ' + last; }; fun uppercase(str) { return str.toUpperCase(); }; export { formatName, uppercase }; // ── Namespace usage (main_utils.vas) ────────────── scwd(wd); import * as utils from './utils.vas'; name = utils.formatName('John', 'Doe'); return utils.uppercase(name); // => "JOHN DOE"
// ── Mixed: default + named (lib.vas) ────────────── fun helper() { return 'Helper function'; }; fun main() { return 'Main function'; }; VERSION = '1.0.0'; export default main; export { helper, VERSION }; // ── Mixed imports (app.vas) ──────────────────────── scwd(wd); import mainFunc, { helper, VERSION } from './lib.vas'; return `${mainFunc()} | ${helper()} | Version: ${VERSION}`; // => "Main function | Helper function | Version: 1.0.0"
-
Aliasing with
asin both import and export specifier lists:import { add as sum } from './math.vas'; export { localName as publicName };
-
Working-directory builtins (
cwd/scwd):// scwd(path) — set current working directory // cwd() — return current working directory scwd(wd); // set to injected path currentPath = cwd(); // read it back // currentPath === wd
-
Execute(filePath)method on theASFfaçade — single call to read, compile, and run a.vasfile and return its result:Dim eng As New ASF eng.InjectVariable "wd", ThisWorkbook.path result = eng.Execute(ThisWorkbook.path & "\main_math.vas") ' result === "5 + 3 = 8, Circle area: 78.53975"
-
-
Internal core changes:
-
Parser (
ASF_Parser.cls):- Four identifiers are now intercepted during tokenization and emitted as
["KEYWORD", …]tokens instead of["IDENT", …]:import,export,default,as - Matching is case-sensitive; only the lower-case forms are recognised as keywords
- Four identifiers are now intercepted during tokenization and emitted as
-
Compiler (
ASF_Compiler.cls):- New instance properties
CurrentModulePathandIsModuleModeset by the VM loader before each module compilation - New
ParseImportStatementmethod — parses all five supported import forms and produces anImportAST node - New
ParseExportStatementmethod — parses named-brace, default-expression, and declaration export forms and produces anExportAST node CompileProgrammain loop now checks forKEYWORD / importandKEYWORD / exporttokens before falling through to the existing statement collector; matched statements are added directly tostmtsASTviaGoTo Compiler_MainLoop
- New instance properties
-
VM (
ASF_VM.cls):- New builtins
cwdandscwdin the Call-case builtin dispatcher; both read/writeGLOBALS_.CURRENT_MODULE_PATH - New
ExecModImport— resolves module path, callsLoadModule, then binds default, namespace, or named imports into the caller's scope; named-export values are resolved throughEvalExprNode(Variable fallback togFuncTable) so that top-levelfundeclarations export correctly - New
ExecModExport— writes named or default export values intogModuleExportsfor the currently executing module path - New
LoadModule— orchestrates the full module lifecycle: circular-dependency check againstgLoadingModules, source read, compilation via a freshASF_Compilerinstance, scope creation, statement execution, pending-export post-processing, and default-export extraction; caches the result ingModuleRegistry - New
ResolveModulePath— for paths beginning with./or../, prepends the current working directory (normalised to forward-slash); otherwise returns the path unchanged - New
ReadModuleSource— delegates toResolveModulePath, appends.vasif the file does not exist without it, raises error 9012 if still missing, then delegates toReadTextFile
- New builtins
-
Globals (
ASF_Globals.cls):gModuleRegistry(ASF_Map) — caches loaded module objects keyed by resolved pathgModuleExports(ASF_Map) — maps each module path to its live exports map during executiongLoadingModules(Collection) — stack of paths currently being loaded; used for circular-dependency detectionCURRENT_MODULE_PATH(String) — the working directory used byResolveModulePathand read/written bycwd/scwd- All four members are initialised in
ASF_InitGlobals
-
ASF UI (
ASF.cls):- New
Execute(filePath As String)method — reads, compiles, and runs a.vasfile in a single call; returns the program output - New
WorkingDirproperty (get/set) — direct access toGLOBALS_.CURRENT_MODULE_PATH - New
ClearModuleCachemethod — resetsgModuleRegistry,gModuleExports,gLoadingModules, andCURRENT_MODULE_PATH - New
ReadTextFile(filePath)method — exposes the VM's binary-stream file reader
- New
-
-
Technical Details:
-
Import AST Node:
Import { type: "Import" source: String // raw string-literal value from 'from' clause defaultImport: String // present only for default-import forms namespaceImport: String // present only for * as X forms namedImports: Collection // present only when { … } specifiers exist; } // each item is an ImportSpecifier -
ImportSpecifier AST Node:
ImportSpecifier { type: "ImportSpecifier" imported: String // name as it appears in the exporting module local: String // name bound in the importing scope (differs when 'as' used) } -
Export AST Node (three shapes):
// default form Export { type: "Export" isDefault: True expression: ExprNode // parsed expression after 'default' } // named-brace form Export { type: "Export" isDefault: False namedExports: Collection // each item is an ExportSpecifier } // declaration form (export fun …) Export { type: "Export" isDefault: False declarationType: "function" declarationName: String // function name; main loop re-parses the declaration } -
ExportSpecifier AST Node:
ExportSpecifier { type: "ExportSpecifier" local: String // name in the module's own scope exported: String // name exposed to importers (differs when 'as' used) } -
Module Object (stored in
gModuleRegistry):Module (ASF_Map) { path: String // resolved absolute path used as cache key loaded: Boolean // True after full execution completes exports: ASF_Map // live named-export bindings (name → value) defaultExport: Variant // present only when module contains 'export default' } -
New Keywords (tokenised as
["KEYWORD", value]):"import" "export" "default" "as" -
Error Messages:
"Unexpected end after import"— import statement truncated (Compiler, #8001)"Expected 'as' after * in import"— namespace import missingas(Compiler, #8002)"Expected identifier after 'as'"—asnot followed by a name (Compiler, #8003 / #8004 / #8011)"Invalid import syntax"— unrecognised token after `import...
-
ASFv2.0.8
Summary
ASF v2.0.8 introduces modern JavaScript-style spread/rest operators (...) and array destructuring assignments, significantly enhancing array manipulation capabilities and function parameter handling.
Highlights
-
Added
-
Spread/rest operator (
...) support:// Spread in array literals arr1 = [1, 2, 3]; arr2 = [0, ...arr1, 4, 5]; // => [0, 1, 2, 3, 4, 5] // Spread in function calls fun add(a, b, c) { return a + b + c; }; numbers = [1, 2, 3]; result = add(...numbers); // => 6 // Rest parameters in functions fun sum(first, ...rest) { total = first; rest.forEach(fun(n) { total = total + n }); return total; }; sum(1, 2, 3, 4, 5); // => 15 // Spread strings into character arrays chars = [...'hello']; // => ['h', 'e', 'l', 'l', 'o']
-
Array destructuring assignments:
// Basic destructuring [a, b, c] = [1, 2, 3]; // a=1, b=2, c=3 // With rest element [first, ...rest] = [1, 2, 3, 4, 5]; // first=1, rest=[2, 3, 4, 5] // Fewer targets than elements [x, y] = [10, 20, 30, 40]; // x=10, y=20 // More targets than elements (assigns Empty) [p, q, r] = [100, 200]; // p=100, q=200, r=Empty // Practical use cases [a, b] = [b, a]; // Swap variables [head, ...tail] = myArray; // Extract head and tail // Combine spread and destructuring arr1 = [1, 2]; arr2 = [3, 4]; [first, ...combined] = [...arr1, ...arr2]; // first=1, combined=[2, 3, 4]
-
-
Internal core changes:
-
Parser (
ASF_Parser.cls):- Added tokenization for
...spread/rest operator - New token type:
"SPREAD"with value"..."
- Added tokenization for
-
Compiler (
ASF_Compiler.cls):- Added
IsSpreadToken()helper function for spread operator detection - Rest parameter support in function and method definitions
- Spread operator handling in array literal compilation (
ParseArrayLiteral) - Spread operator support in function call argument processing
- Array destructuring pattern detection in
ParseStatementTokensToAST - New AST node type:
"ArrayDestructuring"with targets collection and optional rest target - Compile-time validation:
- Rest parameter must be last in function signatures
- Only one rest parameter allowed per function
- Rest element must be last in destructuring patterns
- Multiple rest elements not allowed in destructuring patterns
- Added
-
VM (
ASF_VM.cls):- Spread operator evaluation in
EvalArrayNodewith type handling - Array expansion for arrays, strings, and scalar values
- Spread operator support in function call argument expansion
- Rest parameter handling in function calls with automatic array creation
- New
"ArrayDestructuring"case handler inExecuteStmtNode
- Spread operator evaluation in
-
-
Technical Details:
-
Spread/Rest Token Structure:
Token: ["SPREAD", "..."] -
Rest Parameter AST:
Function/Method node { params: Collection of parameter names restParam: String (optional) ... } -
Array Destructuring AST:
ArrayDestructuring { targets: Collection of Variable nodes restTarget: String (optional) source: Expression node } -
Error Messages:
"Rest parameter must be last parameter"- Rest parameter not in final position"Multiple rest parameters not allowed"- More than one rest parameter in function"Rest element must be last in destructuring"- Rest element not in final position"Multiple rest elements in destructuring"- More than one rest element in pattern"Expected identifier after ... in destructuring"- Invalid rest syntax"Cannot destructure non-array value"- Runtime type validation failure
-
Full Changelog: v2.0.7...v2.0.8
ASFv2.0.7
Summary
ASF v2.0.7 is a minor improvement for ASF core ergonomic.
Highlights
- Improved:
- ASF: the
Runmethod now returns the result of the compiled program being executed.Private Sub ForEachTest() Dim result As Variant Dim engine As ASF: Set engine = New ASF With engine result = .Run(.Compile("o = {x: 10, y: 20}; s = 0; o.forEach(fun(v) { s = s + v }); return(s);")) End With Set engine = Nothing End Sub
- ASF: the
Full Changelog: v2.0.6...v2.0.7
ASFv2.0.6
Summary
ASF v2.0.6 represent an improvement for native objects ergonomics.
Highlights
- Improved:
- VM: users can now call the
foreachmethod over an objet.s=0; c=0; foreach({math: 85, english: 92, science: 78}, fun(val, key){ s = s + val; c += 1 }); return('Average: ' + s/c); // ==> Average: 85
- VM: users can now call the
Full Changelog: v2.0.5...v2.0.6
ASFv2.0.5
Summary
ASF v2.0.5 represent an improvement for VMs collaboration through variable injection.
Highlights
- Improved:
- ASF: users can now inject
ASF_MapandASF_RegexEngineobjects at VM time.
- ASF: users can now inject
Full Changelog: v2.0.4...v2.0.5
ASFv2.0.4
Summary
ASF v2.0.4 is a code clean-up for the library.
Highlights
- Improved:
- Code: Rubberduck code inspection.
Full Changelog: v2.0.3...v2.0.4