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/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)
|