runar-compiler 0.3.1__tar.gz

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 (48) hide show
  1. runar_compiler-0.3.1/PKG-INFO +7 -0
  2. runar_compiler-0.3.1/README.md +168 -0
  3. runar_compiler-0.3.1/pyproject.toml +16 -0
  4. runar_compiler-0.3.1/runar_compiler/__init__.py +3 -0
  5. runar_compiler-0.3.1/runar_compiler/__main__.py +198 -0
  6. runar_compiler-0.3.1/runar_compiler/codegen/__init__.py +0 -0
  7. runar_compiler-0.3.1/runar_compiler/codegen/blake3.py +644 -0
  8. runar_compiler-0.3.1/runar_compiler/codegen/ec.py +920 -0
  9. runar_compiler-0.3.1/runar_compiler/codegen/emit.py +442 -0
  10. runar_compiler-0.3.1/runar_compiler/codegen/optimizer.py +243 -0
  11. runar_compiler-0.3.1/runar_compiler/codegen/sha256.py +562 -0
  12. runar_compiler-0.3.1/runar_compiler/codegen/slh_dsa.py +1181 -0
  13. runar_compiler-0.3.1/runar_compiler/codegen/stack.py +3184 -0
  14. runar_compiler-0.3.1/runar_compiler/compiler.py +530 -0
  15. runar_compiler-0.3.1/runar_compiler/frontend/__init__.py +0 -0
  16. runar_compiler-0.3.1/runar_compiler/frontend/anf_lower.py +1068 -0
  17. runar_compiler-0.3.1/runar_compiler/frontend/anf_optimize.py +388 -0
  18. runar_compiler-0.3.1/runar_compiler/frontend/ast_nodes.py +322 -0
  19. runar_compiler-0.3.1/runar_compiler/frontend/constant_fold.py +488 -0
  20. runar_compiler-0.3.1/runar_compiler/frontend/parser_dispatch.py +41 -0
  21. runar_compiler-0.3.1/runar_compiler/frontend/parser_go.py +1648 -0
  22. runar_compiler-0.3.1/runar_compiler/frontend/parser_move.py +1183 -0
  23. runar_compiler-0.3.1/runar_compiler/frontend/parser_python.py +1402 -0
  24. runar_compiler-0.3.1/runar_compiler/frontend/parser_rust.py +1229 -0
  25. runar_compiler-0.3.1/runar_compiler/frontend/parser_sol.py +1156 -0
  26. runar_compiler-0.3.1/runar_compiler/frontend/parser_ts.py +1343 -0
  27. runar_compiler-0.3.1/runar_compiler/frontend/typecheck.py +872 -0
  28. runar_compiler-0.3.1/runar_compiler/frontend/validator.py +544 -0
  29. runar_compiler-0.3.1/runar_compiler/ir/__init__.py +0 -0
  30. runar_compiler-0.3.1/runar_compiler/ir/loader.py +186 -0
  31. runar_compiler-0.3.1/runar_compiler/ir/types.py +315 -0
  32. runar_compiler-0.3.1/runar_compiler.egg-info/PKG-INFO +7 -0
  33. runar_compiler-0.3.1/runar_compiler.egg-info/SOURCES.txt +46 -0
  34. runar_compiler-0.3.1/runar_compiler.egg-info/dependency_links.txt +1 -0
  35. runar_compiler-0.3.1/runar_compiler.egg-info/requires.txt +3 -0
  36. runar_compiler-0.3.1/runar_compiler.egg-info/top_level.txt +1 -0
  37. runar_compiler-0.3.1/setup.cfg +4 -0
  38. runar_compiler-0.3.1/tests/test_anf_optimize.py +547 -0
  39. runar_compiler-0.3.1/tests/test_compiler.py +410 -0
  40. runar_compiler-0.3.1/tests/test_constant_fold.py +658 -0
  41. runar_compiler-0.3.1/tests/test_emit.py +779 -0
  42. runar_compiler-0.3.1/tests/test_frontend.py +2900 -0
  43. runar_compiler-0.3.1/tests/test_ir_loader.py +578 -0
  44. runar_compiler-0.3.1/tests/test_multiformat.py +210 -0
  45. runar_compiler-0.3.1/tests/test_optimizer.py +667 -0
  46. runar_compiler-0.3.1/tests/test_parsers.py +671 -0
  47. runar_compiler-0.3.1/tests/test_source_compile.py +130 -0
  48. runar_compiler-0.3.1/tests/test_stack.py +971 -0
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: runar-compiler
3
+ Version: 0.3.1
4
+ Summary: Runar smart contract compiler - Python implementation
5
+ Requires-Python: >=3.10
6
+ Provides-Extra: dev
7
+ Requires-Dist: pytest>=7.0; extra == "dev"
@@ -0,0 +1,168 @@
1
+ # Rúnar Python Compiler
2
+
3
+ **Alternative Rúnar compiler implemented in Python.**
4
+
5
+ ---
6
+
7
+ ## Status
8
+
9
+ | Phase | Description | Status |
10
+ |---|---|---|
11
+ | **Phase 1** | IR consumer: accepts canonical ANF IR JSON, performs stack lowering and emission (Passes 5-6). | Implemented |
12
+ | **Phase 2** | Full frontend: parses source files directly (Passes 1-4), produces canonical ANF IR. | Implemented |
13
+
14
+ Phase 1 validates that the Python implementation can produce identical Bitcoin Script from the same ANF IR as the reference compiler. Phase 2 adds an independent frontend that must produce byte-identical ANF IR.
15
+
16
+ ---
17
+
18
+ ## Architecture
19
+
20
+ ### Phase 1: IR Consumer
21
+
22
+ ```
23
+ ANF IR (JSON) --> [Stack Lower] --> [Peephole] --> [Emit] --> Bitcoin Script
24
+ Python pass 5 Optimize Python pass 6
25
+ ```
26
+
27
+ The Python compiler reads canonical ANF IR JSON and performs stack scheduling and opcode emission.
28
+
29
+ ### Phase 2: Full Frontend
30
+
31
+ ```
32
+ .runar.* --> [Parse] --> [Validate] --> [Typecheck] --> [ANF Lower]
33
+ hand-written Python pass 2 Python pass 3 Python pass 4
34
+ parsers
35
+ |
36
+ v
37
+ ANF IR (JSON)
38
+ |
39
+ v
40
+ [Stack Lower] --> [Peephole] --> [Emit] --> Bitcoin Script
41
+ Python pass 5 Optimize Python pass 6
42
+ ```
43
+
44
+ The Python compiler supports **all six input formats** via hand-written recursive descent parsers — the most of any compiler:
45
+
46
+ | Extension | Parser Module |
47
+ |-----------|---------------|
48
+ | `.runar.ts` | `frontend/parser_ts.py` |
49
+ | `.runar.sol` | `frontend/parser_sol.py` |
50
+ | `.runar.move` | `frontend/parser_move.py` |
51
+ | `.runar.go` | `frontend/parser_go.py` |
52
+ | `.runar.rs` | `frontend/parser_rust.py` |
53
+ | `.runar.py` | `frontend/parser_python.py` |
54
+
55
+ All parsers produce the same Rúnar AST (`ContractNode`), and from that point the pipeline is identical.
56
+
57
+ ### Dedicated Codegen Modules
58
+
59
+ - `codegen/ec.py` — EC point operations (`ecAdd`, `ecMul`, `ecMulGen`, `ecNegate`, `ecOnCurve`, etc.)
60
+ - `codegen/slh_dsa.py` — SLH-DSA (SPHINCS+) signature verification
61
+ - `codegen/optimizer.py` — Peephole optimizer (runs on Stack IR between stack lowering and emit)
62
+
63
+ ### ANF EC Optimizer (Pass 4.5)
64
+
65
+ The `frontend/anf_optimize.py` module implements 12 algebraic EC simplification rules that run between ANF lowering and stack lowering. This pass is always enabled and eliminates redundant EC operations (e.g., `ecAdd(P, ecNegate(P))` → identity, `ecMul(G, k)` → `ecMulGen(k)`).
66
+
67
+ ---
68
+
69
+ ## Building
70
+
71
+ No build step required — the compiler is pure Python.
72
+
73
+ ### Prerequisites
74
+
75
+ - Python 3.10+
76
+ - No external dependencies
77
+
78
+ ---
79
+
80
+ ## Running
81
+
82
+ ```bash
83
+ # Full compilation from source (outputs artifact JSON)
84
+ python -m runar_compiler --source MyContract.runar.ts
85
+
86
+ # Output only hex
87
+ python -m runar_compiler --source MyContract.runar.ts --hex
88
+
89
+ # Output only ASM
90
+ python -m runar_compiler --source MyContract.runar.ts --asm
91
+
92
+ # Dump ANF IR for conformance checking
93
+ python -m runar_compiler --source MyContract.runar.ts --emit-ir
94
+
95
+ # Write output to a file
96
+ python -m runar_compiler --source MyContract.runar.ts --output artifacts/MyContract.json
97
+
98
+ # Compile from ANF IR JSON
99
+ python -m runar_compiler --ir input-anf.json
100
+ python -m runar_compiler --ir input-anf.json --hex
101
+ python -m runar_compiler --ir input-anf.json --output artifact.json
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Conformance Testing
107
+
108
+ The Python compiler must pass the same conformance suite as the TypeScript reference compiler.
109
+
110
+ For each test case in `conformance/tests/`:
111
+
112
+ 1. Read source files as input.
113
+ 2. Run the full pipeline (Passes 1-6).
114
+ 3. Compare script hex output with `expected-script.hex` (string equality).
115
+ 4. If `expected-ir.json` exists, also compile from IR and verify the IR-compiled script matches the source-compiled script.
116
+
117
+ Conformance tests include multi-format variants (`.runar.sol`, `.runar.move`, `.runar.go`, `.runar.rs`, `.runar.py`) that are all tested through the same pipeline.
118
+
119
+ ```bash
120
+ # Run conformance from repo root
121
+ pnpm run conformance:python
122
+
123
+ # Or directly
124
+ cd conformance
125
+ python3 -m pytest test_conformance.py -v
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Testing
131
+
132
+ ```bash
133
+ cd compilers/python
134
+ python3 -m pytest
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Project Structure
140
+
141
+ ```
142
+ runar_compiler/
143
+ __init__.py
144
+ __main__.py # CLI entry point
145
+ compiler.py # Pipeline orchestrator (parse → validate → typecheck → ANF → stack → emit)
146
+ frontend/
147
+ ast_nodes.py # Rúnar AST node types (ContractNode, PropertyNode, MethodNode, etc.)
148
+ parser_dispatch.py # File extension → parser dispatch
149
+ parser_ts.py # .runar.ts parser
150
+ parser_sol.py # .runar.sol parser
151
+ parser_move.py # .runar.move parser
152
+ parser_go.py # .runar.go parser
153
+ parser_rust.py # .runar.rs parser
154
+ parser_python.py # .runar.py parser
155
+ validator.py # Pass 2: Language subset validation
156
+ typecheck.py # Pass 3: Type checking
157
+ anf_lower.py # Pass 4: AST → ANF IR
158
+ anf_optimize.py # Pass 4.5: ANF EC optimizer (12 algebraic rules)
159
+ ir/
160
+ types.py # ANF IR type definitions
161
+ loader.py # ANF IR JSON loader
162
+ codegen/
163
+ stack.py # Pass 5: ANF → Stack IR
164
+ optimizer.py # Peephole optimizer
165
+ emit.py # Pass 6: Stack IR → Bitcoin Script
166
+ ec.py # EC point operation codegen
167
+ slh_dsa.py # SLH-DSA signature verification codegen
168
+ ```
@@ -0,0 +1,16 @@
1
+ [project]
2
+ name = "runar-compiler"
3
+ version = "0.3.1"
4
+ description = "Runar smart contract compiler - Python implementation"
5
+ requires-python = ">=3.10"
6
+ dependencies = []
7
+
8
+ [project.optional-dependencies]
9
+ dev = ["pytest>=7.0"]
10
+
11
+ [build-system]
12
+ requires = ["setuptools>=64"]
13
+ build-backend = "setuptools.build_meta"
14
+
15
+ [tool.pytest.ini_options]
16
+ testpaths = ["tests"]
@@ -0,0 +1,3 @@
1
+ """Runar smart contract compiler - Python implementation."""
2
+
3
+ from runar_compiler.compiler import compile_from_source, compile_from_ir
@@ -0,0 +1,198 @@
1
+ """CLI entry point for the Runar Python compiler.
2
+
3
+ Usage:
4
+ python -m runar_compiler --source Contract.runar.py --output artifact.json
5
+ python -m runar_compiler --ir program.json --output artifact.json
6
+ python -m runar_compiler --source Contract.runar.py --hex
7
+ python -m runar_compiler --source Contract.runar.py --asm
8
+ python -m runar_compiler --source Contract.runar.py --emit-ir
9
+
10
+ Direct port of ``compilers/go/main.go``.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import sys
18
+
19
+ from runar_compiler.compiler import (
20
+ CompilationError,
21
+ artifact_to_json,
22
+ compile_from_ir,
23
+ compile_from_source,
24
+ compile_source_to_ir,
25
+ )
26
+
27
+
28
+ def main() -> None:
29
+ parser = argparse.ArgumentParser(
30
+ prog="runar-compiler-python",
31
+ description="Runar smart contract compiler (Python implementation).",
32
+ )
33
+ parser.add_argument(
34
+ "--ir",
35
+ metavar="PATH",
36
+ help="Path to ANF IR JSON file",
37
+ )
38
+ parser.add_argument(
39
+ "--source",
40
+ metavar="PATH",
41
+ help="Path to .runar.* source file",
42
+ )
43
+ parser.add_argument(
44
+ "--output",
45
+ metavar="PATH",
46
+ help="Output artifact path (default: stdout)",
47
+ )
48
+ parser.add_argument(
49
+ "--hex",
50
+ action="store_true",
51
+ help="Output only the script hex (no artifact JSON)",
52
+ )
53
+ parser.add_argument(
54
+ "--asm",
55
+ action="store_true",
56
+ help="Output only the script ASM (no artifact JSON)",
57
+ )
58
+ parser.add_argument(
59
+ "--emit-ir",
60
+ action="store_true",
61
+ help="Output only the ANF IR JSON (requires --source)",
62
+ )
63
+ parser.add_argument(
64
+ "--disable-constant-folding",
65
+ action="store_true",
66
+ help="Disable the ANF constant folding pass",
67
+ )
68
+
69
+ args = parser.parse_args()
70
+
71
+ if not args.ir and not args.source:
72
+ print(
73
+ "Usage: runar-compiler-python [--ir <path> | --source <path>] "
74
+ "[--output <path>] [--hex] [--asm] [--emit-ir]",
75
+ file=sys.stderr,
76
+ )
77
+ print("", file=sys.stderr)
78
+ print(
79
+ "Phase 1: Compile from ANF IR JSON to Bitcoin Script (--ir).",
80
+ file=sys.stderr,
81
+ )
82
+ print(
83
+ "Phase 2: Compile from source to Bitcoin Script (--source).",
84
+ file=sys.stderr,
85
+ )
86
+ sys.exit(1)
87
+
88
+ # Handle --emit-ir: dump ANF IR JSON and exit
89
+ if args.emit_ir:
90
+ if not args.source:
91
+ print("--emit-ir requires --source", file=sys.stderr)
92
+ sys.exit(1)
93
+ try:
94
+ program = compile_source_to_ir(
95
+ args.source,
96
+ disable_constant_folding=args.disable_constant_folding,
97
+ )
98
+ except CompilationError as e:
99
+ print(f"Compilation error: {e}", file=sys.stderr)
100
+ sys.exit(1)
101
+ # Serialize the ANFProgram to camelCase JSON (matching Go/TS output)
102
+ ir_json = json.dumps(_anf_to_camel_dict(program), indent=2, default=str)
103
+ print(ir_json)
104
+ return
105
+
106
+ try:
107
+ if args.source:
108
+ artifact = compile_from_source(
109
+ args.source,
110
+ disable_constant_folding=args.disable_constant_folding,
111
+ )
112
+ else:
113
+ artifact = compile_from_ir(
114
+ args.ir,
115
+ disable_constant_folding=args.disable_constant_folding,
116
+ )
117
+ except CompilationError as e:
118
+ print(f"Compilation error: {e}", file=sys.stderr)
119
+ sys.exit(1)
120
+ except Exception as e:
121
+ print(f"Compilation error: {e}", file=sys.stderr)
122
+ sys.exit(1)
123
+
124
+ # Determine output
125
+ if args.hex:
126
+ output = artifact.script
127
+ elif args.asm:
128
+ output = artifact.asm
129
+ else:
130
+ output = artifact_to_json(artifact)
131
+
132
+ # Write output
133
+ if args.output:
134
+ with open(args.output, "w") as f:
135
+ f.write(output)
136
+ print(f"Output written to {args.output}", file=sys.stderr)
137
+ else:
138
+ print(output)
139
+
140
+
141
+ _SNAKE_TO_CAMEL = {
142
+ "contract_name": "contractName",
143
+ "is_public": "isPublic",
144
+ "iter_var": "iterVar",
145
+ "state_values": "stateValues",
146
+ "initial_value": "initialValue",
147
+ "else_": "else",
148
+ # These stay as snake_case to match Go/TS IR format
149
+ "result_type": "result_type",
150
+ # Both raw_value and value_ref map to "value" in Go JSON (they never coexist)
151
+ "value_ref": "value",
152
+ "raw_value": "value",
153
+ }
154
+
155
+ # Fields that should be excluded from IR output (internal decoded fields)
156
+ _IR_EXCLUDED_FIELDS = frozenset({
157
+ "const_string", "const_big_int", "const_bool", "const_int",
158
+ })
159
+
160
+
161
+ def _snake_key(k: str) -> str:
162
+ return _SNAKE_TO_CAMEL.get(k, k)
163
+
164
+
165
+ def _anf_to_camel_dict(obj: object) -> object:
166
+ """Convert an ANF dataclass tree to a dict matching Go/TS IR JSON format."""
167
+ import json as _json
168
+ from dataclasses import fields, is_dataclass
169
+ if is_dataclass(obj) and not isinstance(obj, type):
170
+ d: dict = {}
171
+ has_raw_value = False
172
+ for f in fields(obj):
173
+ if f.name in _IR_EXCLUDED_FIELDS:
174
+ continue
175
+ v = getattr(obj, f.name)
176
+ if v is None:
177
+ continue
178
+ # raw_value is the canonical Go JSON "value" field — parse and emit its content
179
+ if f.name == "raw_value":
180
+ try:
181
+ d["value"] = _json.loads(v)
182
+ except (ValueError, TypeError):
183
+ d["value"] = v
184
+ has_raw_value = True
185
+ continue
186
+ # Skip value_ref if raw_value was already emitted as "value"
187
+ if f.name == "value_ref" and has_raw_value:
188
+ continue
189
+ key = _snake_key(f.name)
190
+ d[key] = _anf_to_camel_dict(v)
191
+ return d
192
+ if isinstance(obj, list):
193
+ return [_anf_to_camel_dict(item) for item in obj]
194
+ return obj
195
+
196
+
197
+ if __name__ == "__main__":
198
+ main()