Skip to content

Commit 1e3feff

Browse files
authored
Create parse.zig
1 parent d9bfdb4 commit 1e3feff

File tree

1 file changed

+241
-0
lines changed

1 file changed

+241
-0
lines changed

hacker-parser/parse.zig

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
const std = @import("std");
2+
const utils = @import("utils.zig");
3+
pub const ParseResult = 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+
plugins: std.ArrayList([]const u8), // Nowe: pluginy
11+
errors: std.ArrayList([]const u8),
12+
config_data: std.StringHashMap([]const u8),
13+
};
14+
pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, verbose: bool) !ParseResult {
15+
var deps = std.StringHashMap(void).init(allocator);
16+
var libs = std.StringHashMap(void).init(allocator);
17+
var vars_dict = std.StringHashMap([]const u8).init(allocator);
18+
var cmds = std.ArrayList([]const u8).init(allocator);
19+
var includes = std.ArrayList([]const u8).init(allocator);
20+
var binaries = std.ArrayList([]const u8).init(allocator);
21+
var plugins = std.ArrayList([]const u8).init(allocator); // Nowe
22+
var errors = std.ArrayList([]const u8).init(allocator);
23+
var config_data = std.StringHashMap([]const u8).init(allocator);
24+
var in_config = false;
25+
var line_num: u32 = 0;
26+
const home = std.posix.getenv("HOME") orelse "";
27+
const hacker_dir = try std.fs.path.join(allocator, &.{ home, utils.HACKER_DIR_SUFFIX });
28+
defer allocator.free(hacker_dir);
29+
const console = std.io.getStdOut().writer();
30+
const file = std.fs.cwd().openFile(file_path, .{}) catch |err| {
31+
if (err == error.FileNotFound) {
32+
if (verbose) try console.print("File {s} not found\n", .{file_path});
33+
try errors.append(try std.fmt.allocPrint(allocator, "File {s} not found", .{file_path}));
34+
return ParseResult{
35+
.deps = deps,
36+
.libs = libs,
37+
.vars_dict = vars_dict,
38+
.cmds = cmds,
39+
.includes = includes,
40+
.binaries = binaries,
41+
.plugins = plugins,
42+
.errors = errors,
43+
.config_data = config_data,
44+
};
45+
}
46+
return err;
47+
};
48+
defer file.close();
49+
const reader = file.reader();
50+
var line_buf: [4096]u8 = undefined;
51+
while (reader.readUntilDelimiterOrEof(&line_buf, '\n') catch null) |line_slice| {
52+
line_num += 1;
53+
const line_trimmed = std.mem.trim(u8, line_slice, " \t\r\n");
54+
if (line_trimmed.len == 0) continue;
55+
const line = try allocator.dupe(u8, line_trimmed);
56+
defer allocator.free(line);
57+
if (std.mem.eql(u8, line, "[")) {
58+
if (in_config) {
59+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Nested config section", .{line_num}));
60+
}
61+
in_config = true;
62+
continue;
63+
} else if (std.mem.eql(u8, line, "]")) {
64+
if (!in_config) {
65+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Closing ] without [", .{line_num}));
66+
}
67+
in_config = false;
68+
continue;
69+
}
70+
if (in_config) {
71+
if (std.mem.indexOfScalar(u8, line, '=')) |eq_pos| {
72+
const key = std.mem.trim(u8, line[0..eq_pos], " \t");
73+
const value = std.mem.trim(u8, line[eq_pos + 1 ..], " \t");
74+
try config_data.put(try allocator.dupe(u8, key), try allocator.dupe(u8, value));
75+
}
76+
continue;
77+
}
78+
if (std.mem.startsWith(u8, line, "//")) {
79+
const dep = std.mem.trim(u8, line[2..], " \t");
80+
if (dep.len > 0) {
81+
_ = try deps.put(try allocator.dupe(u8, dep), {});
82+
} else {
83+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty system dependency", .{line_num}));
84+
}
85+
} else if (std.mem.startsWith(u8, line, "#")) {
86+
const lib = std.mem.trim(u8, line[1..], " \t");
87+
if (lib.len > 0) {
88+
const lib_dir = try std.fs.path.join(allocator, &.{ hacker_dir, "libs", lib });
89+
defer allocator.free(lib_dir);
90+
const lib_hacker_path = try std.fs.path.join(allocator, &.{ lib_dir, "main.hacker" });
91+
defer allocator.free(lib_hacker_path);
92+
const lib_bin_path = try std.fs.path.join(allocator, &.{ hacker_dir, "libs", lib });
93+
defer allocator.free(lib_bin_path);
94+
if (std.fs.cwd().access(lib_hacker_path, .{})) |_| {
95+
try includes.append(try allocator.dupe(u8, lib));
96+
var sub = try parse_hacker_file(allocator, lib_hacker_path, verbose);
97+
try utils.mergeHashMaps(void, &deps, sub.deps, allocator);
98+
try utils.mergeHashMaps(void, &libs, sub.libs, allocator);
99+
try utils.mergeStringHashMaps(&vars_dict, sub.vars_dict, allocator);
100+
try cmds.appendSlice(sub.cmds.items);
101+
try includes.appendSlice(sub.includes.items);
102+
try binaries.appendSlice(sub.binaries.items);
103+
try plugins.appendSlice(sub.plugins.items); // Merge pluginów
104+
for (sub.errors.items) |sub_err| {
105+
try errors.append(try std.fmt.allocPrint(allocator, "In {s}: {s}", .{ lib, sub_err }));
106+
}
107+
utils.deinitParseResult(&sub, allocator);
108+
} else |_| {}
109+
if (std.posix.access(lib_bin_path, std.posix.X_OK)) |_| {
110+
try binaries.append(try allocator.dupe(u8, lib_bin_path));
111+
} else |_| {
112+
_ = try libs.put(try allocator.dupe(u8, lib), {});
113+
}
114+
} else {
115+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty library/include", .{line_num}));
116+
}
117+
} else if (std.mem.startsWith(u8, line, ">")) {
118+
const cmd_part = std.mem.trim(u8, if (std.mem.indexOfScalar(u8, line[1..], '!')) |pos| line[1 .. 1 + pos] else line[1..], " \t");
119+
if (cmd_part.len > 0) {
120+
try cmds.append(try allocator.dupe(u8, cmd_part));
121+
} else {
122+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty command", .{line_num}));
123+
}
124+
} else if (std.mem.startsWith(u8, line, "@")) {
125+
if (std.mem.indexOfScalar(u8, line[1..], '=')) |eq_pos| {
126+
const var_name = std.mem.trim(u8, line[1 .. 1 + eq_pos], " \t");
127+
const value = std.mem.trim(u8, line[1 + eq_pos + 1 ..], " \t");
128+
if (var_name.len > 0 and value.len > 0) {
129+
try vars_dict.put(try allocator.dupe(u8, var_name), try allocator.dupe(u8, value));
130+
} else {
131+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid variable", .{line_num}));
132+
}
133+
} else {
134+
// Nowe: plugin @ nazwa (bez =)
135+
const plugin_name = std.mem.trim(u8, line[1..], " \t");
136+
if (plugin_name.len > 0) {
137+
const plugin_dir = try std.fs.path.join(allocator, &.{ hacker_dir, "plugins", plugin_name });
138+
defer allocator.free(plugin_dir);
139+
if (std.posix.access(plugin_dir, std.posix.X_OK)) |_| {
140+
try plugins.append(try allocator.dupe(u8, plugin_dir));
141+
} else |_| {
142+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Plugin {s} not found or not executable", .{line_num, plugin_name}));
143+
}
144+
} else {
145+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty plugin name", .{line_num}));
146+
}
147+
}
148+
} else if (std.mem.startsWith(u8, line, "=")) {
149+
if (std.mem.indexOfScalar(u8, line[1..], '>')) |gt_pos| {
150+
const num_str = std.mem.trim(u8, line[1 .. 1 + gt_pos], " \t");
151+
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");
152+
const num = std.fmt.parseInt(i32, num_str, 10) catch {
153+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid loop count", .{line_num}));
154+
continue;
155+
};
156+
if (num < 0) {
157+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Negative loop count", .{line_num}));
158+
continue;
159+
}
160+
if (cmd_part.len > 0) {
161+
var i: i32 = 0;
162+
while (i < num) : (i += 1) {
163+
try cmds.append(try allocator.dupe(u8, cmd_part));
164+
}
165+
} else {
166+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty loop command", .{line_num}));
167+
}
168+
} else {
169+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid loop syntax", .{line_num}));
170+
}
171+
} else if (std.mem.startsWith(u8, line, "?")) {
172+
if (std.mem.indexOfScalar(u8, line[1..], '>')) |gt_pos| {
173+
const condition = std.mem.trim(u8, line[1 .. 1 + gt_pos], " \t");
174+
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");
175+
if (condition.len > 0 and cmd_part.len > 0) {
176+
const if_cmd = try std.fmt.allocPrint(allocator, "if {s}; then {s}; fi", .{ condition, cmd_part });
177+
try cmds.append(if_cmd);
178+
} else {
179+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid conditional", .{line_num}));
180+
}
181+
} else {
182+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid conditional syntax", .{line_num}));
183+
}
184+
} else if (std.mem.startsWith(u8, line, "&")) {
185+
const cmd_part = std.mem.trim(u8, if (std.mem.indexOfScalar(u8, line[1..], '!')) |pos| line[1 .. 1 + pos] else line[1..], " \t");
186+
if (cmd_part.len > 0) {
187+
const bg_cmd = try std.fmt.allocPrint(allocator, "{s} &", .{ cmd_part });
188+
try cmds.append(bg_cmd);
189+
} else {
190+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty background command", .{line_num}));
191+
}
192+
} else if (std.mem.startsWith(u8, line, "!")) {
193+
// pass
194+
} else {
195+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid syntax", .{line_num}));
196+
}
197+
}
198+
if (in_config) {
199+
try errors.append(try allocator.dupe(u8, "Unclosed config section"));
200+
}
201+
if (verbose) {
202+
var dep_keys = try allocator.alloc([]const u8, deps.count());
203+
defer allocator.free(dep_keys);
204+
var i: usize = 0;
205+
var dep_it = deps.keyIterator();
206+
while (dep_it.next()) |key| {
207+
dep_keys[i] = key.*;
208+
i += 1;
209+
}
210+
try console.print("System Deps: {any}\n", .{dep_keys});
211+
var lib_keys = try allocator.alloc([]const u8, libs.count());
212+
defer allocator.free(lib_keys);
213+
i = 0;
214+
var lib_it = libs.keyIterator();
215+
while (lib_it.next()) |key| {
216+
lib_keys[i] = key.*;
217+
i += 1;
218+
}
219+
try console.print("Custom Libs: {any}\n", .{lib_keys});
220+
try console.print("Vars: {any}\n", .{vars_dict});
221+
try console.print("Cmds: {any}\n", .{cmds.items});
222+
try console.print("Includes: {any}\n", .{includes.items});
223+
try console.print("Binaries: {any}\n", .{binaries.items});
224+
try console.print("Plugins: {any}\n", .{plugins.items});
225+
try console.print("Config: {any}\n", .{config_data});
226+
if (errors.items.len > 0) {
227+
try console.print("Errors: {any}\n", .{errors.items});
228+
}
229+
}
230+
return ParseResult{
231+
.deps = deps,
232+
.libs = libs,
233+
.vars_dict = vars_dict,
234+
.cmds = cmds,
235+
.includes = includes,
236+
.binaries = binaries,
237+
.plugins = plugins,
238+
.errors = errors,
239+
.config_data = config_data,
240+
};
241+
}

0 commit comments

Comments
 (0)