1- # parser.py - Parser for Hacker Lang syntax.
2- # Handles // (deps), [ ... ] (config, ignored), > (cmds), ! (comments), @var=value (vars),
3- # =num > cmd (loops), ? condition > cmd (conditionals), # libname (includes).
4-
51import os
62from rich .console import Console
73
84HACKER_DIR = os .path .expanduser ("~/.hacker-lang" )
95
10- def generate_check_cmd (dep ):
11- if dep == 'sudo' :
12- return ''
13- return f"command -v { dep } &> /dev/null || (sudo apt update && sudo apt install -y { dep } )"
14-
156def parse_hacker_file (file_path , verbose = False , console = None ):
167 if console is None :
178 console = Console ()
189
1910 deps = set ()
11+ libs = set ()
2012 vars = {}
2113 cmds = []
2214 includes = []
@@ -54,7 +46,25 @@ def parse_hacker_file(file_path, verbose=False, console=None):
5446 if dep :
5547 deps .add (dep )
5648 else :
57- errors .append (f"Line { line_num } : Empty dependency" )
49+ errors .append (f"Line { line_num } : Empty system dependency" )
50+ elif line .startswith ('#' ):
51+ lib = line [1 :].strip ()
52+ if lib :
53+ lib_path = os .path .join (HACKER_DIR , "libs" , lib , "main.hacker" )
54+ if os .path .exists (lib_path ):
55+ includes .append (lib )
56+ sub_deps , sub_libs , sub_vars , sub_cmds , sub_includes , sub_errors = parse_hacker_file (lib_path , verbose , console )
57+ deps .update (sub_deps )
58+ libs .update (sub_libs )
59+ vars .update (sub_vars )
60+ cmds .extend (sub_cmds )
61+ includes .extend (sub_includes )
62+ for err in sub_errors :
63+ errors .append (f"In { lib } : { err } " )
64+ else :
65+ libs .add (lib )
66+ else :
67+ errors .append (f"Line { line_num } : Empty library/include" )
5868 elif line .startswith ('>' ):
5969 parts = line [1 :].split ('!' , 1 )
6070 cmd = parts [0 ].strip ()
@@ -102,16 +112,13 @@ def parse_hacker_file(file_path, verbose=False, console=None):
102112 errors .append (f"Line { line_num } : Invalid conditional" )
103113 else :
104114 errors .append (f"Line { line_num } : Invalid conditional syntax" )
105- elif line .startswith ('#' ):
106- lib = line [1 :].strip ()
107- if lib :
108- lib_path = os .path .join (HACKER_DIR , "libs" , f"{ lib } .hacker" )
109- if os .path .exists (lib_path ):
110- includes .append (lib )
111- else :
112- errors .append (f"Line { line_num } : Library { lib } not found" )
115+ elif line .startswith ('&' ):
116+ parts = line [1 :].split ('!' , 1 )
117+ cmd = parts [0 ].strip ()
118+ if cmd :
119+ cmds .append (f"{ cmd } &" )
113120 else :
114- errors .append (f"Line { line_num } : Empty include " )
121+ errors .append (f"Line { line_num } : Empty background command " )
115122 elif line .startswith ('!' ):
116123 pass
117124 else :
@@ -121,15 +128,16 @@ def parse_hacker_file(file_path, verbose=False, console=None):
121128 errors .append ("Unclosed config section" )
122129
123130 if verbose :
124- console .print (f"[blue]Deps: { deps } [/blue]" )
131+ console .print (f"[blue]System Deps: { deps } [/blue]" )
132+ console .print (f"[blue]Custom Libs: { libs } [/blue]" )
125133 console .print (f"[blue]Vars: { vars } [/blue]" )
126134 console .print (f"[blue]Cmds: { cmds } [/blue]" )
127135 console .print (f"[blue]Includes: { includes } [/blue]" )
128136 if errors :
129137 console .print (f"[yellow]Errors: { errors } [/yellow]" )
130138
131- return deps , vars , cmds , includes , errors
139+ return deps , libs , vars , cmds , includes , errors
132140
133141 except FileNotFoundError :
134142 console .print (f"[bold red]File { file_path } not found[/bold red]" )
135- return set (), {}, [], [], [f"File { file_path } not found" ]
143+ return set (), set (), {}, [], [], [f"File { file_path } not found" ]
0 commit comments