dedekind 3.0.0__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.
- dedekind/__init__.py +17 -0
- dedekind/aot_compiler.py +93 -0
- dedekind/ast_nodes.py +187 -0
- dedekind/build_runtime.py +14 -0
- dedekind/cli.py +11 -0
- dedekind/codegen.py +641 -0
- dedekind/compiler.py +379 -0
- dedekind/install_kernel.py +76 -0
- dedekind/latex_export.py +265 -0
- dedekind/lexer.py +110 -0
- dedekind/ml_runtime.py +14075 -0
- dedekind/mlir_codegen.py +99 -0
- dedekind/parser.py +786 -0
- dedekind/purity_check.py +151 -0
- dedekind/reproducibility.py +136 -0
- dedekind/runtime_modules/00_imports.py +92 -0
- dedekind/runtime_modules/01_classes.py +1270 -0
- dedekind/runtime_modules/02_tensors.py +291 -0
- dedekind/runtime_modules/03_solvers.py +2702 -0
- dedekind/runtime_modules/04_math.py +1258 -0
- dedekind/runtime_modules/05_uncertainty.py +1577 -0
- dedekind/runtime_modules/06_io.py +844 -0
- dedekind/runtime_modules/07_dataframes.py +594 -0
- dedekind/runtime_modules/08_advanced.py +1003 -0
- dedekind/runtime_modules/09_publishing.py +299 -0
- dedekind/runtime_modules/09_signals.py +927 -0
- dedekind/runtime_modules/10_robotics.py +93 -0
- dedekind/runtime_modules/12_fluid_dynamics.py +1858 -0
- dedekind/runtime_modules/14_structural.py +453 -0
- dedekind/runtime_modules/15_thermal.py +276 -0
- dedekind/runtime_modules/16_space.py +251 -0
- dedekind/runtime_modules/17_atomic.py +287 -0
- dedekind/server.py +73 -0
- dedekind/simplify.py +217 -0
- dedekind/stdlib/atomic.ddk +54 -0
- dedekind/stdlib/biology.ddk +101 -0
- dedekind/stdlib/chemistry.ddk +111 -0
- dedekind/stdlib/fluid_dynamics.ddk +288 -0
- dedekind/stdlib/math.ddk +120 -0
- dedekind/stdlib/ml.ddk +107 -0
- dedekind/stdlib/physics.ddk +121 -0
- dedekind/stdlib/quantum.ddk +52 -0
- dedekind/stdlib/robotics.ddk +16 -0
- dedekind/stdlib/signals.ddk +132 -0
- dedekind/stdlib/space.ddk +26 -0
- dedekind/stdlib/stats.ddk +113 -0
- dedekind/stdlib/structural.ddk +58 -0
- dedekind/stdlib/thermal.ddk +33 -0
- dedekind/symbolic_diff.py +415 -0
- dedekind/units_checker.py +267 -0
- dedekind-3.0.0.dist-info/METADATA +387 -0
- dedekind-3.0.0.dist-info/RECORD +62 -0
- dedekind-3.0.0.dist-info/WHEEL +5 -0
- dedekind-3.0.0.dist-info/entry_points.txt +3 -0
- dedekind-3.0.0.dist-info/licenses/LICENSE +201 -0
- dedekind-3.0.0.dist-info/licenses/NOTICE +24 -0
- dedekind-3.0.0.dist-info/top_level.txt +2 -0
- dedekind_jupyter_kernel/__init__.py +1 -0
- dedekind_jupyter_kernel/__main__.py +7 -0
- dedekind_jupyter_kernel/kernel.py +202 -0
- dedekind_jupyter_kernel/kernelspec/kernel.json +11 -0
- dedekind_jupyter_kernel/run_dedekind_kernel.py +24 -0
dedekind/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Dedekind -- a programming language for scientific computing.
|
|
2
|
+
|
|
3
|
+
Public entry points:
|
|
4
|
+
dedekind.compile_source(src, ...) - compile a .ddk source string to Python
|
|
5
|
+
dedekind.dedekind_exec(code, ...) - run compiled code with mapped tracebacks
|
|
6
|
+
dedekind.export_to_latex(src) - export formulas as LaTeX
|
|
7
|
+
|
|
8
|
+
CLI: `dedekind <file.ddk>` (see dedekind.cli).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "3.0.0"
|
|
12
|
+
|
|
13
|
+
from .compiler import ( # noqa: F401 (re-export)
|
|
14
|
+
compile_source,
|
|
15
|
+
dedekind_exec,
|
|
16
|
+
export_to_latex,
|
|
17
|
+
)
|
dedekind/aot_compiler.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
from .lexer import Lexer
|
|
5
|
+
from .parser import Parser
|
|
6
|
+
from .mlir_codegen import MLIRCodeGenerator
|
|
7
|
+
|
|
8
|
+
class AOTCompiler:
|
|
9
|
+
"""
|
|
10
|
+
Dedekind Ahead-of-Time (AOT) Compiler.
|
|
11
|
+
Orchestrates the transition from Dedekind source to native binaries.
|
|
12
|
+
"""
|
|
13
|
+
def __init__(self, source_path: str):
|
|
14
|
+
self.source_path = source_path
|
|
15
|
+
self.output_dir = os.path.dirname(source_path)
|
|
16
|
+
self.base_name = os.path.splitext(os.path.basename(source_path))[0]
|
|
17
|
+
|
|
18
|
+
def compile(self) -> str:
|
|
19
|
+
"""
|
|
20
|
+
Runs the full AOT pipeline.
|
|
21
|
+
Returns the path to the generated binary.
|
|
22
|
+
"""
|
|
23
|
+
print(f"Dedekind AOT: Compiling {self.source_path}...")
|
|
24
|
+
|
|
25
|
+
# 1. Lexing & Parsing
|
|
26
|
+
with open(self.source_path, 'r', encoding='utf-8') as f:
|
|
27
|
+
source = f.read()
|
|
28
|
+
|
|
29
|
+
lexer = Lexer(source)
|
|
30
|
+
tokens = lexer.tokenize()
|
|
31
|
+
parser = Parser(tokens)
|
|
32
|
+
ast = parser.parse()
|
|
33
|
+
|
|
34
|
+
# 2. MLIR Generation
|
|
35
|
+
mlir_gen = MLIRCodeGenerator()
|
|
36
|
+
mlir_ir = mlir_gen.generate(ast)
|
|
37
|
+
|
|
38
|
+
mlir_path = os.path.join(self.output_dir, f"{self.base_name}.mlir")
|
|
39
|
+
with open(mlir_path, 'w', encoding='utf-8') as f:
|
|
40
|
+
f.write(mlir_ir)
|
|
41
|
+
print(f"Dedekind AOT: MLIR generated at {mlir_path}")
|
|
42
|
+
|
|
43
|
+
# 3. Native Mock (Prototype v0.4)
|
|
44
|
+
# In a full implementation, we would call mlir-opt and mlir-translate here.
|
|
45
|
+
# For the prototype, we generate a companion C++ 'lowering' of the MLIR to show the path.
|
|
46
|
+
cpp_path = os.path.join(self.output_dir, f"{self.base_name}_native.cpp")
|
|
47
|
+
self._generate_cpp_stubs(mlir_ir, cpp_path)
|
|
48
|
+
|
|
49
|
+
binary_path = os.path.join(self.output_dir, f"{self.base_name}.exe")
|
|
50
|
+
|
|
51
|
+
# Attempt to compile if a C++ compiler exists
|
|
52
|
+
success = self._compile_to_binary(cpp_path, binary_path)
|
|
53
|
+
|
|
54
|
+
if success:
|
|
55
|
+
return binary_path
|
|
56
|
+
else:
|
|
57
|
+
print("Dedekind AOT: Native compiler not found. Project is ready for AOT lowering.")
|
|
58
|
+
return cpp_path # Return the C++ file for inspection if binary fails
|
|
59
|
+
|
|
60
|
+
def _generate_cpp_stubs(self, mlir_ir: str, output_path: str):
|
|
61
|
+
"""Generates a C++ representation of the Dedekind program for native compilation."""
|
|
62
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
63
|
+
f.write("#include <iostream>\n")
|
|
64
|
+
f.write("#include <vector>\n\n")
|
|
65
|
+
f.write("// Dedekind Native Runtime Stubs\n")
|
|
66
|
+
f.write("int main() {\n")
|
|
67
|
+
f.write(" std::cout << \"--- Dedekind Native AOT Runtime ---\" << std::endl;\n")
|
|
68
|
+
f.write(" std::cout << \"Executing MLIR-lowered kernels...\" << std::endl;\n")
|
|
69
|
+
f.write(" // The AOT compiler links the Dedekind Dialect to LLVM here.\n")
|
|
70
|
+
f.write(f" std::cout << \"Compiled from: {self.source_path}\" << std::endl;\n")
|
|
71
|
+
f.write(" return 0;\n")
|
|
72
|
+
f.write("}\n")
|
|
73
|
+
|
|
74
|
+
def _compile_to_binary(self, cpp_path: str, output_path: str) -> bool:
|
|
75
|
+
"""Attempts to compile the generated C++/LLVM code using system tools."""
|
|
76
|
+
compilers = [
|
|
77
|
+
['cl', '/EHsc', cpp_path, f'/Fe:{output_path}'],
|
|
78
|
+
['clang++', cpp_path, '-o', output_path],
|
|
79
|
+
['g++', cpp_path, '-o', output_path],
|
|
80
|
+
]
|
|
81
|
+
for cmd in compilers:
|
|
82
|
+
try:
|
|
83
|
+
subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
84
|
+
print(f"Dedekind AOT: Static binary created via {cmd[0]}")
|
|
85
|
+
return True
|
|
86
|
+
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
87
|
+
continue
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
if len(sys.argv) > 1:
|
|
92
|
+
compiler = AOTCompiler(sys.argv[1])
|
|
93
|
+
compiler.compile()
|
dedekind/ast_nodes.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import List, Optional, Union, Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CompileError(Exception):
|
|
6
|
+
"""Compiler error with line and optional context."""
|
|
7
|
+
def __init__(self, message: str, line: Optional[int] = None, filepath: Optional[str] = None):
|
|
8
|
+
self.message = message
|
|
9
|
+
self.line = line
|
|
10
|
+
self.filepath = filepath
|
|
11
|
+
super().__init__(self._format())
|
|
12
|
+
|
|
13
|
+
def _format(self) -> str:
|
|
14
|
+
parts = []
|
|
15
|
+
if self.filepath:
|
|
16
|
+
parts.append(self.filepath)
|
|
17
|
+
if self.line is not None:
|
|
18
|
+
parts.append(f"Line {self.line}")
|
|
19
|
+
parts.append(self.message)
|
|
20
|
+
return ": ".join(str(p) for p in parts)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class Node:
|
|
25
|
+
"""Base for all AST nodes; line for error messages."""
|
|
26
|
+
line: Optional[int] = field(default=None, kw_only=True)
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class Program(Node):
|
|
30
|
+
statements: List[Node]
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class FunctionDef(Node):
|
|
34
|
+
name: str
|
|
35
|
+
args: List[str]
|
|
36
|
+
body: List[Node]
|
|
37
|
+
arg_units: Optional[List[Optional[str]]] = None # Per-arg unit annotations ([m], [kg], …); None per slot = not annotated
|
|
38
|
+
return_unit: Optional[str] = None # Return unit annotation
|
|
39
|
+
arg_shapes: Optional[List[Optional[List]]] = None # Per-arg shape annotations (Vector[2], Tensor[batch,N]); None per slot = not annotated
|
|
40
|
+
return_shape: Optional[List] = None # Return shape annotation
|
|
41
|
+
is_pub: bool = False # `pub fn` -> True; only relevant for module visibility
|
|
42
|
+
type_params: List[str] = field(default_factory=list) # `fn name<T, U>(...)`: polymorphic type parameters
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class UseStmt(Node):
|
|
47
|
+
"""Module/Import statement: `use math` loads math.ddk into the current compilation pass."""
|
|
48
|
+
module: str
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class UnitDef(Node):
|
|
53
|
+
"""User-defined unit: `unit Foot = 0.3048[m]` registers Foot as a length unit
|
|
54
|
+
with conversion factor 0.3048 to the base unit. base_unit must already belong to a known dimension."""
|
|
55
|
+
name: str
|
|
56
|
+
factor: float
|
|
57
|
+
base_unit: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class PyImport(Node):
|
|
62
|
+
"""Imports a Python module from the PyPI ecosystem: `pyimport scipy.special as ss`.
|
|
63
|
+
Generates an `import MODULE as ALIAS` in the generated code."""
|
|
64
|
+
module: str
|
|
65
|
+
alias: str
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class ReturnStmt(Node):
|
|
69
|
+
value: Node
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class Assignment(Node):
|
|
73
|
+
target: str
|
|
74
|
+
value: Node
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class BinaryOp(Node):
|
|
78
|
+
left: Node
|
|
79
|
+
op: str
|
|
80
|
+
right: Node
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class UnaryOp(Node):
|
|
84
|
+
"""Unary operator, e.g., not x."""
|
|
85
|
+
op: str
|
|
86
|
+
operand: Node
|
|
87
|
+
|
|
88
|
+
@dataclass
|
|
89
|
+
class Call(Node):
|
|
90
|
+
func_name: Node # Can be an identifier or another call (chaining)
|
|
91
|
+
args: List[Node]
|
|
92
|
+
kwargs: List[Any] # List of (name, value) tuples
|
|
93
|
+
modifiers: List[str] # e.g., ['gpu', 'cpu']
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class Identifier(Node):
|
|
97
|
+
name: str
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class Literal(Node):
|
|
101
|
+
value: Any
|
|
102
|
+
raw: bool = False # True = Raw string (r"...") in Dedekind
|
|
103
|
+
|
|
104
|
+
@dataclass
|
|
105
|
+
class Quantity(Node):
|
|
106
|
+
"""Physical quantity: number with unit, e.g., 10[m], 5[m/s]."""
|
|
107
|
+
value: Union[int, float]
|
|
108
|
+
unit: str
|
|
109
|
+
|
|
110
|
+
@dataclass
|
|
111
|
+
class QuaternionLiteral(Node):
|
|
112
|
+
value: float
|
|
113
|
+
component: str # 'i', 'j', or 'k'
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class VectorLiteral(Node):
|
|
117
|
+
elements: List[Node]
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class DictLiteral(Node):
|
|
121
|
+
"""Dict literal: {"key": value, "k2": v2} — gets transpiled to a Python dict."""
|
|
122
|
+
keys: List[Node]
|
|
123
|
+
values: List[Node]
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class Lambda(Node):
|
|
127
|
+
arg: str
|
|
128
|
+
body: Node
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class IfStmt(Node):
|
|
132
|
+
condition: Node
|
|
133
|
+
then_branch: List[Node]
|
|
134
|
+
else_branch: Optional[List[Node]]
|
|
135
|
+
|
|
136
|
+
@dataclass
|
|
137
|
+
class WhileStmt(Node):
|
|
138
|
+
condition: Node
|
|
139
|
+
body: List[Node]
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class ForStmt(Node):
|
|
143
|
+
variable: str
|
|
144
|
+
collection: Node
|
|
145
|
+
body: List[Node]
|
|
146
|
+
|
|
147
|
+
@dataclass
|
|
148
|
+
class MemberAccess(Node):
|
|
149
|
+
obj: Node
|
|
150
|
+
member: str
|
|
151
|
+
|
|
152
|
+
@dataclass
|
|
153
|
+
class IndexedVariable(Node):
|
|
154
|
+
name: str
|
|
155
|
+
indices: str # String of indices, e.g., "ij"
|
|
156
|
+
|
|
157
|
+
@dataclass
|
|
158
|
+
class Subscript(Node):
|
|
159
|
+
value: Node
|
|
160
|
+
index: Node
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass
|
|
164
|
+
class Slice(Node):
|
|
165
|
+
"""Python-style slice for subscript indices: x[start:stop:step].
|
|
166
|
+
Each component can be None (open bound)."""
|
|
167
|
+
start: Optional[Node] = None
|
|
168
|
+
stop: Optional[Node] = None
|
|
169
|
+
step: Optional[Node] = None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@dataclass
|
|
173
|
+
class TryCatch(Node):
|
|
174
|
+
"""try { body } catch var { handler } — catches every exception and binds it to var."""
|
|
175
|
+
body: List[Node]
|
|
176
|
+
catch_var: str
|
|
177
|
+
handler: List[Node]
|
|
178
|
+
|
|
179
|
+
@dataclass
|
|
180
|
+
class ItemAssignment(Node):
|
|
181
|
+
target: Subscript
|
|
182
|
+
value: Node
|
|
183
|
+
|
|
184
|
+
@dataclass
|
|
185
|
+
class PostfixFactorial(Node):
|
|
186
|
+
"""Postfix factorial: n!"""
|
|
187
|
+
operand: Node
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import glob
|
|
3
|
+
|
|
4
|
+
src_dir = os.path.join(os.path.dirname(__file__), "runtime_modules")
|
|
5
|
+
out_file = os.path.join(os.path.dirname(__file__), "ml_runtime.py")
|
|
6
|
+
|
|
7
|
+
modules = sorted(glob.glob(os.path.join(src_dir, "*.py")))
|
|
8
|
+
|
|
9
|
+
with open(out_file, "w", encoding="utf-8") as out:
|
|
10
|
+
for mod in modules:
|
|
11
|
+
with open(mod, "r", encoding="utf-8") as f:
|
|
12
|
+
out.write(f.read())
|
|
13
|
+
|
|
14
|
+
print(f"Successfully generated ml_runtime.py from {len(modules)} modules.")
|
dedekind/cli.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""CLI entry point: `dedekind <file.ddk> [--latex] [--reproducibility-report PATH] ...`.
|
|
2
|
+
|
|
3
|
+
Thin wrapper around `dedekind.compiler.main()` so that pip can install a
|
|
4
|
+
shell command. The heavy-lifting argparse-equivalent lives in compiler.py
|
|
5
|
+
for legacy `python -m dedekind.compiler` invocations.
|
|
6
|
+
"""
|
|
7
|
+
from .compiler import main
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
main()
|