Skip to content

Commit 9b8e371

Browse files
refactor(examples): clean up composition and branching demos, enhance RET_FAR documentation
1 parent d9209cc commit 9b8e371

20 files changed

Lines changed: 255 additions & 265 deletions

demos/02_composition.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""02_composition.py — Multi-node chain + DI return-value passing
1+
"""02_composition.py — Multi-node chain
22
33
Usage:
44
python demos/02_composition.py
@@ -10,19 +10,18 @@
1010

1111

1212
@Node()
13-
async def double() -> int:
14-
return 42
13+
async def double() -> None:
14+
pass # placeholder: real logic goes here
1515

1616

1717
@Node()
18-
async def add_one(value: int) -> int:
19-
# DI injects the return value (42) from double into `value`
20-
return value + 1
18+
async def add_one() -> None:
19+
pass # placeholder: real logic goes here
2120

2221

2322
@Node()
24-
async def print_result(value: int) -> None:
25-
print(f"Final result: {value}")
23+
async def print_result() -> None:
24+
print("Composition complete")
2625

2726

2827
async def main() -> None:

demos/04_if_branch.py

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,36 @@
66

77
import asyncio
88

9-
from amrita_sense import IF, NOP, Node, WorkflowInterpreter
9+
from amrita_sense import IF, NOP, Node, NodeType, WorkflowInterpreter
1010

1111

1212
@Node()
13-
async def check_score() -> int:
14-
"""Simulate returning a score"""
15-
return 85
16-
17-
18-
@Node()
19-
async def grade_a(score: int) -> str:
20-
print(f"Score {score}: Excellent")
13+
async def grade_a() -> str:
14+
print("Excellent")
2115
return "A"
2216

2317

2418
@Node()
25-
async def grade_b(score: int) -> str:
26-
print(f"Score {score}: Good")
19+
async def grade_b() -> str:
20+
print("Good")
2721
return "B"
2822

2923

3024
@Node()
31-
async def grade_c(score: int) -> str:
32-
print(f"Score {score}: Pass")
25+
async def grade_c() -> str:
26+
print("Pass")
3327
return "C"
3428

3529

36-
@Node()
37-
async def finished(grade: str) -> None:
38-
print(f"Final grade: {grade}")
39-
40-
4130
async def main() -> None:
31+
# Inline condition nodes via NodeType
32+
cond_a = NodeType(lambda: False, wrap_to_async=False, address_able=False, tag=None)
33+
cond_b = NodeType(lambda: True, wrap_to_async=False, address_able=False, tag=None)
34+
4235
comp = (
43-
IF(
44-
Node(lambda score: score >= 90, wrap_to_async=False), # type: ignore[arg-type]
45-
grade_a,
46-
)
47-
.ELIF(Node(lambda score: score >= 75, wrap_to_async=False), grade_b) # type: ignore[arg-type]
36+
IF(cond_a, grade_a) # type: ignore[arg-type]
37+
.ELIF(cond_b, grade_b) # type: ignore[arg-type]
4838
.ELSE(grade_c)
49-
>> check_score
50-
>> finished
5139
>> NOP
5240
)
5341
rendered = comp.render()

demos/05_while_loop.py

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,48 @@
99
from amrita_sense import DO, NOP, WHILE, Node, WorkflowInterpreter
1010
from amrita_sense.exceptions import BreakLoop
1111

12+
_counter = 0
13+
14+
15+
@Node()
16+
def bump() -> None:
17+
global _counter
18+
_counter += 1
19+
1220

1321
@Node()
14-
def counter() -> int:
15-
"""Increment and return the counter on each call"""
16-
if not hasattr(counter, "n"):
17-
counter.n = 1 # type: ignore[attr-defined]
18-
else:
19-
counter.n += 1 # type: ignore[attr-defined]
20-
return counter.n # type: ignore[attr-defined]
22+
def under_three() -> bool:
23+
return _counter < 3
2124

2225

2326
@Node()
24-
async def under_three(n: int) -> bool:
25-
"""WHILE loop condition"""
26-
return n < 3
27+
def body() -> None:
28+
print(f" WHILE iteration {_counter}")
2729

2830

2931
@Node()
30-
def body(n: int) -> None:
31-
"""Loop body"""
32-
print(f" WHILE iteration {n}")
32+
def cond_dowhile() -> bool:
33+
return _counter < 5
3334

3435

3536
@Node()
36-
def early_break(n: int) -> None:
37-
"""DO loop body: break out on iteration 3"""
38-
print(f" DO-WHILE iteration {n}")
39-
if n >= 3:
37+
def do_body() -> None:
38+
print(f" DO-WHILE iteration {_counter}")
39+
if _counter >= 3:
4040
raise BreakLoop
4141

4242

4343
async def main() -> None:
44+
global _counter
45+
4446
print("=== WHILE example ===")
45-
wf = (counter >> WHILE(under_three).ACTION(body) >> NOP).render()
47+
_counter = 0
48+
wf = (bump >> WHILE(under_three).ACTION(body) >> NOP).render()
4649
await WorkflowInterpreter(wf).run()
4750

48-
# Reset counter
49-
counter.n = 0 # type: ignore[attr-defined]
50-
5151
print("\n=== DO-WHILE example ===")
52-
wf2 = (
53-
counter >> DO(early_break).WHILE(Node(lambda n: n < 5, wrap_to_async=False)) >> NOP # type: ignore[arg-type]
54-
).render()
52+
_counter = 0
53+
wf2 = (bump >> DO(do_body).WHILE(cond_dowhile) >> NOP).render()
5554
await WorkflowInterpreter(wf2).run()
5655

5756

demos/06_try_catch.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import asyncio
88

9-
from amrita_sense import NOP, Node, NodeType, Try, WorkflowInterpreter
9+
from amrita_sense import NOP, Node, Try, WorkflowInterpreter
1010

1111

1212
@Node()
@@ -16,8 +16,8 @@ async def may_fail() -> str:
1616

1717

1818
@Node()
19-
async def handle_error(exc_val: ValueError):
20-
"""Catch ValueError and return fallback"""
19+
async def handle_error(exc_val: ValueError) -> None:
20+
"""Catch ValueError"""
2121
print(f"Caught: {exc_val}")
2222

2323

@@ -44,11 +44,13 @@ async def example_1() -> None:
4444
async def example_2() -> None:
4545
"""Normal execution + THEN + FINALLY"""
4646
print("\n=== Example 2: normal execution + THEN + FINALLY ===")
47+
48+
@Node()
49+
async def always_ok() -> str:
50+
return "all good"
51+
4752
comp = (
48-
Try(NodeType(lambda: "all good", wrap_to_async=False, address_able=False, tag=None)) # type: ignore[arg-type]
49-
.THEN(on_success)
50-
.CATCH(ValueError, handle_error)
51-
.FINALLY(cleanup)
53+
Try(always_ok).THEN(on_success).CATCH(ValueError, handle_error).FINALLY(cleanup)
5254
>> NOP
5355
)
5456
rendered = comp.render()

demos/08_interrupt_suspend.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,18 @@
1010

1111

1212
@Node()
13-
async def step_one() -> int:
13+
async def step_one() -> None:
1414
print("[1] First step")
15-
return 1
1615

1716

1817
@Node()
19-
async def step_two(prev: int) -> int:
20-
print(f"[2] Received previous result: {prev}")
21-
return prev + 1
18+
async def step_two() -> None:
19+
print("[2] Second step")
2220

2321

2422
@Node()
25-
async def step_three(prev: int) -> None:
26-
print(f"[3] Final result: {prev}")
23+
async def step_three() -> None:
24+
print("[3] Third step")
2725

2826

2927
async def main() -> None:

demos/10_event_base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
@dataclass
1616
class NotifyEvent(BaseEvent[str]):
1717
"""Custom event"""
18+
1819
message: str
1920

2021
@property
@@ -40,7 +41,7 @@ async def produce_event() -> str:
4041

4142
@Node()
4243
async def finish(result: str) -> None:
43-
print(f"工作流完成: {result}")
44+
print(f"[Finish] Workflow completed: {result}")
4445

4546

4647
async def main() -> None:

demos/11_event_trigger.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
@dataclass
1616
class AuditEvent(ConstructableEvent):
1717
"""Constructable event — triggered by TRIGGER_EVENT in a workflow"""
18+
1819
action: str
1920

2021
@property
@@ -38,7 +39,7 @@ async def audit_handler(event: AuditEvent) -> None:
3839

3940
@Node()
4041
async def do_work() -> str:
41-
print("执行核心逻辑...")
42+
print("Executing core logic...")
4243
return "completed"
4344

4445

demos/12_inline_workflow.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ def __init__(self, value: int) -> None:
2121
self.interpreter = WorkflowInterpreter(rendered)
2222

2323
@Node()
24-
async def double(self) -> int:
25-
return self.value * 2
24+
async def double(self) -> None:
25+
self.value *= 2
2626

2727
@Node()
28-
async def format(self, prev: int) -> str:
29-
self.result = f"Processed: {prev}"
28+
async def format(self) -> str:
29+
self.result = f"Processed: {self.value}"
3030
return self.result
3131

3232
async def run(self) -> str | None:

demos/13_ret_far.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,56 @@
1-
"""13_ret_far.py — CALL + RET_FAR nested return
1+
"""13_ret_far.py — Manual push + GOTO + RET_FAR pop-and-return
22
33
Usage:
44
python demos/13_ret_far.py
55
"""
66

