@@ -16,6 +16,7 @@ use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
1616use base64:: Engine ;
1717
1818const HACKER_DIR : & str = "~/.hackeros/hacker-lang" ;
19+
1920fn expand_home ( path : & str ) -> String {
2021 if path. starts_with ( "~/" ) {
2122 if let Some ( home) = env:: var_os ( "HOME" ) {
@@ -24,35 +25,55 @@ fn expand_home(path: &str) -> String {
2425 }
2526 path. to_string ( )
2627}
28+
2729#[ derive( Debug ) ]
2830struct Plugin {
2931 name : String ,
30- // Dodatkowe pola dla pluginów, np. path: String ,
32+ is_super : bool ,
3133}
32- fn parse_hacker_file ( path : & Path , verbose : bool , bytes_mode : bool ) -> io:: Result < ( Vec < String > , Vec < String > , Vec < ( String , String ) > , Vec < String > , Vec < String > , Vec < String > , Vec < String > , std:: collections:: HashMap < String , String > , Vec < Plugin > ) > {
34+
35+ fn parse_hacker_file ( path : & Path , verbose : bool , bytes_mode : bool ) -> io:: Result < ( Vec < String > , Vec < String > , Vec < ( String , String ) > , Vec < String > , Vec < String > , Vec < String > , Vec < ( String , String ) > , Vec < String > , std:: collections:: HashMap < String , String > , Vec < Plugin > , std:: collections:: HashMap < String , Vec < String > > ) > {
3336 let file = File :: open ( path) ?;
34- let mut deps = Vec :: new ( ) ;
35- let mut libs = Vec :: new ( ) ;
36- let mut vars = Vec :: new ( ) ;
37- let mut cmds = Vec :: new ( ) ;
38- let mut includes = Vec :: new ( ) ;
39- let mut binaries = Vec :: new ( ) ; // Binary lib paths
40- let mut errors = Vec :: new ( ) ;
41- let mut config = std:: collections:: HashMap :: new ( ) ;
42- let mut plugins = Vec :: new ( ) ; // Nowe: lista pluginów
37+ let mut deps: Vec < String > = Vec :: new ( ) ;
38+ let mut libs: Vec < String > = Vec :: new ( ) ;
39+ let mut vars: Vec < ( String , String ) > = Vec :: new ( ) ;
40+ let mut local_vars: Vec < ( String , String ) > = Vec :: new ( ) ;
41+ let mut cmds: Vec < String > = Vec :: new ( ) ;
42+ let mut includes: Vec < String > = Vec :: new ( ) ;
43+ let mut binaries: Vec < String > = Vec :: new ( ) ; // Binary lib paths
44+ let mut errors: Vec < String > = Vec :: new ( ) ;
45+ let mut config: std:: collections:: HashMap < String , String > = std:: collections:: HashMap :: new ( ) ;
46+ let mut plugins: Vec < Plugin > = Vec :: new ( ) ;
47+ let mut functions: std:: collections:: HashMap < String , Vec < String > > = std:: collections:: HashMap :: new ( ) ;
4348 let mut in_config = false ;
49+ let mut in_comment = false ;
50+ let mut in_function: Option < String > = None ;
4451 let mut line_num = 0 ;
4552 for line in io:: BufReader :: new ( file) . lines ( ) {
4653 line_num += 1 ;
47- let line = line?;
48- let line = line. trim ( ) . to_string ( ) ;
54+ let mut line = line?;
55+ line = line. trim ( ) . to_string ( ) ;
4956 if line. is_empty ( ) {
5057 continue ;
5158 }
59+ if line == "!!" {
60+ in_comment = !in_comment;
61+ continue ;
62+ }
63+ if in_comment {
64+ continue ;
65+ }
66+ let is_super = line. starts_with ( '^' ) ;
67+ if is_super {
68+ line = line[ 1 ..] . trim ( ) . to_string ( ) ;
69+ }
5270 if line == "[" {
5371 if in_config {
5472 errors. push ( format ! ( "Line {}: Nested config section" , line_num) ) ;
5573 }
74+ if in_function. is_some ( ) {
75+ errors. push ( format ! ( "Line {}: Config in function" , line_num) ) ;
76+ }
5677 in_config = true ;
5778 continue ;
5879 } else if line == "]" {
@@ -70,14 +91,67 @@ fn parse_hacker_file(path: &Path, verbose: bool, bytes_mode: bool) -> io::Result
7091 }
7192 continue ;
7293 }
94+ if line == ":" {
95+ if in_function. is_some ( ) {
96+ in_function = None ;
97+ } else {
98+ errors. push ( format ! ( "Line {}: Ending function without start" , line_num) ) ;
99+ }
100+ continue ;
101+ } else if line. starts_with ( ":" ) {
102+ let func_name = line[ 1 ..] . trim ( ) . to_string ( ) ;
103+ if func_name. is_empty ( ) {
104+ errors. push ( format ! ( "Line {}: Empty function name" , line_num) ) ;
105+ continue ;
106+ }
107+ if in_function. is_some ( ) {
108+ errors. push ( format ! ( "Line {}: Nested function" , line_num) ) ;
109+ }
110+ functions. insert ( func_name. clone ( ) , Vec :: new ( ) ) ;
111+ in_function = Some ( func_name) ;
112+ continue ;
113+ } else if line. starts_with ( "." ) {
114+ let func_name = line[ 1 ..] . trim ( ) . to_string ( ) ;
115+ if func_name. is_empty ( ) {
116+ errors. push ( format ! ( "Line {}: Empty function call" , line_num) ) ;
117+ continue ;
118+ }
119+ if let Some ( f_cmds) = functions. get ( & func_name) . cloned ( ) {
120+ let target = if let Some ( ref f) = in_function {
121+ functions. get_mut ( f) . unwrap ( )
122+ } else {
123+ & mut cmds
124+ } ;
125+ for c in & f_cmds {
126+ target. push ( c. clone ( ) ) ;
127+ }
128+ } else {
129+ errors. push ( format ! ( "Line {}: Unknown function {}" , line_num, func_name) ) ;
130+ }
131+ continue ;
132+ }
133+ if in_function. is_some ( ) {
134+ if !line. starts_with ( ">" ) && !line. starts_with ( "=" ) && !line. starts_with ( "?" ) && !line. starts_with ( "&" ) && !line. starts_with ( "!" ) && !line. starts_with ( "@" ) && !line. starts_with ( "$" ) && !line. starts_with ( "\\ " ) {
135+ errors. push ( format ! ( "Line {}: Invalid in function" , line_num) ) ;
136+ continue ;
137+ }
138+ }
73139 if line. starts_with ( "//" ) {
140+ if in_function. is_some ( ) {
141+ errors. push ( format ! ( "Line {}: Deps not allowed in function" , line_num) ) ;
142+ continue ;
143+ }
74144 let dep = line[ 2 ..] . trim ( ) . to_string ( ) ;
75145 if dep. is_empty ( ) {
76146 errors. push ( format ! ( "Line {}: Empty system dependency" , line_num) ) ;
77147 } else {
78148 deps. push ( dep) ;
79149 }
80150 } else if line. starts_with ( "#" ) {
151+ if in_function. is_some ( ) {
152+ errors. push ( format ! ( "Line {}: Libs not allowed in function" , line_num) ) ;
153+ continue ;
154+ }
81155 let lib = line[ 1 ..] . trim ( ) . to_string ( ) ;
82156 if lib. is_empty ( ) {
83157 errors. push ( format ! ( "Line {}: Empty library/include" , line_num) ) ;
@@ -87,10 +161,11 @@ fn parse_hacker_file(path: &Path, verbose: bool, bytes_mode: bool) -> io::Result
87161 let lib_bin_path = expand_home ( & format ! ( "{}/libs/{}" , HACKER_DIR , lib) ) ; // Binary file
88162 if Path :: new ( & lib_hacker_path) . exists ( ) {
89163 includes. push ( lib. clone ( ) ) ;
90- let ( sub_deps, sub_libs, sub_vars, sub_cmds, sub_includes, sub_binaries, sub_errors, sub_config, sub_plugins) = parse_hacker_file ( Path :: new ( & lib_hacker_path) , verbose, bytes_mode) ?;
164+ let ( sub_deps, sub_libs, sub_vars, sub_cmds, sub_includes, sub_binaries, sub_local_vars , sub_errors, sub_config, sub_plugins, sub_functions ) = parse_hacker_file ( Path :: new ( & lib_hacker_path) , verbose, bytes_mode) ?;
91165 deps. extend ( sub_deps) ;
92166 libs. extend ( sub_libs) ;
93167 vars. extend ( sub_vars) ;
168+ local_vars. extend ( sub_local_vars) ;
94169 cmds. extend ( sub_cmds) ;
95170 includes. extend ( sub_includes) ;
96171 binaries. extend ( sub_binaries) ;
@@ -99,6 +174,7 @@ fn parse_hacker_file(path: &Path, verbose: bool, bytes_mode: bool) -> io::Result
99174 }
100175 config. extend ( sub_config) ;
101176 plugins. extend ( sub_plugins) ;
177+ functions. extend ( sub_functions) ;
102178 } else if Path :: new ( & lib_bin_path) . exists ( ) && Path :: new ( & lib_bin_path) . metadata ( ) ?. permissions ( ) . mode ( ) & 0o111 != 0 {
103179 binaries. push ( lib_bin_path. clone ( ) ) ;
104180 if bytes_mode {
@@ -111,11 +187,15 @@ fn parse_hacker_file(path: &Path, verbose: bool, bytes_mode: bool) -> io::Result
111187 }
112188 } else if line. starts_with ( ">" ) {
113189 let parts: Vec < String > = line[ 1 ..] . split ( '!' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
114- let cmd = parts[ 0 ] . clone ( ) ;
190+ let mut cmd = parts[ 0 ] . clone ( ) ;
191+ if is_super {
192+ cmd = format ! ( "sudo {}" , cmd) ;
193+ }
115194 if cmd. is_empty ( ) {
116195 errors. push ( format ! ( "Line {}: Empty command" , line_num) ) ;
117196 } else {
118- cmds. push ( cmd) ;
197+ let target = if let Some ( ref f) = in_function { functions. get_mut ( f) . unwrap ( ) } else { & mut cmds } ;
198+ target. push ( cmd) ;
119199 }
120200 } else if line. starts_with ( "@" ) {
121201 if let Some ( eq_idx) = line. find ( '=' ) {
@@ -127,35 +207,50 @@ fn parse_hacker_file(path: &Path, verbose: bool, bytes_mode: bool) -> io::Result
127207 vars. push ( ( var, value) ) ;
128208 }
129209 } else {
130- // Nowe: obsługa pluginów @ nazwa_pluginu (bez =)
131- let plugin_name = line[ 1 ..] . trim ( ) . to_string ( ) ;
132- if plugin_name. is_empty ( ) {
133- errors. push ( format ! ( "Line {}: Empty plugin name" , line_num) ) ;
210+ errors. push ( format ! ( "Line {}: Invalid @ syntax" , line_num) ) ;
211+ }
212+ } else if line. starts_with ( "$" ) {
213+ if let Some ( eq_idx) = line. find ( '=' ) {
214+ let var = line[ 1 ..eq_idx] . trim ( ) . to_string ( ) ;
215+ let value = line[ eq_idx + 1 ..] . trim ( ) . to_string ( ) ;
216+ if var. is_empty ( ) || value. is_empty ( ) {
217+ errors. push ( format ! ( "Line {}: Invalid local variable" , line_num) ) ;
134218 } else {
135- // Szukaj pluginu w ~/.hackeros/hacker-lang/plugins/ jako binarka lub źródło
136- let plugin_path = expand_home ( & format ! ( "{}/plugins/{}" , HACKER_DIR , plugin_name) ) ;
137- if Path :: new ( & plugin_path) . exists ( ) && Path :: new ( & plugin_path) . metadata ( ) ?. permissions ( ) . mode ( ) & 0o111 != 0 {
138- plugins. push ( Plugin { name : plugin_name. clone ( ) } ) ;
139- if verbose {
140- println ! ( "Loaded plugin: {}" , plugin_name) ;
141- }
142- // Dodaj komendę do wykonania pluginu, np. cmds.push(format!("{} &", plugin_path));
143- } else {
144- errors. push ( format ! ( "Line {}: Plugin {} not found or not executable" , line_num, plugin_name) ) ;
219+ local_vars. push ( ( var, value) ) ;
220+ }
221+ } else {
222+ errors. push ( format ! ( "Line {}: Invalid $ syntax" , line_num) ) ;
223+ }
224+ } else if line. starts_with ( "\\ " ) {
225+ let plugin_name = line[ 1 ..] . trim ( ) . to_string ( ) ;
226+ if plugin_name. is_empty ( ) {
227+ errors. push ( format ! ( "Line {}: Empty plugin name" , line_num) ) ;
228+ } else {
229+ let plugin_path = expand_home ( & format ! ( "{}/plugins/{}" , HACKER_DIR , plugin_name) ) ;
230+ if Path :: new ( & plugin_path) . exists ( ) && Path :: new ( & plugin_path) . metadata ( ) ?. permissions ( ) . mode ( ) & 0o111 != 0 {
231+ plugins. push ( Plugin { name : plugin_name. clone ( ) , is_super } ) ;
232+ if verbose {
233+ println ! ( "Loaded plugin: {}" , plugin_name) ;
145234 }
235+ } else {
236+ errors. push ( format ! ( "Line {}: Plugin {} not found or not executable" , line_num, plugin_name) ) ;
146237 }
147238 }
148239 } else if line. starts_with ( "=" ) {
149240 let parts: Vec < String > = line[ 1 ..] . split ( '>' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
150241 if parts. len ( ) == 2 {
151242 if let Ok ( num) = parts[ 0 ] . parse :: < usize > ( ) {
152243 let cmd_parts: Vec < String > = parts[ 1 ] . split ( '!' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
153- let cmd = cmd_parts[ 0 ] . clone ( ) ;
244+ let mut cmd = cmd_parts[ 0 ] . clone ( ) ;
245+ if is_super {
246+ cmd = format ! ( "sudo {}" , cmd) ;
247+ }
154248 if cmd. is_empty ( ) {
155249 errors. push ( format ! ( "Line {}: Empty loop command" , line_num) ) ;
156250 } else {
251+ let target = if let Some ( ref f) = in_function { functions. get_mut ( f) . unwrap ( ) } else { & mut cmds } ;
157252 for _ in 0 ..num {
158- cmds . push ( cmd. clone ( ) ) ;
253+ target . push ( cmd. clone ( ) ) ;
159254 }
160255 }
161256 } else {
@@ -169,22 +264,31 @@ fn parse_hacker_file(path: &Path, verbose: bool, bytes_mode: bool) -> io::Result
169264 if parts. len ( ) == 2 {
170265 let condition = parts[ 0 ] . clone ( ) ;
171266 let cmd_parts: Vec < String > = parts[ 1 ] . split ( '!' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
172- let cmd = cmd_parts[ 0 ] . clone ( ) ;
267+ let mut cmd = cmd_parts[ 0 ] . clone ( ) ;
268+ if is_super {
269+ cmd = format ! ( "sudo {}" , cmd) ;
270+ }
173271 if condition. is_empty ( ) || cmd. is_empty ( ) {
174272 errors. push ( format ! ( "Line {}: Invalid conditional" , line_num) ) ;
175273 } else {
176- cmds. push ( format ! ( "if {}; then {}; fi" , condition, cmd) ) ;
274+ let if_cmd = format ! ( "if {}; then {}; fi" , condition, cmd) ;
275+ let target = if let Some ( ref f) = in_function { functions. get_mut ( f) . unwrap ( ) } else { & mut cmds } ;
276+ target. push ( if_cmd) ;
177277 }
178278 } else {
179279 errors. push ( format ! ( "Line {}: Invalid conditional syntax" , line_num) ) ;
180280 }
181281 } else if line. starts_with ( "&" ) {
182282 let parts: Vec < String > = line[ 1 ..] . split ( '!' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
183- let cmd = parts[ 0 ] . clone ( ) ;
184- if cmd. is_empty ( ) {
283+ let mut cmd = format ! ( "{} &" , parts[ 0 ] ) ;
284+ if is_super {
285+ cmd = format ! ( "sudo {}" , cmd) ;
286+ }
287+ if parts[ 0 ] . is_empty ( ) {
185288 errors. push ( format ! ( "Line {}: Empty background command" , line_num) ) ;
186289 } else {
187- cmds. push ( format ! ( "{} &" , cmd) ) ;
290+ let target = if let Some ( ref f) = in_function { functions. get_mut ( f) . unwrap ( ) } else { & mut cmds } ;
291+ target. push ( cmd) ;
188292 }
189293 } else if line. starts_with ( "!" ) {
190294 // Comment
@@ -195,27 +299,37 @@ fn parse_hacker_file(path: &Path, verbose: bool, bytes_mode: bool) -> io::Result
195299 if in_config {
196300 errors. push ( "Unclosed config section" . to_string ( ) ) ;
197301 }
302+ if in_comment {
303+ errors. push ( "Unclosed comment block" . to_string ( ) ) ;
304+ }
305+ if in_function. is_some ( ) {
306+ errors. push ( "Unclosed function block" . to_string ( ) ) ;
307+ }
198308 if verbose {
199309 println ! ( "System Deps: {:?}" , deps) ;
200310 println ! ( "Custom Libs: {:?}" , libs) ;
201311 println ! ( "Vars: {:?}" , vars) ;
312+ println ! ( "Local Vars: {:?}" , local_vars) ;
202313 println ! ( "Cmds: {:?}" , cmds) ;
203314 println ! ( "Includes: {:?}" , includes) ;
204315 println ! ( "Binaries: {:?}" , binaries) ;
205316 println ! ( "Plugins: {:?}" , plugins) ;
317+ println ! ( "Functions: {:?}" , functions) ;
206318 println ! ( "Config: {:?}" , config) ;
207319 if !errors. is_empty ( ) {
208320 println ! ( "Errors: {:?}" , errors) ;
209321 }
210322 }
211- Ok ( ( deps, libs, vars, cmds, includes, binaries, errors, config, plugins) )
323+ Ok ( ( deps, libs, vars, cmds, includes, binaries, local_vars , errors, config, plugins, functions ) )
212324}
325+
213326fn generate_check_cmd ( dep : & str ) -> String {
214327 if dep == "sudo" {
215328 return String :: new ( ) ;
216329 }
217330 format ! ( "command -v {} &> /dev/null || (sudo apt update && sudo apt install -y {})" , dep, dep)
218331}
332+
219333fn main ( ) -> io:: Result < ( ) > {
220334 let args: Vec < String > = env:: args ( ) . collect ( ) ;
221335 let mut bytes_mode = false ;
@@ -243,28 +357,42 @@ fn main() -> io::Result<()> {
243357 }
244358 let input_path = Path :: new ( & input_path_str) ;
245359 let output_path = Path :: new ( & output_path_str) ;
246- let ( deps, _libs, vars, cmds, _includes, binaries, errors, _config, plugins) = parse_hacker_file ( input_path, verbose, bytes_mode) ?;
360+ let ( deps, _libs, vars, cmds, _includes, binaries, local_vars , errors, _config, plugins, _functions ) = parse_hacker_file ( input_path, verbose, bytes_mode) ?;
247361 if !errors. is_empty ( ) {
248362 for err in errors {
249363 eprintln ! ( "{}" , err) ;
250364 }
251365 process:: exit ( 1 ) ;
252366 }
253- let mut final_cmds = Vec :: new ( ) ;
367+ let mut substituted_cmds: Vec < String > = Vec :: new ( ) ;
368+ for cmd in cmds {
369+ let mut sub_cmd = cmd. clone ( ) ;
370+ for ( k, v) in & local_vars {
371+ let pattern = format ! ( "${}" , k) ;
372+ sub_cmd = sub_cmd. replace ( & pattern, v) ;
373+ }
374+ substituted_cmds. push ( sub_cmd) ;
375+ }
376+ let mut final_cmds: Vec < String > = Vec :: new ( ) ;
254377 for dep in deps {
255378 let check = generate_check_cmd ( & dep) ;
256379 if !check. is_empty ( ) {
257380 final_cmds. push ( check) ;
258381 }
259382 }
260- final_cmds. extend ( cmds ) ;
383+ final_cmds. extend ( substituted_cmds ) ;
261384 // Dla pluginów, dodaj komendy wykonania
262385 for plugin in plugins {
263386 let plugin_path = expand_home ( & format ! ( "{}/plugins/{}" , HACKER_DIR , plugin. name) ) ;
264- final_cmds. push ( format ! ( "{} &" , plugin_path) ) ;
387+ let cmd = if plugin. is_super {
388+ format ! ( "sudo {} &" , plugin_path)
389+ } else {
390+ format ! ( "{} &" , plugin_path)
391+ } ;
392+ final_cmds. push ( cmd) ;
265393 }
266394 // Dla binarek, extract cmds
267- let mut bin_extract_cmds = Vec :: new ( ) ;
395+ let mut bin_extract_cmds: Vec < String > = Vec :: new ( ) ;
268396 for ( i, bin_path) in binaries. iter ( ) . enumerate ( ) {
269397 let bin_data = fs:: read ( bin_path) ?;
270398 let data_name = format ! ( "bin_lib_{i}" ) ;
@@ -280,7 +408,7 @@ fn main() -> io::Result<()> {
280408 let builder = ObjectBuilder :: new (
281409 isa,
282410 output_path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . as_bytes ( ) . to_vec ( ) ,
283- cranelift_module:: default_libcall_names ( ) ,
411+ cranelift_module:: default_libcall_names ( ) ,
284412 ) . expect ( "ObjectBuilder failed" ) ;
285413 let mut module = ObjectModule :: new ( builder) ;
286414 let pointer_type = module. target_config ( ) . pointer_type ( ) ;
0 commit comments