Passe basic GRAB query !
This commit is contained in:
parent
84495e831f
commit
cda2ee16a8
@ -7,6 +7,17 @@ const ziqlTokenizer = @import("tokenizers/ziql.zig").Tokenizer;
|
||||
const ziqlToken = @import("tokenizers/ziql.zig").Token;
|
||||
const ziqlParser = @import("ziqlParser.zig").Parser;
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
fn send(comptime format: []const u8, args: anytype) void {
|
||||
stdout.print(format, args) catch |err| {
|
||||
std.log.err("Can't send: {any}", .{err});
|
||||
stdout.print("\x03\n", .{}) catch {};
|
||||
};
|
||||
|
||||
stdout.print("\x03\n", .{}) catch {};
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
// TODO: Use an environment variable for the path of the DB
|
||||
checkAndCreateDirectories();
|
||||
@ -16,11 +27,8 @@ pub fn main() !void {
|
||||
|
||||
defer {
|
||||
switch (gpa.deinit()) {
|
||||
.ok => std.debug.print("No memory leak baby !\n", .{}),
|
||||
.leak => {
|
||||
std.debug.print("We fucked it up bro...\n", .{});
|
||||
@panic("=(");
|
||||
},
|
||||
.ok => std.log.debug("No memory leak baby !\n", .{}),
|
||||
.leak => std.log.debug("We fucked it up bro...\n", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +41,8 @@ pub fn main() !void {
|
||||
const line = try std.io.getStdIn().reader().readUntilDelimiterOrEof(line_buf, '\n');
|
||||
|
||||
if (line) |line_str| {
|
||||
const time_initial = std.time.microTimestamp();
|
||||
|
||||
const null_term_line_str = try allocator.dupeZ(u8, line_str[0..line_str.len]);
|
||||
defer allocator.free(null_term_line_str);
|
||||
|
||||
@ -47,20 +57,19 @@ pub fn main() !void {
|
||||
defer allocator.free(null_term_query_str);
|
||||
try runCommand(null_term_query_str);
|
||||
},
|
||||
.keyword_help => std.debug.print("The run command will take a ZiQL query between \" and run it. eg: run \"GRAB User\"\n", .{}),
|
||||
else => std.debug.print("After command run, need a string of a query, eg: \"GRAB User\"\n", .{}),
|
||||
.keyword_help => send("The run command will take a ZiQL query between \" and run it. eg: run \"GRAB User\"\n", .{}),
|
||||
else => send("After command run, need a string of a query, eg: \"GRAB User\"\n", .{}),
|
||||
}
|
||||
},
|
||||
.keyword_schema => {
|
||||
const second_token = cliToker.next();
|
||||
|
||||
switch (second_token.tag) {
|
||||
.keyword_describe => std.debug.print("{s}\n", .{ // TODO: Change that to use the SchemaEngine
|
||||
.keyword_describe => send("{s}\n", .{ // TODO: Change that to use the SchemaEngine
|
||||
\\User (
|
||||
\\ name: str,
|
||||
\\ email: str,
|
||||
\\)
|
||||
\\
|
||||
\\Message (
|
||||
\\ content: str,
|
||||
\\)
|
||||
@ -70,7 +79,7 @@ pub fn main() !void {
|
||||
try data_engine.initDataFolder();
|
||||
},
|
||||
.keyword_help => {
|
||||
std.debug.print("{s}", .{
|
||||
send("{s}", .{
|
||||
\\Here are all available options to use with the schema command:
|
||||
\\
|
||||
\\describe Print the schema use by the current engine.
|
||||
@ -82,7 +91,7 @@ pub fn main() !void {
|
||||
}
|
||||
},
|
||||
.keyword_help => {
|
||||
std.debug.print("{s}", .{
|
||||
send("{s}", .{
|
||||
\\Welcome to ZipponDB!
|
||||
\\
|
||||
\\run To run a query. Args => query: str, the query to execute.
|
||||
@ -95,8 +104,12 @@ pub fn main() !void {
|
||||
},
|
||||
.keyword_quit => break,
|
||||
.eof => {},
|
||||
else => std.debug.print("Command need to start with a keyword, including: run, schema, help and quit\n", .{}),
|
||||
else => send("Command need to start with a keyword, including: run, schema, help and quit\n", .{}),
|
||||
}
|
||||
|
||||
const time_final = std.time.microTimestamp();
|
||||
const duration = time_final - time_initial;
|
||||
std.debug.print("Time: {d:.2}ms\n", .{@as(f64, @floatFromInt(duration)) / 1000.0});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const DataType = @import("types/dataType.zig").DataType;
|
||||
pub const FileEngine = struct {
|
||||
allocator: Allocator,
|
||||
path_to_DATA_dir: []const u8, // The path to the DATA folder
|
||||
max_file_size: usize = 1e+7, // 10mb
|
||||
max_file_size: usize = 5e+4, // 50kb TODO: Change
|
||||
|
||||
const DataEngineError = error{
|
||||
ErrorCreateDataFolder,
|
||||
@ -57,31 +57,26 @@ pub const FileEngine = struct {
|
||||
}
|
||||
|
||||
/// Take a condition and an array of UUID and fill the array with all UUID that match the condition
|
||||
pub fn getUUIDListUsingCondition(self: FileEngine, condition: Condition, uuid_array: *std.ArrayList(UUID)) !void {
|
||||
pub fn getUUIDListUsingCondition(self: *FileEngine, condition: Condition, uuid_array: *std.ArrayList(UUID)) !void {
|
||||
var file_names = std.ArrayList([]const u8).init(self.allocator);
|
||||
self.getFilesNames(condition.struct_name, condition.member_name, &file_names) catch @panic("Can't get list of files");
|
||||
defer {
|
||||
for (file_names.items) |elem| {
|
||||
self.allocator.free(elem);
|
||||
}
|
||||
file_names.deinit();
|
||||
}
|
||||
defer file_names.deinit();
|
||||
|
||||
const sub_path = std.fmt.allocPrint(self.allocator, "{s}/{s}/{s}/{s}", .{ self.path_to_DATA_dir, condition.struct_name, condition.member_name, file_names.items[0] }) catch @panic("Can't create sub_path for init a DataIterator");
|
||||
var current_file = file_names.pop();
|
||||
|
||||
var sub_path = std.fmt.allocPrint(self.allocator, "{s}/{s}/{s}/{s}", .{ self.path_to_DATA_dir, condition.struct_name, condition.member_name, current_file }) catch @panic("Can't create sub_path for init a DataIterator");
|
||||
defer self.allocator.free(sub_path);
|
||||
|
||||
var file = std.fs.cwd().openFile(sub_path, .{}) catch @panic("Can't open first file to init a data iterator");
|
||||
defer file.close();
|
||||
|
||||
var output: [1024 * 50]u8 = undefined; // Maybe need to increase that as it limit the size of a line in files
|
||||
var output: [1024 * 50]u8 = undefined; // Maybe need to increase that as it limit the size of a line in a file
|
||||
var output_fbs = std.io.fixedBufferStream(&output);
|
||||
const writer = output_fbs.writer();
|
||||
|
||||
var buffered = std.io.bufferedReader(file.reader());
|
||||
var reader = buffered.reader();
|
||||
|
||||
var file_index: usize = 0;
|
||||
|
||||
var compare_value: ComparisonValue = undefined;
|
||||
switch (condition.data_type) {
|
||||
.int => compare_value = ComparisonValue{ .int = dataParsing.parseInt(condition.value) },
|
||||
@ -108,14 +103,23 @@ pub const FileEngine = struct {
|
||||
reader.streamUntilDelimiter(writer, '\n', null) catch |err| switch (err) {
|
||||
error.EndOfStream => {
|
||||
output_fbs.reset(); // clear buffer before exit
|
||||
file_index += 1;
|
||||
self.allocator.free(current_file);
|
||||
|
||||
if (file_index == file_names.items.len) break;
|
||||
if (file_names.items.len == 0) break;
|
||||
|
||||
// FIXME: Update the file and reader to be the next file of the list
|
||||
std.debug.print("End of stream\n", .{});
|
||||
current_file = file_names.pop();
|
||||
|
||||
break;
|
||||
// Do I leak memory here ? Do I deinit every time ?
|
||||
self.allocator.free(sub_path);
|
||||
sub_path = std.fmt.allocPrint(self.allocator, "{s}/{s}/{s}/{s}", .{ self.path_to_DATA_dir, condition.struct_name, condition.member_name, current_file }) catch @panic("Can't create sub_path for init a DataIterator");
|
||||
|
||||
// Same here, do I close everytime ?
|
||||
file.close();
|
||||
file = std.fs.cwd().openFile(sub_path, .{}) catch @panic("Can't open first file to init a data iterator");
|
||||
|
||||
buffered = std.io.bufferedReader(file.reader());
|
||||
reader = buffered.reader();
|
||||
continue;
|
||||
}, // file read till the end
|
||||
else => {
|
||||
std.debug.print("Error while reading file: {any}\n", .{err});
|
||||
@ -185,13 +189,9 @@ pub const FileEngine = struct {
|
||||
|
||||
// TODO: Clean a bit the code
|
||||
// Do I need multiple files too ? I mean it duplicate UUID a lot, if it's just to save a name like 'Bob', storing a long UUID is overkill
|
||||
// I could just use a tabular data format with separator using space
|
||||
pub fn writeEntity(self: FileEngine, struct_name: []const u8, data_map: std.StringHashMap([]const u8)) !void {
|
||||
// I could just use a tabular data format with separator using space - Or maybe I encode the uuid to take a minimum space as I always know it size
|
||||
pub fn writeEntity(self: FileEngine, struct_name: []const u8, data_map: std.StringHashMap([]const u8)) !UUID {
|
||||
const uuid_str = UUID.init().format_uuid();
|
||||
defer std.debug.print("Added new {s} successfully using UUID: {s}\n", .{
|
||||
struct_name,
|
||||
uuid_str,
|
||||
});
|
||||
|
||||
const member_names = schemaEngine.structName2structMembers(struct_name);
|
||||
for (member_names) |member_name| {
|
||||
@ -207,7 +207,7 @@ pub const FileEngine = struct {
|
||||
.mode = .read_write,
|
||||
}) catch {
|
||||
std.debug.print("Error opening data file.", .{});
|
||||
return;
|
||||
continue; // TODO: Error handeling
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
@ -219,14 +219,14 @@ pub const FileEngine = struct {
|
||||
const new_file_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}/{s}/{d}.zippondata", .{ self.path_to_DATA_dir, struct_name, member_name, max_index + 1 });
|
||||
defer self.allocator.free(new_file_path);
|
||||
|
||||
std.debug.print("new file path: {s}\n", .{new_file_path});
|
||||
|
||||
const new_file = std.fs.cwd().createFile(new_file_path, .{}) catch @panic("Error creating new data file");
|
||||
defer new_file.close();
|
||||
|
||||
try new_file.writer().print("{s} {s}\n", .{ &uuid_str, data_map.get(member_name).? });
|
||||
}
|
||||
}
|
||||
|
||||
return UUID.parse(&uuid_str);
|
||||
}
|
||||
|
||||
/// Use a filename in the format 1.zippondata and return the 1
|
||||
@ -245,6 +245,7 @@ pub const FileEngine = struct {
|
||||
defer member_dir.close();
|
||||
|
||||
var iter = member_dir.iterate();
|
||||
defer iter.reset();
|
||||
while (try iter.next()) |entry| {
|
||||
if ((entry.kind != std.fs.Dir.Entry.Kind.file) or (std.mem.eql(u8, "main.zippondata", entry.name))) continue;
|
||||
try file_names.*.append(try self.allocator.dupe(u8, entry.name));
|
||||
@ -281,10 +282,10 @@ pub const FileEngine = struct {
|
||||
|
||||
var iter = member_dir.iterate();
|
||||
while (try iter.next()) |entry| {
|
||||
if ((entry.kind != std.fs.Dir.Entry.Kind.file) or (std.mem.eql(u8, "main.zippondata", entry.name))) continue;
|
||||
if (entry.kind != std.fs.Dir.Entry.Kind.file) continue;
|
||||
count += 1;
|
||||
}
|
||||
return count;
|
||||
return count - 1;
|
||||
}
|
||||
|
||||
// TODO: Give the option to keep , dump or erase the data
|
||||
|
@ -7,6 +7,17 @@ const Token = @import("tokenizers/ziql.zig").Token;
|
||||
const UUID = @import("types/uuid.zig").UUID;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
fn send(comptime format: []const u8, args: anytype) void {
|
||||
stdout.print(format, args) catch |err| {
|
||||
std.log.err("Can't send: {any}", .{err});
|
||||
stdout.print("\x03\n", .{}) catch {};
|
||||
};
|
||||
|
||||
stdout.print("\x03\n", .{}) catch {};
|
||||
}
|
||||
|
||||
pub const Parser = struct {
|
||||
allocator: Allocator,
|
||||
state: State,
|
||||
@ -31,6 +42,7 @@ pub const Parser = struct {
|
||||
|
||||
pub fn deinit(self: *Parser) void {
|
||||
self.additional_data.deinit();
|
||||
self.allocator.free(self.struct_name);
|
||||
}
|
||||
|
||||
const Options = struct {
|
||||
@ -50,7 +62,7 @@ pub const Parser = struct {
|
||||
// For the main parse function
|
||||
expect_struct_name,
|
||||
expect_filter,
|
||||
expect_additional_data,
|
||||
parse_additional_data,
|
||||
expect_filter_or_additional_data,
|
||||
expect_new_data,
|
||||
expect_right_arrow,
|
||||
@ -121,23 +133,23 @@ pub const Parser = struct {
|
||||
switch (token.tag) {
|
||||
.keyword_grab => {
|
||||
self.action = .GRAB;
|
||||
self.state = State.expect_struct_name;
|
||||
self.state = .expect_struct_name;
|
||||
},
|
||||
.keyword_add => {
|
||||
self.action = .ADD;
|
||||
self.state = State.expect_struct_name;
|
||||
self.state = .expect_struct_name;
|
||||
},
|
||||
.keyword_update => {
|
||||
self.action = .UPDATE;
|
||||
self.state = State.expect_struct_name;
|
||||
self.state = .expect_struct_name;
|
||||
},
|
||||
.keyword_delete => {
|
||||
self.action = .DELETE;
|
||||
self.state = State.expect_struct_name;
|
||||
self.state = .expect_struct_name;
|
||||
},
|
||||
else => {
|
||||
self.printError("Error: Expected action keyword. Available: GRAB ADD DELETE UPDATE", &token);
|
||||
self.state = State.end;
|
||||
self.state = .end;
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -146,34 +158,34 @@ pub const Parser = struct {
|
||||
self.struct_name = try self.allocator.dupe(u8, self.toker.getTokenSlice(token));
|
||||
if (!schemaEngine.isStructNameExists(self.struct_name)) self.printError("Error: struct name not found in schema.", &token);
|
||||
switch (self.action) {
|
||||
.ADD => self.state = State.expect_new_data,
|
||||
else => self.state = State.expect_filter_or_additional_data,
|
||||
.ADD => self.state = .expect_new_data,
|
||||
else => self.state = .expect_filter_or_additional_data,
|
||||
}
|
||||
},
|
||||
.expect_filter_or_additional_data => {
|
||||
keep_next = true;
|
||||
switch (token.tag) {
|
||||
.l_bracket => self.state = State.expect_additional_data,
|
||||
.l_brace => self.state = State.filter_and_send,
|
||||
.l_bracket => self.state = .parse_additional_data,
|
||||
.l_brace => self.state = .filter_and_send,
|
||||
else => self.printError("Error: Expect [ for additional data or { for a filter", &token),
|
||||
}
|
||||
},
|
||||
.expect_additional_data => {
|
||||
.parse_additional_data => {
|
||||
try self.parseAdditionalData(&self.additional_data);
|
||||
self.state = State.filter_and_send;
|
||||
self.state = .filter_and_send;
|
||||
},
|
||||
.filter_and_send => {
|
||||
var array = std.ArrayList(UUID).init(self.allocator);
|
||||
defer array.deinit();
|
||||
try self.parseFilter(&array, self.struct_name, true);
|
||||
self.sendEntity(array.items);
|
||||
self.state = State.end;
|
||||
self.state = .end;
|
||||
},
|
||||
.expect_new_data => {
|
||||
switch (token.tag) {
|
||||
.l_paren => {
|
||||
keep_next = true;
|
||||
self.state = State.parse_new_data_and_add_data;
|
||||
self.state = .parse_new_data_and_add_data;
|
||||
},
|
||||
else => self.printError("Error: Expecting new data starting with (", &token),
|
||||
}
|
||||
@ -187,8 +199,12 @@ pub const Parser = struct {
|
||||
|
||||
// TODO: Print the list of missing
|
||||
if (!schemaEngine.checkIfAllMemberInMap(self.struct_name, &data_map)) self.printError("Error: Missing member", &token);
|
||||
try self.data_engine.writeEntity(self.struct_name, data_map);
|
||||
self.state = State.end;
|
||||
const uuid = self.data_engine.writeEntity(self.struct_name, data_map) catch {
|
||||
send("ZipponDB error: Couln't write new data to file", .{});
|
||||
continue;
|
||||
};
|
||||
send("Successfully added new {s} with UUID: {s}", .{ self.struct_name, uuid.format_uuid() });
|
||||
self.state = .end;
|
||||
},
|
||||
.UPDATE => {}, // TODO:
|
||||
else => unreachable,
|
||||
@ -204,8 +220,9 @@ pub const Parser = struct {
|
||||
// Maybe to a struct Communicator to handle all communication between use and cli
|
||||
fn sendEntity(self: *Parser, uuid_array: []UUID) void {
|
||||
_ = self;
|
||||
_ = uuid_array;
|
||||
|
||||
std.debug.print("Number of uuid to send: {d}\n", .{uuid_array.len});
|
||||
//send("Number of uuid to send: {d}\n", .{uuid_array.len});
|
||||
}
|
||||
|
||||
// TODO: The parser that check what is between ||
|
||||
@ -414,10 +431,10 @@ pub const Parser = struct {
|
||||
fn parseAdditionalData(self: *Parser, additional_data: *AdditionalData) !void {
|
||||
var token = self.toker.next();
|
||||
var keep_next = false;
|
||||
self.state = State.expect_count_of_entity_to_find;
|
||||
self.state = .expect_count_of_entity_to_find;
|
||||
|
||||
while (self.state != State.end) : ({
|
||||
token = if (!keep_next) self.toker.next() else token;
|
||||
while (self.state != .end) : ({
|
||||
token = if ((!keep_next) and (self.state != .end)) self.toker.next() else token;
|
||||
keep_next = false;
|
||||
}) {
|
||||
switch (self.state) {
|
||||
@ -441,7 +458,7 @@ pub const Parser = struct {
|
||||
.expect_semicolon_OR_right_bracket => {
|
||||
switch (token.tag) {
|
||||
.semicolon => self.state = .expect_member,
|
||||
.r_bracket => self.state = State.end,
|
||||
.r_bracket => self.state = .end,
|
||||
else => self.printError("Error: Expect ';' or ']'.", &token),
|
||||
}
|
||||
},
|
||||
@ -464,10 +481,7 @@ pub const Parser = struct {
|
||||
.expect_comma_OR_r_bracket_OR_l_bracket => {
|
||||
switch (token.tag) {
|
||||
.comma => self.state = .expect_member,
|
||||
.r_bracket => {
|
||||
self.state = State.end;
|
||||
keep_next = true;
|
||||
},
|
||||
.r_bracket => self.state = .end,
|
||||
.l_bracket => {
|
||||
try self.parseAdditionalData(
|
||||
&additional_data.member_to_find.items[additional_data.member_to_find.items.len - 1].additional_data,
|
||||
@ -480,10 +494,7 @@ pub const Parser = struct {
|
||||
.expect_comma_OR_r_bracket => {
|
||||
switch (token.tag) {
|
||||
.comma => self.state = .expect_member,
|
||||
.r_bracket => {
|
||||
self.state = State.end;
|
||||
keep_next = true;
|
||||
},
|
||||
.r_bracket => self.state = .end,
|
||||
else => self.printError("Error: Expected , or ]", &token),
|
||||
}
|
||||
},
|
||||
@ -499,9 +510,9 @@ pub const Parser = struct {
|
||||
var token = self.toker.next();
|
||||
var keep_next = false;
|
||||
var member_name: []const u8 = undefined; // Maybe use allocator.alloc
|
||||
self.state = State.expect_member;
|
||||
self.state = .expect_member;
|
||||
|
||||
while (self.state != State.end) : ({
|
||||
while (self.state != .end) : ({
|
||||
token = if (!keep_next) self.toker.next() else token;
|
||||
keep_next = false;
|
||||
}) {
|
||||
@ -511,7 +522,7 @@ pub const Parser = struct {
|
||||
.identifier => {
|
||||
member_name = self.toker.getTokenSlice(token);
|
||||
if (!schemaEngine.isMemberNameInStruct(self.struct_name, member_name)) self.printError("Member not found in struct.", &token);
|
||||
self.state = State.expect_equal;
|
||||
self.state = .expect_equal;
|
||||
},
|
||||
else => self.printError("Error: Expected member name.", &token),
|
||||
}
|
||||
@ -519,7 +530,7 @@ pub const Parser = struct {
|
||||
.expect_equal => {
|
||||
switch (token.tag) {
|
||||
// TODO: Add more comparison like IN or other stuff
|
||||
.equal => self.state = State.expect_new_value,
|
||||
.equal => self.state = .expect_new_value,
|
||||
else => self.printError("Error: Expected =", &token),
|
||||
}
|
||||
},
|
||||
@ -530,7 +541,7 @@ pub const Parser = struct {
|
||||
switch (token.tag) {
|
||||
.int_literal, .keyword_null => {
|
||||
keep_next = true;
|
||||
self.state = State.add_member_to_map;
|
||||
self.state = .add_member_to_map;
|
||||
},
|
||||
else => self.printError("Error: Expected int", &token),
|
||||
}
|
||||
@ -539,7 +550,7 @@ pub const Parser = struct {
|
||||
switch (token.tag) {
|
||||
.float_literal, .keyword_null => {
|
||||
keep_next = true;
|
||||
self.state = State.add_member_to_map;
|
||||
self.state = .add_member_to_map;
|
||||
},
|
||||
else => self.printError("Error: Expected float", &token),
|
||||
}
|
||||
@ -548,7 +559,7 @@ pub const Parser = struct {
|
||||
switch (token.tag) {
|
||||
.bool_literal_true, .bool_literal_false, .keyword_null => {
|
||||
keep_next = true;
|
||||
self.state = State.add_member_to_map;
|
||||
self.state = .add_member_to_map;
|
||||
},
|
||||
else => self.printError("Error: Expected bool: true false", &token),
|
||||
}
|
||||
@ -557,7 +568,7 @@ pub const Parser = struct {
|
||||
switch (token.tag) {
|
||||
.string_literal, .keyword_null => {
|
||||
keep_next = true;
|
||||
self.state = State.add_member_to_map;
|
||||
self.state = .add_member_to_map;
|
||||
},
|
||||
else => self.printError("Error: Expected string between ''", &token),
|
||||
}
|
||||
@ -568,7 +579,7 @@ pub const Parser = struct {
|
||||
.l_bracket => {
|
||||
const start_index = token.loc.start;
|
||||
token = self.toker.next();
|
||||
while (token.tag != Token.Tag.r_bracket) : (token = self.toker.next()) {
|
||||
while (token.tag != .r_bracket) : (token = self.toker.next()) {
|
||||
switch (token.tag) {
|
||||
.int_literal => continue,
|
||||
else => self.printError("Error: Expected int or ].", &token),
|
||||
@ -576,7 +587,7 @@ pub const Parser = struct {
|
||||
}
|
||||
// Maybe change that as it just recreate a string that is already in the buffer
|
||||
member_map.put(member_name, self.toker.buffer[start_index..token.loc.end]) catch @panic("Couln't add string of array in data map");
|
||||
self.state = State.expect_comma_OR_end;
|
||||
self.state = .expect_comma_OR_end;
|
||||
},
|
||||
else => self.printError("Error: Expected [ to start an array", &token),
|
||||
}
|
||||
@ -586,7 +597,7 @@ pub const Parser = struct {
|
||||
.l_bracket => {
|
||||
const start_index = token.loc.start;
|
||||
token = self.toker.next();
|
||||
while (token.tag != Token.Tag.r_bracket) : (token = self.toker.next()) {
|
||||
while (token.tag != .r_bracket) : (token = self.toker.next()) {
|
||||
switch (token.tag) {
|
||||
.float_literal => continue,
|
||||
else => self.printError("Error: Expected float or ].", &token),
|
||||
@ -594,7 +605,7 @@ pub const Parser = struct {
|
||||
}
|
||||
// Maybe change that as it just recreate a string that is already in the buffer
|
||||
member_map.put(member_name, self.toker.buffer[start_index..token.loc.end]) catch @panic("Couln't add string of array in data map");
|
||||
self.state = State.expect_comma_OR_end;
|
||||
self.state = .expect_comma_OR_end;
|
||||
},
|
||||
else => self.printError("Error: Expected [ to start an array", &token),
|
||||
}
|
||||
@ -604,7 +615,7 @@ pub const Parser = struct {
|
||||
.l_bracket => {
|
||||
const start_index = token.loc.start;
|
||||
token = self.toker.next();
|
||||
while (token.tag != Token.Tag.r_bracket) : (token = self.toker.next()) {
|
||||
while (token.tag != .r_bracket) : (token = self.toker.next()) {
|
||||
switch (token.tag) {
|
||||
.bool_literal_false, .bool_literal_true => continue,
|
||||
else => self.printError("Error: Expected bool or ].", &token),
|
||||
@ -612,7 +623,7 @@ pub const Parser = struct {
|
||||
}
|
||||
// Maybe change that as it just recreate a string that is already in the buffer
|
||||
member_map.put(member_name, self.toker.buffer[start_index..token.loc.end]) catch @panic("Couln't add string of array in data map");
|
||||
self.state = State.expect_comma_OR_end;
|
||||
self.state = .expect_comma_OR_end;
|
||||
},
|
||||
else => self.printError("Error: Expected [ to start an array", &token),
|
||||
}
|
||||
@ -622,7 +633,7 @@ pub const Parser = struct {
|
||||
.l_bracket => {
|
||||
const start_index = token.loc.start;
|
||||
token = self.toker.next();
|
||||
while (token.tag != Token.Tag.r_bracket) : (token = self.toker.next()) {
|
||||
while (token.tag != .r_bracket) : (token = self.toker.next()) {
|
||||
switch (token.tag) {
|
||||
.string_literal => continue,
|
||||
else => self.printError("Error: Expected str or ].", &token),
|
||||
@ -630,7 +641,7 @@ pub const Parser = struct {
|
||||
}
|
||||
// Maybe change that as it just recreate a string that is already in the buffer
|
||||
member_map.put(member_name, self.toker.buffer[start_index..token.loc.end]) catch @panic("Couln't add string of array in data map");
|
||||
self.state = State.expect_comma_OR_end;
|
||||
self.state = .expect_comma_OR_end;
|
||||
},
|
||||
else => self.printError("Error: Expected [ to start an array", &token),
|
||||
}
|
||||
@ -639,13 +650,13 @@ pub const Parser = struct {
|
||||
},
|
||||
.add_member_to_map => {
|
||||
member_map.put(member_name, self.toker.getTokenSlice(token)) catch @panic("Could not add member name and value to map in getMapOfMember");
|
||||
self.state = State.expect_comma_OR_end;
|
||||
self.state = .expect_comma_OR_end;
|
||||
},
|
||||
.add_array_to_map => {},
|
||||
.expect_comma_OR_end => {
|
||||
switch (token.tag) {
|
||||
.r_paren => self.state = State.end,
|
||||
.comma => self.state = State.expect_member,
|
||||
.r_paren => self.state = .end,
|
||||
.comma => self.state = .expect_member,
|
||||
else => self.printError("Error: Expect , or )", &token),
|
||||
}
|
||||
},
|
||||
@ -654,29 +665,28 @@ pub const Parser = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Stop panicking !
|
||||
fn printError(self: *Parser, message: []const u8, token: *Token) void {
|
||||
std.debug.print("\n", .{});
|
||||
std.debug.print("{s}\n", .{self.toker.buffer});
|
||||
stdout.print("\n", .{}) catch {};
|
||||
stdout.print("{s}\n", .{self.toker.buffer}) catch {};
|
||||
|
||||
// Calculate the number of spaces needed to reach the start position.
|
||||
var spaces: usize = 0;
|
||||
while (spaces < token.loc.start) : (spaces += 1) {
|
||||
std.debug.print(" ", .{});
|
||||
stdout.print(" ", .{}) catch {};
|
||||
}
|
||||
|
||||
// Print the '^' characters for the error span.
|
||||
var i: usize = token.loc.start;
|
||||
while (i < token.loc.end) : (i += 1) {
|
||||
std.debug.print("^", .{});
|
||||
stdout.print("^", .{}) catch {};
|
||||
}
|
||||
std.debug.print(" \n", .{}); // Align with the message
|
||||
stdout.print(" \n", .{}) catch {}; // Align with the message
|
||||
|
||||
std.debug.print("{s}\n", .{message});
|
||||
stdout.print("{s}\n", .{message}) catch {};
|
||||
|
||||
std.debug.print("{any}\n{any}\n", .{ token.tag, token.loc });
|
||||
stdout.print("{any}\n{any}\n", .{ token.tag, token.loc }) catch {};
|
||||
|
||||
@panic("");
|
||||
send("", .{});
|
||||
}
|
||||
};
|
||||
|
||||
@ -770,6 +780,38 @@ fn compareUUIDArray(arr1: std.ArrayList(UUID), arr2: std.ArrayList(UUID)) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
test "GRAB with additional data" {
|
||||
try testParsing("GRAB User [1] {age < 18}");
|
||||
try testParsing("GRAB User [name] {age < 18}");
|
||||
try testParsing("GRAB User [100; name] {age < 18}");
|
||||
}
|
||||
|
||||
test "GRAB filter with string" {
|
||||
// TODO: Use a fixe dataset for testing, to choose in the build.zig
|
||||
// It should check if the right number of entity is found too
|
||||
try testParsing("GRAB User {name = 'Brittany Rogers'}");
|
||||
try testParsing("GRAB User {name != 'Brittany Rogers'}");
|
||||
}
|
||||
|
||||
test "GRAB filter with int" {
|
||||
// TODO: Use a fixe dataset for testing, to choose in the build.zig
|
||||
// It should check if the right number of entity is found too
|
||||
try testParsing("GRAB User {age = 18}");
|
||||
try testParsing("GRAB User {age > 18}");
|
||||
try testParsing("GRAB User {age < 18}");
|
||||
try testParsing("GRAB User {age <= 18}");
|
||||
try testParsing("GRAB User {age >= 18}");
|
||||
try testParsing("GRAB User {age != 18}");
|
||||
}
|
||||
|
||||
fn testParsing(source: [:0]const u8) !void {
|
||||
const allocator = std.testing.allocator;
|
||||
var tokenizer = Tokenizer.init(source);
|
||||
var parser = Parser.init(allocator, &tokenizer);
|
||||
defer parser.deinit();
|
||||
try parser.parse();
|
||||
}
|
||||
|
||||
test "Parse condition" {
|
||||
const condition1 = Condition{ .data_type = .int, .member_name = "age", .operation = .superior_or_equal, .struct_name = "User", .value = "26" };
|
||||
try testConditionParsing("age >= 26", condition1);
|
||||
@ -821,7 +863,6 @@ fn testNewDataParsing(source: [:0]const u8, expected_member_map: std.StringHashM
|
||||
var parser = Parser.init(allocator, &tokenizer);
|
||||
parser.struct_name = allocator.dupe(u8, "User") catch @panic("Cant alloc struct name");
|
||||
defer parser.deinit();
|
||||
defer allocator.free(parser.struct_name);
|
||||
|
||||
var data_map = std.StringHashMap([]const u8).init(allocator);
|
||||
defer data_map.deinit();
|
||||
@ -857,6 +898,8 @@ test "Parse filter" {
|
||||
|
||||
var tokenizer = Tokenizer.init("{name = 'Adrien'}");
|
||||
var parser = Parser.init(allocator, &tokenizer);
|
||||
parser.struct_name = allocator.dupe(u8, "User") catch @panic("Cant alloc struct name"); // Otherwise get an error trying to free this when deinit
|
||||
|
||||
defer parser.deinit();
|
||||
_ = tokenizer.next(); // Start at name
|
||||
|
||||
@ -913,7 +956,6 @@ fn testAdditionalData(source: [:0]const u8, expected_AdditionalData: Parser.Addi
|
||||
var parser = Parser.init(allocator, &tokenizer);
|
||||
parser.struct_name = allocator.dupe(u8, "User") catch @panic("Cant alloc struct name");
|
||||
defer parser.deinit();
|
||||
defer allocator.free(parser.struct_name);
|
||||
|
||||
_ = tokenizer.next();
|
||||
parser.parseAdditionalData(&parser.additional_data) catch |err| {
|
||||
|
Loading…
x
Reference in New Issue
Block a user