77
import asyncio
88

9-
from amrita_sense import ALIAS, ARCHIVED_NODES, CALL, NOP, Node, WorkflowInterpreter
10-
from amrita_sense.instructions import RET_FAR
9+
from amrita_sense import ALIAS, NOP, Node, PointerVector, WorkflowInterpreter
10+
from amrita_sense.instructions import GOTO, RET_FAR
1111

1212

1313
@Node()
14-
async def deep_work() -> object:
15-
"""Deep task: simulate an early-exit condition"""
16-
print(" Entering deep task...")
17-
print(" Early exit condition met — executing RET_FAR")
18-
return RET_FAR() # Pop return address and jump back to the outermost caller
14+
async def start() -> None:
15+
print("Start")
1916

2017

2118
@Node()
22-
async def never_reached() -> None:
23-
print(" This line should never print (RET_FAR already jumped out)")
19+
async def save_ret_addr(pc: WorkflowInterpreter) -> None:
20+
"""Manually push a return destination onto _ret_addr_stack."""
21+
print(" Saving return address...")
22+
return_dest = PointerVector(pc.find_addr_alias("after"))
23+
pc._ret_addr_stack.push(return_dest)
2424

2525

2626
@Node()
27-
async def back_to_top() -> None:
28-
print("Back to top level")
27+
async def doing_work() -> None:
28+
"""The section we GOTO into."""
29+
print(" Doing work in the jumped-to section")
30+
31+
32+
@Node()
33+
async def after_return() -> None:
34+
"""RET_FAR pops _ret_addr_stack and jumps here."""
35+
print("Back here (popped via RET_FAR)")
2936

