ASF — Syntax Reference
This document defines the concrete syntax supported by the Advanced Scripting Framework (ASF) and contains a compact BNF grammar, operator precedence, and examples.
Note: ASF is embedded in VBA — scripts are supplied to the ASF compiler which tokenizes and produces an AST that the VM executes. Semicolons (
;) are used as statement separators and are required in ambiguous cases; comma (,) is only used as an argument or element separator.
Quick summary
- Statement separator:
; - Argument/element separator:
, - Single-line comment:
/* ... */(also supports C-style token comments in parser) - Function literal:
fun (params...) { ... } - Top-level function declaration:
fun name(params...) { ... } - Anonymous function values are closures with shared-write semantics (they capture the current runtime scope by reference).
- Array literal:
[ elem1, elem2, ... ] - Object literal:
{ key1: value1, key2: value2 } - VBExpression block:
@(...)— raw VBAexpressions block evaluated via the VBA-Expressions bridge. - Null literal:
null(literal representing absence of value) - Boolean literals:
true,false
BNF grammar (compact)
This BNF uses a mixture of concrete tokens and non-terminals to show the language shape.
<program> ::= <stmts>
<stmts> ::= <stmt> ( ';' <stmt> )* [ ';' ]
<stmt> ::= <if-stmt>
| <for-stmt>
| <while-stmt>
| <try-stmt>
| <switch-stmt>
| <return-stmt>
| <break-stmt>
| <continue-stmt>
| <expr-stmt>
| <print-stmt>
| <func-decl>
| <class-decl>
| <let-decl>
<if-stmt> ::= 'if' '(' <expr> ')' <block>
( 'elseif' '(' <expr> ')' <block> )*
[ 'else' <block> ]
<for-stmt> ::= 'for' '(' <expr> ',' <expr> ',' <expr> ')' <block>
| 'for' '(' IDENT 'in' <expr> ')' <block>
| 'for' '(' IDENT 'of' <expr> ')' <block>
<while-stmt> ::= 'while' '(' <expr> ')' <block>
<try-stmt> ::= 'try' <block> 'catch' '(' IDENT ')' <block>
| 'try' <block> 'catch' <block>
<switch-stmt> ::= 'switch' '(' <expr> ')' '{'
( 'case' <expr> <block> )*
[ 'default' <block> ]
'}'
<return-stmt> ::= 'return' [ '(' <expr> ')' | <expr> ]
<break-stmt> ::= 'break'
<continue-stmt> ::= 'continue'
<expr-stmt> ::= <expr>
| 'super' '(' <arglist> ')'
<print-stmt> ::= 'print' '(' <arglist> ')'
<func-decl> ::= 'fun' IDENT '(' <paramlist> ')' <block>
<class-decl> ::= 'class' IDENT [ 'extends' IDENT ] '{' <class-body> '}'
<class-body> ::= <class-member>*
<class-member> ::= <field-decl>
| <constructor-decl>
| <method-decl>
| <static-method-decl>
<field-decl> ::= 'field' <field-list> ';'
<field-list> ::= <field-item> ( ',' <field-item> )*
<field-item> ::= IDENT [ '=' <expr> ]
<constructor-decl> ::= 'constructor' '(' <paramlist> ')' <block>
<method-decl> ::= IDENT '(' <paramlist> ')' <block>
<static-method-decl> ::= 'static' IDENT '(' <paramlist> ')' <block>
<let-decl> ::= 'let' IDENT [ '=' <expr> ]
<block> ::= '{' <stmts> '}'
| <stmt>
<expr> ::= <assignment>
| <ternary>
<assignment> ::= <postfix> <assign-op> <expr>
<assign-op> ::= '='
| '+='
| '-='
| '*='
| '/='
| '%='
<ternary> ::= <logical-or> [ '?' <expr> ':' <expr> ]
<logical-or> ::= <logical-and> ( '||' <logical-and> )*
<logical-and> ::= <bitwise-or> ( '&&' <bitwise-or> )*
<bitwise-or> ::= <bitwise-xor> ( '|' <bitwise-xor> )*
<bitwise-xor> ::= <bitwise-and> ( '^' <bitwise-and> )*
<bitwise-and> ::= <equality> ( '&' <equality> )*
<equality> ::= <relational> ( ('==' | '!=') <relational> )*
<relational> ::= <shift> ( ('<' | '>' | '<=' | '>=') <shift> )*
<shift> ::= <add> ( ('<<'|'>>') <add> )*
<add> ::= <mul> ( ('+'|'-') <mul> )*
<mul> ::= <unary> ( ('*'|'/'|'%') <unary> )*
<unary> ::= ('+' | '-' | '!' | 'typeof') <unary>
| <power>
<power> ::= <postfix> ( '^' <power> )?
<postfix> ::= <primary> { <postfix-op> }*
<postfix-op> ::= '.' IDENT
| '[' <expr> ']'
| '(' <arglist> ')'
<primary> ::= NUMBER
| STRING
| 'true'
| 'false'
| 'null'
| IDENT
| '[' <elemlist> ']'
| '{' <obj-items> '}'
| 'fun' '(' <paramlist> ')' <block>
| '(' <expr> ')'
| '@' '(' VBA_EXPR ')'
| '`' <template-parts>
| 'new' IDENT '(' <arglist> ')'
| 'this'
| 'super'
<arglist> ::= [ <expr> ( ',' <expr> )* ]
<elemlist> ::= [ <expr> ( ',' <expr> )* ]
<obj-items> ::= [ (<IDENT | STRING> ':' <expr>)
(',' (<IDENT|STRING> ':' <expr>))* ]
<paramlist> ::= [ IDENT ( ',' IDENT )* ]
<string-escape> ::= '\\'
| '\''
| '\"'
| '\n'
| '\t'
| '\r'
<comment> ::= '//' ( any-char-except-newline )* newline
| '/*' ( any-char )* '*/'
<template-parts> ::= ( <template-char>* | '${' <expr> '}' )*
<template-char> ::= any character except '`' or '$'
| '$' not-followed-by '{'
IDENT ::= letter followed by letters/digits/underscore
NUMBER ::= decimal or float
STRING ::= '...' or "..."
VBA_EXPR ::= any raw text until matching ')'
Notes:
- The parser also accepts top-level function declarations using the same
funsyntax with a name:fun name(params) { body }— these are converted to program-level function definitions and stored in the global program table. - Collapsed identifiers like
o.a[2].bare parsed into nested AST nodes (Variable/Member/Index) by the compiler helperParseCollapsedIdentToNode.
Operator precedence & associativity
From highest precedence to lowest:
- Parentheses
()(grouping) - Postfix: calls
(), indexes[], member access.prop(left-to-right chaining) - Exponentiation
^(right-associative) - Unary
+ - ! - Multiplicative
* / % - Additive
+ - - Shifts
<< >> - Relational
< <= > >= - Equality
== != -
Bitwise/logic levels ( , ^, &, …) - Logical AND
&& - Logical OR
|| - Ternary
?:
Literals
- Number:
123,3.14 - String:
'hello'or"hello" - Boolean:
true,false - Null:
null - Array:
[1, 2, [3], 'x'] - Object:
{ k: 1, s: 'x' } - VBExpression:
@({1;0;4})— raw block passed to VBA-expressions evaluator
Constructors
- Array:
[] - String:
'' - Regular Expressions:
regex(<params>?)
Functions & closures
fun(x,y) { return x+y }produces a closure value.- Top-level functions:
fun add(a,b) { return a + b }— compiled into the global program table and callable by name. - Closures implement shared-write semantics: they capture the runtime scope by reference. Mutations to outer-scope variables are visible across all closures that share that scope.
Call semantics
CallClosure(closureMap, argsCollection, thisVal):- The runtime binds parameters in a new scope linked to the closure’s environment.
- Callback functions receive
(value, index, array)when called by array-methods. thisis supported when calling closures via bound method objects or when athisArgis supplied.
Postfix chaining (member/call/index)
- Postfix chaining is supported for arbitrary primaries. Examples:
a.b.c(d)[i].x()- Special-case:
.lengthon an index or array is compiled into.__len__builtin call at compile-time.
Statement rules & semicolons
;separates statements. The parser enforces semicolons more strictly to disambiguate nested constructs (recommended: terminate statements with;when inline or in compact code).- Inside
{ ... }semicolons are not required strictly before}but are required between adjacent statements when ambiguous.
AST node types (high level)
ASF uses Map-based AST nodes internally. Common node type values include:
Literal— { type: “Literal”, value: … }Variable— { type: “Variable”, name: “x” }Member— { type: “Member”, base:, prop: "x" } Index— { type: “Index”, base:, index: } Call— { type: “Call”, callee:, args: [ , ...] } FuncLiteral— { type: “FuncLiteral”, params: […], body:} Array— { type: “Array”, items: [, ...] } Object— { type: “Object”, items: [ (key,node), … ] }VBAexpr— { type: “VBAexpr”, expr: “…” }BuiltinMethod— runtime-built map representing a bound method (whena.mapis evaluated andais an array)
Example snippets
// arithmetic + precedence
return(1 + 2 * 3);
// function + closures (shared-write)
a = 1;
f = fun() { a = a + 1; return a; };
print(f()); // PRINT:2
print(a); // PRINT:2
// arrays / map
a = [1, [2,3]];
b = a.map(fun(x) { if (IsArray(x)) { return x } else { return x * 2 } });
print(b);
// object literal & member call
o = { v: 10, incr: fun(x) { return x + 1 } };
print(o.incr(o.v)); // PRINT:11
// VBExpr embedding (evaluated by VBAexpressions)
a = @({1;0;4});
print(a);
Notes & hints
- null is a valid literal returned by expressions and used to represent absence of value.
- Arrays in ASF are implemented as Variant arrays and honor __option_base (runtime option that sets the base index).
- The compiler will attempt to expand collapsed identifiers like a.b[3].c into nested AST nodes so the VM can handle LValue semantics correctly.
References
See TestRunner.bas for a comprehensive test-driven specification (85+ tests that exercise syntax and runtime behavior).