Skip to content

Commit cf60ec6

Browse files
authored
Update main.py
1 parent d3763da commit cf60ec6

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed

hackerc/src/repl/main.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,219 @@
1+
import os
2+
import subprocess
3+
import tempfile
4+
from pathlib import Path
5+
from typing import Dict, List
16

7+
from prompt_toolkit import PromptSession
8+
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
9+
from prompt_toolkit.history import FileHistory
10+
from prompt_toolkit.styles import Style
11+
from rich.console import Console
12+
from rich.panel import Panel
13+
from rich.text import Text
14+
15+
VERSION = "0.0.9"
16+
HACKER_DIR = Path.home() / ".hackeros" / "hacker-lang"
17+
LIBS_DIR = HACKER_DIR / "libs"
18+
HISTORY_DIR = Path.home() / ".hackeros" / "history"
19+
HISTORY_FILE = HISTORY_DIR / "hacker_repl_history"
20+
21+
console = Console()
22+
23+
# Colorful styles
24+
success_style = "bold green"
25+
error_style = "bold red"
26+
warning_style = "yellow"
27+
info_style = "blue"
28+
prompt_style = "bold purple"
29+
30+
def ensure_dirs():
31+
HACKER_DIR.mkdir(parents=True, exist_ok=True)
32+
LIBS_DIR.mkdir(parents=True, exist_ok=True)
33+
HISTORY_DIR.mkdir(parents=True, exist_ok=True)
34+
35+
def parse_lines(lines: List[str], verbose: bool = False) -> Dict:
36+
deps = []
37+
libs = [] # missing libs
38+
vars_dict = {}
39+
cmds = []
40+
includes = [] # existing libs to include
41+
binaries = []
42+
plugins = []
43+
errors = []
44+
config = {}
45+
in_config = False
46+
47+
for line_num, line in enumerate(lines, 1):
48+
line = line.strip()
49+
if not line or line.startswith("!"):
50+
continue
51+
if line == "[":
52+
if in_config:
53+
errors.append(f"Line {line_num}: Nested config block")
54+
in_config = True
55+
continue
56+
if line == "]":
57+
if not in_config:
58+
errors.append(f"Line {line_num}: Unmatched ']'")
59+
in_config = False
60+
continue
61+
if in_config:
62+
if "=" in line:
63+
k, v = line.split("=", 1)
64+
config[k.strip()] = v.strip()
65+
else:
66+
errors.append(f"Line {line_num}: Invalid config entry: {line}")
67+
continue
68+
if line.startswith("//"):
69+
deps.extend(line[2:].strip().split())
70+
elif line.startswith("#"):
71+
lib_name = line[1:].strip()
72+
if lib_name:
73+
lib_path = LIBS_DIR / lib_name / "main.hacker"
74+
if lib_path.exists():
75+
includes.append(lib_name)
76+
else:
77+
libs.append(lib_name)
78+
elif line.startswith("@"):
79+
var_def = line[1:].strip()
80+
if "=" in var_def:
81+
k, v = var_def.split("=", 1)
82+
vars_dict[k.strip()] = v.strip()
83+
else:
84+
errors.append(f"Line {line_num}: Invalid variable: {line}")
85+
elif line.startswith("="):
86+
parts = line[1:].strip().split(">", 1)
87+
if len(parts) == 2:
88+
try:
89+
n = int(parts[0].strip())
90+
cmd = parts[1].strip()
91+
loop_cmd = f"for i in $(seq 1 {n}); do {cmd}; done"
92+
cmds.append(loop_cmd)
93+
except ValueError:
94+
errors.append(f"Line {line_num}: Invalid loop count: {line}")
95+
else:
96+
errors.append(f"Line {line_num}: Invalid loop syntax: {line}")
97+
elif line.startswith("?"):
98+
parts = line[1:].strip().split(">", 1)
99+
if len(parts) == 2:
100+
cond = parts[0].strip()
101+
cmd = parts[1].strip()
102+
if_cmd = f"if {cond}; then {cmd}; fi"
103+
cmds.append(if_cmd)
104+
else:
105+
errors.append(f"Line {line_num}: Invalid if syntax: {line}")
106+
elif line.startswith("&"):
107+
plugin = line[1:].strip()
108+
plugins.append(plugin)
109+
elif line.startswith(">"):
110+
cmd = line[1:].strip()
111+
cmds.append(cmd)
112+
else:
113+
cmds.append(line)
114+
115+
return {
116+
"deps": list(set(deps)),
117+
"libs": libs,
118+
"vars": vars_dict,
119+
"cmds": cmds,
120+
"includes": includes,
121+
"binaries": binaries,
122+
"plugins": plugins,
123+
"errors": errors,
124+
"config": config
125+
}
126+
127+
def run_repl(verbose: bool = False):
128+
ensure_dirs()
129+
history = FileHistory(str(HISTORY_FILE))
130+
session = PromptSession(
131+
history=history,
132+
auto_suggest=AutoSuggestFromHistory(),
133+
style=Style.from_dict({"prompt": prompt_style}),
134+
)
135+
lines: List[str] = []
136+
in_config = False
137+
console.print(Panel(f"Hacker Lang REPL v{VERSION} - Enhanced Interactive Mode", style=success_style))
138+
console.print(Text("Type 'exit' to quit, 'help' for commands, 'clear' to reset", style=info_style))
139+
console.print(Text("Supported: //deps, #libs, @vars, =loops, ?ifs, &bg, >cmds, [config], !comments", style=info_style))
140+
141+
while True:
142+
try:
143+
prompt = Text("CONFIG> ", style=prompt_style) if in_config else Text("hacker> ", style=prompt_style)
144+
line = session.prompt(prompt)
145+
if not line.strip():
146+
continue
147+
if line == "exit":
148+
break
149+
if line == "help":
150+
console.print(Text("REPL Commands:\n- exit: Quit REPL\n- help: This menu\n- clear: Reset session\n- verbose: Toggle verbose", style=info_style))
151+
continue
152+
if line == "clear":
153+
lines = []
154+
in_config = False
155+
console.print(Text("Session cleared!", style=success_style))
156+
continue
157+
if line == "verbose":
158+
verbose = not verbose
159+
console.print(Text(f"Verbose mode: {verbose}", style=info_style))
160+
continue
161+
162+
lines.append(line)
163+
164+
if line == "[":
165+
in_config = True
166+
continue
167+
elif line == "]":
168+
if not in_config:
169+
console.print(Text("Error: Unmatched ']'", style=error_style))
170+
in_config = False
171+
continue
172+
173+
if not in_config and line and not line.startswith("!"):
174+
parsed = parse_lines(lines, verbose)
175+
if parsed["errors"]:
176+
console.print(Panel("\n".join(parsed["errors"]), title="REPL Errors", style=error_style))
177+
else:
178+
if parsed["libs"]:
179+
console.print(Text(f"Warning: Missing custom libs: {', '.join(parsed['libs'])}", style=warning_style))
180+
with tempfile.NamedTemporaryFile(mode="w+", suffix=".sh", delete=False) as temp_sh:
181+
temp_sh.write("#!/bin/bash\nset -e\n")
182+
for k, v in parsed["vars"].items():
183+
temp_sh.write(f'export {k}="{v}"\n')
184+
for dep in parsed["deps"]:
185+
if dep != "sudo":
186+
temp_sh.write(f'command -v {dep} || (sudo apt update && sudo apt install -y {dep})\n')
187+
for inc in parsed["includes"]:
188+
lib_path = LIBS_DIR / inc / "main.hacker"
189+
with open(lib_path, "r") as lib_f:
190+
temp_sh.write(f"# include {inc}\n")
191+
temp_sh.write(lib_f.read())
192+
temp_sh.write("\n")
193+
for cmd in parsed["cmds"]:
194+
temp_sh.write(f"{cmd}\n")
195+
for bin in parsed["binaries"]:
196+
temp_sh.write(f"{bin}\n")
197+
for plugin in parsed["plugins"]:
198+
temp_sh.write(f"{plugin} &\n")
199+
temp_path = temp_sh.name
200+
os.chmod(temp_path, 0o755)
201+
env = os.environ.copy()
202+
env.update(parsed["vars"])
203+
result = subprocess.run(["bash", temp_path], env=env, capture_output=True, text=True)
204+
os.remove(temp_path)
205+
out_str = result.stdout.strip()
206+
err_str = result.stderr.strip()
207+
if result.returncode != 0:
208+
if err_str:
209+
console.print(Panel(err_str, title="REPL Error", style=error_style))
210+
elif out_str:
211+
console.print(Panel(out_str, title="REPL Output", style=success_style))
212+
except KeyboardInterrupt:
213+
break
214+
console.print(Text("REPL session ended.", style=success_style))
215+
216+
if __name__ == "__main__":
217+
import sys
218+
verbose = "--verbose" in sys.argv
219+
run_repl(verbose=verbose)

0 commit comments

Comments
 (0)