Defining and referencing variables in HTML attributes can be verbose. This is why hyTags uses a stack-based model where commands pass data implicitly. Combined with type-based lookup and type-prefixed commands, stack-based code in hyTags is often quite concise and readable.
When a command produces a result, it's pushed onto the stack. Subsequent commands read values based on their type. Values remain on the stack after being read, so the stack grows as commands execute:
<button>Calculate</button> <on click> <number-new value="10"> <!-- Stack: [10] --> <number-add value="5"> <!-- Stack: [10, 15] --> <number-multiply value="2"> <!-- Stack: [10, 15, 30] --> <debug-log> </on>
Values are not removed when read. A value can be used by multiple commands:
<button>Use twice</button> <on click> <number-new value="10"> <!-- Stack: [10] --> <number-add value="5"> <!-- Stack: [10, 15] --> <number-add value="3"> <!-- Still uses 15, Stack: [10, 15, 18] --> <debug-log> </on>
Commands find values on the stack by type, searching from top to bottom (most recent first). Each value is used only once per command invocation. This allows values of different types to coexist:
<button>Show</button> <on click> <string-new value="Result: "> <!-- Stack: ["Result: "] --> <number-new value="42"> <!-- Stack: ["Result: ", 42] --> <number-to-string> <!-- Stack: ["Result: ", 42, "42"] --> <string-prepend> <!-- Finds "42" first, then "Result: " --> <!-- Stack: [..., "Result: 42"] --> <debug-log> </on>
Use variables to store values by name. Variables are set with
var-set and retrieved with
var-get:
<button>Use variable</button> <on click> <var-set name="x" value="10" type="number"></var-set> <var-get name="x"> <number-add value="5"> <debug-log> <!-- Logs: 15 --> </on>
Use
var-update to modify a variable. The current value is passed to the commands block, and the result is stored back:
<button>Increment</button>
<on click>
<var-set name="count" value="0" type="number"></var-set>
<var-update name="count">
<number-add value="1">
</var-update>
<var-update name="count">
<number-add value="1">
</var-update>
<var-get name="count">
<debug-log>
<!-- Logs: 2 -->
</on>
Variables can be passed directly to command parameters using the
$ prefix:
$param="variable-name". This references the stored value without putting it on the stack:
<button>Add from variable</button> <on click> <var-set name="amount" value="100" type="number"></var-set> <number-new value="50"> <number-add $value="amount"> <debug-log> <!-- Logs: 150 --> </on>
Use
value-scope to run commands in an isolated stack. Values created inside the scope don't pollute the parent stack. This is useful when you need intermediate values that shouldn't remain on the stack:
Many commands with nested blocks use this same pattern: they define an inner stack and accept an optional
return parameter. Examples include
selection-scope,
if-true,
if-false, and
style-animate.
<button>Isolated scope</button>
<on click>
<number-new value="10">
<!-- Stack: [10] -->
<value-scope>
<number-new value="5">
<number-new value="3">
<number-add>
<!-- Inner stack: [5, 3, 8] -->
</value-scope>
<!-- Stack: [10] (inner values discarded) -->
<debug-log>
<!-- Logs: 10 -->
</on>
To pass a result back to the parent stack, use the
return parameter to specify the type. The last value of that type in the inner stack is returned:
<button>Return from scope</button>
<on click>
<number-new value="10">
<!-- Stack: [10] -->
<value-scope return="number">
<number-new value="5">
<number-add value="3">
<!-- Inner stack: [5, 8] -->
</value-scope>
<!-- Stack: [10, 8] (8 returned) -->
<number-add>
<debug-log>
<!-- Logs: 18 -->
</on>
Commands have required and optional parameters. Required parameters without an explicit value are automatically read from the stack by type. Optional parameters without a value become
null and are not read from the stack.
To read an optional parameter from the stack, use
$param without a variable name. For example,
number-to-string has an optional
suffix parameter. Using
$suffix reads the suffix from the stack:
<button>With suffix</button> <on click> <string-new value=" items"> <!-- Stack: [" items"] --> <number-new value="42"> <!-- Stack: [" items", 42] --> <number-to-string $suffix> <!-- Reads suffix from stack --> <debug-log> <!-- Logs: "42 items" --> </on>
If omitted, the parameter becomes
null and is not read from the stack, even when a matching value is available:
<button>Without suffix</button> <on click> <string-new value=" items"> <!-- Stack: [" items"] --> <number-new value="42"> <!-- Stack: [" items", 42] --> <number-to-string> <!-- suffix is null, string ignored --> <debug-log> <!-- Logs: "42" --> </on>
var-set,
var-get, and
var-update.return to pass a value back.