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/repl.py ADDED
@@ -0,0 +1,152 @@
1
+ """
2
+ TENGWAR REPL
3
+
4
+ Interactive Read-Eval-Print Loop for TENGWAR.
5
+ Designed for AI interaction but functional for any user.
6
+ """
7
+ import sys
8
+ import os
9
+ from .lexer import tokenize
10
+ from .parser import parse
11
+ from .interpreter import Interpreter
12
+ from .errors import TengwarError
13
+
14
+
15
+ BANNER = """
16
+ ╔═══════════════════════════════════════════════════╗
17
+ ║ ▄▀▄ ▀▄▀ █ █▀█ █▄ █ ║
18
+ ║ █▀█ █ █ █ █▄█ █ ▀█ ║
19
+ ║ ║
20
+ ║ The AI-Native Programming Language v0.1.0 ║
21
+ ║ tengwarlang.dev ║
22
+ ║ ║
23
+ ║ Type expressions to evaluate. :q to quit. ║
24
+ ║ :h for help. ║
25
+ ╚═══════════════════════════════════════════════════╝
26
+ """
27
+
28
+ HELP = """
29
+ TENGWAR REPL Commands:
30
+ :q, :quit Exit the REPL
31
+ :h, :help Show this help
32
+ :env Show current bindings
33
+ :trace Toggle execution trace
34
+ :reset Reset environment
35
+ :load <file> Load and execute a .tw file
36
+
37
+ Syntax Quick Reference:
38
+ (λ x y (+ x y)) Lambda / function
39
+ expr → name Bind result to name
40
+ (? cond then else) Conditional
41
+ (~ val pat1 res1 ...) Pattern match
42
+ (>> e1 e2 ...) Sequence
43
+ (∥ e1 e2 ...) Parallel
44
+ (:= name value) Define
45
+ (↺ f (λ n ...)) Recursion
46
+ ⟨a b c⟩ Tuple
47
+ ⟦a b c⟧ Vector
48
+ (⊢ assertion) Proof check
49
+
50
+ ASCII alternatives:
51
+ \\ = λ -> = → || = ∥ <| |> = ⟨⟩ [| |] = ⟦⟧
52
+ := = := |- = ⊢ +> = ⊕
53
+ """
54
+
55
+
56
+ def repl(interpreter: Interpreter = None):
57
+ """Start the TENGWAR REPL"""
58
+ if interpreter is None:
59
+ interpreter = Interpreter()
60
+
61
+ print(BANNER)
62
+
63
+ buffer = ""
64
+ prompt = "λ> "
65
+
66
+ while True:
67
+ try:
68
+ line = input(prompt)
69
+ except (EOFError, KeyboardInterrupt):
70
+ print("\n∅")
71
+ break
72
+
73
+ # Commands
74
+ stripped = line.strip()
75
+ if stripped in (':q', ':quit'):
76
+ print("∅")
77
+ break
78
+ if stripped in (':h', ':help'):
79
+ print(HELP)
80
+ continue
81
+ if stripped == ':env':
82
+ for name, val in sorted(interpreter.global_env.bindings.items()):
83
+ print(f" {name} = {repr(val)}")
84
+ print(f"\n Content store: {len(interpreter.global_env.content_store)} entries")
85
+ continue
86
+ if stripped == ':trace':
87
+ interpreter.trace = not interpreter.trace
88
+ print(f" Trace: {'on' if interpreter.trace else 'off'}")
89
+ continue
90
+ if stripped == ':reset':
91
+ interpreter = Interpreter()
92
+ print(" Environment reset.")
93
+ continue
94
+ if stripped.startswith(':load '):
95
+ path = stripped[6:].strip()
96
+ try:
97
+ with open(path, 'r', encoding='utf-8') as f:
98
+ source = f.read()
99
+ result = interpreter.run_source(source)
100
+ print(f" → {repr(result)}")
101
+ except FileNotFoundError:
102
+ print(f" Error: File not found: {path}")
103
+ except TengwarError as e:
104
+ print(f" Error: {e}")
105
+ continue
106
+
107
+ if not stripped:
108
+ continue
109
+
110
+ # Accumulate multiline input (count parens)
111
+ buffer += line + "\n"
112
+ open_parens = buffer.count('(') - buffer.count(')')
113
+ open_parens += buffer.count('⟨') - buffer.count('⟩')
114
+ open_parens += buffer.count('⟦') - buffer.count('⟧')
115
+
116
+ if open_parens > 0:
117
+ prompt = ".. "
118
+ continue
119
+
120
+ # Evaluate
121
+ try:
122
+ source = buffer.strip()
123
+ buffer = ""
124
+ prompt = "λ> "
125
+
126
+ result = interpreter.run_source(source)
127
+ if result is not None:
128
+ print(f" → {repr(result)}")
129
+ except TengwarError as e:
130
+ print(f" Error: {e}")
131
+ except Exception as e:
132
+ print(f" Internal error: {e}")
133
+
134
+
135
+ def run_file(path: str, trace: bool = False):
136
+ """Execute an TENGWAR source file"""
137
+ interpreter = Interpreter()
138
+ interpreter.trace = trace
139
+
140
+ with open(path, 'r', encoding='utf-8') as f:
141
+ source = f.read()
142
+
143
+ try:
144
+ result = interpreter.run_source(source)
145
+ return result
146
+ except TengwarError as e:
147
+ print(f"Error: {e}", file=sys.stderr)
148
+ sys.exit(1)
149
+
150
+
151
+ if __name__ == '__main__':
152
+ repl()
tengwar/vm.py ADDED
@@ -0,0 +1,425 @@
1
+ """
2
+ TENGWAR Bytecode VM
3
+
4
+ Compiles AST to stack-based bytecode and executes it.
5
+ Gives 10-100x speedup over tree-walking for tight loops.
6
+
7
+ Architecture:
8
+ - Stack-based VM (like Python, JVM, Lua)
9
+ - Compile AST → bytecode (list of instructions)
10
+ - Execute bytecode in a tight loop
11
+ - Falls back to tree-walker for complex forms (modules, parallel, etc.)
12
+ """
13
+
14
+ from typing import Any, List, Optional, Dict
15
+ from .ast_nodes import *
16
+ from .interpreter import (
17
+ Interpreter, Environment, TengwarValue, TengwarInt, TengwarFloat,
18
+ TengwarStr, TengwarBool, TengwarUnit, TengwarVector, TengwarTuple,
19
+ TengwarClosure, TengwarBuiltin
20
+ )
21
+
22
+
23
+ # === OPCODES ===
24
+
25
+ class BC:
26
+ """Bytecode opcodes"""
27
+ CONST = 0 # push constant (arg: index into constants pool)
28
+ LOAD = 1 # load variable (arg: name string)
29
+ STORE = 2 # store variable (arg: name string)
30
+ POP = 3 # discard top of stack
31
+ ADD = 4 # pop 2, push sum
32
+ SUB = 5
33
+ MUL = 6
34
+ DIV = 7
35
+ MOD = 8
36
+ EQ = 9
37
+ NEQ = 10
38
+ LT = 11
39
+ GT = 12
40
+ LTE = 13
41
+ GTE = 14
42
+ AND = 15
43
+ OR = 16
44
+ NOT = 17
45
+ JUMP = 18 # unconditional jump (arg: target)
46
+ JUMP_IF_FALSE = 19 # conditional jump (arg: target)
47
+ CALL = 20 # call function (arg: num_args)
48
+ RETURN = 21 # return top of stack
49
+ MAKE_VEC = 22 # make vector (arg: num_elements)
50
+ MAKE_TUPLE = 23 # make tuple (arg: num_elements)
51
+ MAKE_CLOSURE = 24 # make closure (arg: index into code objects)
52
+ NOP = 25 # no operation (used for patching)
53
+ DUP = 26 # duplicate top of stack
54
+ NEG = 27 # negate top of stack
55
+
56
+
57
+ class Instruction:
58
+ __slots__ = ('op', 'arg')
59
+ def __init__(self, op: int, arg: Any = None):
60
+ self.op = op
61
+ self.arg = arg
62
+ def __repr__(self):
63
+ names = {v: k for k, v in BC.__dict__.items() if isinstance(v, int)}
64
+ return f"{names.get(self.op, '?')}({self.arg})"
65
+
66
+
67
+ class CodeObject:
68
+ """Compiled function"""
69
+ __slots__ = ('params', 'code', 'constants', 'name')
70
+ def __init__(self, params: list, code: list, constants: list, name: str = "<fn>"):
71
+ self.params = params
72
+ self.code = code
73
+ self.constants = constants
74
+ self.name = name
75
+
76
+
77
+ class CompiledClosure:
78
+ """Runtime closure wrapping a CodeObject"""
79
+ __slots__ = ('code_obj', 'env')
80
+ def __init__(self, code_obj: CodeObject, env: Environment):
81
+ self.code_obj = code_obj
82
+ self.env = env
83
+
84
+
85
+ # === COMPILER ===
86
+
87
+ class Compiler:
88
+ """Compile AST to bytecode"""
89
+
90
+ def __init__(self):
91
+ self.code: List[Instruction] = []
92
+ self.constants: List[TengwarValue] = []
93
+ self.code_objects: List[CodeObject] = []
94
+
95
+ def _emit(self, op: int, arg: Any = None) -> int:
96
+ idx = len(self.code)
97
+ self.code.append(Instruction(op, arg))
98
+ return idx
99
+
100
+ def _add_const(self, val: TengwarValue) -> int:
101
+ self.constants.append(val)
102
+ return len(self.constants) - 1
103
+
104
+ def compile(self, node: ASTNode) -> CodeObject:
105
+ """Compile an AST node to a code object"""
106
+ self.code = []
107
+ self.constants = []
108
+ self._compile_node(node)
109
+ self._emit(BC.RETURN)
110
+ return CodeObject(
111
+ params=[],
112
+ code=self.code[:],
113
+ constants=self.constants[:],
114
+ name="<main>"
115
+ )
116
+
117
+ def _compile_node(self, node: ASTNode):
118
+ if isinstance(node, IntLit):
119
+ idx = self._add_const(TengwarInt(node.value))
120
+ self._emit(BC.CONST, idx)
121
+
122
+ elif isinstance(node, FloatLit):
123
+ idx = self._add_const(TengwarFloat(node.value))
124
+ self._emit(BC.CONST, idx)
125
+
126
+ elif isinstance(node, StrLit):
127
+ idx = self._add_const(TengwarStr(node.value))
128
+ self._emit(BC.CONST, idx)
129
+
130
+ elif isinstance(node, BoolLit):
131
+ idx = self._add_const(TengwarBool(node.value))
132
+ self._emit(BC.CONST, idx)
133
+
134
+ elif isinstance(node, UnitLit):
135
+ idx = self._add_const(TengwarUnit())
136
+ self._emit(BC.CONST, idx)
137
+
138
+ elif isinstance(node, Symbol):
139
+ self._emit(BC.LOAD, node.name)
140
+
141
+ elif isinstance(node, BinOp):
142
+ self._compile_node(node.left)
143
+ self._compile_node(node.right)
144
+ op_map = {
145
+ '+': BC.ADD, '-': BC.SUB, '*': BC.MUL, '/': BC.DIV,
146
+ '%': BC.MOD, '=': BC.EQ, '!=': BC.NEQ,
147
+ '<': BC.LT, '>': BC.GT, '<=': BC.LTE, '>=': BC.GTE,
148
+ '&': BC.AND, '|': BC.OR,
149
+ }
150
+ self._emit(op_map.get(node.op, BC.ADD))
151
+
152
+ elif isinstance(node, UnOp):
153
+ self._compile_node(node.operand)
154
+ if node.op == '!':
155
+ self._emit(BC.NOT)
156
+ elif node.op == '-':
157
+ self._emit(BC.NEG)
158
+
159
+ elif isinstance(node, Cond):
160
+ self._compile_node(node.condition)
161
+ jump_false = self._emit(BC.JUMP_IF_FALSE, None)
162
+ self._compile_node(node.then_branch)
163
+ jump_end = self._emit(BC.JUMP, None)
164
+ # Patch false jump
165
+ self.code[jump_false].arg = len(self.code)
166
+ self._compile_node(node.else_branch)
167
+ # Patch end jump
168
+ self.code[jump_end].arg = len(self.code)
169
+
170
+ elif isinstance(node, Vector):
171
+ for elem in node.elements:
172
+ self._compile_node(elem)
173
+ self._emit(BC.MAKE_VEC, len(node.elements))
174
+
175
+ elif isinstance(node, Tuple):
176
+ for elem in node.elements:
177
+ self._compile_node(elem)
178
+ self._emit(BC.MAKE_TUPLE, len(node.elements))
179
+
180
+ elif isinstance(node, Define):
181
+ self._compile_node(node.value)
182
+ if isinstance(node.name, Symbol):
183
+ self._emit(BC.STORE, node.name.name)
184
+ else:
185
+ self._emit(BC.STORE, str(node.name))
186
+
187
+ elif isinstance(node, Lambda):
188
+ # Compile lambda body as a separate code object
189
+ inner = Compiler()
190
+ inner._compile_node(node.body)
191
+ inner._emit(BC.RETURN)
192
+ params = [p.name if isinstance(p, Symbol) else str(p) for p in node.params]
193
+ co = CodeObject(
194
+ params=params,
195
+ code=inner.code[:],
196
+ constants=inner.constants[:],
197
+ name="<lambda>"
198
+ )
199
+ co_idx = len(self.code_objects)
200
+ self.code_objects.append(co)
201
+ self._emit(BC.MAKE_CLOSURE, co_idx)
202
+
203
+ elif isinstance(node, Apply):
204
+ self._compile_node(node.func)
205
+ for arg in node.args:
206
+ self._compile_node(arg)
207
+ self._emit(BC.CALL, len(node.args))
208
+
209
+ elif isinstance(node, Seq):
210
+ for i, expr in enumerate(node.exprs):
211
+ self._compile_node(expr)
212
+ if i < len(node.exprs) - 1:
213
+ self._emit(BC.POP)
214
+
215
+ elif isinstance(node, Program):
216
+ for i, expr in enumerate(node.body):
217
+ self._compile_node(expr)
218
+ if i < len(node.body) - 1:
219
+ self._emit(BC.POP)
220
+
221
+ elif isinstance(node, Let):
222
+ # Compile let bindings
223
+ for name_node, val_node in node.bindings:
224
+ self._compile_node(val_node)
225
+ name = name_node.name if isinstance(name_node, Symbol) else str(name_node)
226
+ self._emit(BC.STORE, name)
227
+ self._compile_node(node.body)
228
+
229
+ elif isinstance(node, Recurse):
230
+ # Compile recursive definition
231
+ name = node.name.name if isinstance(node.name, Symbol) else str(node.name)
232
+ self._compile_node(node.body)
233
+ self._emit(BC.STORE, name)
234
+
235
+ else:
236
+ # Fall back: store a reference to the AST node for interpreter eval
237
+ idx = self._add_const(node) # Store AST node as constant
238
+ self._emit(BC.CONST, idx)
239
+
240
+
241
+ # === VM ===
242
+
243
+ class VM:
244
+ """Execute bytecode"""
245
+
246
+ def __init__(self, interpreter: Interpreter):
247
+ self.interpreter = interpreter
248
+ self.compiler = Compiler()
249
+
250
+ def execute(self, code_obj: CodeObject, env: Environment = None) -> TengwarValue:
251
+ """Execute a code object"""
252
+ if env is None:
253
+ env = self.interpreter.global_env
254
+
255
+ stack: list = []
256
+ ip = 0
257
+ code = code_obj.code
258
+ consts = code_obj.constants
259
+
260
+ while ip < len(code):
261
+ instr = code[ip]
262
+ op = instr.op
263
+
264
+ if op == BC.CONST:
265
+ val = consts[instr.arg]
266
+ # If it's an AST node (fallback), evaluate it with interpreter
267
+ if isinstance(val, ASTNode):
268
+ val = self.interpreter.eval(val, env)
269
+ stack.append(val)
270
+
271
+ elif op == BC.LOAD:
272
+ try:
273
+ stack.append(env.get(instr.arg))
274
+ except:
275
+ # Try interpreter builtins
276
+ stack.append(self.interpreter.global_env.get(instr.arg))
277
+
278
+ elif op == BC.STORE:
279
+ val = stack[-1] # Peek, don't pop (store returns the value)
280
+ env.set(instr.arg, val)
281
+
282
+ elif op == BC.POP:
283
+ if stack:
284
+ stack.pop()
285
+
286
+ elif op == BC.ADD:
287
+ b, a = stack.pop(), stack.pop()
288
+ if isinstance(a, TengwarStr):
289
+ stack.append(TengwarStr(a.value + self.interpreter._display(b)))
290
+ elif isinstance(a, TengwarFloat) or isinstance(b, TengwarFloat):
291
+ stack.append(TengwarFloat(self.interpreter._num_val(a) + self.interpreter._num_val(b)))
292
+ else:
293
+ stack.append(TengwarInt(a.value + b.value))
294
+
295
+ elif op == BC.SUB:
296
+ b, a = stack.pop(), stack.pop()
297
+ if isinstance(a, TengwarFloat) or isinstance(b, TengwarFloat):
298
+ stack.append(TengwarFloat(self.interpreter._num_val(a) - self.interpreter._num_val(b)))
299
+ else:
300
+ stack.append(TengwarInt(a.value - b.value))
301
+
302
+ elif op == BC.MUL:
303
+ b, a = stack.pop(), stack.pop()
304
+ if isinstance(a, TengwarFloat) or isinstance(b, TengwarFloat):
305
+ stack.append(TengwarFloat(self.interpreter._num_val(a) * self.interpreter._num_val(b)))
306
+ else:
307
+ stack.append(TengwarInt(a.value * b.value))
308
+
309
+ elif op == BC.DIV:
310
+ b, a = stack.pop(), stack.pop()
311
+ stack.append(TengwarFloat(self.interpreter._num_val(a) / self.interpreter._num_val(b)))
312
+
313
+ elif op == BC.MOD:
314
+ b, a = stack.pop(), stack.pop()
315
+ if isinstance(a, TengwarFloat) or isinstance(b, TengwarFloat):
316
+ stack.append(TengwarFloat(self.interpreter._num_val(a) % self.interpreter._num_val(b)))
317
+ else:
318
+ stack.append(TengwarInt(a.value % b.value))
319
+
320
+ elif op == BC.EQ:
321
+ b, a = stack.pop(), stack.pop()
322
+ stack.append(TengwarBool(self.interpreter._values_equal(a, b)))
323
+
324
+ elif op == BC.NEQ:
325
+ b, a = stack.pop(), stack.pop()
326
+ stack.append(TengwarBool(not self.interpreter._values_equal(a, b)))
327
+
328
+ elif op == BC.LT:
329
+ b, a = stack.pop(), stack.pop()
330
+ stack.append(TengwarBool(self.interpreter._num_val(a) < self.interpreter._num_val(b)))
331
+
332
+ elif op == BC.GT:
333
+ b, a = stack.pop(), stack.pop()
334
+ stack.append(TengwarBool(self.interpreter._num_val(a) > self.interpreter._num_val(b)))
335
+
336
+ elif op == BC.LTE:
337
+ b, a = stack.pop(), stack.pop()
338
+ stack.append(TengwarBool(self.interpreter._num_val(a) <= self.interpreter._num_val(b)))
339
+
340
+ elif op == BC.GTE:
341
+ b, a = stack.pop(), stack.pop()
342
+ stack.append(TengwarBool(self.interpreter._num_val(a) >= self.interpreter._num_val(b)))
343
+
344
+ elif op == BC.AND:
345
+ b, a = stack.pop(), stack.pop()
346
+ stack.append(TengwarBool(self.interpreter._is_truthy(a) and self.interpreter._is_truthy(b)))
347
+
348
+ elif op == BC.OR:
349
+ b, a = stack.pop(), stack.pop()
350
+ stack.append(TengwarBool(self.interpreter._is_truthy(a) or self.interpreter._is_truthy(b)))
351
+
352
+ elif op == BC.NOT:
353
+ a = stack.pop()
354
+ stack.append(TengwarBool(not self.interpreter._is_truthy(a)))
355
+
356
+ elif op == BC.NEG:
357
+ a = stack.pop()
358
+ if isinstance(a, TengwarFloat):
359
+ stack.append(TengwarFloat(-a.value))
360
+ else:
361
+ stack.append(TengwarInt(-self.interpreter._num_val(a)))
362
+
363
+ elif op == BC.JUMP:
364
+ ip = instr.arg
365
+ continue
366
+
367
+ elif op == BC.JUMP_IF_FALSE:
368
+ cond = stack.pop()
369
+ if not self.interpreter._is_truthy(cond):
370
+ ip = instr.arg
371
+ continue
372
+
373
+ elif op == BC.CALL:
374
+ nargs = instr.arg
375
+ args = [stack.pop() for _ in range(nargs)][::-1]
376
+ func = stack.pop()
377
+
378
+ if isinstance(func, CompiledClosure):
379
+ # Execute compiled function
380
+ child_env = Environment(parent=func.env)
381
+ for param, arg in zip(func.code_obj.params, args):
382
+ child_env.set(param, arg)
383
+ result = self.execute(func.code_obj, child_env)
384
+ stack.append(result)
385
+ elif isinstance(func, TengwarBuiltin):
386
+ result = func.func(args)
387
+ stack.append(result)
388
+ elif isinstance(func, TengwarClosure):
389
+ result = self.interpreter._call_function(func, args)
390
+ stack.append(result)
391
+ else:
392
+ raise RuntimeError(f"Cannot call: {type(func)}")
393
+
394
+ elif op == BC.RETURN:
395
+ return stack[-1] if stack else TengwarUnit()
396
+
397
+ elif op == BC.MAKE_VEC:
398
+ n = instr.arg
399
+ elements = [stack.pop() for _ in range(n)][::-1]
400
+ stack.append(TengwarVector(elements))
401
+
402
+ elif op == BC.MAKE_TUPLE:
403
+ n = instr.arg
404
+ elements = [stack.pop() for _ in range(n)][::-1]
405
+ stack.append(TengwarTuple(elements))
406
+
407
+ elif op == BC.MAKE_CLOSURE:
408
+ co = self.compiler.code_objects[instr.arg]
409
+ stack.append(CompiledClosure(co, env))
410
+
411
+ elif op == BC.DUP:
412
+ stack.append(stack[-1])
413
+
414
+ ip += 1
415
+
416
+ return stack[-1] if stack else TengwarUnit()
417
+
418
+ def run_source(self, source: str) -> TengwarValue:
419
+ """Compile and execute source code"""
420
+ from .lexer import tokenize
421
+ from .parser import parse
422
+ tokens = tokenize(source)
423
+ ast = parse(tokens)
424
+ code_obj = self.compiler.compile(ast)
425
+ return self.execute(code_obj)