Skip to content

Releases: ECP-Solutions/ASF

ASFv3.1.1

14 Feb 15:57

Choose a tag to compare

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 CallByName operations:

      ' 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>)
  • Internal core changes:

    • VM (ASF_VM.cls):
      • Added CastVBAparam() function for safe VBA parameter conversion
      • Modified CallObjectMethod() to wrap all arguments with CastVBAparam()
      • 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
  • 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

13 Feb 18:52
0a9f0e3

Choose a tag to compare

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)
    • AppAccess Property — 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)
    • VBAexpressions Type Safety — Fixed scope creation bug.

  • Improved

    • ParsePrimaryNode refactoring — 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 AppAccess property (Boolean) to control host application exposure
      • Added APP_ACCESS_ private member (default: False)
      • Modified Run() to check AppAccess before exposing host application object
      • Enhanced security model for controlled object access
    • 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
    • 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 Application

      Expression: $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
      
    • ParsePrimaryNode Refactoring:

      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
      • CallByName used for maximum compatibility across Office versions
      • AppAccess defaults to False for 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

07 Feb 18:50
1a267b6

Choose a tag to compare

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)
    • VarToASF method — 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_PredeclaredId to True for predeclared usage
      • Added CastInjectedVars property (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 accept ParamArray params() for runtime placeholders
      • Added EVALUATOR_ private member for VBAexpressions instance
      • Added CAST_INJECTED_VARIABLES_ flag (default: True)
    • Parser (ASF_Parser.cls):

      • Added tokenization for placeholder variables ($1, $2, $3, ...)
      • Placeholder detection: $ followed by one or more digits
      • Placeholders tokenized as IDENT type for seamless integration
    • VM (ASF_VM.cls):

      • Added CastInjectedVars property to control automatic type conversion
      • Modified InjectVariable() to respect CAST_INJECTED_VARIABLES_ flag
      • Modified RunProgramByIndex() to accept optional params parameter
      • Automatic placeholder mapping: parameters map to $1, $2, $3, ... in scope
      • Added VarToASF() method for safe variables casting to ASF
      • Added tmpVar for non-cast variable assignment
      • Enhanced injected variable handling with On Error Resume Next and .Remove()
  • 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 compatibility
  • CastInjectedVars defaults to True (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

04 Feb 02:13

Choose a tag to compare

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 propN matching function name
      • All 28 functions implemented using VBA native math functions (Atn, Exp, Log, Sqr, etc.)
      • Variadic support for hypot, max, min using For Each loops over evaluated collection
  • 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's Round() (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

03 Feb 02:02
61a8b18

Choose a tag to compare

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.
    .vas stands for VBA Advanced Scripting.
    ReadModuleSource resolves .vas automatically; bare module names without an extension are tried first as-is, then with .vas appended.

Highlights

  • Added

    • .vas file extension — the official source-file extension for all VBA Advanced Scripting files:

    • Module system — import / export statements 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 as in 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 the ASF façade — single call to read, compile, and run a .vas file 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
    • Compiler (ASF_Compiler.cls):

      • New instance properties CurrentModulePath and IsModuleMode set by the VM loader before each module compilation
      • New ParseImportStatement method — parses all five supported import forms and produces an Import AST node
      • New ParseExportStatement method — parses named-brace, default-expression, and declaration export forms and produces an Export AST node
      • CompileProgram main loop now checks for KEYWORD / import and KEYWORD / export tokens before falling through to the existing statement collector; matched statements are added directly to stmtsAST via GoTo Compiler_MainLoop
    • VM (ASF_VM.cls):

      • New builtins cwd and scwd in the Call-case builtin dispatcher; both read/write GLOBALS_.CURRENT_MODULE_PATH
      • New ExecModImport — resolves module path, calls LoadModule, then binds default, namespace, or named imports into the caller's scope; named-export values are resolved through EvalExprNode (Variable fallback to gFuncTable) so that top-level fun declarations export correctly
      • New ExecModExport — writes named or default export values into gModuleExports for the currently executing module path
      • New LoadModule — orchestrates the full module lifecycle: circular-dependency check against gLoadingModules, source read, compilation via a fresh ASF_Compiler instance, scope creation, statement execution, pending-export post-processing, and default-export extraction; caches the result in gModuleRegistry
      • New ResolveModulePath — for paths beginning with ./ or ../, prepends the current working directory (normalised to forward-slash); otherwise returns the path unchanged
      • New ReadModuleSource — delegates to ResolveModulePath, appends .vas if the file does not exist without it, raises error 9012 if still missing, then delegates to ReadTextFile
    • Globals (ASF_Globals.cls):

      • gModuleRegistry (ASF_Map) — caches loaded module objects keyed by resolved path
      • gModuleExports (ASF_Map) — maps each module path to its live exports map during execution
      • gLoadingModules (Collection) — stack of paths currently being loaded; used for circular-dependency detection
      • CURRENT_MODULE_PATH (String) — the working directory used by ResolveModulePath and read/written by cwd/scwd
      • All four members are initialised in ASF_InitGlobals
    • ASF UI (ASF.cls):

      • New Execute(filePath As String) method — reads, compiles, and runs a .vas file in a single call; returns the program output
      • New WorkingDir property (get/set) — direct access to GLOBALS_.CURRENT_MODULE_PATH
      • New ClearModuleCache method — resets gModuleRegistry, gModuleExports, gLoadingModules, and CURRENT_MODULE_PATH
      • New ReadTextFile(filePath) method — exposes the VM's binary-stream file reader
  • 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 missing as (Compiler, #8002)
      • "Expected identifier after 'as'"as not followed by a name (Compiler, #8003 / #8004 / #8011)
      • "Invalid import syntax" — unrecognised token after `import...
Read more

ASFv2.0.8

02 Feb 00:56
ecd50eb

Choose a tag to compare

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 "..."
    • 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
    • VM (ASF_VM.cls):

      • Spread operator evaluation in EvalArrayNode with 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 in ExecuteStmtNode
  • 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

28 Jan 21:56
9b03caa

Choose a tag to compare

Summary

ASF v2.0.7 is a minor improvement for ASF core ergonomic.


Highlights

  • Improved:
    • ASF: the Run method 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

Full Changelog: v2.0.6...v2.0.7

ASFv2.0.6

24 Jan 01:41
adf9e73

Choose a tag to compare

Summary

ASF v2.0.6 represent an improvement for native objects ergonomics.


Highlights

  • Improved:
    • VM: users can now call the foreach method 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

Full Changelog: v2.0.5...v2.0.6

ASFv2.0.5

23 Jan 10:10
30674c7

Choose a tag to compare

Summary

ASF v2.0.5 represent an improvement for VMs collaboration through variable injection.


Highlights

  • Improved:
    • ASF: users can now inject ASF_Map and ASF_RegexEngine objects at VM time.

Full Changelog: v2.0.4...v2.0.5

ASFv2.0.4

23 Jan 02:28
46d4609

Choose a tag to compare

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