You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/guide/advanced/built-in_instruction_set/jump_clause.md
-12Lines changed: 0 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,8 +5,6 @@
5
5
> **Common foundation**
6
6
> Both rely on the same alias registry (`ALIAS`), perform address resolution during `_pre_check`, and use the `@markup` decorator to manage jump markers. Understanding these common mechanisms helps clarify the difference between them.
7
7
8
-
---
9
-
10
8
## Non-self-compiled direct nodes
11
9
12
10
`GOTO` and `CALL` are **not**`SelfCompileInstruction`. They do not expand into `NodeCompose` at compile time; instead, they exist as single jump nodes in the workflow array.
@@ -17,8 +15,6 @@ That means:
17
15
- runtime behavior is expressed entirely through address resolution and pointer rewriting
18
16
- they do not create new Bubbles or nesting scopes
19
17
20
-
---
21
-
22
18
## GOTO
23
19
24
20
`GOTO` is a factory wrapper for `JumpNode`. At runtime, it calls the interpreter’s `jump_to` method and performs a **one-way, non-returning** pointer rewrite.
@@ -42,8 +38,6 @@ That means:
42
38
-**Branch merging**: multiple conditional branches converge on the same `NOP` point.
43
39
-**State machine transitions**: jump to different next states based on runtime conditions.
44
40
45
-
---
46
-
47
41
## CALL
48
42
49
43
`CALL` is a factory wrapper for `CallNode`. At runtime, it calls the interpreter’s `call_sub` method and performs a **push → jump → execute → pop** subroutine call.
@@ -69,8 +63,6 @@ That means:
69
63
-**Modular decomposition**: split complex workflows into independent subprocedures and keep the main flow simple.
70
64
-**Interrupt handling**: external systems can invoke predefined interrupt handlers via `call_sub(interrupt=True)`.
| Underlying API |`pc.jump_to(addr)`|`pc.call_sub(addr)`|
83
75
84
-
---
85
-
86
76
## Usage notes
87
77
88
78
-**GOTO is not a substitute for loops**: `GOTO` does not provide return semantics. Jumping out of a loop with `GOTO` will not correctly manage the loop state. Use `BreakLoop` for loop exit and `CALL` for reusable subroutines.
89
79
-**CALL targets must be addressable**: the target node or entry node must have `address_able=True`, which is required by `ALIAS`.
90
80
-**GOTO and CALL share alias space**: both look up aliases in `alias2vector_map`. Avoid alias name conflicts.
91
81
-**CALL return depends on stack integrity**: a `GOTO` inside a subroutine sets `_jump_marked`, which can cause `call_sub` to skip stack restoration. Understand that `GOTO` inside a subroutine can override normal return behavior.
Copy file name to clipboardExpand all lines: docs/guide/advanced/built-in_instruction_set/sentinel_clause.md
-6Lines changed: 0 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,8 +2,6 @@
2
2
3
3
`NOP` and `INTERRUPT` are two special atomic instructions in AmritaSense’s instruction set. They are not `SelfCompileInstruction` and have no compile-time expansion. Instead, they exist as individual nodes in the workflow, and their behavior is defined purely at runtime.
4
4
5
-
---
6
-
7
5
## NOP sentinel instruction
8
6
9
7
`NOP` (No Operation) is an **empty sentinel node**. Its significance is not in doing work, but in **standing in place** to provide a valid jump target.
Copy file name to clipboardExpand all lines: docs/guide/advanced/built-in_instruction_set/try_clause.md
-12Lines changed: 0 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,8 +2,6 @@
2
2
3
3
AmritaSense provides a complete exception handling instruction system that aligns closely with Python’s `try-except-else-finally`. Before using it, ask yourself: **when should you use instruction-based exception handling, and when should you write `try-except` inside a node?**
> Use Python for exceptions that belong inside a node. Use instructions for workflow-level exception control. They can be mixed — a TRY block may contain nodes that use their own inline try-catch.
2. A single `TRY` structure may define at most one `FINALLY` and one `THEN`.
87
83
3.`CATCH` may appear multiple times and is matched top-to-bottom with short-circuit behavior.
88
84
89
-
---
90
-
91
85
## Runtime execution logic
92
86
93
87
`TryClause` is a `SelfCompileInstruction` and expands at compile time into:
@@ -110,8 +104,6 @@ The runtime behavior of `TryNode` is:
110
104
- if no catch matches, re-raise the exception
111
105
4.**Regardless of exception**: the `finally` block executes via `call_near` if defined.
112
106
113
-
---
114
-
115
107
## Exception penetration rules
116
108
117
109
When the `WorkflowInterpreter` is initialized with `exception_ignored`, those exception types are not caught by any `CATCH` block:
@@ -129,8 +121,6 @@ When `TryNode` encounters one of these exceptions, it re-raises it immediately,
129
121
-**`BreakLoop`** can jump out of the innermost loop and is not intercepted by intermediate exception handlers.
130
122
-**Critical errors** can bypass local fault tolerance and reach a global handler.
131
123
132
-
---
133
-
134
124
## Usage examples
135
125
136
126
### Instruction orchestration: API call fault tolerance
@@ -178,8 +168,6 @@ TRY(risky_op)\
178
168
TRY(acquire_resource).FINALLY(release_resource)
179
169
```
180
170
181
-
---
182
-
183
171
## Dependency injection in exception handlers
184
172
185
173
Nodes in `CATCH`, `THEN`, and `FINALLY` blocks can use `Depends` normally. The caught exception object itself can also be injected into a handler node — this is a unique advantage of instruction-based exception handling over inline try-catch.
Copy file name to clipboardExpand all lines: docs/guide/advanced/built-in_instruction_set/while_clause.md
-12Lines changed: 0 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,8 +2,6 @@
2
2
3
3
AmritaSense provides two standard loop paradigms: `WHILE` (check before executing) and `DO-WHILE` (execute before checking). Both are `SelfCompileInstruction` and expand at compile time into a fixed structure containing jump nodes. At runtime, the loop semantics are implemented entirely through pointer offsets and `jump_near`, without any external state flags.
4
4
5
-
---
6
-
7
5
## Compile-time expansion structure
8
6
9
7
### WHILE loop
@@ -41,8 +39,6 @@ AmritaSense provides two standard loop paradigms: `WHILE` (check before executin
41
39
> **Key difference**
42
40
> WHILE checks the condition before the loop body; DO-WHILE executes the loop body before the condition. Both use `NOP` as the unified loop exit.
43
41
44
-
---
45
-
46
42
## Runtime execution flow
47
43
48
44
### WHILE
@@ -75,8 +71,6 @@ Within `action` or `do_node`, you can `raise BreakLoop` to implement `break` sem
75
71
76
72
Returning early from `action` or `do_node` ends the current node execution. The interpreter then naturally advances to `CheckUpNode` (for `WHILE`) or `DowhileNode` (for `DO-WHILE`), starting the next iteration. This behavior is equivalent to `continue`.
77
73
78
-
---
79
-
80
74
## `GOTO` restrictions inside loops
81
75
82
76
The compile-time structure of `WHILE` and `DO-WHILE` is fixed. `WhileNode` and `DONode` rely on `call_offset` and `jump_near` relative offsets to perform condition checks and body execution.
@@ -89,8 +83,6 @@ If a `GOTO` inside the loop body jumps outside the loop structure:
89
83
90
84
Therefore, **do not use `GOTO` to jump out of a loop**. Use `BreakLoop` to exit the loop, and use `CALL` for reusable subroutine execution.
| The condition must be evaluated before the body |`WHILE`|
137
127
| The condition only becomes available after the body |`DO-WHILE`|
138
128
139
-
---
140
-
141
129
> **Condition nodes are nodes**
142
130
> Unlike graph model frameworks that hardcode conditions into routing functions, AmritaSense treats the loop condition itself as a `Node[bool]`. That means the condition can be asynchronous, can accept dependency injection, and can be suspended before evaluation. This is the direct embodiment of the “everything is a node” philosophy in loop structures.
Copy file name to clipboardExpand all lines: docs/guide/advanced/child_node.md
-8Lines changed: 0 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,8 +4,6 @@ AmritaSense provides a complete subroutine call mechanism. Unlike `GOTO`’s one
4
4
5
5
This chapter starts from the interpreter’s low-level API and explains call stack management, argument passing, and how to invoke subroutines in node code.
6
6
7
-
---
8
-
9
7
## 4.3.1 `call_sub`: the interpreter’s low-level call primitive
10
8
11
9
`call_sub` is a low-level call primitive provided by `WorkflowInterpreter`. Both the composition-level `CALL` instruction and node-internal subroutine calls ultimately use it. Its core workflow is:
@@ -30,8 +28,6 @@ This design lets the same call primitive serve both internal reuse and external
30
28
31
29
After the subroutine completes, `call_sub` checks the `_jump_marked` flag. If the subroutine executed a jump operation such as `GOTO`, that flag is set to `True`. In that case, the `finally` block **does not restore the original execution pointer** — the new jump target is preserved, and the interpreter continues from there. This ensures subroutine-internal jumps can correctly affect the main workflow control flow.
32
30
33
-
---
34
-
35
31
## 4.3.2 Passing arguments to subroutines
36
32
37
33
`call_sub` supports passing extra positional and keyword arguments directly. These arguments are merged into the available parameter pool for the subroutine’s entry node during dependency resolution.
@@ -61,8 +57,6 @@ Subroutine nodes can use both operands passed via `call_sub` and dependencies de
61
57
If a subroutine entry node declares a dependency via `Depends` and that provider returns `None`, the workflow raises an exception and terminates. This is different from an event system where a `None` return might be treated as a “skip.” Node execution is atomic, and failed dependency resolution means the node cannot run.
62
58
:::
63
59
64
-
---
65
-
66
60
## 4.3.3 Call stack and return address restoration
67
61
68
62
The call stack is the core data structure of AmritaSense’s subroutine mechanism, ensuring correct return behavior for nested calls.
@@ -99,8 +93,6 @@ Every `call_sub` pushes the current address, and every return pops the top addre
99
93
100
94
If the subroutine executes `GOTO` or another jump operation, `_jump_marked` is set to `True`. In that case, `call_sub` skips restoring the saved pointer and does not pop the return address. This means the subroutine’s internal jump can “override” normal return behavior. Developers should understand that `GOTO` inside a subroutine may prevent automatic return stack cleanup and may require explicit stack management.
101
95
102
-
---
103
-
104
96
### Summary
105
97
106
98
`call_sub` and the call stack form the low-level foundation of AmritaSense’s subroutine system. The composition-level `CALL` instruction is a declarative wrapper around this primitive, while node-internal `call_sub` gives developers full freedom to construct dynamic call chains in code. In the next chapter, we’ll explore how to combine these features with external interruption to build subroutine libraries that can be safely injected from outside.
Copy file name to clipboardExpand all lines: docs/guide/advanced/custom_instruction.md
-10Lines changed: 0 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,8 +2,6 @@
2
2
3
3
AmritaSense’s built-in instruction set already covers core control flow such as conditionals, loops, and exception handling. But when those basic instructions repeatedly appear in fixed patterns, you can encapsulate them as **new instructions** with `SelfCompileInstruction`. This extension does not modify the interpreter; it only expands into standard node compositions at compile time, and at runtime it behaves exactly like built-in instructions.
- Jump addresses are handled by the built-in instructions, so `RetryClause` does not need to manage offsets manually.
154
148
- Users see only `RetryClause(...)`, while the expansion remains transparent.
155
149
156
-
---
157
-
158
150
## 4.7.4 Example 2: conditional execution wrapper
159
151
160
152
Encapsulate the common pattern “execute a node when a condition is true, otherwise skip it” as a single instruction.
@@ -195,8 +187,6 @@ class ExecuteWhenElse(SelfCompileInstruction):
195
187
)
196
188
```
197
189
198
-
---
199
-
200
190
## Design principles for custom instructions
201
191
202
192
1.**Encapsulate patterns, not logic**: custom instructions should encapsulate recurring composition patterns (retry, conditional execution, timeout protection), not concrete business logic. Business logic belongs inside nodes.
Copy file name to clipboardExpand all lines: docs/guide/advanced/custom_node.md
+16-9Lines changed: 16 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,8 +2,6 @@
2
2
3
3
In AmritaSense, nodes are the basic units of execution flow. Built-in instructions are ultimately expanded into node compositions, while custom nodes are the direct way developers encapsulate their business logic. This chapter explains node essence, lifecycle, and how to use `POINTER_DEPENDS` to gain full interpreter control when necessary.
4
4
5
-
---
6
-
7
5
## 4.6.1 The `@Node` decorator and node essence
8
6
9
7
The `@Node()` decorator converts an ordinary Python function or coroutine into a workflow node. It does not do anything complex — it simply packages the function object, signature metadata, and a few data fields into a `Node` instance.
@@ -34,8 +32,6 @@ Each node is essentially a thin wrapper around the original function. It preserv
34
32
35
33
**Everything is a node** — this is AmritaSense’s core philosophy. Conditionals, loop bodies, exception handlers, and GOTO targets are all `Node` or `BaseNode` instances. Custom nodes are no exception.
36
34
37
-
---
38
-
39
35
## 4.6.2 Handling sync and async nodes
40
36
41
37
AmritaSense unifies synchronous and asynchronous nodes. In `_call()`, it decides how to execute based on `iscoroutinefunction` and `wrap_to_async`.
@@ -84,8 +80,6 @@ This is appropriate for extremely lightweight sync operations, such as simple co
84
80
85
81
The `Node` class is also memory-optimized using `__slots__`, avoiding a default `__dict__` and keeping each node as lightweight as possible.
86
82
87
-
---
88
-
89
83
## 4.6.3 Node lifecycle and atomicity
90
84
91
85
### Lifecycle
@@ -108,8 +102,6 @@ Node atomicity is guaranteed by the **interpreter lock** and **cooperative inter
108
102
109
103
If a custom node class inherits from `BaseNode`, it can override `_pre_check`. This method is called before each node execution and can access the current interpreter instance to perform address validation, alias lookup, or other checks. `CallNode` and `JumpNode` both use this mechanism to resolve aliases to addresses before their first execution.
110
104
111
-
---
112
-
113
105
## 4.6.4 `POINTER_DEPENDS`: access to the interpreter
114
106
115
107
`POINTER_DEPENDS` is a special dependency injection factory that allows a node to obtain the current `WorkflowInterpreter` instance.
@@ -140,7 +132,22 @@ With interpreter access, nodes can directly manipulate pointers and the call sta
140
132
141
133
Therefore, **inject `POINTER_DEPENDS` only when necessary**. Most nodes should use normal Python logic and composition-level instructions (`IF`, `WHILE`, `CALL`) to express control flow, and only directly access the interpreter when instructions cannot express the desired behavior.
142
134
143
-
---
135
+
## 4.6.5 Safe runtime integration
136
+
137
+
Custom nodes that need runtime context should use `POINTER_DEPENDS` to access `WorkflowInterpreter` rather than manipulating internal interpreter state directly. The interpreter exposes safe APIs such as:
138
+
139
+
-`jump_to(addr)`
140
+
-`jump_near(addr)`
141
+
-`jump_offset(offset)`
142
+
-`call_sub(addr, *args, interrupt=False)`
143
+
-`find_addr_alias(alias)`
144
+
-`object_io`
145
+
146
+
Because `object_io` is a generic `SuspendObjectStream` subclass, custom nodes can also participate in external suspend/resume or streaming I/O patterns without changing the core interpreter loop.
147
+
148
+
::: tip
149
+
If you only need data dependencies, prefer `Depends(...)` with provider functions. Use `POINTER_DEPENDS` only for workflow control or low-level runtime inspection.
0 commit comments