ledge-lang 1.1.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.
- ledge_lang/__init__.py +192 -0
- ledge_lang/__main__.py +3 -0
- ledge_lang/ai_types.py +931 -0
- ledge_lang/ast_nodes.py +331 -0
- ledge_lang/audit_store.py +534 -0
- ledge_lang/backends.py +545 -0
- ledge_lang/calibration.py +387 -0
- ledge_lang/cli.py +586 -0
- ledge_lang/comparison.py +353 -0
- ledge_lang/compiler/__init__.py +27 -0
- ledge_lang/compiler/ccodegen.py +943 -0
- ledge_lang/compiler/codegen.py +656 -0
- ledge_lang/compiler/targets.py +414 -0
- ledge_lang/core_types.py +413 -0
- ledge_lang/debugger.py +356 -0
- ledge_lang/formatter.py +444 -0
- ledge_lang/interpreter.py +1432 -0
- ledge_lang/jit.py +226 -0
- ledge_lang/lexer.py +483 -0
- ledge_lang/linter.py +315 -0
- ledge_lang/lsp.py +655 -0
- ledge_lang/nl_interface.py +222 -0
- ledge_lang/parser.py +913 -0
- ledge_lang/profiler.py +179 -0
- ledge_lang/stdlib.py +611 -0
- ledge_lang/studio/__init__.py +0 -0
- ledge_lang/studio/server.py +174 -0
- ledge_lang/test_runner.py +103 -0
- ledge_lang/typechecker.py +536 -0
- ledge_lang/vm.py +940 -0
- ledge_lang-1.1.0.dist-info/METADATA +418 -0
- ledge_lang-1.1.0.dist-info/RECORD +36 -0
- ledge_lang-1.1.0.dist-info/WHEEL +5 -0
- ledge_lang-1.1.0.dist-info/entry_points.txt +2 -0
- ledge_lang-1.1.0.dist-info/licenses/LICENSE +21 -0
- ledge_lang-1.1.0.dist-info/top_level.txt +1 -0
ledge_lang/__init__.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ledge is a programming language and governance runtime for auditable, uncertainty-aware AI decisions.
|
|
3
|
+
|
|
4
|
+
Quick start:
|
|
5
|
+
from ledge_lang import run
|
|
6
|
+
run('show "Hello, Ledge"', output_fn=print)
|
|
7
|
+
|
|
8
|
+
New in v0.2:
|
|
9
|
+
- Lazy generators: infinite sequences work correctly
|
|
10
|
+
- Python FFI: import "python:numpy" as np
|
|
11
|
+
- Real parallel execution via threading
|
|
12
|
+
- Type enforcement on 'set' (when declared)
|
|
13
|
+
- Error messages with suggestions
|
|
14
|
+
- 0 crashes on adversarial input (fuzzer-verified)
|
|
15
|
+
- 284/284 conformance tests passing (100%)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .lexer import Lexer, LexError
|
|
19
|
+
from .parser import Parser, ParseError
|
|
20
|
+
from .interpreter import (
|
|
21
|
+
Interpreter, LedgeError, NOTHING,
|
|
22
|
+
LedgeList, LedgeMap, LedgeFunction, LedgeInstance,
|
|
23
|
+
LedgeLazyGenerator, PythonModule, PythonObject,
|
|
24
|
+
_repr as ledge_repr
|
|
25
|
+
)
|
|
26
|
+
from .calibration import calibrate, CalibrationReport
|
|
27
|
+
from .comparison import compare_models, ModelComparisonReport
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from .audit_store import activate_global_store as _activate_store
|
|
31
|
+
_activate_store()
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
__version__ = "1.1.0"
|
|
36
|
+
__all__ = [
|
|
37
|
+
"run", "run_file", "compile_ledge", "LedgeREPL",
|
|
38
|
+
"LexError", "ParseError", "LedgeError",
|
|
39
|
+
"NOTHING", "LedgeList", "LedgeMap", "LedgeLazyGenerator",
|
|
40
|
+
"ledge_repr", "__version__",
|
|
41
|
+
"calibrate", "CalibrationReport",
|
|
42
|
+
"compare_models", "ModelComparisonReport",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def compile_ledge(source: str):
|
|
47
|
+
"""Lex + parse Ledge source. Returns AST Program node."""
|
|
48
|
+
tokens = Lexer(source).tokenize()
|
|
49
|
+
return Parser(tokens).parse()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def run(source: str, output_fn=None, ai_backend=None, env=None, allowed_modules=None, reset_audit=True):
|
|
53
|
+
"""
|
|
54
|
+
Run Ledge source code.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
source: Ledge source string
|
|
58
|
+
output_fn: callable(str) for show output — defaults to silent
|
|
59
|
+
ai_backend: dict of AI instruction handlers:
|
|
60
|
+
{"analyze": fn(text, mode), "generate": fn(prompt, mode),
|
|
61
|
+
"ask": fn(question), "embed": fn(text),
|
|
62
|
+
"classify": fn(text, labels)}
|
|
63
|
+
env: optional pre-populated Env
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
(output_lines: list[str], final_value)
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
lines, v = run('show 2 + 2', output_fn=print)
|
|
70
|
+
# prints: 4
|
|
71
|
+
# lines = ['4'], v = NOTHING
|
|
72
|
+
"""
|
|
73
|
+
ast = compile_ledge(source)
|
|
74
|
+
# Reset audit trail per run to prevent cross-contamination (H02)
|
|
75
|
+
if reset_audit:
|
|
76
|
+
try:
|
|
77
|
+
from .ai_types import GLOBAL_AUDIT
|
|
78
|
+
GLOBAL_AUDIT.reset()
|
|
79
|
+
except Exception:
|
|
80
|
+
pass
|
|
81
|
+
interp = Interpreter(
|
|
82
|
+
output_fn=output_fn or (lambda x: None),
|
|
83
|
+
ai_backend=ai_backend,
|
|
84
|
+
source=source
|
|
85
|
+
)
|
|
86
|
+
if allowed_modules is not None:
|
|
87
|
+
interp._allowed_modules = set(allowed_modules)
|
|
88
|
+
result = interp.run(ast, env)
|
|
89
|
+
return interp.output_lines, result
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def run_file(path: str, output_fn=None, ai_backend=None):
|
|
93
|
+
"""Run a .ledge file. output_fn defaults to print."""
|
|
94
|
+
with open(path, encoding="utf-8") as f:
|
|
95
|
+
source = f.read()
|
|
96
|
+
return run(source, output_fn=output_fn or print, ai_backend=ai_backend)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class LedgeREPL:
|
|
100
|
+
"""Interactive Read-Eval-Print Loop for Ledge."""
|
|
101
|
+
|
|
102
|
+
def __init__(self, ai_backend=None):
|
|
103
|
+
self.interp = Interpreter(ai_backend=ai_backend)
|
|
104
|
+
self.env = self.interp._globals
|
|
105
|
+
|
|
106
|
+
def eval_line(self, source: str):
|
|
107
|
+
"""Evaluate source. Returns (output_lines, value, error_or_None)."""
|
|
108
|
+
try:
|
|
109
|
+
ast = compile_ledge(source)
|
|
110
|
+
self.interp.output_lines = []
|
|
111
|
+
result = self.interp.run(ast, self.env)
|
|
112
|
+
return self.interp.output_lines, result, None
|
|
113
|
+
except (LexError, ParseError, LedgeError) as e:
|
|
114
|
+
return [], NOTHING, str(e)
|
|
115
|
+
|
|
116
|
+
def run(self):
|
|
117
|
+
"""Start the interactive REPL."""
|
|
118
|
+
print(f"Ledge {__version__} — Type 'stop' to exit, 'help' for help")
|
|
119
|
+
print(f"Tip: import \"python:numpy\" as np — full Python ecosystem available\n")
|
|
120
|
+
buf = []
|
|
121
|
+
|
|
122
|
+
while True:
|
|
123
|
+
try:
|
|
124
|
+
line = input("... " if buf else ">>> ")
|
|
125
|
+
except (EOFError, KeyboardInterrupt):
|
|
126
|
+
print("\nGoodbye.")
|
|
127
|
+
break
|
|
128
|
+
|
|
129
|
+
if line.strip() == "stop":
|
|
130
|
+
print("Goodbye."); break
|
|
131
|
+
|
|
132
|
+
if line.strip() == "help":
|
|
133
|
+
print("""
|
|
134
|
+
Ledge REPL help:
|
|
135
|
+
stop Exit
|
|
136
|
+
help This message
|
|
137
|
+
show expr Print value
|
|
138
|
+
define x as val Create variable
|
|
139
|
+
import "math" as m Use stdlib
|
|
140
|
+
import "python:numpy" as np Use Python packages
|
|
141
|
+
|
|
142
|
+
Keywords: define, set, if, else, for, while, match, check, recover,
|
|
143
|
+
return, yield, parallel, given, type, import, show, pass
|
|
144
|
+
""")
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
buf.append(line)
|
|
148
|
+
source = "\n".join(buf)
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
ast = compile_ledge(source)
|
|
152
|
+
self.interp.output_lines = []
|
|
153
|
+
result = self.interp.run(ast, self.env)
|
|
154
|
+
if result is not NOTHING and not self.interp.output_lines:
|
|
155
|
+
print(ledge_repr(result))
|
|
156
|
+
buf = []
|
|
157
|
+
except ParseError as e:
|
|
158
|
+
msg = str(e)
|
|
159
|
+
if "Expected indented block" in msg:
|
|
160
|
+
pass # keep buffering
|
|
161
|
+
else:
|
|
162
|
+
print(f" {e}"); buf = []
|
|
163
|
+
except (LexError, LedgeError) as e:
|
|
164
|
+
print(f" {e}"); buf = []
|
|
165
|
+
|
|
166
|
+
# Optional: AI backends (require pip install openai / pip install anthropic)
|
|
167
|
+
def get_backend(provider: str = "auto", api_key: str = None):
|
|
168
|
+
"""
|
|
169
|
+
Get an AI backend for Ledge.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
provider: "openai", "anthropic", or "auto" (default)
|
|
173
|
+
api_key: API key (or use OPENAI_API_KEY / ANTHROPIC_API_KEY env vars)
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Backend dict for use with run(..., ai_backend=backend)
|
|
177
|
+
Returns None if no provider is available.
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
backend = get_backend() # auto-detect from environment
|
|
181
|
+
if backend:
|
|
182
|
+
lines, _ = run(source, ai_backend=backend)
|
|
183
|
+
"""
|
|
184
|
+
from .backends import auto_backend, openai_backend, anthropic_backend
|
|
185
|
+
if provider == "auto":
|
|
186
|
+
return auto_backend()
|
|
187
|
+
elif provider == "openai":
|
|
188
|
+
return openai_backend(api_key=api_key)
|
|
189
|
+
elif provider == "anthropic":
|
|
190
|
+
return anthropic_backend(api_key=api_key)
|
|
191
|
+
else:
|
|
192
|
+
raise ValueError(f"Unknown provider: {provider}. Use: auto, openai, anthropic")
|
ledge_lang/__main__.py
ADDED