11const std = @import ("std" );
2- const HACKER_DIR_SUFFIX = "/.hackeros/hacker-lang" ;
3- fn parse_hacker_file (allocator : std.mem.Allocator , file_path : []const u8 , verbose : bool ) ! struct {
4- deps : std .StringHashMap (void ),
5- libs : std .StringHashMap (void ),
6- vars_dict : std .StringHashMap ([]const u8 ),
7- cmds : std .ArrayList ([]const u8 ),
8- includes : std .ArrayList ([]const u8 ),
9- binaries : std .ArrayList ([]const u8 ),
10- errors : std .ArrayList ([]const u8 ),
11- config_data : std .StringHashMap ([]const u8 ),
12- } {
13- var deps = std .StringHashMap (void ).init (allocator );
14- var libs = std .StringHashMap (void ).init (allocator );
15- var vars_dict = std .StringHashMap ([]const u8 ).init (allocator );
16- var cmds = std .ArrayList ([]const u8 ).init (allocator );
17- var includes = std .ArrayList ([]const u8 ).init (allocator );
18- var binaries = std .ArrayList ([]const u8 ).init (allocator );
19- var errors = std .ArrayList ([]const u8 ).init (allocator );
20- var config_data = std .StringHashMap ([]const u8 ).init (allocator );
21- var in_config = false ;
22- var line_num : u32 = 0 ;
23- const home = std .posix .getenv ("HOME" ) orelse "" ;
24- const hacker_dir = try std .fs .path .join (allocator , &.{ home , HACKER_DIR_SUFFIX });
25- defer allocator .free (hacker_dir );
26- const console = std .io .getStdOut ().writer ();
27- const file = std .fs .cwd ().openFile (file_path , .{}) catch | err | {
28- if (err == error .FileNotFound ) {
29- if (verbose ) try console .print ("File {s} not found\n " , .{file_path });
30- try errors .append (try std .fmt .allocPrint (allocator , "File {s} not found" , .{file_path }));
31- return .{
32- .deps = deps ,
33- .libs = libs ,
34- .vars_dict = vars_dict ,
35- .cmds = cmds ,
36- .includes = includes ,
37- .binaries = binaries ,
38- .errors = errors ,
39- .config_data = config_data ,
40- };
41- }
42- return err ;
43- };
44- defer file .close ();
45- const reader = file .reader ();
46- var line_buf : [4096 ]u8 = undefined ;
47- while (reader .readUntilDelimiterOrEof (& line_buf , '\n ' ) catch null ) | line_slice | {
48- line_num += 1 ;
49- const line_trimmed = std .mem .trim (u8 , line_slice , " \t \r \n " );
50- if (line_trimmed .len == 0 ) continue ;
51- const line = try allocator .dupe (u8 , line_trimmed );
52- defer allocator .free (line );
53- if (std .mem .eql (u8 , line , "[" )) {
54- if (in_config ) {
55- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Nested config section" , .{line_num }));
56- }
57- in_config = true ;
58- continue ;
59- } else if (std .mem .eql (u8 , line , "]" )) {
60- if (! in_config ) {
61- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Closing ] without [" , .{line_num }));
62- }
63- in_config = false ;
64- continue ;
65- }
66- if (in_config ) {
67- if (std .mem .indexOfScalar (u8 , line , '=' )) | eq_pos | {
68- const key = std .mem .trim (u8 , line [0.. eq_pos ], " \t " );
69- const value = std .mem .trim (u8 , line [eq_pos + 1 .. ], " \t " );
70- try config_data .put (try allocator .dupe (u8 , key ), try allocator .dupe (u8 , value ));
71- }
72- continue ;
73- }
74- if (std .mem .startsWith (u8 , line , "//" )) {
75- const dep = std .mem .trim (u8 , line [2.. ], " \t " );
76- if (dep .len > 0 ) {
77- _ = try deps .put (try allocator .dupe (u8 , dep ), {});
78- } else {
79- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Empty system dependency" , .{line_num }));
80- }
81- } else if (std .mem .startsWith (u8 , line , "#" )) {
82- const lib = std .mem .trim (u8 , line [1.. ], " \t " );
83- if (lib .len > 0 ) {
84- const lib_dir = try std .fs .path .join (allocator , &.{ hacker_dir , "libs" , lib });
85- defer allocator .free (lib_dir );
86- const lib_hacker_path = try std .fs .path .join (allocator , &.{ lib_dir , "main.hacker" });
87- defer allocator .free (lib_hacker_path );
88- const lib_bin_path = try std .fs .path .join (allocator , &.{ hacker_dir , "libs" , lib });
89- defer allocator .free (lib_bin_path );
90- if (std .fs .cwd ().access (lib_hacker_path , .{})) | _ | {
91- try includes .append (try allocator .dupe (u8 , lib ));
92- var sub = try parse_hacker_file (allocator , lib_hacker_path , verbose );
93- var dep_it = sub .deps .keyIterator ();
94- while (dep_it .next ()) | sub_dep_ptr | {
95- const sub_dep = sub_dep_ptr .* ;
96- _ = try deps .put (try allocator .dupe (u8 , sub_dep ), {});
97- }
98- var lib_it = sub .libs .keyIterator ();
99- while (lib_it .next ()) | sub_lib_ptr | {
100- const sub_lib = sub_lib_ptr .* ;
101- _ = try libs .put (try allocator .dupe (u8 , sub_lib ), {});
102- }
103- {
104- var sub_it = sub .vars_dict .iterator ();
105- while (sub_it .next ()) | entry | {
106- try vars_dict .put (try allocator .dupe (u8 , entry .key_ptr .* ), try allocator .dupe (u8 , entry .value_ptr .* ));
107- }
108- }
109- try cmds .appendSlice (sub .cmds .items );
110- try includes .appendSlice (sub .includes .items );
111- try binaries .appendSlice (sub .binaries .items );
112- for (sub .errors .items ) | sub_err | {
113- try errors .append (try std .fmt .allocPrint (allocator , "In {s}: {s}" , .{ lib , sub_err }));
114- }
115- // Deinit sub resources
116- sub .deps .deinit ();
117- sub .libs .deinit ();
118- sub .vars_dict .deinit ();
119- sub .cmds .deinit ();
120- sub .includes .deinit ();
121- sub .binaries .deinit ();
122- sub .errors .deinit ();
123- sub .config_data .deinit ();
124- } else | _ | {} // ignore access error for now
125- if (std .posix .access (lib_bin_path , std .posix .X_OK )) | _ | {
126- try binaries .append (try allocator .dupe (u8 , lib_bin_path ));
127- } else | _ | {
128- _ = try libs .put (try allocator .dupe (u8 , lib ), {});
129- }
130- } else {
131- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Empty library/include" , .{line_num }));
132- }
133- } else if (std .mem .startsWith (u8 , line , ">" )) {
134- const cmd_part = std .mem .trim (u8 , if (std .mem .indexOfScalar (u8 , line [1.. ], '!' )) | pos | line [1 .. 1 + pos ] else line [1.. ], " \t " );
135- if (cmd_part .len > 0 ) {
136- try cmds .append (try allocator .dupe (u8 , cmd_part ));
137- } else {
138- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Empty command" , .{line_num }));
139- }
140- } else if (std .mem .startsWith (u8 , line , "@" )) {
141- if (std .mem .indexOfScalar (u8 , line [1.. ], '=' )) | eq_pos | {
142- const var_name = std .mem .trim (u8 , line [1 .. 1 + eq_pos ], " \t " );
143- const value = std .mem .trim (u8 , line [1 + eq_pos + 1 .. ], " \t " );
144- if (var_name .len > 0 and value .len > 0 ) {
145- try vars_dict .put (try allocator .dupe (u8 , var_name ), try allocator .dupe (u8 , value ));
146- } else {
147- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Invalid variable" , .{line_num }));
148- }
149- } else {
150- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Missing = in variable" , .{line_num }));
151- }
152- } else if (std .mem .startsWith (u8 , line , "=" )) {
153- if (std .mem .indexOfScalar (u8 , line [1.. ], '>' )) | gt_pos | {
154- const num_str = std .mem .trim (u8 , line [1 .. 1 + gt_pos ], " \t " );
155- const cmd_part = std .mem .trim (u8 , if (std .mem .indexOfScalar (u8 , line [1 + gt_pos + 1 .. ], '!' )) | pos | line [1 + gt_pos + 1 .. 1 + gt_pos + 1 + pos ] else line [1 + gt_pos + 1 .. ], " \t " );
156- const num = std .fmt .parseInt (i32 , num_str , 10 ) catch {
157- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Invalid loop count" , .{line_num }));
158- continue ;
159- };
160- if (num < 0 ) {
161- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Negative loop count" , .{line_num }));
162- continue ;
163- }
164- if (cmd_part .len > 0 ) {
165- var i : i32 = 0 ;
166- while (i < num ) : (i += 1 ) {
167- try cmds .append (try allocator .dupe (u8 , cmd_part ));
168- }
169- } else {
170- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Empty loop command" , .{line_num }));
171- }
172- } else {
173- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Invalid loop syntax" , .{line_num }));
174- }
175- } else if (std .mem .startsWith (u8 , line , "?" )) {
176- if (std .mem .indexOfScalar (u8 , line [1.. ], '>' )) | gt_pos | {
177- const condition = std .mem .trim (u8 , line [1 .. 1 + gt_pos ], " \t " );
178- const cmd_part = std .mem .trim (u8 , if (std .mem .indexOfScalar (u8 , line [1 + gt_pos + 1 .. ], '!' )) | pos | line [1 + gt_pos + 1 .. 1 + gt_pos + 1 + pos ] else line [1 + gt_pos + 1 .. ], " \t " );
179- if (condition .len > 0 and cmd_part .len > 0 ) {
180- const if_cmd = try std .fmt .allocPrint (allocator , "if {s}; then {s}; fi" , .{ condition , cmd_part });
181- try cmds .append (if_cmd );
182- } else {
183- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Invalid conditional" , .{line_num }));
184- }
185- } else {
186- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Invalid conditional syntax" , .{line_num }));
187- }
188- } else if (std .mem .startsWith (u8 , line , "&" )) {
189- const cmd_part = std .mem .trim (u8 , if (std .mem .indexOfScalar (u8 , line [1.. ], '!' )) | pos | line [1 .. 1 + pos ] else line [1.. ], " \t " );
190- if (cmd_part .len > 0 ) {
191- const bg_cmd = try std .fmt .allocPrint (allocator , "{s} &" , .{ cmd_part });
192- try cmds .append (bg_cmd );
193- } else {
194- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Empty background command" , .{line_num }));
195- }
196- } else if (std .mem .startsWith (u8 , line , "!" )) {
197- // pass
198- } else {
199- try errors .append (try std .fmt .allocPrint (allocator , "Line {d}: Invalid syntax" , .{line_num }));
200- }
201- }
202- if (in_config ) {
203- try errors .append (try allocator .dupe (u8 , "Unclosed config section" ));
204- }
205- if (verbose ) {
206- var dep_keys = try allocator .alloc ([]const u8 , deps .count ());
207- defer allocator .free (dep_keys );
208- var i : usize = 0 ;
209- var dep_it = deps .keyIterator ();
210- while (dep_it .next ()) | key | {
211- dep_keys [i ] = key .* ;
212- i += 1 ;
213- }
214- try console .print ("System Deps: {any}\n " , .{dep_keys });
215- var lib_keys = try allocator .alloc ([]const u8 , libs .count ());
216- defer allocator .free (lib_keys );
217- i = 0 ;
218- var lib_it = libs .keyIterator ();
219- while (lib_it .next ()) | key | {
220- lib_keys [i ] = key .* ;
221- i += 1 ;
222- }
223- try console .print ("Custom Libs: {any}\n " , .{lib_keys });
224- try console .print ("Vars: {any}\n " , .{vars_dict });
225- try console .print ("Cmds: {any}\n " , .{cmds .items });
226- try console .print ("Includes: {any}\n " , .{includes .items });
227- try console .print ("Binaries: {any}\n " , .{binaries .items });
228- try console .print ("Config: {any}\n " , .{config_data });
229- if (errors .items .len > 0 ) {
230- try console .print ("Errors: {any}\n " , .{errors .items });
231- }
232- }
233- return .{
234- .deps = deps ,
235- .libs = libs ,
236- .vars_dict = vars_dict ,
237- .cmds = cmds ,
238- .includes = includes ,
239- .binaries = binaries ,
240- .errors = errors ,
241- .config_data = config_data ,
242- };
243- }
2+ const parse = @import ("parse.zig" );
3+ const utils = @import ("utils.zig" );
2444pub fn main () ! void {
2455 var gpa = std .heap .GeneralPurposeAllocator (.{}){};
2466 defer _ = gpa .deinit ();
@@ -262,43 +22,29 @@ pub fn main() !void {
26222 try std .io .getStdErr ().writer ().print ("Usage: hacker-parser [--verbose] <file>\n " , .{});
26323 std .process .exit (1 );
26424 }
265- var res = try parse_hacker_file (allocator , file_path .? , verbose );
266- defer res .deps .deinit ();
267- defer res .libs .deinit ();
268- defer res .vars_dict .deinit ();
269- defer res .cmds .deinit ();
270- defer res .includes .deinit ();
271- defer res .binaries .deinit ();
272- defer res .errors .deinit ();
273- defer res .config_data .deinit ();
274- var deps_list = std .ArrayList ([]const u8 ).init (allocator );
275- defer deps_list .deinit ();
276- var dep_it = res .deps .keyIterator ();
277- while (dep_it .next ()) | key | {
278- try deps_list .append (key .* );
279- }
280- var libs_list = std .ArrayList ([]const u8 ).init (allocator );
281- defer libs_list .deinit ();
282- var lib_it = res .libs .keyIterator ();
283- while (lib_it .next ()) | key | {
284- try libs_list .append (key .* );
285- }
25+ var res = try parse .parse_hacker_file (allocator , file_path .? , verbose );
26+ defer utils .deinitParseResult (& res , allocator );
27+ try outputJson (res );
28+ }
29+ fn outputJson (res : parse.ParseResult ) ! void {
28630 const stdout = std .io .getStdOut ().writer ();
28731 try stdout .print ("{{" , .{});
28832 try stdout .print ("\" deps\" :[" , .{});
28933 var first = true ;
290- for (deps_list .items ) | d | {
34+ var dep_it = res .deps .keyIterator ();
35+ while (dep_it .next ()) | key | {
29136 if (! first ) try stdout .print ("," , .{});
29237 first = false ;
293- try std .json .encodeJsonString (d , .{}, stdout );
38+ try std .json .encodeJsonString (key .* , .{}, stdout );
29439 }
29540 try stdout .print ("]," , .{});
29641 try stdout .print ("\" libs\" :[" , .{});
29742 first = true ;
298- for (libs_list .items ) | l | {
43+ var lib_it = res .libs .keyIterator ();
44+ while (lib_it .next ()) | key | {
29945 if (! first ) try stdout .print ("," , .{});
30046 first = false ;
301- try std .json .encodeJsonString (l , .{}, stdout );
47+ try std .json .encodeJsonString (key .* , .{}, stdout );
30248 }
30349 try stdout .print ("]," , .{});
30450 try stdout .print ("\" vars\" :{{" , .{});
@@ -336,6 +82,14 @@ pub fn main() !void {
33682 try std .json .encodeJsonString (b , .{}, stdout );
33783 }
33884 try stdout .print ("]," , .{});
85+ try stdout .print ("\" plugins\" :[" , .{});
86+ first = true ;
87+ for (res .plugins .items ) | p | {
88+ if (! first ) try stdout .print ("," , .{});
89+ first = false ;
90+ try std .json .encodeJsonString (p , .{}, stdout );
91+ }
92+ try stdout .print ("]," , .{});
33993 try stdout .print ("\" errors\" :[" , .{});
34094 first = true ;
34195 for (res .errors .items ) | e | {
0 commit comments