Expressions¶
DV Flow Manager supports dynamic parameter evaluation using the ${{ }}
expression syntax. Expressions allow parameters to reference other parameters,
perform calculations, and make decisions based on configuration values.
Expression Syntax¶
Expressions are enclosed in ${{ }} markers:
tasks:
- name: message
uses: std.Message
with:
msg: "Value is ${{ some_parameter }}"
Any parameter value can contain expressions. They are evaluated during task graph elaboration, before any task execution begins.
Basic Usage¶
Parameter References¶
Reference package and task parameters by name:
package:
name: my_pkg
with:
version:
type: str
value: "1.0"
debug:
type: bool
value: false
tasks:
- name: build
with:
build_type:
type: str
value: "debug"
uses: std.Message
with:
msg: "Building version ${{ version }} in ${{ build_type }} mode"
Expressions can reference:
Package parameters: Defined in the package’s
withsectionTask parameters: Defined in the task’s
withsectionParent task parameters: In compound tasks, access parent parameters
Arithmetic Operations¶
Perform calculations within expressions:
package:
name: example
with:
base_value:
type: int
value: 100
tasks:
- name: task1
uses: std.Message
with:
msg: "Double value: ${{ base_value * 2 }}"
- name: task2
uses: std.Message
with:
msg: "Sum: ${{ base_value + 50 }}"
Supported operators:
Arithmetic:
+,-,*,/,//,%,**Comparison:
==,!=,<,<=,>,>=Logical:
and,or,not
Boolean Expressions¶
Use boolean expressions for conditional logic:
package:
name: example
with:
debug:
type: bool
value: false
optimization:
type: int
value: 2
tasks:
- name: check
uses: std.Message
iff: ${{ debug and optimization < 2 }}
with:
msg: "Debug mode with low optimization"
String Operations¶
Concatenate strings and format values:
package:
name: example
with:
prefix:
type: str
value: "build"
version:
type: str
value: "1.0"
tasks:
- name: task1
uses: std.Message
with:
msg: "${{ prefix }}_v${{ version }}" # "build_v1.0"
Expression Contexts¶
Package Scope¶
At package level, expressions can reference package parameters:
package:
name: example
with:
compiler:
type: str
value: "gcc"
version:
type: str
value: "11"
imports:
- name: toolchain
with:
compiler: ${{ compiler }}
version: ${{ version }}
Task Scope¶
Within tasks, expressions can access:
Package parameters
Task parameters
Parent compound task parameters (if nested)
tasks:
- name: parent
with:
mode:
type: str
value: "release"
body:
- name: child
uses: std.Message
with:
# Access parent's 'mode' parameter
msg: "Running in ${{ mode }} mode"
Matrix Variables¶
When using matrix strategy, expressions can reference matrix variables:
tasks:
- name: test_suite
strategy:
matrix:
test: ["test1", "test2", "test3"]
seed: [100, 200, 300]
body:
- name: run_test
uses: std.Message
with:
msg: "Running ${{ matrix.test }} with seed ${{ matrix.seed }}"
This generates 9 tasks (3 tests × 3 seeds) with appropriate parameter values.
Advanced Features¶
Conditional Expressions¶
Use ternary-like conditional logic:
package:
name: example
with:
debug:
type: bool
value: false
tasks:
- name: build
shell: bash
run: ${{ "make debug" if debug else "make release" }}
Note: Python-style conditional expressions (x if condition else y) are supported.
List and Dict Operations¶
Access list elements and dictionary values:
package:
name: example
with:
flags:
type: list
value: ["-O2", "-Wall", "-Werror"]
config:
type: map
value:
arch: "x86_64"
os: "linux"
tasks:
- name: task1
uses: std.Message
with:
msg: "First flag: ${{ flags[0] }}" # "-O2"
- name: task2
uses: std.Message
with:
msg: "Architecture: ${{ config['arch'] }}" # "x86_64"
Function Calls¶
Limited set of built-in functions are available:
tasks:
- name: example
uses: std.Message
with:
msg: ${{ len(some_list) }} # List length
msg: ${{ str(some_int) }} # Convert to string
msg: ${{ int(some_str) }} # Convert to integer
Available functions:
len(x): Length of list/stringstr(x): Convert to stringint(x): Convert to integerbool(x): Convert to boolean
Expression Evaluation Order¶
Expressions are evaluated in the following order:
Package load time: Package-level expressions in imports and overrides
Task elaboration: Task parameter expressions
Condition evaluation:
iffexpressions for conditional tasksMatrix expansion: Matrix variable substitution
This order ensures that:
Configuration is resolved before task creation
Parameters are available when needed
Dependencies are properly evaluated
Runtime Expressions¶
Expressions can reference runtime data from task dependencies using special variables:
inputs - Outputs from dependency tasks (available at task runtime)
memento - Cached data from previous run (for incremental builds)
These expressions are automatically deferred during graph construction and evaluated when the task executes.
Example:
tasks:
- name: producer
scope: root
produces: [std.FileSet]
run: |
echo '{"type":"std.FileSet","files":["a.sv","b.sv"]}'
shell: bash
- name: consumer
scope: root
needs: [producer]
consumes: [std.FileSet]
with:
file_count:
type: int
value: "${{ inputs | length }}"
run: |
echo "Received ${file_count} inputs"
shell: bash
The expression ${{ inputs | length }} cannot be evaluated during graph construction
(inputs aren’t known yet), so it’s stored as a deferred expression and evaluated
when consumer runs, after producer completes.
Variable References¶
Variables can be referenced using the $ prefix for explicit variable references:
package:
with:
threshold:
type: int
value: 10
tasks:
- name: check
with:
result:
value: "${{ $threshold * 2 }}"
This is particularly useful when working with filter parameters.
Array and Object Operations¶
Access array and object elements using indexing:
# Array indexing
first_item: "${{ my_array[0] }}"
second_item: "${{ my_array[1] }}"
# Array slicing
first_three: "${{ my_array[:3] }}"
from_third: "${{ my_array[2:] }}"
middle: "${{ my_array[2:5] }}"
# Object field access
value: "${{ config.settings.timeout }}"
by_key: "${{ data['key_name'] }}"
# Array iteration
all_items: "${{ items[] }}"
JQ-Style Builtins¶
Expressions support JQ-style builtin functions for data transformation using
the pipe operator |:
# String operations
parts: "${{ path | split(\"/\") }}"
basename: "${{ path | split(\"/\") | last }}"
extension: "${{ filename | split(\".\") | last }}"
# Array operations
count: "${{ items | length }}"
sorted: "${{ values | sort }}"
unique_vals: "${{ data | unique }}"
reversed: "${{ list | reverse }}"
first_val: "${{ items | first }}"
last_val: "${{ items | last }}"
# Type checking
item_type: "${{ value | type }}"
Available builtins:
length- Get length of array/object/stringkeys- Get keys of objectvalues- Get values of objectsort- Sort arrayunique- Remove duplicates from arrayreverse- Reverse arrayfirst- Get first elementlast- Get last elementflatten- Flatten nested arraystype- Get type name (“array”, “object”, “string”, “number”, “boolean”, “null”)split(sep)- Split string by separator
Chaining Operations¶
Chain multiple operations using the pipe operator:
# Multiple builtins
result: "${{ data | unique | sort | reverse }}"
# Indexing with builtins
last_unique: "${{ items | unique | last }}"
# Complex pipeline
filename: "${{ full_path | split(\"/\") | last }}"
Limitations¶
Expressions have some limitations to maintain safety and predictability:
No side effects: Expressions cannot modify state
No I/O: Cannot read files or access network
No arbitrary code: Limited to safe expression subset
Evaluation Phases:
Static evaluation: Most expressions evaluated during graph construction
Runtime evaluation: Expressions referencing
inputsormementodeferred until task execution
These limitations ensure that:
Flow specifications remain declarative
Task graphs can be analyzed before execution
Flows are reproducible and predictable
Best Practices¶
Keep Expressions Simple¶
Prefer simple, readable expressions:
Good:
msg: "Version ${{ version }}"
iff: ${{ debug }}
Avoid:
msg: ${{ "v" + str(major) + "." + str(minor) + ("_debug" if debug else "") }}
Use Parameters for Complex Logic¶
Move complex logic into parameters rather than inline expressions:
Good:
package:
with:
full_version:
type: str
value: "${{ major }}.${{ minor }}${{ '_debug' if debug else '' }}"
tasks:
- name: build
uses: std.Message
with:
msg: "Building version ${{ full_version }}"
Document Parameter Relationships¶
When parameters depend on each other, document the relationships:
package:
with:
optimization:
type: int
value: 2
doc: Optimization level (0-3)
debug_symbols:
type: bool
value: false
doc: |
Enable debug symbols. Typically true when optimization is 0,
false otherwise. Can be overridden explicitly.
Common Patterns¶
Version Construction¶
package:
with:
major:
type: int
value: 1
minor:
type: int
value: 0
patch:
type: int
value: 0
version:
type: str
value: "${{ major }}.${{ minor }}.${{ patch }}"
Path Construction¶
package:
with:
install_prefix:
type: str
value: "/opt/tools"
tool_name:
type: str
value: "mytool"
tool_path:
type: str
value: "${{ install_prefix }}/${{ tool_name }}"
Conditional Features¶
package:
with:
enable_coverage:
type: bool
value: false
enable_assertions:
type: bool
value: true
debug_mode:
type: bool
value: "${{ enable_coverage or enable_assertions }}"
Parameter Selection¶
package:
with:
simulator:
type: str
value: "verilator"
debug_level:
type: int
value: 0
tasks:
- name: sim
uses: std.Message
with:
msg: "Using simulator: ${{ simulator }} with debug=${{ debug_level }}"