WIRL workflow DSL grammar and parser
Project description
wirl-lang — Workflow DSL (BNF + Parser)
This package contains the grammar and parser for the WIRL workflow DSL. The DSL compiles to an explicit directed graph with optional guarded loops. This subfolder is self-contained: a BNF grammar and a Python parser that produces a structured AST you can feed into an engine.
Scope of this folder
- Grammar:
grammar/wirl.bnf - Parser and AST dataclasses:
grammar/wirl_parser.py - Public API:
parse_wirl_to_objects(path: str) -> Workflow
Quick start
from wirl_lang import parse_wirl_to_objects
workflow = parse_wirl_to_objects(
"workflow_definitions/paper_rename_workflow/paper_rename_workflow.wirl"
)
print(workflow.name)
for node in workflow.nodes:
print(type(node).__name__, getattr(node, "name", None))
DSL at a glance
- Workflow file defines exactly one runnable graph:
workflow <Name> { ... }. - Nodes are external function calls with declared inputs/outputs and immutable
const { ... }. - Data dependencies are implicit: a node becomes eligible once its inputs are available.
- No shared mutable state. All values are immutable except outputs with reducer
(append)which accumulate into a list. - Optional inputs/outputs use a trailing
?in their declaration. Optional inputs do not create execution dependencies. - Guarded loops via
cyclewith aguard { ... }clause andmax_iterations: <int>. - Conditional execution via
when { <boolean-expr> }on nodes and guards. - Human-in-the-loop
hitl { ... }andretry { ... }are present in the grammar; see status below.
Example (excerpt)
This is a shortened excerpt of the working workflow in workflow_definitions/paper_rename_workflow/paper_rename_workflow.wirl:
workflow PaperRenameWorkflow {
metadata {
description: "Paper Rename workflow"
owner: "madmag77"
version: "1.0"
files_extension: "pdf"
}
inputs {
String drafts_folder_path
String processed_folder_path
}
outputs {
List<String> processed_files = ReturnProcessedFiles.processed_files
}
node GetFiles {
call get_files
inputs {
String drafts_folder_path = drafts_folder_path
}
outputs {
List<String> file_paths
}
}
cycle RenameLoop {
inputs {
List<String> initial_file_paths_to_process = GetFiles.file_paths
List<String> remaining_file_paths_to_process = ReadPdfFile.remaining_file_paths
String processed_folder_path = processed_folder_path
}
outputs {
List<String> processed_files = CheckAllFilesProcessed.processed_files
}
node ReadPdfFile {
call read_pdf_file
inputs {
List<T<Image>> file_paths = RenameLoop.remaining_file_paths_to_process?
List<String> initial_file_paths_to_process = RenameLoop.initial_file_paths_to_process
}
const {
pages_to_read: 2
}
outputs {
List<T<Image>> pages
String file_path
List<String> remaining_file_paths
Bool no_files_to_process
}
}
...
guard {
inputs {
Bool is_done = CheckAllFilesProcessed.is_done
}
when {
CheckAllFilesProcessed.is_done
}
}
max_iterations: 10
}
Grammar highlights
- Top-level:
workflow <Name> { metadata? inputs? outputs? node* cycle* }
- Nodes:
node <Name> { call <module_or_func> inputs { ... } outputs { ... } const { ... } when { ... } hitl { ... } retry { ... } }
- Parameters:
- Declarations:
TYPE name (= value)? ? - Values: literal or reference
OtherNode.outputName - Optional: trailing
?(e.g.,String clarifications?) - Reducers on outputs:
(append) TYPE nameor default(last)
- Declarations:
- Cycles:
cycle <Name> { inputs { ... } outputs { ... } node* guard { inputs { ... } when { ... } } max_iterations: INT }
- Conditionals:
when { <boolean-expr> }on nodes and inside guards
Parser API
parse_wirl_to_objects(path: str) -> Workflow- Returns a
Workflowdataclass with:name: strmetadata: Optional[Metadata](entries: Dict[str, str])inputs: List[Input],outputs: List[Output]nodes: List[NodeClass | CycleClass]
NodeClass:name,call,inputs,outputs,when,hitl,retry,constantsCycleClass:name,inputs,outputs,nodes,guard,max_iterationsOutput.reducer:"last"(default) or"append"
- Returns a
Type system
- Supported tokens in the grammar:
Bool, Int, Float, String, File, Object<...>, List<T>(free-formTYPEtoken covers these and generics). - Current parser does not enforce types; it records the declared type name string.
Feature status
- Implemented in grammar and parser:
- Workflows, nodes, inputs/outputs, constants, reducers
(append) when { ... }conditionscyclewithguard { ... }andmax_iterations
- Workflows, nodes, inputs/outputs, constants, reducers
- Present in grammar but not fully wired in the AST yet:
retry { attempts, backoff, policy }(parsed token exists; transformer wiring pending)hitl { correlation, timeout }(accepted; current transformer sets a placeholder/default)
- Planned validations (not yet implemented):
- Missing required inputs, unused outputs, type mismatches
- Single entry/exit checks and reachability
Design notes (execution semantics)
- One file ⇒ one graph with a single entry and a single exit.
- Dataflow drive: node executes when all non-optional inputs become available and
whenblock is evaluated to True (if any). - All values are immutable;
(append)outputs accumulate across iterations/paths. - Guarded loops model Pregel-style supersteps; parallelism can be expressed with multiple nodes eligible in the same step. A
parallelblock may be added in the future.
When Block Evaluation Rules
The when blocks use special truthiness evaluation that differs from standard Python boolean logic:
- False conditions: Only
Noneor explicitFalseevaluate to false - True conditions: All other values evaluate to true, including:
- Empty containers:
[],{},"" - Zero values:
0,0.0 - Objects with custom types not defined in the evaluation context
- Empty containers:
This design ensures that:
- Only explicit absence (
None) or negation (False) prevents node execution - Empty results from previous nodes don't block subsequent processing
- Type definitions don't need to be available during condition evaluation
Examples:
node ProcessIfResults {
when {
DataLoader.items // True even if items is []
}
}
node SkipIfDisabled {
when {
Config.enabled // False only if None or explicit False
}
}
node HandleCustomObjects {
when {
Parser.objects // True even if objects contains undefined types
}
}
Local development
- Edit the grammar in
grammar/wirl.bnfand the transformer ingrammar/wirl_parser.py. - After grammar changes, re-run your parser-driven test or the quick-start snippet above.
FAQ
- Can I reference optional outputs? Yes. Declare the consuming input as optional with a trailing
?and assign fromProducer.optionalOutput. - Do optional inputs block execution? No. Absence of an optional input does not prevent node scheduling.
- How do I append across iterations? Mark the output with
(append)in its declaration, e.g.,(append) List<String> processed_files.
License
This package is released under the repository’s root license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wirl_lang-0.1.1.tar.gz.
File metadata
- Download URL: wirl_lang-0.1.1.tar.gz
- Upload date:
- Size: 11.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3df20e6a560481790f2709375378d98f0cd783fb35ab69536202b96cd966557
|
|
| MD5 |
63bbc198daa9d01ba44c24f06230a326
|
|
| BLAKE2b-256 |
3a3b87b2ddd0414137c863f9f918647941ef9fb642d4e7189b68c4a8207ac402
|
File details
Details for the file wirl_lang-0.1.1-py3-none-any.whl.
File metadata
- Download URL: wirl_lang-0.1.1-py3-none-any.whl
- Upload date:
- Size: 7.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70117050b3c489c5972d0ad3314dcc69e9c8250ece14efbd63b0b55d6752c957
|
|
| MD5 |
45c748383ddce3880ca7dfa0ef244708
|
|
| BLAKE2b-256 |
309ec7980a7b8e75f93d43e970e4b2cb6e15059d1180017f9cb46ce193b37924
|