Skip to content

Commit fe7a634

Browse files
authored
Update parse.zig
1 parent 3bb5804 commit fe7a634

File tree

1 file changed

+158
-25
lines changed

1 file changed

+158
-25
lines changed

hacker-parser/parse.zig

Lines changed: 158 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,40 @@
11
const std = @import("std");
22
const utils = @import("utils.zig");
3+
4+
pub const Plugin = struct {
5+
path: []const u8,
6+
is_super: bool,
7+
};
8+
39
pub const ParseResult = struct {
410
deps: std.StringHashMap(void),
511
libs: std.StringHashMap(void),
612
vars_dict: std.StringHashMap([]const u8),
13+
local_vars: std.StringHashMap([]const u8),
714
cmds: std.ArrayList([]const u8),
815
includes: std.ArrayList([]const u8),
916
binaries: std.ArrayList([]const u8),
10-
plugins: std.ArrayList([]const u8), // Nowe: pluginy
17+
plugins: std.ArrayList(Plugin),
18+
functions: std.StringHashMap(std.ArrayList([]const u8)),
1119
errors: std.ArrayList([]const u8),
1220
config_data: std.StringHashMap([]const u8),
1321
};
22+
1423
pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, verbose: bool) !ParseResult {
1524
var deps = std.StringHashMap(void).init(allocator);
1625
var libs = std.StringHashMap(void).init(allocator);
1726
var vars_dict = std.StringHashMap([]const u8).init(allocator);
27+
var local_vars = std.StringHashMap([]const u8).init(allocator);
1828
var cmds = std.ArrayList([]const u8).init(allocator);
1929
var includes = std.ArrayList([]const u8).init(allocator);
2030
var binaries = std.ArrayList([]const u8).init(allocator);
21-
var plugins = std.ArrayList([]const u8).init(allocator); // Nowe
31+
var plugins = std.ArrayList(Plugin).init(allocator);
32+
var functions = std.StringHashMap(std.ArrayList([]const u8)).init(allocator);
2233
var errors = std.ArrayList([]const u8).init(allocator);
2334
var config_data = std.StringHashMap([]const u8).init(allocator);
2435
var in_config = false;
36+
var in_comment = false;
37+
var in_function: ?[]const u8 = null;
2538
var line_num: u32 = 0;
2639
const home = std.posix.getenv("HOME") orelse "";
2740
const hacker_dir = try std.fs.path.join(allocator, &.{ home, utils.HACKER_DIR_SUFFIX });
@@ -35,10 +48,12 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
3548
.deps = deps,
3649
.libs = libs,
3750
.vars_dict = vars_dict,
51+
.local_vars = local_vars,
3852
.cmds = cmds,
3953
.includes = includes,
4054
.binaries = binaries,
4155
.plugins = plugins,
56+
.functions = functions,
4257
.errors = errors,
4358
.config_data = config_data,
4459
};
@@ -52,12 +67,26 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
5267
line_num += 1;
5368
const line_trimmed = std.mem.trim(u8, line_slice, " \t\r\n");
5469
if (line_trimmed.len == 0) continue;
55-
const line = try allocator.dupe(u8, line_trimmed);
70+
var line = try allocator.dupe(u8, line_trimmed);
5671
defer allocator.free(line);
72+
if (std.mem.eql(u8, line, "!!")) {
73+
in_comment = !in_comment;
74+
continue;
75+
}
76+
if (in_comment) continue;
77+
const is_super = std.mem.startsWith(u8, line, "^");
78+
if (is_super) {
79+
const new_line = std.mem.trim(u8, line[1..], " \t");
80+
allocator.free(line);
81+
line = try allocator.dupe(u8, new_line);
82+
}
5783
if (std.mem.eql(u8, line, "[")) {
5884
if (in_config) {
5985
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Nested config section", .{line_num}));
6086
}
87+
if (in_function != null) {
88+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Config in function", .{line_num}));
89+
}
6190
in_config = true;
6291
continue;
6392
} else if (std.mem.eql(u8, line, "]")) {
@@ -75,14 +104,64 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
75104
}
76105
continue;
77106
}
107+
if (std.mem.eql(u8, line, ":")) {
108+
if (in_function != null) {
109+
in_function = null;
110+
} else {
111+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Ending function without start", .{line_num}));
112+
}
113+
continue;
114+
} else if (std.mem.startsWith(u8, line, ":")) {
115+
const func_name = std.mem.trim(u8, line[1..], " \t");
116+
if (func_name.len == 0) {
117+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty function name", .{line_num}));
118+
continue;
119+
}
120+
if (in_function != null) {
121+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Nested function", .{line_num}));
122+
}
123+
const func_name_dupe = try allocator.dupe(u8, func_name);
124+
try functions.put(func_name_dupe, std.ArrayList([]const u8).init(allocator));
125+
in_function = func_name_dupe;
126+
continue;
127+
} else if (std.mem.startsWith(u8, line, ".")) {
128+
const func_name = std.mem.trim(u8, line[1..], " \t");
129+
if (func_name.len == 0) {
130+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty function call", .{line_num}));
131+
continue;
132+
}
133+
if (functions.get(func_name)) |func_cmds| {
134+
var target = if (in_function) |f| functions.getPtr(f).? else &cmds;
135+
for (func_cmds.items) |c| {
136+
try target.append(try allocator.dupe(u8, c));
137+
}
138+
} else {
139+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Unknown function {s}", .{line_num, func_name}));
140+
}
141+
continue;
142+
}
143+
if (in_function != null) {
144+
if (!std.mem.startsWith(u8, line, ">") and !std.mem.startsWith(u8, line, "=") and !std.mem.startsWith(u8, line, "?") and !std.mem.startsWith(u8, line, "&") and !std.mem.startsWith(u8, line, "!") and !std.mem.startsWith(u8, line, "@") and !std.mem.startsWith(u8, line, "$") and !std.mem.startsWith(u8, line, "\\")) {
145+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid in function", .{line_num}));
146+
continue;
147+
}
148+
}
78149
if (std.mem.startsWith(u8, line, "//")) {
150+
if (in_function != null) {
151+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Deps not allowed in function", .{line_num}));
152+
continue;
153+
}
79154
const dep = std.mem.trim(u8, line[2..], " \t");
80155
if (dep.len > 0) {
81156
_ = try deps.put(try allocator.dupe(u8, dep), {});
82157
} else {
83158
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty system dependency", .{line_num}));
84159
}
85160
} else if (std.mem.startsWith(u8, line, "#")) {
161+
if (in_function != null) {
162+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Libs not allowed in function", .{line_num}));
163+
continue;
164+
}
86165
const lib = std.mem.trim(u8, line[1..], " \t");
87166
if (lib.len > 0) {
88167
const lib_dir = try std.fs.path.join(allocator, &.{ hacker_dir, "libs", lib });
@@ -97,10 +176,12 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
97176
try utils.mergeHashMaps(void, &deps, sub.deps, allocator);
98177
try utils.mergeHashMaps(void, &libs, sub.libs, allocator);
99178
try utils.mergeStringHashMaps(&vars_dict, sub.vars_dict, allocator);
179+
try utils.mergeStringHashMaps(&local_vars, sub.local_vars, allocator);
100180
try cmds.appendSlice(sub.cmds.items);
101181
try includes.appendSlice(sub.includes.items);
102182
try binaries.appendSlice(sub.binaries.items);
103-
try plugins.appendSlice(sub.plugins.items); // Merge pluginów
183+
try plugins.appendSlice(sub.plugins.items);
184+
try utils.mergeFunctionMaps(&functions, sub.functions, allocator);
104185
for (sub.errors.items) |sub_err| {
105186
try errors.append(try std.fmt.allocPrint(allocator, "In {s}: {s}", .{ lib, sub_err }));
106187
}
@@ -116,8 +197,15 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
116197
}
117198
} else if (std.mem.startsWith(u8, line, ">")) {
118199
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));
200+
var cmd = try allocator.dupe(u8, cmd_part);
201+
if (is_super) {
202+
const sudo_cmd = try std.fmt.allocPrint(allocator, "sudo {s}", .{cmd});
203+
allocator.free(cmd);
204+
cmd = sudo_cmd;
205+
}
206+
if (cmd.len > 0) {
207+
var target = if (in_function) |f| functions.getPtr(f).? else &cmds;
208+
try target.append(cmd);
121209
} else {
122210
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty command", .{line_num}));
123211
}
@@ -131,24 +219,38 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
131219
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid variable", .{line_num}));
132220
}
133221
} 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-
}
222+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid @ syntax", .{line_num}));
223+
}
224+
} else if (std.mem.startsWith(u8, line, "$")) {
225+
if (std.mem.indexOfScalar(u8, line[1..], '=')) |eq_pos| {
226+
const var_name = std.mem.trim(u8, line[1 .. 1 + eq_pos], " \t");
227+
const value = std.mem.trim(u8, line[1 + eq_pos + 1 ..], " \t");
228+
if (var_name.len > 0 and value.len > 0) {
229+
try local_vars.put(try allocator.dupe(u8, var_name), try allocator.dupe(u8, value));
144230
} else {
145-
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty plugin name", .{line_num}));
231+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid local variable", .{line_num}));
146232
}
233+
} else {
234+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid $ syntax", .{line_num}));
235+
}
236+
} else if (std.mem.startsWith(u8, line, "\\")) {
237+
const plugin_name = std.mem.trim(u8, line[1..], " \t");
238+
if (plugin_name.len > 0) {
239+
const plugin_dir = try std.fs.path.join(allocator, &.{ hacker_dir, "plugins", plugin_name });
240+
if (std.posix.access(plugin_dir, std.posix.X_OK)) |_| {
241+
try plugins.append(Plugin{ .path = plugin_dir, .is_super = is_super });
242+
} else |_| {
243+
allocator.free(plugin_dir);
244+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Plugin {s} not found or not executable", .{line_num, plugin_name}));
245+
}
246+
} else {
247+
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty plugin name", .{line_num}));
147248
}
148249
} else if (std.mem.startsWith(u8, line, "=")) {
149250
if (std.mem.indexOfScalar(u8, line[1..], '>')) |gt_pos| {
150251
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");
252+
const cmd_part_str = 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 ..];
253+
const cmd_part = std.mem.trim(u8, cmd_part_str, " \t");
152254
const num = std.fmt.parseInt(i32, num_str, 10) catch {
153255
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid loop count", .{line_num}));
154256
continue;
@@ -157,10 +259,17 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
157259
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Negative loop count", .{line_num}));
158260
continue;
159261
}
262+
var target = if (in_function) |f| functions.getPtr(f).? else &cmds;
160263
if (cmd_part.len > 0) {
161264
var i: i32 = 0;
162265
while (i < num) : (i += 1) {
163-
try cmds.append(try allocator.dupe(u8, cmd_part));
266+
var cmd = try allocator.dupe(u8, cmd_part);
267+
if (is_super) {
268+
const sudo_cmd = try std.fmt.allocPrint(allocator, "sudo {s}", .{cmd});
269+
allocator.free(cmd);
270+
cmd = sudo_cmd;
271+
}
272+
try target.append(cmd);
164273
}
165274
} else {
166275
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty loop command", .{line_num}));
@@ -171,21 +280,35 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
171280
} else if (std.mem.startsWith(u8, line, "?")) {
172281
if (std.mem.indexOfScalar(u8, line[1..], '>')) |gt_pos| {
173282
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");
283+
const cmd_part_str = 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 ..];
284+
const cmd_part = std.mem.trim(u8, cmd_part_str, " \t");
285+
var cmd = cmd_part;
286+
if (is_super) {
287+
cmd = try std.fmt.allocPrint(allocator, "sudo {s}", .{cmd_part});
288+
defer allocator.free(cmd);
289+
}
175290
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);
291+
const if_cmd = try std.fmt.allocPrint(allocator, "if {s}; then {s}; fi", .{ condition, cmd });
292+
var target = if (in_function) |f| functions.getPtr(f).? else &cmds;
293+
try target.append(if_cmd);
178294
} else {
179295
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid conditional", .{line_num}));
180296
}
181297
} else {
182298
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Invalid conditional syntax", .{line_num}));
183299
}
184300
} 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");
301+
const cmd_part_str = if (std.mem.indexOfScalar(u8, line[1..], '!')) |pos| line[1 .. 1 + pos] else line[1..];
302+
const cmd_part = std.mem.trim(u8, cmd_part_str, " \t");
303+
var cmd = try std.fmt.allocPrint(allocator, "{s} &", .{ cmd_part });
304+
if (is_super) {
305+
const sudo_cmd = try std.fmt.allocPrint(allocator, "sudo {s}", .{cmd});
306+
allocator.free(cmd);
307+
cmd = sudo_cmd;
308+
}
186309
if (cmd_part.len > 0) {
187-
const bg_cmd = try std.fmt.allocPrint(allocator, "{s} &", .{ cmd_part });
188-
try cmds.append(bg_cmd);
310+
var target = if (in_function) |f| functions.getPtr(f).? else &cmds;
311+
try target.append(cmd);
189312
} else {
190313
try errors.append(try std.fmt.allocPrint(allocator, "Line {d}: Empty background command", .{line_num}));
191314
}
@@ -198,6 +321,12 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
198321
if (in_config) {
199322
try errors.append(try allocator.dupe(u8, "Unclosed config section"));
200323
}
324+
if (in_comment) {
325+
try errors.append(try allocator.dupe(u8, "Unclosed comment block"));
326+
}
327+
if (in_function != null) {
328+
try errors.append(try allocator.dupe(u8, "Unclosed function block"));
329+
}
201330
if (verbose) {
202331
var dep_keys = try allocator.alloc([]const u8, deps.count());
203332
defer allocator.free(dep_keys);
@@ -218,10 +347,12 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
218347
}
219348
try console.print("Custom Libs: {any}\n", .{lib_keys});
220349
try console.print("Vars: {any}\n", .{vars_dict});
350+
try console.print("Local Vars: {any}\n", .{local_vars});
221351
try console.print("Cmds: {any}\n", .{cmds.items});
222352
try console.print("Includes: {any}\n", .{includes.items});
223353
try console.print("Binaries: {any}\n", .{binaries.items});
224354
try console.print("Plugins: {any}\n", .{plugins.items});
355+
try console.print("Functions: {any}\n", .{functions});
225356
try console.print("Config: {any}\n", .{config_data});
226357
if (errors.items.len > 0) {
227358
try console.print("Errors: {any}\n", .{errors.items});
@@ -231,10 +362,12 @@ pub fn parse_hacker_file(allocator: std.mem.Allocator, file_path: []const u8, ve
231362
.deps = deps,
232363
.libs = libs,
233364
.vars_dict = vars_dict,
365+
.local_vars = local_vars,
234366
.cmds = cmds,
235367
.includes = includes,
236368
.binaries = binaries,
237369
.plugins = plugins,
370+
.functions = functions,
238371
.errors = errors,
239372
.config_data = config_data,
240373
};

0 commit comments

Comments
 (0)