vibecod 2.0.0__tar.gz
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-2.0.0/PKG-INFO +36 -0
- vibecod-2.0.0/README.md +9 -0
- vibecod-2.0.0/setup.cfg +4 -0
- vibecod-2.0.0/setup.py +37 -0
- vibecod-2.0.0/vibecod/__init__.py +1 -0
- vibecod-2.0.0/vibecod/__main__.py +55 -0
- vibecod-2.0.0/vibecod/core.py +643 -0
- vibecod-2.0.0/vibecod.egg-info/PKG-INFO +36 -0
- vibecod-2.0.0/vibecod.egg-info/SOURCES.txt +10 -0
- vibecod-2.0.0/vibecod.egg-info/dependency_links.txt +1 -0
- vibecod-2.0.0/vibecod.egg-info/entry_points.txt +3 -0
- vibecod-2.0.0/vibecod.egg-info/top_level.txt +1 -0
vibecod-2.0.0/PKG-INFO
ADDED
|
@@ -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!")'
|
vibecod-2.0.0/README.md
ADDED
vibecod-2.0.0/setup.cfg
ADDED
vibecod-2.0.0/setup.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name="vibecod",
|
|
8
|
+
version="2.0.0",
|
|
9
|
+
author="VibaCoding",
|
|
10
|
+
author_email="roma.lukanin.00@gmail.com",
|
|
11
|
+
description="VIBE Language — гибридный язык с квантовой душой",
|
|
12
|
+
long_description=long_description,
|
|
13
|
+
long_description_content_type="text/markdown",
|
|
14
|
+
url="https://github.com/VibaCoding/vibecod",
|
|
15
|
+
project_urls={
|
|
16
|
+
"Bug Tracker": "https://github.com/VibaCoding/vibecod/issues",
|
|
17
|
+
"Source Code": "https://github.com/VibaCoding/vibecod",
|
|
18
|
+
},
|
|
19
|
+
packages=find_packages(),
|
|
20
|
+
classifiers=[
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Topic :: Software Development :: Interpreters",
|
|
27
|
+
],
|
|
28
|
+
python_requires=">=3.8",
|
|
29
|
+
entry_points={
|
|
30
|
+
"console_scripts": [
|
|
31
|
+
"vibe = vibecod.__main__:main",
|
|
32
|
+
"vibecod = vibecod.__main__:main",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
install_requires=[],
|
|
36
|
+
include_package_data=True,
|
|
37
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .core import run_code, run_file, Interpreter
|
|
@@ -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()
|
|
@@ -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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vibecod
|