3037

3138
async def main() -> None:
3239
print("=== RET_FAR example ===")
33-
sub = ARCHIVED_NODES(ALIAS(deep_work, "deep"))
40+
# Pattern: manual push → GOTO → RET_FAR pop-and-return
41+
# 1) save_ret_addr pushes "after" address onto _ret_addr_stack
42+
# 2) GOTO("work") jumps to the work section
43+
# 3) RET_FAR() pops the saved address and jumps back
3444
comp = (
35-
Node(lambda: print("Start")) # type: ignore[arg-type]
36-
>> CALL("deep")
37-
>> back_to_top
45+
start
46+
>> save_ret_addr
47+
>> GOTO("work")
48+
>> ALIAS(after_return, "after")
49+
>> GOTO("end")
50+
>> ALIAS(doing_work, "work")
51+
>> RET_FAR()
52+
>> ALIAS(NOP, "end")
3853
>> NOP
39-
>> sub
4054
)
4155
await WorkflowInterpreter(comp.render()).run()
4256

demos/15_step_by.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@
1010

1111

1212
@Node()
13-
async def a() -> int:
14-
return 1
13+
async def a() -> None:
14+
print("Node A")
1515

1616

1717
@Node()
18-
async def b(prev: int) -> int:
19-
return prev * 10
18+
async def b() -> None:
19+
print("Node B")
2020

2121

2222
@Node()
23-
async def c(prev: int) -> None:
24-
print(f"Final value: {prev}")
23+
async def c() -> None:
24+
print("Node C")
2525

2626

2727
async def main() -> None:
@@ -34,9 +34,6 @@ async def main() -> None:
3434
ptr = interpreter._pointer.base_addr
3535
print(f"Step {step}: output={result!r}, pointer={ptr}")
3636

37-
# Inspect state and decide next action here.
38-
# e.g.: if step >= 2: break
39-
4037

4138
if __name__ == "__main__":
4239
asyncio.run(main())

0 commit comments

Comments
 (0)