vibecod 2.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.
vibecod/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .core import run_code, run_file, Interpreter
vibecod/__main__.py ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env python3
2
+ import sys
3
+ import argparse
4
+ from .core import run_code, run_file, Interpreter, Lexer, Parser
5
+
6
+ def main():
7
+ parser = argparse.ArgumentParser(description="VIBEcod Language")
8
+ parser.add_argument("-c", "--command", help="Execute one line of VIBE code")
9
+ parser.add_argument("file", nargs="?", help="VIBE source file")
10
+ parser.add_argument("-v", "--version", action="store_true", help="Show version")
11
+
12
+ args = parser.parse_args()
13
+
14
+ if args.version:
15
+ print("VIBEcod 2.0.0")
16
+ return
17
+
18
+ if args.command:
19
+ # Режим "как у clang": vibecod -c 'spit("hello")'
20
+ run_code(args.command)
21
+ elif args.file:
22
+ run_file(args.file)
23
+ else:
24
+ # REPL режим
25
+ print("\033[96mVIBEcod REPL v2.0\033[0m")
26
+ interp = Interpreter()
27
+ while True:
28
+ try:
29
+ line = input("\033[93mvibe> \033[0m")
30
+ except (EOFError, KeyboardInterrupt):
31
+ print("\nstay vibe")
32
+ break
33
+ if line.strip() in ('exit', 'quit'):
34
+ break
35
+ if not line.strip():
36
+ continue
37
+ try:
38
+ lexer = Lexer(line)
39
+ tokens = lexer.tokenize()
40
+ parser = Parser(tokens)
41
+ ast = parser.parse()
42
+ if ast.statements:
43
+ last = ast.statements[-1]
44
+ from .core import ExprStmt
45
+ if isinstance(last, ExprStmt):
46
+ res = interp.eval(last.expr, interp.globals)
47
+ if res is not None:
48
+ print(f"\033[90m=> {interp._str(res)}\033[0m")
49
+ else:
50
+ interp.exec(last, interp.globals)
51
+ except Exception as e:
52
+ print(f"\033[91m{e}\033[0m")
53
+
54
+ if __name__ == "__main__":
55
+ main()
vibecod/core.py ADDED
@@ -0,0 +1,643 @@
1
+ #!/usr/bin/env python3
2
+ import sys
3
+ from dataclasses import dataclass, field
4
+ from enum import Enum
5
+ from typing import List, Any, Optional, Dict, Callable
6
+
7
+ # ============================================================
8
+ # ERRORS
9
+ # ============================================================
10
+ class VIBEError(Exception):
11
+ def __init__(self, msg: str, line: int = 0, col: int = 0):
12
+ self.msg = msg
13
+ self.line = line
14
+ self.col = col
15
+ super().__init__(f"[{line}:{col}] {msg}" if line else msg)
16
+
17
+ class VIBESyntaxError(VIBEError): pass
18
+ class VIBERuntimeError(VIBEError): pass
19
+ class VIBEReturnSignal(Exception):
20
+ def __init__(self, value): self.value = value
21
+
22
+ # ============================================================
23
+ # TOKENS
24
+ # ============================================================
25
+ class TT(Enum):
26
+ VIBE = "vibe"; YEET = "yeet"; FLORP = "florp"
27
+ SPIT = "spit"; SPIT_COLOR = "spit_color"
28
+ LOOPIT = "loopit"; GONE = "gone"
29
+ MAYBE = "maybe"; NOPE = "nope"
30
+ FN = "fn"; YOINK = "yoink"
31
+ USE = "use"
32
+ IDENTIFIER = "IDENTIFIER"
33
+ NUMBER = "NUMBER"
34
+ STRING = "STRING"
35
+ BOOLEAN = "BOOLEAN"
36
+ NULL = "NULL"
37
+ ASSIGN = "="; PLUS = "+"; MINUS = "-"
38
+ MULTIPLY = "*"; DIVIDE = "/"; CONCAT = "+-"
39
+ LT = "<"; GT = ">"; LTE = "<="; GTE = ">="
40
+ EQ = "=="; NEQ = "!="
41
+ AND = "&&"; OR = "||"; NOT = "!"
42
+ INC = "++"; DEC = "--"
43
+ DOT = "."
44
+ LPAREN = "("; RPAREN = ")"
45
+ LBRACE = "{"; RBRACE = "}"
46
+ LBRACKET = "["; RBRACKET = "]"
47
+ SEMICOLON = ";"; COMMA = ","
48
+ ARROW = "->"
49
+ EOF = "EOF"
50
+
51
+ @dataclass
52
+ class Token:
53
+ type: TT
54
+ value: Any
55
+ line: int
56
+ col: int
57
+
58
+ # ============================================================
59
+ # LEXER
60
+ # ============================================================
61
+ KEYWORDS = {
62
+ 'vibe': TT.VIBE, 'yeet': TT.YEET, 'florp': TT.FLORP,
63
+ 'spit': TT.SPIT, 'spit_color': TT.SPIT_COLOR,
64
+ 'loopit': TT.LOOPIT, 'gone': TT.GONE,
65
+ 'maybe': TT.MAYBE, 'nope': TT.NOPE,
66
+ 'fn': TT.FN, 'yoink': TT.YOINK,
67
+ 'use': TT.USE,
68
+ 'true': TT.BOOLEAN, 'false': TT.BOOLEAN,
69
+ 'null': TT.NULL,
70
+ }
71
+
72
+ class Lexer:
73
+ def __init__(self, source: str, filename: str = "<input>"):
74
+ self.src = source
75
+ self.pos = 0
76
+ self.line = 1
77
+ self.col = 1
78
+
79
+ def error(self, msg):
80
+ raise VIBESyntaxError(msg, self.line, self.col)
81
+
82
+ def peek(self, offset=0):
83
+ i = self.pos + offset
84
+ return self.src[i] if i < len(self.src) else None
85
+
86
+ def advance(self):
87
+ ch = self.src[self.pos]
88
+ self.pos += 1
89
+ if ch == '\n':
90
+ self.line += 1
91
+ self.col = 1
92
+ else:
93
+ self.col += 1
94
+ return ch
95
+
96
+ def skip_whitespace(self):
97
+ while self.pos < len(self.src) and self.src[self.pos].isspace():
98
+ self.advance()
99
+
100
+ def skip_comment(self):
101
+ while self.pos < len(self.src) and self.src[self.pos] != '\n':
102
+ self.advance()
103
+
104
+ def read_string(self):
105
+ line, col = self.line, self.col
106
+ self.advance()
107
+ buf = []
108
+ while self.pos < len(self.src):
109
+ ch = self.src[self.pos]
110
+ if ch == '"':
111
+ self.advance()
112
+ return Token(TT.STRING, ''.join(buf), line, col)
113
+ if ch == '\\':
114
+ self.advance()
115
+ esc = self.advance()
116
+ buf.append({'n':'\n','t':'\t','\\':'\\','"':'"'}.get(esc, esc))
117
+ else:
118
+ buf.append(self.advance())
119
+ self.error("Unterminated string")
120
+
121
+ def read_number(self):
122
+ line, col = self.line, self.col
123
+ buf = []
124
+ is_float = False
125
+ while self.pos < len(self.src) and (self.src[self.pos].isdigit() or self.src[self.pos] == '.'):
126
+ if self.src[self.pos] == '.':
127
+ if is_float: self.error("Invalid number")
128
+ is_float = True
129
+ buf.append(self.advance())
130
+ s = ''.join(buf)
131
+ return Token(TT.NUMBER, float(s) if is_float else int(s), line, col)
132
+
133
+ def read_identifier(self):
134
+ line, col = self.line, self.col
135
+ buf = []
136
+ while self.pos < len(self.src) and (self.src[self.pos].isalnum() or self.src[self.pos] == '_'):
137
+ buf.append(self.advance())
138
+ word = ''.join(buf)
139
+ tt = KEYWORDS.get(word)
140
+ if tt == TT.BOOLEAN:
141
+ return Token(TT.BOOLEAN, word == 'true', line, col)
142
+ if tt == TT.NULL:
143
+ return Token(TT.NULL, None, line, col)
144
+ if tt:
145
+ return Token(tt, word, line, col)
146
+ return Token(TT.IDENTIFIER, word, line, col)
147
+
148
+ def tokenize(self):
149
+ tokens = []
150
+ while self.pos < len(self.src):
151
+ self.skip_whitespace()
152
+ if self.pos >= len(self.src):
153
+ break
154
+ line, col = self.line, self.col
155
+ ch = self.src[self.pos]
156
+ if ch == '/' and self.peek(1) == '/':
157
+ self.skip_comment()
158
+ continue
159
+ if ch == '"':
160
+ tokens.append(self.read_string())
161
+ continue
162
+ if ch.isdigit():
163
+ tokens.append(self.read_number())
164
+ continue
165
+ if ch.isalpha() or ch == '_':
166
+ tokens.append(self.read_identifier())
167
+ continue
168
+ two = self.src[self.pos:self.pos+2] if self.pos+1 < len(self.src) else ''
169
+ if two == '+-':
170
+ self.advance(); self.advance()
171
+ tokens.append(Token(TT.CONCAT, '+-', line, col)); continue
172
+ if two == '++':
173
+ self.advance(); self.advance()
174
+ tokens.append(Token(TT.INC, '++', line, col)); continue
175
+ if two == '--':
176
+ self.advance(); self.advance()
177
+ tokens.append(Token(TT.DEC, '--', line, col)); continue
178
+ if two == '==':
179
+ self.advance(); self.advance()
180
+ tokens.append(Token(TT.EQ, '==', line, col)); continue
181
+ if two == '!=':
182
+ self.advance(); self.advance()
183
+ tokens.append(Token(TT.NEQ, '!=', line, col)); continue
184
+ if two == '&&':
185
+ self.advance(); self.advance()
186
+ tokens.append(Token(TT.AND, '&&', line, col)); continue
187
+ if two == '||':
188
+ self.advance(); self.advance()
189
+ tokens.append(Token(TT.OR, '||', line, col)); continue
190
+ if two == '<=':
191
+ self.advance(); self.advance()
192
+ tokens.append(Token(TT.LTE, '<=', line, col)); continue
193
+ if two == '>=':
194
+ self.advance(); self.advance()
195
+ tokens.append(Token(TT.GTE, '>=', line, col)); continue
196
+ if two == '->':
197
+ self.advance(); self.advance()
198
+ tokens.append(Token(TT.ARROW, '->', line, col)); continue
199
+ single = {
200
+ '+': TT.PLUS, '-': TT.MINUS, '*': TT.MULTIPLY, '/': TT.DIVIDE,
201
+ '<': TT.LT, '>': TT.GT, '=': TT.ASSIGN, '!': TT.NOT,
202
+ '(': TT.LPAREN, ')': TT.RPAREN,
203
+ '{': TT.LBRACE, '}': TT.RBRACE,
204
+ '[': TT.LBRACKET, ']': TT.RBRACKET,
205
+ ';': TT.SEMICOLON, ',': TT.COMMA, '.': TT.DOT,
206
+ }
207
+ if ch in single:
208
+ self.advance()
209
+ tokens.append(Token(single[ch], ch, line, col))
210
+ continue
211
+ self.error(f"Unknown character '{ch}'")
212
+ tokens.append(Token(TT.EOF, None, self.line, self.col))
213
+ return tokens
214
+
215
+ # ============================================================
216
+ # AST NODES
217
+ # ============================================================
218
+ @dataclass
219
+ class Node: line: int = 0; col: int = 0
220
+ @dataclass
221
+ class NumLit(Node): value: Any = None
222
+ @dataclass
223
+ class StrLit(Node): value: str = ""
224
+ @dataclass
225
+ class BoolLit(Node): value: bool = False
226
+ @dataclass
227
+ class VarExpr(Node): name: str = ""
228
+ @dataclass
229
+ class BinOp(Node): left: Node = None; op: str = ""; right: Node = None
230
+ @dataclass
231
+ class CallExpr(Node): callee: Node = None; args: List[Node] = field(default_factory=list)
232
+ @dataclass
233
+ class AssignExpr(Node): name: str = ""; value: Node = None
234
+ @dataclass
235
+ class VarDecl(Node): var_type: str = ""; name: str = ""; value: Node = None
236
+ @dataclass
237
+ class SpitStmt(Node): value: Node = None
238
+ @dataclass
239
+ class SpitColorStmt(Node): color: Node = None; text: Node = None
240
+ @dataclass
241
+ class LoopItStmt(Node): init: Node = None; condition: Node = None; increment: Node = None; body: List[Node] = field(default_factory=list)
242
+ @dataclass
243
+ class MaybeStmt(Node): condition: Node = None; body: List[Node] = field(default_factory=list); elifs: List[tuple] = field(default_factory=list); else_body: Optional[List[Node]] = None
244
+ @dataclass
245
+ class FnDecl(Node): name: str = ""; params: List[str] = field(default_factory=list); body: List[Node] = field(default_factory=list)
246
+ @dataclass
247
+ class UseStmt(Node): module: str = ""
248
+ @dataclass
249
+ class ExprStmt(Node): expr: Node = None
250
+ @dataclass
251
+ class Program(Node): statements: List[Node] = field(default_factory=list)
252
+
253
+ # ============================================================
254
+ # PARSER
255
+ # ============================================================
256
+ class Parser:
257
+ def __init__(self, tokens):
258
+ self.tokens = tokens
259
+ self.pos = 0
260
+ def cur(self):
261
+ return self.tokens[self.pos]
262
+ def eat(self, *types):
263
+ tok = self.cur()
264
+ if tok.type not in types:
265
+ raise VIBESyntaxError(f"Expected {types}, got {tok.type}", tok.line, tok.col)
266
+ self.pos += 1
267
+ return tok
268
+ def parse(self):
269
+ stmts = []
270
+ while not self.cur().type == TT.EOF:
271
+ stmts.append(self.parse_stmt())
272
+ return Program(stmts)
273
+ def parse_stmt(self):
274
+ tok = self.cur()
275
+ if tok.type in (TT.VIBE, TT.YEET, TT.FLORP):
276
+ self.eat(tok.type)
277
+ name = self.eat(TT.IDENTIFIER).value
278
+ self.eat(TT.ASSIGN)
279
+ val = self.parse_expr()
280
+ return VarDecl(tok.type.value, name, val, tok.line, tok.col)
281
+ if tok.type == TT.SPIT:
282
+ self.eat(TT.SPIT)
283
+ self.eat(TT.LPAREN)
284
+ val = self.parse_expr()
285
+ self.eat(TT.RPAREN)
286
+ return SpitStmt(val, tok.line, tok.col)
287
+ if tok.type == TT.SPIT_COLOR:
288
+ self.eat(TT.SPIT_COLOR)
289
+ self.eat(TT.LPAREN)
290
+ color = self.parse_expr()
291
+ self.eat(TT.COMMA)
292
+ text = self.parse_expr()
293
+ self.eat(TT.RPAREN)
294
+ return SpitColorStmt(color, text, tok.line, tok.col)
295
+ if tok.type == TT.LOOPIT:
296
+ self.eat(TT.LOOPIT)
297
+ self.eat(TT.LPAREN)
298
+ if self.cur().type in (TT.VIBE, TT.YEET, TT.FLORP):
299
+ init = self.parse_stmt()
300
+ else:
301
+ name = self.eat(TT.IDENTIFIER).value
302
+ self.eat(TT.ASSIGN)
303
+ val = self.parse_expr()
304
+ init = VarDecl('vibe', name, val)
305
+ self.eat(TT.SEMICOLON)
306
+ cond = self.parse_expr()
307
+ self.eat(TT.SEMICOLON)
308
+ inc_name = self.eat(TT.IDENTIFIER).value
309
+ if self.cur().type == TT.INC:
310
+ self.eat(TT.INC)
311
+ inc = AssignExpr(inc_name, BinOp(VarExpr(inc_name), '+', NumLit(1)))
312
+ else:
313
+ self.eat(TT.ASSIGN)
314
+ inc_val = self.parse_expr()
315
+ inc = AssignExpr(inc_name, inc_val)
316
+ self.eat(TT.RPAREN)
317
+ self.eat(TT.LBRACE)
318
+ body = []
319
+ while not self.cur().type == TT.RBRACE:
320
+ body.append(self.parse_stmt())
321
+ self.eat(TT.RBRACE)
322
+ return LoopItStmt(init, cond, inc, body, tok.line, tok.col)
323
+ if tok.type == TT.MAYBE:
324
+ self.eat(TT.MAYBE)
325
+ self.eat(TT.LPAREN)
326
+ cond = self.parse_expr()
327
+ self.eat(TT.RPAREN)
328
+ self.eat(TT.LBRACE)
329
+ body = []
330
+ while not self.cur().type == TT.RBRACE:
331
+ body.append(self.parse_stmt())
332
+ self.eat(TT.RBRACE)
333
+ elifs = []
334
+ else_body = None
335
+ while self.cur().type == TT.MAYBE and self.tokens[self.pos+1].type == TT.MAYBE:
336
+ self.eat(TT.MAYBE)
337
+ self.eat(TT.LPAREN)
338
+ econd = self.parse_expr()
339
+ self.eat(TT.RPAREN)
340
+ self.eat(TT.LBRACE)
341
+ ebody = []
342
+ while not self.cur().type == TT.RBRACE:
343
+ ebody.append(self.parse_stmt())
344
+ self.eat(TT.RBRACE)
345
+ elifs.append((econd, ebody))
346
+ if self.cur().type == TT.NOPE:
347
+ self.eat(TT.NOPE)
348
+ self.eat(TT.LBRACE)
349
+ else_body = []
350
+ while not self.cur().type == TT.RBRACE:
351
+ else_body.append(self.parse_stmt())
352
+ self.eat(TT.RBRACE)
353
+ return MaybeStmt(cond, body, elifs, else_body, tok.line, tok.col)
354
+ if tok.type == TT.FN:
355
+ self.eat(TT.FN)
356
+ name = self.eat(TT.IDENTIFIER).value
357
+ self.eat(TT.LPAREN)
358
+ params = []
359
+ if not self.cur().type == TT.RPAREN:
360
+ params.append(self.eat(TT.IDENTIFIER).value)
361
+ while self.cur().type == TT.COMMA:
362
+ self.eat(TT.COMMA)
363
+ params.append(self.eat(TT.IDENTIFIER).value)
364
+ self.eat(TT.RPAREN)
365
+ self.eat(TT.LBRACE)
366
+ body = []
367
+ while not self.cur().type == TT.RBRACE:
368
+ body.append(self.parse_stmt())
369
+ self.eat(TT.RBRACE)
370
+ return FnDecl(name, params, body, tok.line, tok.col)
371
+ if tok.type == TT.YOINK:
372
+ self.eat(TT.YOINK)
373
+ val = self.parse_expr() if not self.cur().type in (TT.RBRACE, TT.EOF) else None
374
+ return ExprStmt(CallExpr(VarExpr("__return__"), [val] if val else []))
375
+ if tok.type == TT.IDENTIFIER:
376
+ name = self.eat(TT.IDENTIFIER).value
377
+ if self.cur().type == TT.ASSIGN:
378
+ self.eat(TT.ASSIGN)
379
+ val = self.parse_expr()
380
+ return ExprStmt(AssignExpr(name, val))
381
+ self.pos -= 1
382
+ expr = self.parse_expr()
383
+ return ExprStmt(expr)
384
+ return self.parse_expr()
385
+ def parse_expr(self):
386
+ return self.parse_or()
387
+ def parse_or(self):
388
+ left = self.parse_and()
389
+ while self.cur().type == TT.OR:
390
+ op = self.eat(TT.OR).value
391
+ right = self.parse_and()
392
+ left = BinOp(left, op, right)
393
+ return left
394
+ def parse_and(self):
395
+ left = self.parse_eq()
396
+ while self.cur().type == TT.AND:
397
+ op = self.eat(TT.AND).value
398
+ right = self.parse_eq()
399
+ left = BinOp(left, op, right)
400
+ return left
401
+ def parse_eq(self):
402
+ left = self.parse_compare()
403
+ while self.cur().type in (TT.EQ, TT.NEQ):
404
+ op = self.eat(self.cur().type).value
405
+ right = self.parse_compare()
406
+ left = BinOp(left, op, right)
407
+ return left
408
+ def parse_compare(self):
409
+ left = self.parse_concat()
410
+ if self.cur().type in (TT.LT, TT.GT, TT.LTE, TT.GTE, TT.GONE):
411
+ op = self.eat(self.cur().type)
412
+ op_val = '<' if op.type == TT.GONE else op.value
413
+ right = self.parse_concat()
414
+ left = BinOp(left, op_val, right)
415
+ return left
416
+ def parse_concat(self):
417
+ left = self.parse_add()
418
+ while self.cur().type == TT.CONCAT:
419
+ self.eat(TT.CONCAT)
420
+ right = self.parse_add()
421
+ left = BinOp(left, '+-', right)
422
+ return left
423
+ def parse_add(self):
424
+ left = self.parse_mul()
425
+ while self.cur().type in (TT.PLUS, TT.MINUS):
426
+ op = self.eat(self.cur().type).value
427
+ right = self.parse_mul()
428
+ left = BinOp(left, op, right)
429
+ return left
430
+ def parse_mul(self):
431
+ left = self.parse_unary()
432
+ while self.cur().type in (TT.MULTIPLY, TT.DIVIDE):
433
+ op = self.eat(self.cur().type).value
434
+ right = self.parse_unary()
435
+ left = BinOp(left, op, right)
436
+ return left
437
+ def parse_unary(self):
438
+ if self.cur().type == TT.NOT:
439
+ op = self.eat(TT.NOT).value
440
+ return BinOp(None, op, self.parse_unary())
441
+ if self.cur().type == TT.MINUS:
442
+ op = self.eat(TT.MINUS).value
443
+ return BinOp(NumLit(0), op, self.parse_unary())
444
+ return self.parse_primary()
445
+ def parse_primary(self):
446
+ tok = self.cur()
447
+ if tok.type == TT.NUMBER:
448
+ self.eat(TT.NUMBER)
449
+ return NumLit(tok.value, tok.line, tok.col)
450
+ if tok.type == TT.STRING:
451
+ self.eat(TT.STRING)
452
+ return StrLit(tok.value, tok.line, tok.col)
453
+ if tok.type == TT.BOOLEAN:
454
+ self.eat(TT.BOOLEAN)
455
+ return BoolLit(tok.value, tok.line, tok.col)
456
+ if tok.type == TT.IDENTIFIER:
457
+ name = self.eat(TT.IDENTIFIER).value
458
+ if self.cur().type == TT.LPAREN:
459
+ self.eat(TT.LPAREN)
460
+ args = []
461
+ if not self.cur().type == TT.RPAREN:
462
+ args.append(self.parse_expr())
463
+ while self.cur().type == TT.COMMA:
464
+ self.eat(TT.COMMA)
465
+ args.append(self.parse_expr())
466
+ self.eat(TT.RPAREN)
467
+ return CallExpr(VarExpr(name), args)
468
+ return VarExpr(name, tok.line, tok.col)
469
+ if tok.type == TT.LPAREN:
470
+ self.eat(TT.LPAREN)
471
+ expr = self.parse_expr()
472
+ self.eat(TT.RPAREN)
473
+ return expr
474
+ raise VIBESyntaxError(f"Unexpected token {tok.type}", tok.line, tok.col)
475
+
476
+ # ============================================================
477
+ # ENVIRONMENT
478
+ # ============================================================
479
+ class Env:
480
+ def __init__(self, parent=None):
481
+ self.vars = {}
482
+ self.consts = set()
483
+ self.parent = parent
484
+ def get(self, name, line=0, col=0):
485
+ if name in self.vars:
486
+ return self.vars[name]
487
+ if self.parent:
488
+ return self.parent.get(name, line, col)
489
+ raise VIBERuntimeError(f"Undefined '{name}'", line, col)
490
+ def set(self, name, val, line=0, col=0):
491
+ if name in self.consts:
492
+ raise VIBERuntimeError(f"Cannot reassign yeet '{name}'", line, col)
493
+ if name in self.vars or not self.parent:
494
+ self.vars[name] = val
495
+ else:
496
+ self.parent.set(name, val, line, col)
497
+ def define(self, name, val, is_const=False):
498
+ self.vars[name] = val
499
+ if is_const:
500
+ self.consts.add(name)
501
+
502
+ @dataclass
503
+ class VIBEFunction:
504
+ name: str
505
+ params: List[str]
506
+ body: List[Node]
507
+ closure: Env
508
+
509
+ @dataclass
510
+ class NativeFunction:
511
+ name: str
512
+ fn: Callable
513
+
514
+ # ============================================================
515
+ # INTERPRETER
516
+ # ============================================================
517
+ COLORS = {'red': '\033[91m', 'green': '\033[92m', 'yellow': '\033[93m', 'blue': '\033[94m', 'magenta': '\033[95m', 'cyan': '\033[96m', 'white': '\033[97m', 'black': '\033[30m'}
518
+
519
+ class Interpreter:
520
+ def __init__(self):
521
+ self.globals = Env()
522
+ self.globals.define('spit', NativeFunction('spit', lambda args: print(self._str(args[0]), flush=True)))
523
+ self.globals.define('spit_color', NativeFunction('spit_color', lambda args: print(f"{COLORS.get(str(args[0]), '')}{self._str(args[1])}\033[0m", flush=True)))
524
+ self.globals.define('len', NativeFunction('len', lambda args: len(args[0])))
525
+ self.globals.define('str', NativeFunction('str', lambda args: str(args[0])))
526
+ self.globals.define('num', NativeFunction('num', lambda args: float(args[0])))
527
+ self.globals.define('push', NativeFunction('push', lambda args: args[0].append(args[1]) or args[0]))
528
+ self.globals.define('pop', NativeFunction('pop', lambda args: args[0].pop()))
529
+ self.globals.define('exit', NativeFunction('exit', lambda args: sys.exit(0)))
530
+
531
+ def _str(self, val):
532
+ if isinstance(val, list): return "[" + ", ".join(self._str(v) for v in val) + "]"
533
+ if isinstance(val, bool): return "true" if val else "false"
534
+ if val is None: return "null"
535
+ return str(val)
536
+
537
+ def eval(self, node, env):
538
+ if isinstance(node, NumLit): return node.value
539
+ if isinstance(node, StrLit): return node.value
540
+ if isinstance(node, BoolLit): return node.value
541
+ if isinstance(node, VarExpr): return env.get(node.name, node.line, node.col)
542
+ if isinstance(node, BinOp):
543
+ if node.op == '+-':
544
+ return self._str(self.eval(node.left, env)) + self._str(self.eval(node.right, env))
545
+ l = self.eval(node.left, env)
546
+ r = self.eval(node.right, env)
547
+ if node.op == '+': return l + r
548
+ if node.op == '-': return l - r
549
+ if node.op == '*': return l * r
550
+ if node.op == '/': return l / r
551
+ if node.op == '<': return l < r
552
+ if node.op == '>': return l > r
553
+ if node.op == '==': return l == r
554
+ if node.op == '!=': return l != r
555
+ if node.op == '&&': return l and r
556
+ if node.op == '||': return l or r
557
+ return None
558
+ if isinstance(node, AssignExpr):
559
+ val = self.eval(node.value, env)
560
+ env.set(node.name, val, node.line, node.col)
561
+ return val
562
+ if isinstance(node, CallExpr):
563
+ callee = self.eval(node.callee, env)
564
+ args = [self.eval(a, env) for a in node.args]
565
+ if isinstance(callee, NativeFunction):
566
+ return callee.fn(args)
567
+ if isinstance(callee, VIBEFunction):
568
+ if len(args) != len(callee.params):
569
+ raise VIBERuntimeError(f"Expected {len(callee.params)} args, got {len(args)}", node.line, node.col)
570
+ fn_env = Env(callee.closure)
571
+ for p, a in zip(callee.params, args):
572
+ fn_env.define(p, a)
573
+ try:
574
+ self.exec_block(callee.body, fn_env)
575
+ except VIBEReturnSignal as ret:
576
+ return ret.value
577
+ return None
578
+ raise VIBERuntimeError(f"Not callable: {callee}", node.line, node.col)
579
+ raise VIBERuntimeError(f"Unknown node {type(node)}", node.line, node.col)
580
+
581
+ def exec(self, node, env):
582
+ if isinstance(node, Program):
583
+ for s in node.statements:
584
+ self.exec(s, env)
585
+ elif isinstance(node, VarDecl):
586
+ val = self.eval(node.value, env)
587
+ env.define(node.name, val, node.var_type == 'yeet')
588
+ elif isinstance(node, SpitStmt):
589
+ print(self._str(self.eval(node.value, env)), flush=True)
590
+ elif isinstance(node, SpitColorStmt):
591
+ col = self._str(self.eval(node.color, env))
592
+ txt = self._str(self.eval(node.text, env))
593
+ print(f"{COLORS.get(col, '')}{txt}\033[0m", flush=True)
594
+ elif isinstance(node, LoopItStmt):
595
+ loop_env = Env(env)
596
+ self.exec(node.init, loop_env)
597
+ while self.eval(node.condition, loop_env):
598
+ body_env = Env(loop_env)
599
+ try:
600
+ self.exec_block(node.body, body_env)
601
+ except VIBEReturnSignal:
602
+ raise
603
+ self.exec(node.increment, loop_env)
604
+ elif isinstance(node, MaybeStmt):
605
+ if self.eval(node.condition, env):
606
+ self.exec_block(node.body, Env(env))
607
+ else:
608
+ for cond, body in node.elifs:
609
+ if self.eval(cond, env):
610
+ self.exec_block(body, Env(env))
611
+ return
612
+ if node.else_body:
613
+ self.exec_block(node.else_body, Env(env))
614
+ elif isinstance(node, FnDecl):
615
+ env.define(node.name, VIBEFunction(node.name, node.params, node.body, env))
616
+ elif isinstance(node, ExprStmt):
617
+ self.eval(node.expr, env)
618
+ elif isinstance(node, UseStmt):
619
+ pass
620
+ else:
621
+ self.eval(node, env)
622
+
623
+ def exec_block(self, stmts, env):
624
+ for s in stmts:
625
+ self.exec(s, env)
626
+
627
+ def run(self, prog):
628
+ self.exec(prog, self.globals)
629
+
630
+ # ============================================================
631
+ # PUBLIC API
632
+ # ============================================================
633
+ def run_code(code: str):
634
+ lexer = Lexer(code)
635
+ tokens = lexer.tokenize()
636
+ parser = Parser(tokens)
637
+ ast = parser.parse()
638
+ interp = Interpreter()
639
+ interp.run(ast)
640
+
641
+ def run_file(path: str):
642
+ with open(path, 'r') as f:
643
+ run_code(f.read())
@@ -0,0 +1,36 @@
1
+ Metadata-Version: 2.4
2
+ Name: vibecod
3
+ Version: 2.0.0
4
+ Summary: VIBE Language — гибридный язык с квантовой душой
5
+ Home-page: https://github.com/VibaCoding/vibecod
6
+ Author: VibaCoding
7
+ Author-email: roma.lukanin.00@gmail.com
8
+ Project-URL: Bug Tracker, https://github.com/VibaCoding/vibecod/issues
9
+ Project-URL: Source Code, https://github.com/VibaCoding/vibecod
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Software Development :: Interpreters
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: project-url
25
+ Dynamic: requires-python
26
+ Dynamic: summary
27
+
28
+ # VIBEcod
29
+
30
+ VIBE Language — гибридный язык с квантовой душой
31
+
32
+ ## Установка
33
+
34
+ ```bash
35
+ pip install vibecod
36
+ vibe -c 'spit("Hello VIBE!")'
@@ -0,0 +1,8 @@
1
+ vibecod/__init__.py,sha256=97ydNCIvEfL8IBIa0EKQJwgVeHOgVo7UTYHHqSbgfik,50
2
+ vibecod/__main__.py,sha256=ZQozQ0-zCl46M2YAocVw1fZt0q1H_wJe7q88lyIdxfM,1899
3
+ vibecod/core.py,sha256=YtePrW3CtaWjR388iJQ8Iou0t-iLhCh1PBsBkMBdINs,24877
4
+ vibecod-2.0.0.dist-info/METADATA,sha256=Pd-3JHbdZeiqptu3-8eETH5uu6R2NZ4PdQg2J88rpAA,1105
5
+ vibecod-2.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
6
+ vibecod-2.0.0.dist-info/entry_points.txt,sha256=jfhm-Lfd5tCBcUmL97Jhg-mtcNm4bKxiXvjC20S1eco,79
7
+ vibecod-2.0.0.dist-info/top_level.txt,sha256=OiyFny7SinzwJ9jV4dm4Nwh-Fns3gll3L5C8E4Fzb8I,8
8
+ vibecod-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ vibe = vibecod.__main__:main
3
+ vibecod = vibecod.__main__:main
@@ -0,0 +1 @@
1
+ vibecod