clm-kernel 0.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.
- clm/__init__.py +230 -0
- clm/__main__.py +25 -0
- clm/cross_cutting/__init__.py +50 -0
- clm/cross_cutting/_test_runner.py +36 -0
- clm/cross_cutting/async_utils.py +25 -0
- clm/cross_cutting/cel_builder.py +123 -0
- clm/cross_cutting/cel_converter.py +697 -0
- clm/cross_cutting/cel_engine.py +252 -0
- clm/cross_cutting/cel_ops.py +72 -0
- clm/cross_cutting/cel_ops_db.py +157 -0
- clm/cross_cutting/cel_ops_files.py +99 -0
- clm/cross_cutting/cel_ops_state.py +71 -0
- clm/cross_cutting/cel_ops_strings.py +64 -0
- clm/cross_cutting/cel_resolver.py +63 -0
- clm/cross_cutting/cel_slices.py +56 -0
- clm/cross_cutting/cel_store.py +330 -0
- clm/cross_cutting/clm_logger.py +140 -0
- clm/cross_cutting/config/__init__.py +155 -0
- clm/cross_cutting/config/config_constants.py +392 -0
- clm/cross_cutting/config/env_parameters.py +90 -0
- clm/cross_cutting/config/logging.py +378 -0
- clm/cross_cutting/config/settings.py +504 -0
- clm/cross_cutting/context.py +5 -0
- clm/cross_cutting/domain_models.py +55 -0
- clm/cross_cutting/domain_types.py +61 -0
- clm/cross_cutting/effects.py +67 -0
- clm/cross_cutting/errors.py +153 -0
- clm/cross_cutting/governance.py +331 -0
- clm/cross_cutting/guards.py +41 -0
- clm/cross_cutting/mcard.py +321 -0
- clm/cross_cutting/native.py +185 -0
- clm/cross_cutting/observability.py +289 -0
- clm/cross_cutting/pcard.py +434 -0
- clm/cross_cutting/registry.py +71 -0
- clm/cross_cutting/resources.py +84 -0
- clm/cross_cutting/secrets.py +44 -0
- clm/cross_cutting/subprocess_utils.py +118 -0
- clm/cross_cutting/telemetry.py +164 -0
- clm/cross_cutting/testing/__init__.py +8 -0
- clm/cross_cutting/testing/comparator.py +187 -0
- clm/cross_cutting/testing/datasets.py +87 -0
- clm/cross_cutting/timeutil.py +20 -0
- clm/cross_cutting/types/new_type_check.py +0 -0
- clm/cross_cutting/utils/__init__.py +1 -0
- clm/cross_cutting/utils/url_safety.py +50 -0
- clm/cross_cutting/vcard.py +560 -0
- clm/cross_cutting/writer.py +118 -0
- clm/layer0/README.md +50 -0
- clm/layer0/__init__.py +223 -0
- clm/layer0/algebra.py +60 -0
- clm/layer0/algebra_baldwin.py +133 -0
- clm/layer0/algebra_baldwin_generators.py +36 -0
- clm/layer0/algebra_equivalence.py +262 -0
- clm/layer0/algebra_ingest.py +306 -0
- clm/layer0/algebra_manifest_io.py +156 -0
- clm/layer0/assembler.py +481 -0
- clm/layer0/cel/__init__.py +23 -0
- clm/layer0/cel_eval.py +491 -0
- clm/layer0/codec.py +106 -0
- clm/layer0/context.py +122 -0
- clm/layer0/db.py +160 -0
- clm/layer0/exceptions.py +73 -0
- clm/layer0/fibration.py +106 -0
- clm/layer0/gates.py +50 -0
- clm/layer0/hash.py +80 -0
- clm/layer0/loader.py +826 -0
- clm/layer0/math_utils.py +163 -0
- clm/layer0/mcard.py +96 -0
- clm/layer0/model/__init__.py +0 -0
- clm/layer0/model/action.py +223 -0
- clm/layer0/model/card.py +280 -0
- clm/layer0/model/card_triad.py +427 -0
- clm/layer0/model/compatibility.py +60 -0
- clm/layer0/model/dictionary.py +46 -0
- clm/layer0/model/dots.py +331 -0
- clm/layer0/model/event_producer.py +88 -0
- clm/layer0/model/g_time.py +89 -0
- clm/layer0/model/handle.py +163 -0
- clm/layer0/model/hash/__init__.py +6 -0
- clm/layer0/model/hash/algorithms/__init__.py +3 -0
- clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
- clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
- clm/layer0/model/hash/constants.py +53 -0
- clm/layer0/model/hash/enums.py +60 -0
- clm/layer0/model/hash/validator.py +307 -0
- clm/layer0/model/interpreter.py +275 -0
- clm/layer0/model/pagination.py +45 -0
- clm/layer0/model/pcard.py +716 -0
- clm/layer0/model/schema.py +44 -0
- clm/layer0/model/utils/__init__.py +1 -0
- clm/layer0/model/utils/content_analyzer.py +152 -0
- clm/layer0/model/validators/__init__.py +1 -0
- clm/layer0/model/validators/base_validator.py +44 -0
- clm/layer0/model/validators/binary_validator.py +66 -0
- clm/layer0/model/validators/text_validator.py +82 -0
- clm/layer0/model/validators/validation_registry.py +49 -0
- clm/layer0/model/vcard.py +823 -0
- clm/layer0/model/vcard_ext/__init__.py +39 -0
- clm/layer0/model/vcard_ext/core.py +60 -0
- clm/layer0/model/vcard_ext/network.py +23 -0
- clm/layer0/model/vcard_ext/observability.py +63 -0
- clm/layer0/model/vcard_ext/storage.py +60 -0
- clm/layer0/model/vcard_ext/vendors.py +211 -0
- clm/layer0/model/vcard_sandwich.py +119 -0
- clm/layer0/model/vcard_vocabulary.py +463 -0
- clm/layer0/model/workflow.py +274 -0
- clm/layer0/narrative.py +431 -0
- clm/layer0/ontology/collection.py +125 -0
- clm/layer0/parser.py +429 -0
- clm/layer0/relations.py +73 -0
- clm/layer0/rule_evaluator.py +251 -0
- clm/layer0/schema/__init__.py +38 -0
- clm/layer0/schema/ddl.py +97 -0
- clm/layer0/templates/abstract.yaml +99 -0
- clm/layer0/templates/balanced.yaml +253 -0
- clm/layer0/templates/concrete.yaml +172 -0
- clm/layer0/type_registry.py +223 -0
- clm/layer0/types.py +204 -0
- clm/layer0/utils.py +886 -0
- clm/layer0/verifier_core.py +262 -0
- clm/layer1/__init__.py +31 -0
- clm/layer1/action_dispatcher.py +275 -0
- clm/layer1/adapter_registry.py +233 -0
- clm/layer1/algebra_baldwin_generators.py +192 -0
- clm/layer1/arrow_compiler.py +525 -0
- clm/layer1/batch_processor.py +152 -0
- clm/layer1/cel/__init__.py +27 -0
- clm/layer1/cel/builder.py +173 -0
- clm/layer1/engine.py +759 -0
- clm/layer1/engine_async.py +89 -0
- clm/layer1/eval_callback.py +49 -0
- clm/layer1/gate_composer.py +229 -0
- clm/layer1/hoare_sandwich.py +298 -0
- clm/layer1/identity_guard.py +348 -0
- clm/layer1/matrix.py +138 -0
- clm/layer1/neural_runtime.py +221 -0
- clm/layer1/operations/__init__.py +123 -0
- clm/layer1/operations/augment.py +156 -0
- clm/layer1/operations/base.py +109 -0
- clm/layer1/operations/builtins.py +338 -0
- clm/layer1/operations/dispatch.py +144 -0
- clm/layer1/operations/exclude.py +107 -0
- clm/layer1/operations/handle.py +197 -0
- clm/layer1/operations/identity.py +139 -0
- clm/layer1/operations/invert.py +66 -0
- clm/layer1/operations/loader.py +116 -0
- clm/layer1/operations/manipulators/__init__.py +23 -0
- clm/layer1/operations/manipulators/document.py +211 -0
- clm/layer1/operations/manipulators/media.py +134 -0
- clm/layer1/operations/manipulators/structured.py +159 -0
- clm/layer1/operations/manipulators/tabular.py +334 -0
- clm/layer1/operations/port.py +65 -0
- clm/layer1/operations/services.py +529 -0
- clm/layer1/operations/split.py +117 -0
- clm/layer1/operations/substitute.py +75 -0
- clm/layer1/petri.py +1052 -0
- clm/layer1/pocketflow_orchestrator.py +408 -0
- clm/layer1/pocketflow_scheduler.py +519 -0
- clm/layer1/protocols.py +197 -0
- clm/layer1/runtime_adapter.py +718 -0
- clm/layer1/runtimes/__init__.py +71 -0
- clm/layer1/runtimes/_generated_assertions.py +125 -0
- clm/layer1/runtimes/_generated_types.py +42 -0
- clm/layer1/runtimes/base.py +265 -0
- clm/layer1/runtimes/binary.py +101 -0
- clm/layer1/runtimes/factory.py +203 -0
- clm/layer1/runtimes/javascript.py +321 -0
- clm/layer1/runtimes/javascript_runtime.js +33 -0
- clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
- clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
- clm/layer1/runtimes/lambda_calc.py +126 -0
- clm/layer1/runtimes/python.py +288 -0
- clm/layer1/runtimes/python_runtime.py +41 -0
- clm/layer1/runtimes/script.py +114 -0
- clm/layer1/sandbox.py +334 -0
- clm/layer1/savepoint_guard.py +136 -0
- clm/layer1/sparse_net.py +122 -0
- clm/layer1/spec_tester.py +165 -0
- clm/layer1/store.py +295 -0
- clm/layer1/test_runner.py +498 -0
- clm/layer1/type_inspector.py +166 -0
- clm/layer1/verifier.py +772 -0
- clm/layer1/vm.py +91 -0
- clm/layer2/__init__.py +78 -0
- clm/layer2/card_collection.py +678 -0
- clm/layer2/classifier.py +479 -0
- clm/layer2/engine/__init__.py +1 -0
- clm/layer2/engine/abstract_sql_engine.py +320 -0
- clm/layer2/engine/base.py +193 -0
- clm/layer2/engine/duckdb_engine.py +576 -0
- clm/layer2/engine/sqlite_engine.py +518 -0
- clm/layer2/fiber/__init__.py +31 -0
- clm/layer2/fiber/context.py +89 -0
- clm/layer2/fiber/disposable.py +163 -0
- clm/layer2/fiber/fiber.py +125 -0
- clm/layer2/fiber/hoare.py +191 -0
- clm/layer2/file_io.py +376 -0
- clm/layer2/file_registrar.py +77 -0
- clm/layer2/ingest.py +202 -0
- clm/layer2/lineage.py +322 -0
- clm/layer2/mcard_fs.py +262 -0
- clm/layer2/merkle.py +11 -0
- clm/layer2/mime.py +244 -0
- clm/layer2/mime_detector.py +245 -0
- clm/layer2/private_collection.py +118 -0
- clm/layer2/shared_store.py +11 -0
- clm/layer2/storage/__init__.py +190 -0
- clm/layer2/storage/db_bitemporal.py +141 -0
- clm/layer2/storage/db_driver.py +331 -0
- clm/layer2/storage/db_hyperlinks.py +214 -0
- clm/layer2/storage/db_pool.py +139 -0
- clm/layer2/storage/db_repository.py +229 -0
- clm/layer2/storage/savepoint.py +78 -0
- clm/layer2/storage/sqlite_wal.py +74 -0
- clm/layer2/tridb.py +245 -0
- clm/layer2/vcard_receipt.py +71 -0
- clm/layer2/vfs.py +17 -0
- clm/layer3/__init__.py +141 -0
- clm/layer3/agency/__init__.py +19 -0
- clm/layer3/agency/config.py +220 -0
- clm/layer3/agency/providers/__init__.py +15 -0
- clm/layer3/agency/providers/base.py +91 -0
- clm/layer3/agency/providers/mlc_llm.py +169 -0
- clm/layer3/agency/providers/ollama.py +203 -0
- clm/layer3/agency/router.py +71 -0
- clm/layer3/agency/runtime.py +524 -0
- clm/layer3/gateway.py +353 -0
- clm/layer3/open_interpreter_adapter.py +159 -0
- clm/layer3/rag/__init__.py +70 -0
- clm/layer3/rag/cli.py +228 -0
- clm/layer3/rag/config.py +153 -0
- clm/layer3/rag/embeddings/__init__.py +16 -0
- clm/layer3/rag/embeddings/base.py +79 -0
- clm/layer3/rag/embeddings/ollama.py +211 -0
- clm/layer3/rag/embeddings/vision.py +290 -0
- clm/layer3/rag/engine.py +276 -0
- clm/layer3/rag/graph/__init__.py +22 -0
- clm/layer3/rag/graph/community.py +198 -0
- clm/layer3/rag/graph/engine.py +433 -0
- clm/layer3/rag/graph/extractor.py +255 -0
- clm/layer3/rag/graph/schema.py +115 -0
- clm/layer3/rag/graph/store.py +641 -0
- clm/layer3/rag/indexer.py +333 -0
- clm/layer3/rag/llm_providers.json +52 -0
- clm/layer3/rag/semantic_versioning.py +313 -0
- clm/layer3/rag/vector/__init__.py +28 -0
- clm/layer3/rag/vector/handle_vector_store.py +749 -0
- clm/layer3/rag/vector/schema.py +151 -0
- clm/layer3/rag/vector/store.py +632 -0
- clm/layer3/reticulum/__init__.py +55 -0
- clm/layer3/reticulum/bridge.py +74 -0
- clm/layer3/reticulum/daemon.py +88 -0
- clm/layer3/reticulum/discovery.py +164 -0
- clm/layer3/reticulum/identity.py +107 -0
- clm/layer3/reticulum/media.py +148 -0
- clm/layer3/reticulum/transport.py +133 -0
- clm/layer3/satori/__init__.py +209 -0
- clm/layer3/satori/alpha_conversion.py +168 -0
- clm/layer3/satori/beta_reduction.py +359 -0
- clm/layer3/satori/continuation.py +38 -0
- clm/layer3/satori/eta_conversion.py +205 -0
- clm/layer3/satori/free_variables.py +163 -0
- clm/layer3/satori/io_effects.py +370 -0
- clm/layer3/satori/lambda_runtime.py +726 -0
- clm/layer3/satori/lambda_term.py +230 -0
- clm/layer3/satori/sheaf_audit.py +261 -0
- clm/layer3/satori/speech_act.py +279 -0
- clm/layer4/README.md +9 -0
- clm/layer4/__init__.py +67 -0
- clm/layer4/_sovereign_executor.py +580 -0
- clm/layer4/action_commands.py +719 -0
- clm/layer4/api.py +236 -0
- clm/layer4/bootstrap/__init__.py +13 -0
- clm/layer4/bootstrap/bootstrapper.py +118 -0
- clm/layer4/bootstrap/cleanup.py +44 -0
- clm/layer4/bootstrap/clm_hooks.py +49 -0
- clm/layer4/bootstrap/genesis.py +77 -0
- clm/layer4/bootstrap/injection.py +125 -0
- clm/layer4/cli.py +549 -0
- clm/layer4/collection_manager.py +553 -0
- clm/layer4/commands/__init__.py +81 -0
- clm/layer4/commands/action_cmd.py +42 -0
- clm/layer4/commands/check_cmd.py +82 -0
- clm/layer4/commands/envelope.py +73 -0
- clm/layer4/commands/evaluate_cmd.py +224 -0
- clm/layer4/commands/mcard_cmd.py +24 -0
- clm/layer4/commands/petri_cmd.py +92 -0
- clm/layer4/commands/tridb_cmd.py +319 -0
- clm/layer4/commands/version_cmd.py +27 -0
- clm/layer4/diff_commands.py +167 -0
- clm/layer4/eoa_sponsor.py +232 -0
- clm/layer4/events.py +69 -0
- clm/layer4/flux_gateway.py +133 -0
- clm/layer4/gateway.py +91 -0
- clm/layer4/loader.py +307 -0
- clm/layer4/mcard_commands.py +166 -0
- clm/layer4/membrane.py +273 -0
- clm/layer4/navigation.py +50 -0
- clm/layer4/protocol/protocol_entry_python.py +108 -0
- clm/layer4/protocol/python_sync.py +126 -0
- clm/layer4/runner.py +150 -0
- clm/layer4/satori.py +262 -0
- clm/layer4/server/python_sync_client.py +150 -0
- clm/layer4/server/python_sync_server.py +139 -0
- clm/layer4/server/service_api.py +315 -0
- clm/layer4/server/start_servers.py +107 -0
- clm/layer4/server/stop_servers.py +24 -0
- clm/layer4/simulated_optimization_fix.py +0 -0
- clm/layer4/storage.py +463 -0
- clm/layer4/tridb_admin.py +268 -0
- clm/layer4/websocket/test_ws_client.py +104 -0
- clm/layer4/websocket/test_ws_server.py +138 -0
- clm/layer4/websocket/ws_server_local.py +160 -0
- clm/layer5/__init__.py +51 -0
- clm/layer5/async_batch_writer.py +133 -0
- clm/layer5/learning.py +276 -0
- clm/layer5/telemetry_hook.py +107 -0
- clm/layer5/type_lattice.py +238 -0
- clm/py.typed +0 -0
- clm/schemas/__init__.py +652 -0
- clm/schemas/abstract_face_schema.yaml +77 -0
- clm/schemas/cel_schema.yaml +416 -0
- clm/schemas/lifecycle/__init__.py +45 -0
- clm/schemas/lifecycle/models.py +123 -0
- clm/schemas/lifecycle/service.py +364 -0
- clm/schemas/mcard_schema.sql +192 -0
- clm/schemas/mcard_vector_schema.sql +283 -0
- clm/schemas/mlp-context-v1.json +91 -0
- clm_kernel-0.0.0.dist-info/METADATA +121 -0
- clm_kernel-0.0.0.dist-info/RECORD +333 -0
- clm_kernel-0.0.0.dist-info/WHEEL +4 -0
- clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
- clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lambda Runtime - PTR interface for Lambda Calculus
|
|
3
|
+
|
|
4
|
+
Provides a unified runtime interface for Lambda Calculus operations,
|
|
5
|
+
integrating the parser and all conversion modules.
|
|
6
|
+
|
|
7
|
+
This module provides Python parity with mcard-js/src/ptr/lambda/LambdaRuntime.ts
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, Literal
|
|
12
|
+
|
|
13
|
+
from clm.layer2.card_collection import CardCollection
|
|
14
|
+
from clm.layer3.satori.alpha_conversion import alpha_equivalent, alpha_normalize
|
|
15
|
+
from clm.layer3.satori.beta_reduction import (
|
|
16
|
+
ReductionStrategy,
|
|
17
|
+
beta_reduce,
|
|
18
|
+
normalize,
|
|
19
|
+
)
|
|
20
|
+
from clm.layer3.satori.eta_conversion import eta_expand, eta_reduce
|
|
21
|
+
from clm.layer3.satori.free_variables import free_variables, is_closed
|
|
22
|
+
from clm.layer3.satori.io_effects import create_io_handler
|
|
23
|
+
from clm.layer3.satori.lambda_term import (
|
|
24
|
+
AbsTerm,
|
|
25
|
+
AppTerm,
|
|
26
|
+
VarTerm,
|
|
27
|
+
load_term,
|
|
28
|
+
mk_abs,
|
|
29
|
+
mk_app,
|
|
30
|
+
mk_var,
|
|
31
|
+
pretty_print_deep,
|
|
32
|
+
store_term,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
# Parser
|
|
37
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ParseError(Exception):
|
|
41
|
+
"""Error during Lambda expression parsing"""
|
|
42
|
+
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def parse_lambda_expression(collection: CardCollection, expr: str) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Parse a Lambda expression string and store terms in collection.
|
|
49
|
+
|
|
50
|
+
Syntax:
|
|
51
|
+
<term> ::= <var> | <abs> | <app> | "(" <term> ")"
|
|
52
|
+
<var> ::= [a-zA-Z][a-zA-Z0-9']*
|
|
53
|
+
<abs> ::= ("λ" | "\\") <var> "." <term>
|
|
54
|
+
<app> ::= <term> <term>
|
|
55
|
+
|
|
56
|
+
Returns the hash of the parsed term.
|
|
57
|
+
"""
|
|
58
|
+
parser = LambdaParser(collection, expr)
|
|
59
|
+
return parser.parse()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class LambdaParser:
|
|
63
|
+
"""Recursive descent parser for Lambda expressions"""
|
|
64
|
+
|
|
65
|
+
def __init__(self, collection: CardCollection, input_str: str):
|
|
66
|
+
self.collection = collection
|
|
67
|
+
self.input = input_str.strip()
|
|
68
|
+
self.pos = 0
|
|
69
|
+
|
|
70
|
+
def parse(self) -> str:
|
|
71
|
+
"""Parse the entire input and return term hash"""
|
|
72
|
+
result = self._parse_term()
|
|
73
|
+
self._skip_whitespace()
|
|
74
|
+
if self.pos < len(self.input):
|
|
75
|
+
raise ParseError(
|
|
76
|
+
f"Unexpected character at position {self.pos}: '{self.input[self.pos]}'"
|
|
77
|
+
)
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
def _skip_whitespace(self):
|
|
81
|
+
"""Skip whitespace characters"""
|
|
82
|
+
while self.pos < len(self.input) and self.input[self.pos] in " \t\n\r":
|
|
83
|
+
self.pos += 1
|
|
84
|
+
|
|
85
|
+
def _peek(self) -> str | None:
|
|
86
|
+
"""Look at current character without consuming"""
|
|
87
|
+
self._skip_whitespace()
|
|
88
|
+
if self.pos >= len(self.input):
|
|
89
|
+
return None
|
|
90
|
+
return self.input[self.pos]
|
|
91
|
+
|
|
92
|
+
def _consume(self, char: str):
|
|
93
|
+
"""Consume expected character"""
|
|
94
|
+
self._skip_whitespace()
|
|
95
|
+
if self.pos >= len(self.input):
|
|
96
|
+
raise ParseError(f"Expected '{char}' but reached end of input")
|
|
97
|
+
if self.input[self.pos] != char:
|
|
98
|
+
raise ParseError(
|
|
99
|
+
f"Expected '{char}' but found '{self.input[self.pos]}' at position {self.pos}"
|
|
100
|
+
)
|
|
101
|
+
self.pos += 1
|
|
102
|
+
|
|
103
|
+
def _parse_term(self) -> str:
|
|
104
|
+
"""Parse a Lambda term (handles application as left-associative)"""
|
|
105
|
+
atoms = []
|
|
106
|
+
|
|
107
|
+
while True:
|
|
108
|
+
atom = self._parse_atom()
|
|
109
|
+
if atom is None:
|
|
110
|
+
break
|
|
111
|
+
atoms.append(atom)
|
|
112
|
+
|
|
113
|
+
if not atoms:
|
|
114
|
+
raise ParseError(f"Expected term at position {self.pos}")
|
|
115
|
+
|
|
116
|
+
# Build left-associative application
|
|
117
|
+
result = atoms[0]
|
|
118
|
+
for i in range(1, len(atoms)):
|
|
119
|
+
result = store_term(self.collection, mk_app(result, atoms[i]))
|
|
120
|
+
|
|
121
|
+
return result
|
|
122
|
+
|
|
123
|
+
def _parse_atom(self) -> str | None:
|
|
124
|
+
"""Parse an atomic term (variable, abstraction, or parenthesized)"""
|
|
125
|
+
self._skip_whitespace()
|
|
126
|
+
|
|
127
|
+
if self.pos >= len(self.input):
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
c = self.input[self.pos]
|
|
131
|
+
|
|
132
|
+
# Lambda/backslash - abstraction
|
|
133
|
+
if c == "λ" or c == "\\":
|
|
134
|
+
return self._parse_abstraction()
|
|
135
|
+
|
|
136
|
+
# Open paren - grouped term
|
|
137
|
+
if c == "(":
|
|
138
|
+
return self._parse_grouped()
|
|
139
|
+
|
|
140
|
+
# String literal
|
|
141
|
+
if c == '"':
|
|
142
|
+
return self._parse_string()
|
|
143
|
+
|
|
144
|
+
# Variable
|
|
145
|
+
if c.isalpha() or c == "_":
|
|
146
|
+
return self._parse_variable()
|
|
147
|
+
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
def _parse_string(self) -> str:
|
|
151
|
+
"""Parse a double-quoted string literal as a variable"""
|
|
152
|
+
# Consume opening quote
|
|
153
|
+
self._consume('"')
|
|
154
|
+
|
|
155
|
+
start = self.pos
|
|
156
|
+
while self.pos < len(self.input) and self.input[self.pos] != '"':
|
|
157
|
+
self.pos += 1
|
|
158
|
+
|
|
159
|
+
if self.pos >= len(self.input):
|
|
160
|
+
raise ParseError(f"Unterminated string literal starting at {start - 1}")
|
|
161
|
+
|
|
162
|
+
content = self.input[start : self.pos]
|
|
163
|
+
|
|
164
|
+
# Consume closing quote
|
|
165
|
+
self._consume('"')
|
|
166
|
+
|
|
167
|
+
# Store as variable with quotes preserved to indicate string-ness
|
|
168
|
+
return store_term(self.collection, mk_var(f'"{content}"'))
|
|
169
|
+
|
|
170
|
+
def _parse_variable(self) -> str:
|
|
171
|
+
"""Parse a variable name"""
|
|
172
|
+
self._skip_whitespace()
|
|
173
|
+
start = self.pos
|
|
174
|
+
|
|
175
|
+
# First character must be letter
|
|
176
|
+
if self.pos >= len(self.input) or not self.input[self.pos].isalpha():
|
|
177
|
+
raise ParseError(f"Expected variable at position {self.pos}")
|
|
178
|
+
|
|
179
|
+
# Read variable name
|
|
180
|
+
while self.pos < len(self.input) and (
|
|
181
|
+
self.input[self.pos].isalnum() or self.input[self.pos] in ("'", "_")
|
|
182
|
+
):
|
|
183
|
+
self.pos += 1
|
|
184
|
+
|
|
185
|
+
name = self.input[start : self.pos]
|
|
186
|
+
return store_term(self.collection, mk_var(name))
|
|
187
|
+
|
|
188
|
+
def _parse_abstraction(self) -> str:
|
|
189
|
+
"""Parse an abstraction: λx.M or \\x.M"""
|
|
190
|
+
self._skip_whitespace()
|
|
191
|
+
|
|
192
|
+
# Consume lambda symbol
|
|
193
|
+
if self.input[self.pos] == "λ" or self.input[self.pos] == "\\":
|
|
194
|
+
self.pos += 1
|
|
195
|
+
else:
|
|
196
|
+
raise ParseError(f"Expected λ or \\ at position {self.pos}")
|
|
197
|
+
|
|
198
|
+
self._skip_whitespace()
|
|
199
|
+
|
|
200
|
+
# Parse parameter
|
|
201
|
+
start = self.pos
|
|
202
|
+
while self.pos < len(self.input) and (
|
|
203
|
+
self.input[self.pos].isalnum() or self.input[self.pos] in ("'", "_")
|
|
204
|
+
):
|
|
205
|
+
self.pos += 1
|
|
206
|
+
|
|
207
|
+
if self.pos == start:
|
|
208
|
+
raise ParseError(f"Expected parameter name at position {self.pos}")
|
|
209
|
+
|
|
210
|
+
param = self.input[start : self.pos]
|
|
211
|
+
|
|
212
|
+
# Consume dot
|
|
213
|
+
self._skip_whitespace()
|
|
214
|
+
self._consume(".")
|
|
215
|
+
|
|
216
|
+
# Parse body
|
|
217
|
+
body = self._parse_term()
|
|
218
|
+
|
|
219
|
+
return store_term(self.collection, mk_abs(param, body))
|
|
220
|
+
|
|
221
|
+
def _parse_grouped(self) -> str:
|
|
222
|
+
"""Parse a parenthesized term: (M)"""
|
|
223
|
+
self._consume("(")
|
|
224
|
+
term = self._parse_term()
|
|
225
|
+
self._consume(")")
|
|
226
|
+
return term
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
230
|
+
# Lambda Runtime
|
|
231
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
LambdaOperation = Literal[
|
|
234
|
+
"parse",
|
|
235
|
+
"normalize",
|
|
236
|
+
"normalize-applicative",
|
|
237
|
+
"normalize-lazy",
|
|
238
|
+
"beta-reduce",
|
|
239
|
+
"alpha-equiv",
|
|
240
|
+
"alpha-normalize",
|
|
241
|
+
"eta-reduce",
|
|
242
|
+
"eta-expand",
|
|
243
|
+
"free-vars",
|
|
244
|
+
"is-closed",
|
|
245
|
+
"pretty-print",
|
|
246
|
+
"check-readiness",
|
|
247
|
+
"num-add",
|
|
248
|
+
"num-sub",
|
|
249
|
+
"num-mul",
|
|
250
|
+
"num-div",
|
|
251
|
+
"http-request",
|
|
252
|
+
"church-to-int",
|
|
253
|
+
]
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@dataclass
|
|
257
|
+
class LambdaConfig:
|
|
258
|
+
"""Configuration for Lambda runtime operations"""
|
|
259
|
+
|
|
260
|
+
operation: LambdaOperation = "normalize"
|
|
261
|
+
strategy: ReductionStrategy = "normal"
|
|
262
|
+
max_steps: int = 100
|
|
263
|
+
io_effects: dict[str, Any] | None = None
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@dataclass
|
|
267
|
+
class LambdaRuntimeResult:
|
|
268
|
+
"""Result from Lambda runtime execution"""
|
|
269
|
+
|
|
270
|
+
success: bool
|
|
271
|
+
term_hash: str | None = None
|
|
272
|
+
pretty_print: str | None = None
|
|
273
|
+
free_variables: list[str] | None = None
|
|
274
|
+
is_closed: bool | None = None
|
|
275
|
+
steps: int | None = None
|
|
276
|
+
alpha_equivalent: bool | None = None
|
|
277
|
+
eta_equivalent: bool | None = None
|
|
278
|
+
error: str | None = None
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class LambdaRuntime:
|
|
282
|
+
"""
|
|
283
|
+
Lambda Calculus runtime for PTR.
|
|
284
|
+
|
|
285
|
+
Provides α-β-η conversions on MCard-stored Lambda terms.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
def __init__(self, collection: CardCollection):
|
|
289
|
+
self.collection = collection
|
|
290
|
+
|
|
291
|
+
def execute(
|
|
292
|
+
self,
|
|
293
|
+
code_or_hash: str,
|
|
294
|
+
context: dict[str, Any],
|
|
295
|
+
config: dict[str, Any],
|
|
296
|
+
_chapter_dir: str = "",
|
|
297
|
+
) -> LambdaRuntimeResult:
|
|
298
|
+
"""
|
|
299
|
+
Execute a Lambda operation.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
code_or_hash: Either a Lambda expression string or a term hash
|
|
303
|
+
context: Additional context (may contain 'expression', 'other_hash')
|
|
304
|
+
config: Operation configuration
|
|
305
|
+
chapter_dir: Working directory (unused)
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
LambdaRuntimeResult with operation output
|
|
309
|
+
"""
|
|
310
|
+
try:
|
|
311
|
+
operation = config.get("operation", "normalize")
|
|
312
|
+
strategy = config.get("strategy", "normal")
|
|
313
|
+
max_steps = config.get("max_steps", config.get("maxSteps", 100))
|
|
314
|
+
|
|
315
|
+
# Parse expression if provided in context
|
|
316
|
+
if context.get("expression"):
|
|
317
|
+
term_hash = parse_lambda_expression(
|
|
318
|
+
self.collection, context["expression"]
|
|
319
|
+
)
|
|
320
|
+
elif code_or_hash and len(code_or_hash) == 64:
|
|
321
|
+
# Looks like a hash
|
|
322
|
+
term_hash = code_or_hash
|
|
323
|
+
elif code_or_hash and code_or_hash.strip():
|
|
324
|
+
# Parse as expression
|
|
325
|
+
term_hash = parse_lambda_expression(self.collection, code_or_hash)
|
|
326
|
+
else:
|
|
327
|
+
return LambdaRuntimeResult(
|
|
328
|
+
success=False, error="No expression or hash provided"
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Execute operation
|
|
332
|
+
if operation == "parse":
|
|
333
|
+
return self._parse(context)
|
|
334
|
+
|
|
335
|
+
elif operation == "normalize":
|
|
336
|
+
return self._normalize(term_hash, strategy, max_steps, config)
|
|
337
|
+
|
|
338
|
+
elif operation == "normalize-applicative":
|
|
339
|
+
return self._normalize(term_hash, "applicative", max_steps, config)
|
|
340
|
+
|
|
341
|
+
elif operation == "normalize-lazy":
|
|
342
|
+
return self._normalize(term_hash, "lazy", max_steps, config)
|
|
343
|
+
|
|
344
|
+
elif operation == "beta-reduce":
|
|
345
|
+
return self._beta_reduce(term_hash)
|
|
346
|
+
|
|
347
|
+
elif operation == "alpha-equiv":
|
|
348
|
+
other_hash = context.get("other_hash")
|
|
349
|
+
if not other_hash:
|
|
350
|
+
return LambdaRuntimeResult(
|
|
351
|
+
success=False,
|
|
352
|
+
error="alpha-equiv requires 'other_hash' in context",
|
|
353
|
+
)
|
|
354
|
+
return self._alpha_equiv(term_hash, other_hash)
|
|
355
|
+
|
|
356
|
+
elif operation == "alpha-normalize":
|
|
357
|
+
return self._alpha_normalize(term_hash)
|
|
358
|
+
|
|
359
|
+
elif operation == "eta-reduce":
|
|
360
|
+
return self._eta_reduce(term_hash)
|
|
361
|
+
|
|
362
|
+
elif operation == "eta-expand":
|
|
363
|
+
return self._eta_expand(term_hash)
|
|
364
|
+
|
|
365
|
+
elif operation == "free-vars":
|
|
366
|
+
return self._free_vars(term_hash)
|
|
367
|
+
|
|
368
|
+
elif operation == "is-closed":
|
|
369
|
+
return self._is_closed(term_hash)
|
|
370
|
+
|
|
371
|
+
elif operation == "pretty-print":
|
|
372
|
+
return self._pretty_print(term_hash)
|
|
373
|
+
|
|
374
|
+
elif operation == "check-readiness":
|
|
375
|
+
return self._check_readiness(context)
|
|
376
|
+
|
|
377
|
+
elif operation.startswith("num-"):
|
|
378
|
+
return self._numeric_op(operation, context)
|
|
379
|
+
|
|
380
|
+
elif operation == "http-request":
|
|
381
|
+
return self._http_request(context)
|
|
382
|
+
|
|
383
|
+
elif operation == "church-to-int":
|
|
384
|
+
return self._church_to_int(term_hash)
|
|
385
|
+
|
|
386
|
+
else:
|
|
387
|
+
return LambdaRuntimeResult(
|
|
388
|
+
success=False, error=f"Unknown operation: {operation}"
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
except ParseError as e:
|
|
392
|
+
return LambdaRuntimeResult(success=False, error=f"Parse error: {str(e)}")
|
|
393
|
+
except (ValueError, TypeError, RuntimeError, RecursionError) as e:
|
|
394
|
+
return LambdaRuntimeResult(success=False, error=str(e))
|
|
395
|
+
|
|
396
|
+
def _parse(self, context: dict[str, Any]) -> LambdaRuntimeResult:
|
|
397
|
+
"""Parse a Lambda expression"""
|
|
398
|
+
expr = context.get("expression", "")
|
|
399
|
+
if not expr:
|
|
400
|
+
return LambdaRuntimeResult(success=False, error="No expression to parse")
|
|
401
|
+
|
|
402
|
+
term_hash = parse_lambda_expression(self.collection, expr)
|
|
403
|
+
pretty = pretty_print_deep(self.collection, term_hash)
|
|
404
|
+
|
|
405
|
+
return LambdaRuntimeResult(
|
|
406
|
+
success=True, term_hash=term_hash, pretty_print=pretty
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
def _normalize(
|
|
410
|
+
self, term_hash: str, strategy: str, max_steps: int, config: dict[str, Any]
|
|
411
|
+
) -> LambdaRuntimeResult:
|
|
412
|
+
"""Normalize a term with optional IO effects"""
|
|
413
|
+
# Create IO effects handler from config
|
|
414
|
+
io_handler = create_io_handler(config)
|
|
415
|
+
|
|
416
|
+
# IO callback for step events
|
|
417
|
+
def on_step(step: int, hash: str) -> None:
|
|
418
|
+
if io_handler.is_enabled():
|
|
419
|
+
pretty = pretty_print_deep(self.collection, hash)
|
|
420
|
+
io_handler.emit_step(step, hash, pretty)
|
|
421
|
+
|
|
422
|
+
result = normalize(
|
|
423
|
+
self.collection,
|
|
424
|
+
term_hash,
|
|
425
|
+
strategy,
|
|
426
|
+
max_steps,
|
|
427
|
+
on_step=on_step if io_handler.is_enabled() else None,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
if result is None:
|
|
431
|
+
if io_handler.is_enabled():
|
|
432
|
+
io_handler.emit_error("Normalization failed or diverged", 0)
|
|
433
|
+
return LambdaRuntimeResult(
|
|
434
|
+
success=False, error="Normalization failed or diverged"
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
pretty = pretty_print_deep(self.collection, result.normal_form)
|
|
438
|
+
|
|
439
|
+
# IO Effect: emit completion
|
|
440
|
+
if io_handler.is_enabled():
|
|
441
|
+
io_handler.emit_complete(
|
|
442
|
+
result.normal_form, pretty, result.steps, result.reduction_path
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
return LambdaRuntimeResult(
|
|
446
|
+
success=True,
|
|
447
|
+
term_hash=result.normal_form,
|
|
448
|
+
pretty_print=pretty,
|
|
449
|
+
steps=result.steps,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
def _beta_reduce(self, term_hash: str) -> LambdaRuntimeResult:
|
|
453
|
+
"""Single beta reduction step"""
|
|
454
|
+
result = beta_reduce(self.collection, term_hash)
|
|
455
|
+
|
|
456
|
+
if result is None:
|
|
457
|
+
return LambdaRuntimeResult(success=False, error="Not a redex")
|
|
458
|
+
|
|
459
|
+
pretty = pretty_print_deep(self.collection, result)
|
|
460
|
+
|
|
461
|
+
return LambdaRuntimeResult(success=True, term_hash=result, pretty_print=pretty)
|
|
462
|
+
|
|
463
|
+
def _alpha_equiv(self, hash1: str, hash2: str) -> LambdaRuntimeResult:
|
|
464
|
+
"""Check alpha equivalence"""
|
|
465
|
+
equiv = alpha_equivalent(self.collection, hash1, hash2)
|
|
466
|
+
|
|
467
|
+
if equiv is None:
|
|
468
|
+
return LambdaRuntimeResult(success=False, error="Terms not found")
|
|
469
|
+
|
|
470
|
+
return LambdaRuntimeResult(success=True, alpha_equivalent=equiv)
|
|
471
|
+
|
|
472
|
+
def _alpha_normalize(self, term_hash: str) -> LambdaRuntimeResult:
|
|
473
|
+
"""Normalize bound variable names"""
|
|
474
|
+
result = alpha_normalize(self.collection, term_hash)
|
|
475
|
+
|
|
476
|
+
if result is None:
|
|
477
|
+
return LambdaRuntimeResult(
|
|
478
|
+
success=False, error="Alpha normalization failed"
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
pretty = pretty_print_deep(self.collection, result)
|
|
482
|
+
|
|
483
|
+
return LambdaRuntimeResult(success=True, term_hash=result, pretty_print=pretty)
|
|
484
|
+
|
|
485
|
+
def _eta_reduce(self, term_hash: str) -> LambdaRuntimeResult:
|
|
486
|
+
"""Eta reduce a term"""
|
|
487
|
+
result = eta_reduce(self.collection, term_hash)
|
|
488
|
+
|
|
489
|
+
if result is None:
|
|
490
|
+
return LambdaRuntimeResult(success=False, error="Eta reduction failed")
|
|
491
|
+
|
|
492
|
+
pretty = pretty_print_deep(self.collection, result)
|
|
493
|
+
|
|
494
|
+
return LambdaRuntimeResult(success=True, term_hash=result, pretty_print=pretty)
|
|
495
|
+
|
|
496
|
+
def _eta_expand(self, term_hash: str) -> LambdaRuntimeResult:
|
|
497
|
+
"""Eta expand a term"""
|
|
498
|
+
result = eta_expand(self.collection, term_hash)
|
|
499
|
+
|
|
500
|
+
if result is None:
|
|
501
|
+
return LambdaRuntimeResult(success=False, error="Eta expansion failed")
|
|
502
|
+
|
|
503
|
+
pretty = pretty_print_deep(self.collection, result)
|
|
504
|
+
|
|
505
|
+
return LambdaRuntimeResult(success=True, term_hash=result, pretty_print=pretty)
|
|
506
|
+
|
|
507
|
+
def _free_vars(self, term_hash: str) -> LambdaRuntimeResult:
|
|
508
|
+
"""Get free variables"""
|
|
509
|
+
fv = free_variables(self.collection, term_hash)
|
|
510
|
+
|
|
511
|
+
if fv is None:
|
|
512
|
+
return LambdaRuntimeResult(success=False, error="Term not found")
|
|
513
|
+
|
|
514
|
+
return LambdaRuntimeResult(
|
|
515
|
+
success=True, term_hash=term_hash, free_variables=sorted(fv)
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
def _is_closed(self, term_hash: str) -> LambdaRuntimeResult:
|
|
519
|
+
"""Check if term is closed"""
|
|
520
|
+
closed = is_closed(self.collection, term_hash)
|
|
521
|
+
|
|
522
|
+
if closed is None:
|
|
523
|
+
return LambdaRuntimeResult(success=False, error="Term not found")
|
|
524
|
+
|
|
525
|
+
pretty = pretty_print_deep(self.collection, term_hash)
|
|
526
|
+
|
|
527
|
+
return LambdaRuntimeResult(
|
|
528
|
+
success=True, term_hash=term_hash, pretty_print=pretty, is_closed=closed
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
def _pretty_print(self, term_hash: str) -> LambdaRuntimeResult:
|
|
532
|
+
"""Pretty print a term"""
|
|
533
|
+
pretty = pretty_print_deep(self.collection, term_hash)
|
|
534
|
+
|
|
535
|
+
return LambdaRuntimeResult(
|
|
536
|
+
success=True, term_hash=term_hash, pretty_print=pretty
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
def _check_readiness(self, context: dict[str, Any]) -> LambdaRuntimeResult:
|
|
540
|
+
"""Check if a runtime or network is ready."""
|
|
541
|
+
from clm.layer1.runtimes import RuntimeFactory
|
|
542
|
+
|
|
543
|
+
runtime_name = context.get("runtime_name", "lambda")
|
|
544
|
+
|
|
545
|
+
# Get status for all runtimes
|
|
546
|
+
status_map = RuntimeFactory.get_detailed_status()
|
|
547
|
+
|
|
548
|
+
if runtime_name == "all":
|
|
549
|
+
available = all(s["available"] for s in status_map.values())
|
|
550
|
+
return LambdaRuntimeResult(
|
|
551
|
+
success=True,
|
|
552
|
+
pretty_print=f"All Runtimes Ready: {available}",
|
|
553
|
+
is_closed=available,
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
status = status_map.get(runtime_name.lower())
|
|
557
|
+
if not status:
|
|
558
|
+
return LambdaRuntimeResult(
|
|
559
|
+
success=False, error=f"Unknown runtime: {runtime_name}"
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
return LambdaRuntimeResult(
|
|
563
|
+
success=True,
|
|
564
|
+
pretty_print=f"Runtime {runtime_name} status: {status['available']}",
|
|
565
|
+
is_closed=status["available"],
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
def _numeric_op(
|
|
569
|
+
self, operation: str, context: dict[str, Any]
|
|
570
|
+
) -> LambdaRuntimeResult:
|
|
571
|
+
"""Perform a built-in numeric operation."""
|
|
572
|
+
a = context.get("a", 0)
|
|
573
|
+
b = context.get("b", 0)
|
|
574
|
+
|
|
575
|
+
try:
|
|
576
|
+
val_a = float(a)
|
|
577
|
+
val_b = float(b)
|
|
578
|
+
|
|
579
|
+
if operation == "num-add":
|
|
580
|
+
result = val_a + val_b
|
|
581
|
+
elif operation == "num-sub":
|
|
582
|
+
result = val_a - val_b
|
|
583
|
+
elif operation == "num-mul":
|
|
584
|
+
result = val_a * val_b
|
|
585
|
+
elif operation == "num-div":
|
|
586
|
+
if val_b == 0:
|
|
587
|
+
return LambdaRuntimeResult(success=False, error="Division by zero")
|
|
588
|
+
result = val_a / val_b
|
|
589
|
+
else:
|
|
590
|
+
return LambdaRuntimeResult(
|
|
591
|
+
success=False, error=f"Unknown numeric op: {operation}"
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
# Convert result back to string for pretty print
|
|
595
|
+
res_str = str(int(result) if result.is_integer() else result)
|
|
596
|
+
return LambdaRuntimeResult(success=True, pretty_print=res_str)
|
|
597
|
+
except (ValueError, TypeError):
|
|
598
|
+
return LambdaRuntimeResult(success=False, error="Invalid numeric inputs")
|
|
599
|
+
|
|
600
|
+
def _http_request(self, context: dict[str, Any]) -> LambdaRuntimeResult:
|
|
601
|
+
"""Perform a built-in network request."""
|
|
602
|
+
from clm.layer1.runtimes import RuntimeFactory
|
|
603
|
+
|
|
604
|
+
net_executor = RuntimeFactory.get_executor("network")
|
|
605
|
+
if not net_executor:
|
|
606
|
+
return LambdaRuntimeResult(
|
|
607
|
+
success=False, error="Network runtime not available"
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
# Forward request to NetworkRuntime
|
|
611
|
+
# We need a dummy target as execute expects it
|
|
612
|
+
from clm.layer0.model.card import MCard
|
|
613
|
+
|
|
614
|
+
dummy_target = MCard("dummy")
|
|
615
|
+
|
|
616
|
+
# Build config from context
|
|
617
|
+
config = {
|
|
618
|
+
"method": context.get("method", "GET"),
|
|
619
|
+
"url": context.get("url"),
|
|
620
|
+
"headers": context.get("headers", {}),
|
|
621
|
+
"body": context.get("body"),
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if not config["url"]:
|
|
625
|
+
return LambdaRuntimeResult(
|
|
626
|
+
success=False, error="No URL provided for http-request"
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
try:
|
|
630
|
+
res = net_executor.execute({}, dummy_target, config)
|
|
631
|
+
return LambdaRuntimeResult(success=True, pretty_print=str(res))
|
|
632
|
+
except (OSError, ConnectionError, RuntimeError, ValueError) as e:
|
|
633
|
+
return LambdaRuntimeResult(success=False, error=f"Network error: {str(e)}")
|
|
634
|
+
|
|
635
|
+
def _church_to_int(self, term_hash: str) -> LambdaRuntimeResult:
|
|
636
|
+
"""
|
|
637
|
+
Decode a Church numeral to a regular integer.
|
|
638
|
+
|
|
639
|
+
Church numeral n = λf.λx.f^n(x) where f is applied n times.
|
|
640
|
+
|
|
641
|
+
Algorithm:
|
|
642
|
+
1. Normalize the term first
|
|
643
|
+
2. Expect form: Abs(f, Abs(x, body))
|
|
644
|
+
3. Count how many times 'f' appears in application position in body
|
|
645
|
+
"""
|
|
646
|
+
try:
|
|
647
|
+
# First normalize the term
|
|
648
|
+
result = normalize(self.collection, term_hash, "normal", 1000)
|
|
649
|
+
|
|
650
|
+
if result is None:
|
|
651
|
+
return LambdaRuntimeResult(
|
|
652
|
+
success=False, error="Failed to normalize term"
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
pretty = pretty_print_deep(self.collection, result.normal_form)
|
|
656
|
+
|
|
657
|
+
# Decode Church numeral structure: λf.λx.body
|
|
658
|
+
count = self._count_church_applications(result.normal_form)
|
|
659
|
+
|
|
660
|
+
return LambdaRuntimeResult(
|
|
661
|
+
success=True,
|
|
662
|
+
term_hash=result.normal_form,
|
|
663
|
+
pretty_print=f"{count} (Church: {pretty})",
|
|
664
|
+
)
|
|
665
|
+
except (ValueError, TypeError, ArithmeticError, RecursionError) as e:
|
|
666
|
+
return LambdaRuntimeResult(
|
|
667
|
+
success=False, error=f"Church decode error: {str(e)}"
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
def _count_church_applications(self, term_hash: str) -> int:
|
|
671
|
+
"""
|
|
672
|
+
Count applications in a Church numeral body.
|
|
673
|
+
Church numeral n has the form: λf.λx.f(f(f(...f(x)...)))
|
|
674
|
+
where f appears n times.
|
|
675
|
+
"""
|
|
676
|
+
term = load_term(self.collection, term_hash)
|
|
677
|
+
if term is None:
|
|
678
|
+
return -1
|
|
679
|
+
|
|
680
|
+
# Expect: Abs(f, Abs(x, body))
|
|
681
|
+
if not isinstance(term, AbsTerm):
|
|
682
|
+
return -1
|
|
683
|
+
|
|
684
|
+
f_var = term.param
|
|
685
|
+
inner_term = load_term(self.collection, term.body)
|
|
686
|
+
|
|
687
|
+
if inner_term is None or not isinstance(inner_term, AbsTerm):
|
|
688
|
+
return -1
|
|
689
|
+
|
|
690
|
+
x_var = inner_term.param
|
|
691
|
+
|
|
692
|
+
# Count how many times we see App(f, ...) wrapping
|
|
693
|
+
count = 0
|
|
694
|
+
current_hash = inner_term.body
|
|
695
|
+
|
|
696
|
+
while True:
|
|
697
|
+
body = load_term(self.collection, current_hash)
|
|
698
|
+
if body is None:
|
|
699
|
+
break
|
|
700
|
+
|
|
701
|
+
if isinstance(body, AppTerm):
|
|
702
|
+
func = load_term(self.collection, body.func)
|
|
703
|
+
|
|
704
|
+
# Check if function is the 'f' variable
|
|
705
|
+
if func and isinstance(func, VarTerm) and func.name == f_var:
|
|
706
|
+
count += 1
|
|
707
|
+
current_hash = body.arg
|
|
708
|
+
else:
|
|
709
|
+
# Not a simple Church numeral structure
|
|
710
|
+
break
|
|
711
|
+
elif isinstance(body, VarTerm) and body.name == x_var:
|
|
712
|
+
# Reached the base case 'x'
|
|
713
|
+
return count
|
|
714
|
+
else:
|
|
715
|
+
break
|
|
716
|
+
|
|
717
|
+
return count
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
# Register with Layer 1 runtime factory (INV-383-01 / DEL-383-04)
|
|
721
|
+
try:
|
|
722
|
+
from clm.layer1.runtimes.factory import register_external_executor
|
|
723
|
+
|
|
724
|
+
register_external_executor("lambda", LambdaRuntime)
|
|
725
|
+
except ImportError:
|
|
726
|
+
pass
|