tengwar 0.3.1__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.
- tengwar/__init__.py +20 -0
- tengwar/__main__.py +8 -0
- tengwar/ast_nodes.py +351 -0
- tengwar/binary_ast.py +654 -0
- tengwar/errors.py +43 -0
- tengwar/interpreter.py +1845 -0
- tengwar/lexer.py +483 -0
- tengwar/mcp_server.py +496 -0
- tengwar/parser.py +603 -0
- tengwar/repl.py +152 -0
- tengwar/vm.py +425 -0
- tengwar-0.3.1.dist-info/METADATA +202 -0
- tengwar-0.3.1.dist-info/RECORD +17 -0
- tengwar-0.3.1.dist-info/WHEEL +5 -0
- tengwar-0.3.1.dist-info/entry_points.txt +2 -0
- tengwar-0.3.1.dist-info/licenses/LICENSE +21 -0
- tengwar-0.3.1.dist-info/top_level.txt +1 -0
tengwar/mcp_server.py
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TENGWAR MCP Server
|
|
4
|
+
|
|
5
|
+
Model Context Protocol server that exposes Tengwar as a tool
|
|
6
|
+
to any MCP-compatible AI agent (Claude, etc).
|
|
7
|
+
|
|
8
|
+
Tools exposed:
|
|
9
|
+
- tengwar_eval: Execute Tengwar source code
|
|
10
|
+
- tengwar_eval_binary: Execute base64-encoded binary AST
|
|
11
|
+
- tengwar_sandbox: Execute in sandboxed mode (safe for untrusted code)
|
|
12
|
+
|
|
13
|
+
Resources exposed:
|
|
14
|
+
- tengwar://builtins: List of all available builtins
|
|
15
|
+
- tengwar://syntax: Quick syntax reference
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
python -m tengwar.mcp_server # stdio mode
|
|
19
|
+
python -m tengwar.mcp_server --port 8080 # HTTP mode (if supported)
|
|
20
|
+
|
|
21
|
+
MCP config (claude_desktop_config.json):
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"tengwar": {
|
|
25
|
+
"command": "python",
|
|
26
|
+
"args": ["-m", "tengwar.mcp_server"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
import sys
|
|
33
|
+
import json
|
|
34
|
+
import traceback
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
# Import Tengwar
|
|
38
|
+
from .interpreter import Interpreter, TengwarValue
|
|
39
|
+
from .binary_ast import run_b64, ASTBuilder
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# === MCP Protocol Implementation (stdio JSON-RPC) ===
|
|
43
|
+
|
|
44
|
+
class TengwarMCPServer:
|
|
45
|
+
"""MCP server exposing Tengwar as tools for AI agents."""
|
|
46
|
+
|
|
47
|
+
def __init__(self):
|
|
48
|
+
self.interpreter = Interpreter()
|
|
49
|
+
self.sandbox_interpreter = Interpreter(sandbox=True)
|
|
50
|
+
self.sandbox_interpreter.max_steps = 1_000_000
|
|
51
|
+
|
|
52
|
+
def handle_request(self, request: dict) -> dict:
|
|
53
|
+
"""Handle a JSON-RPC request."""
|
|
54
|
+
method = request.get("method", "")
|
|
55
|
+
req_id = request.get("id")
|
|
56
|
+
params = request.get("params", {})
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
if method == "initialize":
|
|
60
|
+
return self._initialize(req_id, params)
|
|
61
|
+
elif method == "tools/list":
|
|
62
|
+
return self._list_tools(req_id)
|
|
63
|
+
elif method == "tools/call":
|
|
64
|
+
return self._call_tool(req_id, params)
|
|
65
|
+
elif method == "resources/list":
|
|
66
|
+
return self._list_resources(req_id)
|
|
67
|
+
elif method == "resources/read":
|
|
68
|
+
return self._read_resource(req_id, params)
|
|
69
|
+
elif method == "ping":
|
|
70
|
+
return self._result(req_id, {})
|
|
71
|
+
elif method == "notifications/initialized":
|
|
72
|
+
return None # No response needed
|
|
73
|
+
else:
|
|
74
|
+
return self._error(req_id, -32601, f"Method not found: {method}")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
return self._error(req_id, -32603, str(e))
|
|
77
|
+
|
|
78
|
+
def _result(self, req_id, result):
|
|
79
|
+
return {"jsonrpc": "2.0", "id": req_id, "result": result}
|
|
80
|
+
|
|
81
|
+
def _error(self, req_id, code, message):
|
|
82
|
+
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": code, "message": message}}
|
|
83
|
+
|
|
84
|
+
def _initialize(self, req_id, params):
|
|
85
|
+
return self._result(req_id, {
|
|
86
|
+
"protocolVersion": "2024-11-05",
|
|
87
|
+
"capabilities": {
|
|
88
|
+
"tools": {},
|
|
89
|
+
"resources": {}
|
|
90
|
+
},
|
|
91
|
+
"serverInfo": {
|
|
92
|
+
"name": "tengwar",
|
|
93
|
+
"version": "0.3.0"
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
def _list_tools(self, req_id):
|
|
98
|
+
return self._result(req_id, {
|
|
99
|
+
"tools": [
|
|
100
|
+
{
|
|
101
|
+
"name": "tengwar_eval",
|
|
102
|
+
"description": (
|
|
103
|
+
"Execute Tengwar code. Tengwar is a functional language optimized for AI — "
|
|
104
|
+
"23% fewer tokens than Python for data operations, with 80+ builtins including "
|
|
105
|
+
"map, filter, reduce, scan, chunks, partition, unique, flat-map, pattern matching, "
|
|
106
|
+
"dictionaries, JSON, and Python interop. "
|
|
107
|
+
"Example: (sum (for x (range 1 11) when (even? x) (* x 2))) → 60"
|
|
108
|
+
),
|
|
109
|
+
"inputSchema": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"properties": {
|
|
112
|
+
"code": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"description": "Tengwar source code to execute"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"required": ["code"]
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"name": "tengwar_sandbox",
|
|
122
|
+
"description": (
|
|
123
|
+
"Execute Tengwar code in a secure sandbox. No file I/O, no network, "
|
|
124
|
+
"no Python imports, with a 1M step limit. Safe for untrusted code. "
|
|
125
|
+
"Pure computation only."
|
|
126
|
+
),
|
|
127
|
+
"inputSchema": {
|
|
128
|
+
"type": "object",
|
|
129
|
+
"properties": {
|
|
130
|
+
"code": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"description": "Tengwar source code to execute (sandboxed)"
|
|
133
|
+
},
|
|
134
|
+
"max_steps": {
|
|
135
|
+
"type": "integer",
|
|
136
|
+
"description": "Maximum execution steps (default: 1000000)",
|
|
137
|
+
"default": 1000000
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"required": ["code"]
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"name": "tengwar_eval_binary",
|
|
145
|
+
"description": (
|
|
146
|
+
"Execute a base64-encoded Tengwar binary AST. "
|
|
147
|
+
"Zero syntax errors possible — the binary format maps directly to AST nodes. "
|
|
148
|
+
"Use tengwar_eval to generate base64 with: (encode-b64 \"(+ 1 2)\")"
|
|
149
|
+
),
|
|
150
|
+
"inputSchema": {
|
|
151
|
+
"type": "object",
|
|
152
|
+
"properties": {
|
|
153
|
+
"b64": {
|
|
154
|
+
"type": "string",
|
|
155
|
+
"description": "Base64-encoded binary AST"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"required": ["b64"]
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "tengwar_multi",
|
|
163
|
+
"description": (
|
|
164
|
+
"Execute multiple Tengwar expressions sharing the same environment. "
|
|
165
|
+
"Define functions in earlier expressions, use them in later ones. "
|
|
166
|
+
"Returns the result of the last expression."
|
|
167
|
+
),
|
|
168
|
+
"inputSchema": {
|
|
169
|
+
"type": "object",
|
|
170
|
+
"properties": {
|
|
171
|
+
"expressions": {
|
|
172
|
+
"type": "array",
|
|
173
|
+
"items": {"type": "string"},
|
|
174
|
+
"description": "Array of Tengwar expressions to execute in sequence"
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"required": ["expressions"]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
def _call_tool(self, req_id, params):
|
|
184
|
+
tool_name = params.get("name", "")
|
|
185
|
+
args = params.get("arguments", {})
|
|
186
|
+
|
|
187
|
+
if tool_name == "tengwar_eval":
|
|
188
|
+
return self._eval(req_id, args.get("code", ""), sandbox=False)
|
|
189
|
+
elif tool_name == "tengwar_sandbox":
|
|
190
|
+
max_steps = args.get("max_steps", 1_000_000)
|
|
191
|
+
self.sandbox_interpreter.max_steps = max_steps
|
|
192
|
+
self.sandbox_interpreter.step_count = 0
|
|
193
|
+
return self._eval(req_id, args.get("code", ""), sandbox=True)
|
|
194
|
+
elif tool_name == "tengwar_eval_binary":
|
|
195
|
+
return self._eval_binary(req_id, args.get("b64", ""))
|
|
196
|
+
elif tool_name == "tengwar_multi":
|
|
197
|
+
return self._eval_multi(req_id, args.get("expressions", []))
|
|
198
|
+
else:
|
|
199
|
+
return self._error(req_id, -32602, f"Unknown tool: {tool_name}")
|
|
200
|
+
|
|
201
|
+
def _eval(self, req_id, code: str, sandbox: bool = False):
|
|
202
|
+
try:
|
|
203
|
+
interp = self.sandbox_interpreter if sandbox else self.interpreter
|
|
204
|
+
result = interp.run_source(code)
|
|
205
|
+
return self._result(req_id, {
|
|
206
|
+
"content": [{
|
|
207
|
+
"type": "text",
|
|
208
|
+
"text": repr(result)
|
|
209
|
+
}]
|
|
210
|
+
})
|
|
211
|
+
except Exception as e:
|
|
212
|
+
return self._result(req_id, {
|
|
213
|
+
"content": [{
|
|
214
|
+
"type": "text",
|
|
215
|
+
"text": f"Error: {e}"
|
|
216
|
+
}],
|
|
217
|
+
"isError": True
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
def _eval_binary(self, req_id, b64: str):
|
|
221
|
+
try:
|
|
222
|
+
result = run_b64(b64, self.interpreter)
|
|
223
|
+
return self._result(req_id, {
|
|
224
|
+
"content": [{
|
|
225
|
+
"type": "text",
|
|
226
|
+
"text": repr(result)
|
|
227
|
+
}]
|
|
228
|
+
})
|
|
229
|
+
except Exception as e:
|
|
230
|
+
return self._result(req_id, {
|
|
231
|
+
"content": [{
|
|
232
|
+
"type": "text",
|
|
233
|
+
"text": f"Error: {e}"
|
|
234
|
+
}],
|
|
235
|
+
"isError": True
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
def _eval_multi(self, req_id, expressions: list):
|
|
239
|
+
try:
|
|
240
|
+
result = None
|
|
241
|
+
for expr in expressions:
|
|
242
|
+
result = self.interpreter.run_source(expr)
|
|
243
|
+
return self._result(req_id, {
|
|
244
|
+
"content": [{
|
|
245
|
+
"type": "text",
|
|
246
|
+
"text": repr(result)
|
|
247
|
+
}]
|
|
248
|
+
})
|
|
249
|
+
except Exception as e:
|
|
250
|
+
return self._result(req_id, {
|
|
251
|
+
"content": [{
|
|
252
|
+
"type": "text",
|
|
253
|
+
"text": f"Error: {e}"
|
|
254
|
+
}],
|
|
255
|
+
"isError": True
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
def _list_resources(self, req_id):
|
|
259
|
+
return self._result(req_id, {
|
|
260
|
+
"resources": [
|
|
261
|
+
{
|
|
262
|
+
"uri": "tengwar://builtins",
|
|
263
|
+
"name": "Tengwar Builtins",
|
|
264
|
+
"description": "Complete list of all 80+ built-in functions",
|
|
265
|
+
"mimeType": "text/plain"
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"uri": "tengwar://syntax",
|
|
269
|
+
"name": "Tengwar Syntax Reference",
|
|
270
|
+
"description": "Quick syntax guide for writing Tengwar code",
|
|
271
|
+
"mimeType": "text/plain"
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
def _read_resource(self, req_id, params):
|
|
277
|
+
uri = params.get("uri", "")
|
|
278
|
+
|
|
279
|
+
if uri == "tengwar://builtins":
|
|
280
|
+
return self._result(req_id, {
|
|
281
|
+
"contents": [{
|
|
282
|
+
"uri": uri,
|
|
283
|
+
"mimeType": "text/plain",
|
|
284
|
+
"text": BUILTINS_REFERENCE
|
|
285
|
+
}]
|
|
286
|
+
})
|
|
287
|
+
elif uri == "tengwar://syntax":
|
|
288
|
+
return self._result(req_id, {
|
|
289
|
+
"contents": [{
|
|
290
|
+
"uri": uri,
|
|
291
|
+
"mimeType": "text/plain",
|
|
292
|
+
"text": SYNTAX_REFERENCE
|
|
293
|
+
}]
|
|
294
|
+
})
|
|
295
|
+
else:
|
|
296
|
+
return self._error(req_id, -32602, f"Unknown resource: {uri}")
|
|
297
|
+
|
|
298
|
+
def run_stdio(self):
|
|
299
|
+
"""Run as stdio MCP server."""
|
|
300
|
+
for line in sys.stdin:
|
|
301
|
+
line = line.strip()
|
|
302
|
+
if not line:
|
|
303
|
+
continue
|
|
304
|
+
try:
|
|
305
|
+
request = json.loads(line)
|
|
306
|
+
response = self.handle_request(request)
|
|
307
|
+
if response is not None:
|
|
308
|
+
sys.stdout.write(json.dumps(response) + "\n")
|
|
309
|
+
sys.stdout.flush()
|
|
310
|
+
except json.JSONDecodeError:
|
|
311
|
+
error = {
|
|
312
|
+
"jsonrpc": "2.0",
|
|
313
|
+
"id": None,
|
|
314
|
+
"error": {"code": -32700, "message": "Parse error"}
|
|
315
|
+
}
|
|
316
|
+
sys.stdout.write(json.dumps(error) + "\n")
|
|
317
|
+
sys.stdout.flush()
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
# === Reference Content ===
|
|
321
|
+
|
|
322
|
+
BUILTINS_REFERENCE = """TENGWAR BUILTINS (v0.3)
|
|
323
|
+
|
|
324
|
+
COLLECTIONS:
|
|
325
|
+
(map f vec) Apply f to each element
|
|
326
|
+
(filter f vec) Keep elements where f returns true
|
|
327
|
+
(reduce f init vec) Fold left with accumulator
|
|
328
|
+
(flat-map f vec) Map then flatten one level
|
|
329
|
+
(find f vec) First element where f is true
|
|
330
|
+
(index-of f vec) Index of first match
|
|
331
|
+
(take n vec) First n elements
|
|
332
|
+
(drop n vec) Remove first n elements
|
|
333
|
+
(take-while f vec) Take while f is true
|
|
334
|
+
(drop-while f vec) Drop while f is true
|
|
335
|
+
(zip-with f v1 v2) Combine two vectors element-wise
|
|
336
|
+
(group-by f vec) Group by key function → dict
|
|
337
|
+
(unique vec) Remove duplicates
|
|
338
|
+
(frequencies vec) Count occurrences → dict
|
|
339
|
+
(partition f vec) Split into (matches, non-matches)
|
|
340
|
+
(scan f init vec) Running accumulation
|
|
341
|
+
(chunks n vec) Split into groups of n
|
|
342
|
+
(interleave v1 v2) Alternate elements
|
|
343
|
+
(repeat n val) Vector of n copies
|
|
344
|
+
(iterate f init n) Generate by repeated application
|
|
345
|
+
(sort vec) Sort ascending
|
|
346
|
+
(sort-by f vec) Sort by key function
|
|
347
|
+
(reverse vec) Reverse order
|
|
348
|
+
(concat v1 v2) Concatenate vectors
|
|
349
|
+
(len vec) Length
|
|
350
|
+
(head vec) First element
|
|
351
|
+
(tail vec) All but first
|
|
352
|
+
(last vec) Last element
|
|
353
|
+
(nth n vec) Element at index n
|
|
354
|
+
(range a b) Numbers from a to b-1
|
|
355
|
+
(sum vec) Sum all elements
|
|
356
|
+
(product vec) Multiply all elements
|
|
357
|
+
(any? f vec) True if any match
|
|
358
|
+
(all? f vec) True if all match
|
|
359
|
+
(count f vec) Count matches
|
|
360
|
+
(for-each f vec) Execute f for side effects
|
|
361
|
+
|
|
362
|
+
DICTIONARIES:
|
|
363
|
+
(dict k1 v1 k2 v2 ...) Create dictionary
|
|
364
|
+
(dict-get d key) Get value (optional default)
|
|
365
|
+
(dict-set d key val) Set key → new dict
|
|
366
|
+
(dict-del d key) Remove key → new dict
|
|
367
|
+
(dict-keys d) All keys as vector
|
|
368
|
+
(dict-vals d) All values as vector
|
|
369
|
+
(dict-pairs d) Key-value pairs as vector of tuples
|
|
370
|
+
(dict-has? d key) Key exists?
|
|
371
|
+
(dict-merge d1 d2) Merge two dicts
|
|
372
|
+
(dict-size d) Number of entries
|
|
373
|
+
|
|
374
|
+
STRINGS:
|
|
375
|
+
(fmt template args...) String interpolation: (fmt "Hi {}!" name)
|
|
376
|
+
(split str sep) Split string
|
|
377
|
+
(join sep vec) Join with separator
|
|
378
|
+
(upper str) Uppercase
|
|
379
|
+
(lower str) Lowercase
|
|
380
|
+
(trim str) Strip whitespace
|
|
381
|
+
(replace str old new) Replace substring
|
|
382
|
+
(starts-with? str pre) Prefix check
|
|
383
|
+
(ends-with? str suf) Suffix check
|
|
384
|
+
(chars str) String → vector of chars
|
|
385
|
+
(char-at str n) Character at index
|
|
386
|
+
(pad-left str n ch) Left pad to width
|
|
387
|
+
(pad-right str n ch) Right pad to width
|
|
388
|
+
|
|
389
|
+
MATH:
|
|
390
|
+
(abs x) Absolute value
|
|
391
|
+
(min a b ...) or (min v) Minimum
|
|
392
|
+
(max a b ...) or (max v) Maximum
|
|
393
|
+
(clamp x lo hi) Clamp to range
|
|
394
|
+
(floor x) Floor
|
|
395
|
+
(ceil x) Ceiling
|
|
396
|
+
(round x) Round
|
|
397
|
+
pi e inf nan Constants
|
|
398
|
+
(rand) Random float [0, 1)
|
|
399
|
+
(rand-int lo hi) Random int [lo, hi)
|
|
400
|
+
|
|
401
|
+
TYPE PREDICATES:
|
|
402
|
+
(int? x) (float? x) (num? x) (str? x) (bool? x)
|
|
403
|
+
(vec? x) (tuple? x) (dict? x) (fn? x) (nil? x) (py? x)
|
|
404
|
+
(type x) Type name as string
|
|
405
|
+
(zero? x) (pos? x) (neg? x) (even? x) (odd? x)
|
|
406
|
+
(divides? n d) n divisible by d?
|
|
407
|
+
(empty? x) Empty collection or string?
|
|
408
|
+
|
|
409
|
+
JSON:
|
|
410
|
+
(json-parse str) Parse JSON → Tengwar value
|
|
411
|
+
(json-encode val) Encode to JSON string
|
|
412
|
+
|
|
413
|
+
FILE I/O:
|
|
414
|
+
(read-file path) Read file contents
|
|
415
|
+
(write-file path str) Write string to file
|
|
416
|
+
(append-file path str) Append to file
|
|
417
|
+
(file-exists? path) Check if file exists
|
|
418
|
+
|
|
419
|
+
HTTP:
|
|
420
|
+
(http-get url) GET request → dict with status, headers, body
|
|
421
|
+
(http-post url body) POST request
|
|
422
|
+
|
|
423
|
+
PYTHON INTEROP:
|
|
424
|
+
(py-import "module") Import Python module
|
|
425
|
+
(py-call obj "method" args...) Call method
|
|
426
|
+
(py-attr obj "attr") Get attribute
|
|
427
|
+
(py-eval "expression") Evaluate Python expression
|
|
428
|
+
obj.attr Dot access on Python objects
|
|
429
|
+
|
|
430
|
+
SYSTEM:
|
|
431
|
+
(time-ms) Current time in milliseconds
|
|
432
|
+
(sleep ms) Sleep for milliseconds
|
|
433
|
+
(uuid) Generate UUID
|
|
434
|
+
(env-get name) Environment variable
|
|
435
|
+
|
|
436
|
+
COMPREHENSION:
|
|
437
|
+
(for x coll body) → (map (fn x body) coll)
|
|
438
|
+
(for x coll when pred body) → filter + map
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
SYNTAX_REFERENCE = """TENGWAR SYNTAX REFERENCE (v0.3)
|
|
442
|
+
|
|
443
|
+
LITERALS:
|
|
444
|
+
42 3.14 "hello" true false nil
|
|
445
|
+
|
|
446
|
+
COLLECTIONS:
|
|
447
|
+
⟦1 2 3⟧ or [1 2 3] Vector
|
|
448
|
+
⟨1 2 3⟩ Tuple
|
|
449
|
+
(dict "a" 1 "b" 2) Dictionary
|
|
450
|
+
|
|
451
|
+
FUNCTIONS:
|
|
452
|
+
(λ x y (+ x y)) Lambda (or: fn x y (+ x y))
|
|
453
|
+
{+ _ 1} Short lambda (_ is parameter)
|
|
454
|
+
(defn name args... body) Define named function
|
|
455
|
+
|
|
456
|
+
CONTROL FLOW:
|
|
457
|
+
(? cond then else) Conditional (or: if)
|
|
458
|
+
(cond (t1 v1) (t2 v2) (_ default)) Multi-branch
|
|
459
|
+
(match expr (pat body) ...) Pattern matching
|
|
460
|
+
|
|
461
|
+
BINDINGS:
|
|
462
|
+
(≡ name value) Define (or: def)
|
|
463
|
+
(let x 1 y 2 (+ x y)) Local bindings
|
|
464
|
+
(>> e1 → name e2) Sequence + bind
|
|
465
|
+
|
|
466
|
+
ITERATION:
|
|
467
|
+
(for x coll body) Comprehension (map)
|
|
468
|
+
(for x coll when p body) Comprehension (filter + map)
|
|
469
|
+
(pipe val f1 f2 f3) Pipeline
|
|
470
|
+
|
|
471
|
+
RECURSION:
|
|
472
|
+
(↺ name (λ args body)) Recursive definition (or: rec)
|
|
473
|
+
(defn name args body) Named function (auto-recursive)
|
|
474
|
+
|
|
475
|
+
ERROR HANDLING:
|
|
476
|
+
(throw expr) Throw error
|
|
477
|
+
(catch expr handler) Catch error
|
|
478
|
+
(try thunk handler) Try/catch returning (ok?, result)
|
|
479
|
+
|
|
480
|
+
PARALLEL:
|
|
481
|
+
(‖ e1 e2 e3) Parallel execution (or: par)
|
|
482
|
+
|
|
483
|
+
PYTHON:
|
|
484
|
+
(py-import "module") Import
|
|
485
|
+
(py-call obj "method" args...)
|
|
486
|
+
obj.attr Dot access
|
|
487
|
+
"""
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def main():
|
|
491
|
+
server = TengwarMCPServer()
|
|
492
|
+
server.run_stdio()
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
if __name__ == "__main__":
|
|
496
|
+
main()
|