pineforge-codegen 0.6.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. pineforge_codegen/__init__.py +53 -0
  2. pineforge_codegen/analyzer/__init__.py +60 -0
  3. pineforge_codegen/analyzer/base.py +1563 -0
  4. pineforge_codegen/analyzer/call_handlers.py +895 -0
  5. pineforge_codegen/analyzer/contracts.py +163 -0
  6. pineforge_codegen/analyzer/diagnostics.py +118 -0
  7. pineforge_codegen/analyzer/tables.py +204 -0
  8. pineforge_codegen/analyzer/types.py +250 -0
  9. pineforge_codegen/ast_nodes.py +293 -0
  10. pineforge_codegen/codegen/__init__.py +78 -0
  11. pineforge_codegen/codegen/base.py +1381 -0
  12. pineforge_codegen/codegen/emit_top.py +875 -0
  13. pineforge_codegen/codegen/helpers.py +163 -0
  14. pineforge_codegen/codegen/helpers_syminfo.py +134 -0
  15. pineforge_codegen/codegen/input.py +189 -0
  16. pineforge_codegen/codegen/security.py +1564 -0
  17. pineforge_codegen/codegen/ta.py +298 -0
  18. pineforge_codegen/codegen/tables.py +613 -0
  19. pineforge_codegen/codegen/types.py +573 -0
  20. pineforge_codegen/codegen/visit_call.py +1305 -0
  21. pineforge_codegen/codegen/visit_expr.py +701 -0
  22. pineforge_codegen/codegen/visit_stmt.py +729 -0
  23. pineforge_codegen/errors.py +98 -0
  24. pineforge_codegen/lexer.py +531 -0
  25. pineforge_codegen/parser.py +1198 -0
  26. pineforge_codegen/pragmas.py +117 -0
  27. pineforge_codegen/signatures.py +808 -0
  28. pineforge_codegen/support_checker.py +1111 -0
  29. pineforge_codegen/symbols.py +118 -0
  30. pineforge_codegen/tokens.py +406 -0
  31. pineforge_codegen/tv_input_choices.py +86 -0
  32. pineforge_codegen-0.6.5.dist-info/METADATA +462 -0
  33. pineforge_codegen-0.6.5.dist-info/RECORD +35 -0
  34. pineforge_codegen-0.6.5.dist-info/WHEEL +4 -0
  35. pineforge_codegen-0.6.5.dist-info/licenses/LICENSE +197 -0
