Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions pineforge_codegen/analyzer/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ def __init__(self, ast: Program, filename: str = "<stdin>") -> None:
# Track user-defined function tuple returns
self._func_returns_tuple: dict[str, bool] = {}
self._func_tuple_element_count: dict[str, int] = {}
self._func_tuple_element_types: dict[str, tuple[PineType, ...]] = {}
self._tuple_element_types_by_node: dict[int, tuple[PineType, ...]] = {}
# Track user-defined functions whose body returns a UDT instance —
# maps func_name -> UDT type name. Detected from the body's final
# expression (``=> Sample.new(...)`` or last stmt ``Sample.new(...)``).
Expand Down Expand Up @@ -1484,6 +1486,7 @@ def _visit_FuncDef(self, node: FuncDef) -> PineType:
# Detect if function returns a tuple (last stmt is TupleLiteral)
self._func_returns_tuple[node.name] = False
self._func_tuple_element_count[node.name] = 0
self._func_tuple_element_types[node.name] = ()
if node.body:
last_stmt = node.body[-1]
tuple_node = None
Expand All @@ -1494,6 +1497,9 @@ def _visit_FuncDef(self, node: FuncDef) -> PineType:
if tuple_node is not None:
self._func_returns_tuple[node.name] = True
self._func_tuple_element_count[node.name] = len(tuple_node.elements)
self._func_tuple_element_types[node.name] = (
self._tuple_element_types_by_node.get(id(tuple_node), ())
)

# Detect if the function returns a UDT instance via ``T.new(...)`` —
# used by codegen to emit the C++ return type as the struct name and
Expand Down Expand Up @@ -2329,6 +2335,7 @@ def _visit_ColorLiteral(self, node: ColorLiteral) -> PineType:
return PineType.COLOR

def _visit_TupleLiteral(self, node: TupleLiteral) -> PineType:
for elem in node.elements:
self._visit(elem)
self._tuple_element_types_by_node[id(node)] = tuple(
self._visit(elem) for elem in node.elements
)
return PineType.FLOAT
20 changes: 20 additions & 0 deletions pineforge_codegen/analyzer/call_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,26 @@ def _handle_request_call(self, func_name: str, node: FuncCall) -> PineType:
and isinstance(expr_node.callee.object, Identifier)):
expr_ns = expr_node.callee.object.name
expr_func = expr_node.callee.member
elif isinstance(expr_node.callee, Identifier):
expr_func = expr_node.callee.name
if self._func_returns_tuple.get(expr_func, False):
tuple_size = self._func_tuple_element_count.get(expr_func, 0)
tuple_types = self._func_tuple_element_types.get(expr_func, ())
if (
tuple_size != 2
or len(tuple_types) != 2
or any(
item not in (PineType.INT, PineType.FLOAT)
for item in tuple_types
)
):
self._error(
"request.security tuple-return helpers currently support "
"exactly two numeric elements",
expr_node.loc,
)
else:
returns_tuple = True
if expr_ns == "ta":
if expr_func == "vwap":
merged_v = list(expr_node.args)
Expand Down
1 change: 1 addition & 0 deletions pineforge_codegen/codegen/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2185,6 +2185,7 @@ def generate(self) -> str:
for item in self._security_calls:
sec_id = item["sec_id"]
expr_node = item["expr_node"]
self._validate_security_persistent_var_control_flow(expr_node)
returns_tuple = item.get("returns_tuple", False)
tuple_size = item.get("tuple_size", 0)
if item.get("is_lower_tf_array"):
Expand Down
Loading
Loading