@pineforge/codegen-pyodide 0.7.0
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.
- package/README.md +25 -0
- package/index.mjs +20 -0
- package/package.json +32 -0
- package/pineforge_codegen/__init__.py +53 -0
- package/pineforge_codegen/analyzer/__init__.py +60 -0
- package/pineforge_codegen/analyzer/base.py +1566 -0
- package/pineforge_codegen/analyzer/call_handlers.py +895 -0
- package/pineforge_codegen/analyzer/contracts.py +163 -0
- package/pineforge_codegen/analyzer/diagnostics.py +118 -0
- package/pineforge_codegen/analyzer/tables.py +204 -0
- package/pineforge_codegen/analyzer/types.py +261 -0
- package/pineforge_codegen/ast_nodes.py +293 -0
- package/pineforge_codegen/codegen/__init__.py +78 -0
- package/pineforge_codegen/codegen/base.py +1381 -0
- package/pineforge_codegen/codegen/emit_top.py +875 -0
- package/pineforge_codegen/codegen/helpers.py +163 -0
- package/pineforge_codegen/codegen/helpers_syminfo.py +132 -0
- package/pineforge_codegen/codegen/input.py +189 -0
- package/pineforge_codegen/codegen/security.py +1564 -0
- package/pineforge_codegen/codegen/ta.py +298 -0
- package/pineforge_codegen/codegen/tables.py +683 -0
- package/pineforge_codegen/codegen/types.py +592 -0
- package/pineforge_codegen/codegen/visit_call.py +1387 -0
- package/pineforge_codegen/codegen/visit_expr.py +729 -0
- package/pineforge_codegen/codegen/visit_stmt.py +766 -0
- package/pineforge_codegen/errors.py +98 -0
- package/pineforge_codegen/lexer.py +531 -0
- package/pineforge_codegen/parser.py +1198 -0
- package/pineforge_codegen/pragmas.py +117 -0
- package/pineforge_codegen/signatures.py +808 -0
- package/pineforge_codegen/support_checker.py +1223 -0
- package/pineforge_codegen/symbols.py +118 -0
- package/pineforge_codegen/tokens.py +406 -0
- package/pineforge_codegen/tv_input_choices.py +86 -0
- package/pineforge_codegen-0.7.0.tar.gz +0 -0
- package/release.json +7 -0
- package/tables.json +1653 -0
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# @pineforge/codegen-pyodide
|
|
2
|
+
|
|
3
|
+
Gate-validated Pyodide payload for the PineScript v6 → C++ transpiler. Built and
|
|
4
|
+
published from `pineforge-codegen-oss` by `.github/workflows/publish-pyodide.yml`.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
- `pineforge_codegen-<version>.tar.gz` — the gate-validated archive (unpack into Pyodide).
|
|
8
|
+
- `pineforge_codegen/` — unpacked Python source (put on `PYTHONPATH` for Node oracle/grammar tooling).
|
|
9
|
+
- `tables.json` — introspected codegen tables (the app renders `tables.generated.ts` from this).
|
|
10
|
+
- `release.json` — `{ codegen, pyodide, python, emscripten, sha256 }`.
|
|
11
|
+
- `index.mjs` — `release`, `tables`, `archivePath`, `codegenSourceDir`.
|
|
12
|
+
|
|
13
|
+
## One-time bootstrap (maintainer, manual)
|
|
14
|
+
This is a NEW package; npm OIDC Trusted Publishing can only be configured after
|
|
15
|
+
the package exists:
|
|
16
|
+
1. Build locally: `npm run gate:full && node scripts/build-npm-package.mjs`.
|
|
17
|
+
2. From `npm/`, do the first publish with a granular npm token:
|
|
18
|
+
`npm publish --access=public` (one time).
|
|
19
|
+
3. On npmjs.com, configure the package's Trusted Publisher: GitHub Actions,
|
|
20
|
+
repo `pineforge-4pass/pineforge-codegen-oss`, workflow `publish-pyodide.yml`.
|
|
21
|
+
4. Thereafter releases publish via OIDC (no token): run `publish-pyodide.yml`
|
|
22
|
+
via **workflow_dispatch with `dry_run=false`**. NOTE: tag pushes currently run
|
|
23
|
+
a DRY-RUN only (a safe validation) — the sole real-publish path is dispatch +
|
|
24
|
+
`dry_run=false`. To switch to tag-driven releases later, change the publish
|
|
25
|
+
condition in the workflow.
|
package/index.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Entry for @pineforge/codegen-pyodide. Resolves the packaged payload paths and
|
|
2
|
+
// metadata so consumers (the app's build + Node oracle/grammar tooling) read
|
|
3
|
+
// everything from this one dependency — no git submodule.
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
|
|
11
|
+
export const release = require("./release.json");
|
|
12
|
+
export const tables = require("./tables.json");
|
|
13
|
+
|
|
14
|
+
// Absolute path to the gate-validated Pyodide archive (gztar) to unpackArchive.
|
|
15
|
+
export const archivePath = join(HERE, `pineforge_codegen-${release.codegen}.tar.gz`);
|
|
16
|
+
|
|
17
|
+
// Absolute path to the unpacked Python source dir's PARENT — put on PYTHONPATH
|
|
18
|
+
// so `import pineforge_codegen` resolves (oracle tests, grammar gen).
|
|
19
|
+
export const sourceRoot = HERE;
|
|
20
|
+
export const codegenSourceDir = join(HERE, "pineforge_codegen");
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pineforge/codegen-pyodide",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Gate-validated Pyodide payload for the PineScript v6 -> C++ transpiler: archive (run in Pyodide), unpacked source, introspected tables, and release metadata.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.mjs",
|
|
9
|
+
"./tables.json": "./tables.json",
|
|
10
|
+
"./release.json": "./release.json"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"index.mjs",
|
|
14
|
+
"pineforge_codegen-*.tar.gz",
|
|
15
|
+
"pineforge_codegen/**/*",
|
|
16
|
+
"tables.json",
|
|
17
|
+
"release.json"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"pyodide": "314.0.0"
|
|
24
|
+
},
|
|
25
|
+
"keywords": ["pinescript", "transpiler", "pyodide", "codegen"],
|
|
26
|
+
"homepage": "https://github.com/pineforge-4pass/pineforge-codegen-oss",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/pineforge-4pass/pineforge-codegen-oss.git"
|
|
30
|
+
},
|
|
31
|
+
"license": "PolyForm-Noncommercial-1.0.0"
|
|
32
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""PineScript v6 to C++ transpiler."""
|
|
2
|
+
|
|
3
|
+
from .lexer import Lexer
|
|
4
|
+
from .parser import Parser
|
|
5
|
+
from .analyzer import Analyzer
|
|
6
|
+
from .codegen import CodeGen
|
|
7
|
+
from .pragmas import extract_pf_trace_pragmas
|
|
8
|
+
from .support_checker import check_support_or_raise
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def transpile(pine_source: str, *, check_support: bool = True, filename: str = "<input>") -> str:
|
|
12
|
+
"""Transpile PineScript v6 source code to C++ code.
|
|
13
|
+
|
|
14
|
+
Semantic rules enforced in :class:`Analyzer` (before codegen), including:
|
|
15
|
+
user ``enum`` blocks must appear **above** ``input.enum(Enum.member, ...)`` uses.
|
|
16
|
+
|
|
17
|
+
Before analysis, :func:`support_checker.check_support_or_raise` rejects
|
|
18
|
+
scripts that use language constructs PineForge cannot faithfully execute
|
|
19
|
+
(e.g. ``indicator()`` declarations, prohibited variables such as
|
|
20
|
+
``bar_index``, disallowed ``request.security`` parameters). Pass
|
|
21
|
+
``check_support=False`` to bypass this gate (intended for tests of legacy
|
|
22
|
+
fixtures only).
|
|
23
|
+
|
|
24
|
+
``// @pf-trace name=expr`` pragmas are extracted from ``pine_source``
|
|
25
|
+
via a pre-pass (the lexer strips comments before the parser sees them)
|
|
26
|
+
and attached to the analyzer context for codegen. See
|
|
27
|
+
:mod:`pineforge_codegen.pragmas` for the syntax.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
pine_source: PineScript v6 source string.
|
|
31
|
+
check_support: When True (default) the support checker runs after
|
|
32
|
+
parsing and raises ``CompileError`` on any unsupported feature
|
|
33
|
+
before semantic analysis or codegen.
|
|
34
|
+
filename: Source name threaded into every ``SourceLocation`` so the
|
|
35
|
+
``file:line:col`` shown in ``CompileError`` (both ``str()`` and
|
|
36
|
+
:meth:`~pineforge_codegen.errors.CompileError.format`) points back
|
|
37
|
+
at the caller's file. Defaults to ``"<input>"``.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Generated C++ source string.
|
|
41
|
+
"""
|
|
42
|
+
pragmas = extract_pf_trace_pragmas(pine_source)
|
|
43
|
+
tokens = Lexer(pine_source, filename=filename).tokenize()
|
|
44
|
+
ast = Parser(tokens, source=pine_source, filename=filename).parse()
|
|
45
|
+
if check_support:
|
|
46
|
+
check_support_or_raise(ast, filename=filename)
|
|
47
|
+
ctx = Analyzer(ast, filename=filename).analyze()
|
|
48
|
+
# Attach after analysis: pragma expressions are not part of the
|
|
49
|
+
# program body, so the analyzer never inspects them; the codegen
|
|
50
|
+
# consumes them directly from the context to emit the on_bar tail
|
|
51
|
+
# ``if (trace_enabled_) { trace(...); ... }`` block.
|
|
52
|
+
ctx.pf_trace_pragmas = pragmas
|
|
53
|
+
return CodeGen(ctx).generate()
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Analyzer package facade.
|
|
2
|
+
|
|
3
|
+
Historically the semantic analyzer lived in a single ``analyzer.py`` module
|
|
4
|
+
that hit ~2,340 lines and ~74 methods on a single ``Analyzer`` class. This
|
|
5
|
+
package is the structural skeleton for an incremental split: ``base.py``
|
|
6
|
+
currently holds the original ``Analyzer`` verbatim, and future steps will
|
|
7
|
+
peel focused mixins out into sibling files (e.g. ``visit_func``,
|
|
8
|
+
``visit_udt``, ``visit_control``, ``visit_expr``, ``visit_call``, ``types``,
|
|
9
|
+
``helpers``) without changing the public API:
|
|
10
|
+
|
|
11
|
+
from pineforge_codegen.analyzer import Analyzer # primary
|
|
12
|
+
from pineforge_codegen.analyzer import AnalyzerContext # context
|
|
13
|
+
from pineforge_codegen.analyzer import TA_CLASS_MAP, ... # tables
|
|
14
|
+
|
|
15
|
+
Re-exports preserved for ``support_checker.py``, ``codegen/base.py`` and
|
|
16
|
+
external tests that imported the dataclasses or module-level dispatch
|
|
17
|
+
tables. Add new helper modules under ``compiler/transpiler/analyzer/`` and
|
|
18
|
+
re-export their public surface here.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from .base import Analyzer
|
|
22
|
+
from .contracts import (
|
|
23
|
+
# Output dataclasses (consumed by codegen + tests). Defined in
|
|
24
|
+
# contracts.py so the package's import graph stays a strict DAG.
|
|
25
|
+
AnalyzerContext,
|
|
26
|
+
FixnanCallSite,
|
|
27
|
+
FuncInfo,
|
|
28
|
+
MutableGlobalInfo,
|
|
29
|
+
SecurityCallInfo,
|
|
30
|
+
TACallSite,
|
|
31
|
+
)
|
|
32
|
+
from .tables import (
|
|
33
|
+
# Module-level dispatch tables (consumed by codegen + support_checker).
|
|
34
|
+
TA_CLASS_MAP,
|
|
35
|
+
TA_PERIOD_ARG,
|
|
36
|
+
TA_TUPLE_RETURNS,
|
|
37
|
+
TA_MULTI_CTOR,
|
|
38
|
+
TA_NO_CTOR,
|
|
39
|
+
BUILTIN_VARS,
|
|
40
|
+
BAR_FIELDS,
|
|
41
|
+
SKIP_FUNCS,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"Analyzer",
|
|
46
|
+
"TACallSite",
|
|
47
|
+
"FuncInfo",
|
|
48
|
+
"FixnanCallSite",
|
|
49
|
+
"MutableGlobalInfo",
|
|
50
|
+
"SecurityCallInfo",
|
|
51
|
+
"AnalyzerContext",
|
|
52
|
+
"TA_CLASS_MAP",
|
|
53
|
+
"TA_PERIOD_ARG",
|
|
54
|
+
"TA_TUPLE_RETURNS",
|
|
55
|
+
"TA_MULTI_CTOR",
|
|
56
|
+
"TA_NO_CTOR",
|
|
57
|
+
"BUILTIN_VARS",
|
|
58
|
+
"BAR_FIELDS",
|
|
59
|
+
"SKIP_FUNCS",
|
|
60
|
+
]
|