@@ -0,0 +1,117 @@
1
+ """Pre-lex pragma extraction for PineForge.
2
+
3
+ This module recognises ``// @pf-trace name=expr`` line comments in Pine
4
+ source and lifts them into a structured list the codegen consumes when
5
+ emitting the per-bar instrumentation hook at the bottom of every
6
+ ``on_bar()``.
7
+
8
+ Why a pre-pass?
9
+ The :class:`Lexer` strips both block (``/* ... */``) and line
10
+ (``//``) comments before the parser ever sees them, so by the time
11
+ we have an AST the original pragma text is gone. We instead walk
12
+ the raw source once, regex-match each pragma line, then run the
13
+ expression body through the same :class:`Lexer` /
14
+ :class:`Parser` machinery used for normal Pine expressions. This
15
+ keeps a single source of truth for Pine syntax and ensures pragma
16
+ expressions support the full grammar (logical operators, member
17
+ access, function calls, ternaries, ...).
18
+
19
+ Pragma syntax (kept deliberately strict so unrelated comments are
20
+ untouched)::
21
+
22
+ // @pf-trace <id>=<expr>
23
+
24
+ * ``//`` followed by at least one space, then ``@pf-trace``.
25
+ * ``<id>`` matches ``[A-Za-z_][A-Za-z0-9_]*`` (the trace label).
26
+ * ``<expr>`` is any Pine expression evaluated at script-top scope.
27
+
28
+ Multiple pragmas may appear on consecutive (or non-consecutive) lines;
29
+ their source order is preserved in the output so the codegen emits the
30
+ matching ``trace(...)`` calls in the same order. Block comments and
31
+ line comments not starting with ``// @pf-trace`` are ignored. Mid-line
32
+ trailing pragmas (``x = 1 // @pf-trace ...``) are intentionally NOT
33
+ recognised — pragmas must occupy the whole line so they are
34
+ unambiguous to read by humans and trivial to grep.
35
+ """
36
+
37
+ from __future__ import annotations
38
+
39
+ import re
40
+ from dataclasses import dataclass
41
+ from typing import Any
42
+
43
+ from .lexer import Lexer
44
+ from .parser import Parser
45
+
46
+
47
+ # Anchored to start/end of line so a stray ``// @pf-trace`` substring
48
+ # inside a string literal or block comment cannot match. ``\s+`` after
49
+ # ``//`` requires at least one space before ``@pf-trace`` (the spec is
50
+ # ``// @pf-trace ``, distinct from Pine's ``//@version=N``).
51
+ _PRAGMA_RE = re.compile(
52
+ r"^\s*//\s+@pf-trace\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+?)\s*$"
53
+ )
54
+
55
+
56
+ @dataclass
57
+ class PfTracePragma:
58
+ """One ``// @pf-trace name=expr`` annotation extracted from Pine source.
59
+
60
+ Attributes:
61
+ name: The trace label (left-hand side of ``=``); matches
62
+ ``[A-Za-z_][A-Za-z0-9_]*`` so it can be safely embedded in
63
+ a C++ string literal without escaping.
64
+ expr_source: Raw Pine expression text (right-hand side of
65
+ ``=``), retained for diagnostics and the test harness.
66
+ expr_node: AST node parsed from ``expr_source`` via the
67
+ standard Pine expression parser. Codegen feeds this through
68
+ ``_visit_expr`` to obtain the C++ form.
69
+ line: 1-based source line where the pragma was found.
70
+ """
71
+
72
+ name: str
73
+ expr_source: str
74
+ expr_node: Any
75
+ line: int
76
+
77
+
78
+ def extract_pf_trace_pragmas(source: str) -> list[PfTracePragma]:
79
+ """Scan ``source`` for ``// @pf-trace`` line comments.
80
+
81
+ Returns the pragmas in source order. The expression on the
82
+ right-hand side of ``=`` is run through the project's own
83
+ :class:`Lexer` followed by :meth:`Parser._parse_expression` so the
84
+ full Pine expression grammar is supported (binary / unary
85
+ operators, ternaries, member access, function calls, subscripts).
86
+
87
+ Args:
88
+ source: Raw Pine source text.
89
+
90
+ Returns:
91
+ A list of :class:`PfTracePragma` entries in source order. Empty
92
+ list when no pragmas are present (the common case for legacy
93
+ scripts) — callers should treat this as the zero-overhead
94
+ path.
95
+ """
96
+ pragmas: list[PfTracePragma] = []
97
+ for lineno, raw in enumerate(source.splitlines(), start=1):
98
+ m = _PRAGMA_RE.match(raw)
99
+ if not m:
100
+ continue
101
+ name = m.group(1)
102
+ expr_source = m.group(2)
103
+ # Reuse the full Pine-source -> AST pipeline by lexing + parsing
104
+ # the expression body in isolation. ``Parser._parse_expression``
105
+ # is the same entry the statement parser uses for RHS values,
106
+ # so anything legal in ``x = <expr>`` is legal here.
107
+ tokens = Lexer(expr_source).tokenize()
108
+ expr_node = Parser(tokens, source=expr_source)._parse_expression()
109
+ pragmas.append(
110
+ PfTracePragma(
111
+ name=name,
112
+ expr_source=expr_source,
113
+ expr_node=expr_node,
114
+ line=lineno,
115
+ )
116
+ )
117
+ return pragmas