Start cleaning before working one 0.2
This commit is contained in:
parent
c93538f4b0
commit
29af5a7ac0
@ -85,8 +85,7 @@ pub fn main() !void {
|
||||
var token = cliToker.next();
|
||||
state = .expect_main_command;
|
||||
|
||||
while ((state != .end) and (state != .quit)) : (token = cliToker.next()) {
|
||||
switch (state) {
|
||||
while ((state != .end) and (state != .quit)) : (token = cliToker.next()) switch (state) {
|
||||
.expect_main_command => switch (token.tag) {
|
||||
.keyword_run => {
|
||||
if (!file_engine.usable) {
|
||||
@ -262,12 +261,11 @@ pub fn main() !void {
|
||||
},
|
||||
|
||||
.quit, .end => break,
|
||||
}
|
||||
};
|
||||
}
|
||||
if (state == .quit) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn runQuery(null_term_query_str: [:0]const u8, file_engine: *FileEngine) void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
|
@ -8,17 +8,25 @@ const SchemaStruct = @import("schemaParser.zig").Parser.SchemaStruct;
|
||||
const SchemaParser = @import("schemaParser.zig").Parser;
|
||||
const SchemaTokenizer = @import("tokenizers/schema.zig").Tokenizer;
|
||||
const SchemaToken = @import("tokenizers/schema.zig").Token;
|
||||
const AdditionalData = @import("ziqlParser.zig").Parser.AdditionalData;
|
||||
const AdditionalData = @import("parsing-tools/additionalData.zig").AdditionalData;
|
||||
|
||||
//TODO: Create a union class and chose between file and memory
|
||||
// TODO: Use those errors everywhere in this file
|
||||
const FileEngineError = error{
|
||||
SchemaFileNotFound,
|
||||
SchemaNotConform,
|
||||
DATAFolderNotFound,
|
||||
StructFolderNotFound,
|
||||
CantMakeDir,
|
||||
CantMakeFile,
|
||||
};
|
||||
|
||||
/// Manage everything that is relate to read or write in files
|
||||
/// Or even get stats, whatever. If it touch files, it's here
|
||||
pub const FileEngine = struct {
|
||||
allocator: Allocator,
|
||||
usable: bool,
|
||||
path_to_ZipponDB_dir: []const u8, // Make that into a list
|
||||
max_file_size: usize = 5e+4, // 50kb TODO: Change
|
||||
path_to_ZipponDB_dir: []const u8, // TODO: Put in config file
|
||||
max_file_size: usize = 5e+4, // 50kb TODO: Put in config file
|
||||
null_terminated_schema_buff: [:0]u8,
|
||||
struct_array: std.ArrayList(SchemaStruct),
|
||||
|
||||
@ -81,10 +89,6 @@ pub const FileEngine = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn setPath(self: *FileEngine, path: []const u8) void {
|
||||
self.path_to_ZipponDB_dir = path;
|
||||
}
|
||||
|
||||
/// Take a list of UUID and, a buffer array and the additional data to write into the buffer the JSON to send
|
||||
/// TODO: Optimize
|
||||
/// FIXME: Array of string are not working
|
||||
@ -161,7 +165,8 @@ pub const FileEngine = struct {
|
||||
}
|
||||
}
|
||||
|
||||
if (founded) {
|
||||
if (!founded) continue;
|
||||
|
||||
try out_writer.writeAll("{");
|
||||
try out_writer.writeAll("id:\"");
|
||||
try out_writer.print("{s}", .{output_fbs.getWritten()[0..36]});
|
||||
@ -169,7 +174,8 @@ pub const FileEngine = struct {
|
||||
for (self.structName2structMembers(struct_name), self.structName2DataType(struct_name)) |member_name, member_type| {
|
||||
token = data_toker.next();
|
||||
// FIXME: When relationship will be implemented, need to check if the len of NON link is 0
|
||||
if ((additional_data.member_to_find.items.len == 0) or (self.isMemberNameInAdditionalData(self.locToSlice(member_name), additional_data))) {
|
||||
if (!(additional_data.member_to_find.items.len == 0) or !(additional_data.contains(self.locToSlice(member_name)))) continue;
|
||||
|
||||
// write the member name and = sign
|
||||
try out_writer.print("{s}: ", .{self.locToSlice(member_name)});
|
||||
|
||||
@ -200,23 +206,14 @@ pub const FileEngine = struct {
|
||||
}
|
||||
try out_writer.writeAll(", ");
|
||||
}
|
||||
}
|
||||
try out_writer.writeAll("}");
|
||||
try out_writer.writeAll(", ");
|
||||
}
|
||||
}
|
||||
// Write the end }
|
||||
try out_writer.writeAll("]");
|
||||
}
|
||||
|
||||
fn isMemberNameInAdditionalData(_: *FileEngine, member_name: []const u8, additional_data: AdditionalData) bool {
|
||||
for (additional_data.member_to_find.items) |elem| {
|
||||
if (std.mem.eql(u8, member_name, elem.name)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Use a struct name to populate a list with all UUID of this struct
|
||||
/// TODO: Optimize this, I'm sure I can do better than that
|
||||
pub fn getAllUUIDList(self: *FileEngine, struct_name: []const u8, uuid_array: *std.ArrayList(UUID)) !void {
|
||||
const max_file_index = try self.maxFileIndex(struct_name);
|
||||
var current_index: usize = 0;
|
||||
@ -431,9 +428,7 @@ 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 - Or maybe I encode the uuid to take a minimum space as I always know it size
|
||||
// Do I need a map here ? Cant I use something else ?
|
||||
pub fn writeEntity(self: *FileEngine, struct_name: []const u8, data_map: std.StringHashMap([]const u8)) !UUID {
|
||||
const uuid = UUID.init();
|
||||
|
||||
@ -566,7 +561,14 @@ pub const FileEngine = struct {
|
||||
}
|
||||
}
|
||||
|
||||
if (founded) {
|
||||
if (!founded) {
|
||||
// stream until the delimiter
|
||||
output_fbs.reset();
|
||||
try new_file.writeAll(" ");
|
||||
try reader.streamUntilDelimiter(writer, '\n', null);
|
||||
try new_file.writeAll(output_fbs.getWritten());
|
||||
try new_file.writeAll("\n");
|
||||
} else {
|
||||
for (self.structName2structMembers(struct_name), self.structName2DataType(struct_name)) |member_name, member_type| {
|
||||
// For all collum in the right order, check if the key is in the map, if so use it to write the new value, otherwise use the old file
|
||||
output_fbs.reset();
|
||||
@ -610,13 +612,6 @@ pub const FileEngine = struct {
|
||||
|
||||
try reader.streamUntilDelimiter(writer, '\n', null);
|
||||
try new_file.writeAll("\n");
|
||||
} else {
|
||||
// stream until the delimiter
|
||||
output_fbs.reset();
|
||||
try new_file.writeAll(" ");
|
||||
try reader.streamUntilDelimiter(writer, '\n', null);
|
||||
try new_file.writeAll(output_fbs.getWritten());
|
||||
try new_file.writeAll("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -807,40 +802,13 @@ pub const FileEngine = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe make it so it use itself to search if it find a directory
|
||||
fn getDirTotalSize(self: FileEngine, dir: std.fs.Dir) !u64 {
|
||||
var total: u64 = 0;
|
||||
var stat: std.fs.File.Stat = undefined;
|
||||
var iter = dir.iterate();
|
||||
while (try iter.next()) |entry| {
|
||||
if (entry.kind == .directory) {
|
||||
const sub_dir = try dir.openDir(entry.name, .{ .iterate = true });
|
||||
total += try self.getDirTotalSize(sub_dir);
|
||||
}
|
||||
|
||||
if (entry.kind != .file) continue;
|
||||
stat = try dir.statFile(entry.name);
|
||||
total += stat.size;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
const FileError = error{
|
||||
SchemaFileNotFound,
|
||||
SchemaNotConform,
|
||||
DATAFolderNotFound,
|
||||
StructFolderNotFound,
|
||||
CantMakeDir,
|
||||
CantMakeFile,
|
||||
};
|
||||
|
||||
/// Request a path to a schema file and then create the struct folder
|
||||
/// TODO: Check if some data already exist and if so ask if the user want to delete it and make a backup
|
||||
pub fn initDataFolder(self: *FileEngine, path_to_schema_file: []const u8) FileError!void {
|
||||
pub fn initDataFolder(self: *FileEngine, path_to_schema_file: []const u8) FileEngineError!void {
|
||||
var schema_buf = self.allocator.alloc(u8, 1024 * 50) catch @panic("Cant allocate the schema buffer");
|
||||
defer self.allocator.free(schema_buf);
|
||||
|
||||
const file = std.fs.cwd().openFile(path_to_schema_file, .{}) catch return FileError.SchemaFileNotFound;
|
||||
const file = std.fs.cwd().openFile(path_to_schema_file, .{}) catch return FileEngineError.SchemaFileNotFound;
|
||||
defer file.close();
|
||||
|
||||
const len = file.readAll(schema_buf) catch @panic("Can't read schema file");
|
||||
@ -860,19 +828,19 @@ pub const FileEngine = struct {
|
||||
const path = std.fmt.allocPrint(self.allocator, "{s}/DATA", .{self.path_to_ZipponDB_dir}) catch @panic("Cant allocate path");
|
||||
defer self.allocator.free(path);
|
||||
|
||||
var data_dir = std.fs.cwd().openDir(path, .{}) catch return FileError.DATAFolderNotFound;
|
||||
var data_dir = std.fs.cwd().openDir(path, .{}) catch return FileEngineError.DATAFolderNotFound;
|
||||
defer data_dir.close();
|
||||
|
||||
for (self.struct_array.items) |struct_item| {
|
||||
data_dir.makeDir(self.locToSlice(struct_item.name)) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => return FileError.CantMakeDir,
|
||||
else => return FileEngineError.CantMakeDir,
|
||||
};
|
||||
const struct_dir = data_dir.openDir(self.locToSlice(struct_item.name), .{}) catch return FileError.StructFolderNotFound;
|
||||
const struct_dir = data_dir.openDir(self.locToSlice(struct_item.name), .{}) catch return FileEngineError.StructFolderNotFound;
|
||||
|
||||
_ = struct_dir.createFile("0.zippondata", .{}) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => {},
|
||||
else => return FileError.CantMakeFile,
|
||||
else => return FileEngineError.CantMakeFile,
|
||||
};
|
||||
}
|
||||
|
||||
@ -880,6 +848,7 @@ pub const FileEngine = struct {
|
||||
}
|
||||
|
||||
// Stuff for schema
|
||||
// TODO: Check all those functions and remove if not use
|
||||
|
||||
pub fn readSchemaFile(allocator: Allocator, sub_path: []const u8, buffer: []u8) !usize {
|
||||
const path = try std.fmt.allocPrint(allocator, "{s}/schema.zipponschema", .{sub_path});
|
||||
@ -1011,6 +980,7 @@ test "Get list of UUID using condition" {
|
||||
// Series of functions to use just before creating an entity.
|
||||
// Will transform the string of data into data of the right type./
|
||||
|
||||
// TODO: Put those functions somewhere else
|
||||
pub fn parseInt(value_str: []const u8) i64 {
|
||||
return std.fmt.parseInt(i64, value_str, 10) catch return 0;
|
||||
}
|
||||
|
40
src/parsing-tools/additionalData.zig
Normal file
40
src/parsing-tools/additionalData.zig
Normal file
@ -0,0 +1,40 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
/// This is the [] part
|
||||
/// IDK if saving it into the Parser struct is a good idea
|
||||
pub const AdditionalData = struct {
|
||||
entity_count_to_find: usize = 0,
|
||||
member_to_find: std.ArrayList(AdditionalDataMember),
|
||||
|
||||
pub fn init(allocator: Allocator) AdditionalData {
|
||||
return AdditionalData{ .member_to_find = std.ArrayList(AdditionalDataMember).init(allocator) };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *AdditionalData) void {
|
||||
for (0..self.member_to_find.items.len) |i| {
|
||||
self.member_to_find.items[i].additional_data.deinit();
|
||||
}
|
||||
|
||||
self.member_to_find.deinit();
|
||||
}
|
||||
|
||||
pub fn contains(additional_data: AdditionalData, member_name: []const u8) bool {
|
||||
for (additional_data.member_to_find.items) |elem| {
|
||||
if (std.mem.eql(u8, member_name, elem.name)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// This is name in: [name]
|
||||
// There is an additional data because it can be [friend [1; name]]
|
||||
pub const AdditionalDataMember = struct {
|
||||
name: []const u8,
|
||||
additional_data: AdditionalData,
|
||||
|
||||
pub fn init(allocator: Allocator, name: []const u8) AdditionalDataMember {
|
||||
const additional_data = AdditionalData.init(allocator);
|
||||
return AdditionalDataMember{ .name = name, .additional_data = additional_data };
|
||||
}
|
||||
};
|
31
src/parsing-tools/utils.zig
Normal file
31
src/parsing-tools/utils.zig
Normal file
@ -0,0 +1,31 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn getEnvVariables(allocator: std.mem.Allocator, variable: []const u8) ?[]const u8 {
|
||||
var env_map = std.process.getEnvMap(allocator) catch return null;
|
||||
defer env_map.deinit();
|
||||
|
||||
var iter = env_map.iterator();
|
||||
|
||||
while (iter.next()) |entry| {
|
||||
if (std.mem.eql(u8, entry.key_ptr.*, variable)) return allocator.dupe(u8, entry.value_ptr.*) catch return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getDirTotalSize(dir: std.fs.Dir) !u64 {
|
||||
var total: u64 = 0;
|
||||
var stat: std.fs.File.Stat = undefined;
|
||||
var iter = dir.iterate();
|
||||
while (try iter.next()) |entry| {
|
||||
if (entry.kind == .directory) {
|
||||
const sub_dir = try dir.openDir(entry.name, .{ .iterate = true });
|
||||
total += try getDirTotalSize(sub_dir);
|
||||
}
|
||||
|
||||
if (entry.kind != .file) continue;
|
||||
stat = try dir.statFile(entry.name);
|
||||
total += stat.size;
|
||||
}
|
||||
return total;
|
||||
}
|
@ -6,6 +6,7 @@ const Token = @import("tokenizers/schema.zig").Token;
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
// Fuse this with the same function in the ZiQL parser
|
||||
fn send(comptime format: []const u8, args: anytype) void {
|
||||
stdout.print(format, args) catch |err| {
|
||||
std.log.err("Can't send: {any}", .{err});
|
||||
@ -68,8 +69,7 @@ pub const Parser = struct {
|
||||
while ((state != .end) and (state != .invalid)) : ({
|
||||
token = if (!keep_next) self.toker.next() else token;
|
||||
keep_next = false;
|
||||
}) {
|
||||
switch (state) {
|
||||
}) switch (state) {
|
||||
.expect_struct_name_OR_end => switch (token.tag) {
|
||||
.identifier => {
|
||||
state = .expect_l_paren;
|
||||
@ -184,8 +184,7 @@ pub const Parser = struct {
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// if invalid, empty the list
|
||||
if (state == .invalid) {
|
||||
|
@ -123,9 +123,94 @@ pub const UUID = struct {
|
||||
// Zero UUID
|
||||
pub const zero: UUID = .{ .bytes = .{0} ** 16 };
|
||||
|
||||
// Convenience function to return a new v4 UUID.
|
||||
pub fn newV4() UUID {
|
||||
return UUID.init();
|
||||
// TODO: Optimize both
|
||||
pub fn OR(arr1: *std.ArrayList(UUID), arr2: *std.ArrayList(UUID)) !void {
|
||||
for (0..arr2.items.len) |i| {
|
||||
if (!containUUID(arr1.*, arr2.items[i])) {
|
||||
try arr1.append(arr2.items[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn AND(arr1: *std.ArrayList(UUID), arr2: *std.ArrayList(UUID)) !void {
|
||||
var i: usize = 0;
|
||||
for (0..arr1.items.len) |_| {
|
||||
if (!containUUID(arr2.*, arr1.items[i])) {
|
||||
_ = arr1.orderedRemove(i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "OR & AND" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
var right_arr = std.ArrayList(UUID).init(allocator);
|
||||
defer right_arr.deinit();
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000005"));
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000006"));
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000007"));
|
||||
|
||||
var left_arr1 = std.ArrayList(UUID).init(allocator);
|
||||
defer left_arr1.deinit();
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
||||
|
||||
var expected_arr1 = std.ArrayList(UUID).init(allocator);
|
||||
defer expected_arr1.deinit();
|
||||
try expected_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try expected_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
|
||||
try AND(&left_arr1, &right_arr);
|
||||
try std.testing.expect(compareUUIDArray(left_arr1, expected_arr1));
|
||||
|
||||
var left_arr2 = std.ArrayList(UUID).init(allocator);
|
||||
defer left_arr2.deinit();
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
||||
|
||||
var expected_arr2 = std.ArrayList(UUID).init(allocator);
|
||||
defer expected_arr2.deinit();
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000005"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000006"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000007"));
|
||||
|
||||
try OR(&left_arr2, &right_arr);
|
||||
|
||||
try std.testing.expect(compareUUIDArray(left_arr2, expected_arr2));
|
||||
}
|
||||
|
||||
fn containUUID(arr: std.ArrayList(UUID), value: UUID) bool {
|
||||
return for (arr.items) |elem| {
|
||||
if (value.compare(elem)) break true;
|
||||
} else false;
|
||||
}
|
||||
|
||||
fn compareUUIDArray(arr1: std.ArrayList(UUID), arr2: std.ArrayList(UUID)) bool {
|
||||
if (arr1.items.len != arr2.items.len) {
|
||||
std.debug.print("Not same array len when comparing UUID. arr1: {d} arr2: {d}\n", .{ arr1.items.len, arr2.items.len });
|
||||
return false;
|
||||
}
|
||||
|
||||
for (0..arr1.items.len) |i| {
|
||||
if (!containUUID(arr2, arr1.items[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
test "parse and format" {
|
||||
|
@ -1,14 +0,0 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn getEnvVariables(allocator: std.mem.Allocator, variable: []const u8) ?[]const u8 {
|
||||
var env_map = std.process.getEnvMap(allocator) catch return null;
|
||||
defer env_map.deinit();
|
||||
|
||||
var iter = env_map.iterator();
|
||||
|
||||
while (iter.next()) |entry| {
|
||||
if (std.mem.eql(u8, entry.key_ptr.*, variable)) return allocator.dupe(u8, entry.value_ptr.*) catch return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
@ -4,48 +4,22 @@ const Condition = @import("fileEngine.zig").FileEngine.Condition;
|
||||
const Tokenizer = @import("tokenizers/ziql.zig").Tokenizer;
|
||||
const Token = @import("tokenizers/ziql.zig").Token;
|
||||
const UUID = @import("types/uuid.zig").UUID;
|
||||
const AND = @import("types/uuid.zig").AND;
|
||||
const OR = @import("types/uuid.zig").OR;
|
||||
const AdditionalData = @import("parsing-tools/additionalData.zig").AdditionalData;
|
||||
const AdditionalDataMember = @import("parsing-tools/additionalData.zig").AdditionalDataMember;
|
||||
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,
|
||||
toker: *Tokenizer,
|
||||
additional_data: AdditionalData,
|
||||
struct_name: []const u8 = undefined,
|
||||
file_engine: *FileEngine,
|
||||
|
||||
action: enum { GRAB, ADD, UPDATE, DELETE } = undefined,
|
||||
|
||||
pub fn init(allocator: Allocator, toker: *Tokenizer, file_engine: *FileEngine) Parser {
|
||||
// Do I need to init a FileEngine at each Parser, can't I put it in the CLI parser instead ?
|
||||
return Parser{
|
||||
.allocator = allocator,
|
||||
.toker = toker,
|
||||
.state = .start,
|
||||
.additional_data = AdditionalData.init(allocator),
|
||||
.file_engine = file_engine,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Parser) void {
|
||||
self.additional_data.deinit();
|
||||
self.allocator.free(self.struct_name);
|
||||
}
|
||||
|
||||
const Options = struct {
|
||||
members_for_ordering: std.ArrayList([]const u8), // The list in the right order of member name to use to order the result
|
||||
sense_for_ordering: enum { ASC, DESC },
|
||||
const ZiQlParserError = error{
|
||||
SynthaxError,
|
||||
MemberNotFound,
|
||||
MemberMissing,
|
||||
StructNotFound,
|
||||
FeatureMissing,
|
||||
ParsingValueError,
|
||||
ConditionError,
|
||||
};
|
||||
|
||||
const State = enum {
|
||||
@ -89,46 +63,58 @@ pub const Parser = struct {
|
||||
add_array_to_map,
|
||||
};
|
||||
|
||||
const ZiQlParserError = error{
|
||||
SynthaxError,
|
||||
MemberNotFound,
|
||||
MemberMissing,
|
||||
StructNotFound,
|
||||
FeatureMissing,
|
||||
ParsingValueError,
|
||||
ConditionError,
|
||||
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 {};
|
||||
};
|
||||
|
||||
/// This is the [] part
|
||||
/// IDK if saving it into the Parser struct is a good idea
|
||||
pub const AdditionalData = struct {
|
||||
entity_count_to_find: usize = 0,
|
||||
member_to_find: std.ArrayList(AdditionalDataMember),
|
||||
|
||||
pub fn init(allocator: Allocator) AdditionalData {
|
||||
return AdditionalData{ .member_to_find = std.ArrayList(AdditionalDataMember).init(allocator) };
|
||||
stdout.print("\x03\n", .{}) catch {};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *AdditionalData) void {
|
||||
for (0..self.member_to_find.items.len) |i| {
|
||||
self.member_to_find.items[i].additional_data.deinit();
|
||||
}
|
||||
|
||||
self.member_to_find.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
// This is name in: [name]
|
||||
// There is an additional data because it can be [friend [1; name]]
|
||||
const AdditionalDataMember = struct {
|
||||
name: []const u8,
|
||||
pub const Parser = struct {
|
||||
allocator: Allocator,
|
||||
state: State,
|
||||
toker: *Tokenizer,
|
||||
additional_data: AdditionalData,
|
||||
struct_name: []const u8 = undefined,
|
||||
file_engine: *FileEngine,
|
||||
|
||||
pub fn init(allocator: Allocator, name: []const u8) AdditionalDataMember {
|
||||
const additional_data = AdditionalData.init(allocator);
|
||||
return AdditionalDataMember{ .name = name, .additional_data = additional_data };
|
||||
}
|
||||
action: enum { GRAB, ADD, UPDATE, DELETE } = undefined,
|
||||
|
||||
pub fn init(allocator: Allocator, toker: *Tokenizer, file_engine: *FileEngine) Parser {
|
||||
// Do I need to init a FileEngine at each Parser, can't I put it in the CLI parser instead ?
|
||||
return Parser{
|
||||
.allocator = allocator,
|
||||
.toker = toker,
|
||||
.state = .start,
|
||||
.additional_data = AdditionalData.init(allocator),
|
||||
.file_engine = file_engine,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Parser) void {
|
||||
self.additional_data.deinit();
|
||||
self.allocator.free(self.struct_name);
|
||||
}
|
||||
|
||||
// TODO: Update to use ASC and DESC
|
||||
// Maybe create a Sender struct or something like that
|
||||
fn sendEntity(self: *Parser, uuid_list: *std.ArrayList(UUID)) void {
|
||||
var buffer = std.ArrayList(u8).init(self.allocator);
|
||||
defer buffer.deinit();
|
||||
|
||||
// Pop some element if the array is too long
|
||||
if ((self.additional_data.entity_count_to_find != 0) and (self.additional_data.entity_count_to_find < uuid_list.items.len)) {
|
||||
const to_pop = uuid_list.items.len - self.additional_data.entity_count_to_find;
|
||||
for (0..to_pop) |_| _ = uuid_list.pop();
|
||||
}
|
||||
|
||||
// Im gonna need a function in the file engine to parse and write in the buffer
|
||||
self.file_engine.parseAndWriteToSend(self.struct_name, uuid_list.items, &buffer, self.additional_data) catch @panic("Error parsing data to send");
|
||||
|
||||
send("{s}", .{buffer.items});
|
||||
}
|
||||
|
||||
pub fn parse(self: *Parser) !void {
|
||||
var token = self.toker.next();
|
||||
@ -137,8 +123,7 @@ pub const Parser = struct {
|
||||
while (self.state != State.end) : ({
|
||||
token = if (!keep_next) self.toker.next() else token;
|
||||
keep_next = false;
|
||||
}) {
|
||||
switch (self.state) {
|
||||
}) switch (self.state) {
|
||||
.start => switch (token.tag) {
|
||||
.keyword_grab => {
|
||||
self.action = .GRAB;
|
||||
@ -180,10 +165,7 @@ pub const Parser = struct {
|
||||
.DELETE => .filter_and_delete,
|
||||
else => unreachable,
|
||||
},
|
||||
.eof => {
|
||||
self.state = .filter_and_send;
|
||||
keep_next = true;
|
||||
},
|
||||
.eof => self.state = .filter_and_send,
|
||||
else => return self.printError("Error: Expect [ for additional data or { for a filter", &token, ZiQlParserError.SynthaxError),
|
||||
}
|
||||
},
|
||||
@ -301,31 +283,7 @@ pub const Parser = struct {
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Update to use ASC and DESC
|
||||
fn sendEntity(self: *Parser, uuid_list: *std.ArrayList(UUID)) void {
|
||||
var buffer = std.ArrayList(u8).init(self.allocator);
|
||||
defer buffer.deinit();
|
||||
|
||||
// Pop some element if the array is too long
|
||||
if ((self.additional_data.entity_count_to_find != 0) and (self.additional_data.entity_count_to_find < uuid_list.items.len)) {
|
||||
const to_pop = uuid_list.items.len - self.additional_data.entity_count_to_find;
|
||||
for (0..to_pop) |_| _ = uuid_list.pop();
|
||||
}
|
||||
|
||||
// Im gonna need a function in the file engine to parse and write in the buffer
|
||||
self.file_engine.parseAndWriteToSend(self.struct_name, uuid_list.items, &buffer, self.additional_data) catch @panic("Error parsing data to send");
|
||||
|
||||
send("{s}", .{buffer.items});
|
||||
}
|
||||
|
||||
// TODO: The parser that check what is between ||
|
||||
// For now only |ASC name, age|
|
||||
fn parseOptions(self: *Parser) void {
|
||||
_ = self;
|
||||
};
|
||||
}
|
||||
|
||||
/// Take an array of UUID and populate it with what match what is between {}
|
||||
@ -342,8 +300,7 @@ pub const Parser = struct {
|
||||
while (self.state != State.end) : ({
|
||||
token = if (!keep_next) self.toker.next() else token;
|
||||
keep_next = false;
|
||||
}) {
|
||||
switch (self.state) {
|
||||
}) switch (self.state) {
|
||||
.expect_left_condition => switch (token.tag) {
|
||||
.r_brace => {
|
||||
try self.file_engine.getAllUUIDList(struct_name, left_array);
|
||||
@ -407,8 +364,7 @@ pub const Parser = struct {
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return token;
|
||||
}
|
||||
@ -423,8 +379,7 @@ pub const Parser = struct {
|
||||
while (self.state != State.end) : ({
|
||||
token = if (!keep_next) self.toker.next() else token;
|
||||
keep_next = false;
|
||||
}) {
|
||||
switch (self.state) {
|
||||
}) switch (self.state) {
|
||||
.expect_member => switch (token.tag) {
|
||||
.identifier => {
|
||||
if (!self.file_engine.isMemberNameInStruct(condition.struct_name, self.toker.getTokenSlice(token))) {
|
||||
@ -525,10 +480,10 @@ pub const Parser = struct {
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check if the condition is valid
|
||||
// TODO: Mqke q function outside the Parser
|
||||
switch (condition.operation) {
|
||||
.equal => switch (condition.data_type) {
|
||||
.int, .float, .str, .bool, .id => {},
|
||||
@ -577,8 +532,7 @@ pub const Parser = struct {
|
||||
while (self.state != .end) : ({
|
||||
token = if ((!keep_next) and (self.state != .end)) self.toker.next() else token;
|
||||
keep_next = false;
|
||||
}) {
|
||||
switch (self.state) {
|
||||
}) switch (self.state) {
|
||||
.expect_count_of_entity_to_find => switch (token.tag) {
|
||||
.int_literal => {
|
||||
const count = std.fmt.parseInt(usize, self.toker.getTokenSlice(token), 10) catch {
|
||||
@ -633,8 +587,7 @@ pub const Parser = struct {
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Take the tokenizer and return a map of the ADD action.
|
||||
@ -649,8 +602,7 @@ pub const Parser = struct {
|
||||
while (self.state != .end) : ({
|
||||
token = if (!keep_next) self.toker.next() else token;
|
||||
keep_next = false;
|
||||
}) {
|
||||
switch (self.state) {
|
||||
}) switch (self.state) {
|
||||
.expect_member => switch (token.tag) {
|
||||
.identifier => {
|
||||
member_name = self.toker.getTokenSlice(token);
|
||||
@ -782,11 +734,11 @@ pub const Parser = struct {
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Print an error and send it to the user pointing to the token
|
||||
/// TODO: There is a duplicate of this somewhere, make it a single function
|
||||
fn printError(self: *Parser, message: []const u8, token: *Token, err: ZiQlParserError) ZiQlParserError {
|
||||
stdout.print("\n", .{}) catch {};
|
||||
stdout.print("{s}\n", .{message}) catch {};
|
||||
@ -810,96 +762,6 @@ pub const Parser = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Optimize both
|
||||
fn OR(arr1: *std.ArrayList(UUID), arr2: *std.ArrayList(UUID)) !void {
|
||||
for (0..arr2.items.len) |i| {
|
||||
if (!containUUID(arr1.*, arr2.items[i])) {
|
||||
try arr1.append(arr2.items[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn AND(arr1: *std.ArrayList(UUID), arr2: *std.ArrayList(UUID)) !void {
|
||||
var i: usize = 0;
|
||||
for (0..arr1.items.len) |_| {
|
||||
if (!containUUID(arr2.*, arr1.items[i])) {
|
||||
_ = arr1.orderedRemove(i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "OR & AND" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
var right_arr = std.ArrayList(UUID).init(allocator);
|
||||
defer right_arr.deinit();
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000005"));
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000006"));
|
||||
try right_arr.append(try UUID.parse("00000000-0000-0000-0000-000000000007"));
|
||||
|
||||
var left_arr1 = std.ArrayList(UUID).init(allocator);
|
||||
defer left_arr1.deinit();
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
||||
try left_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
||||
|
||||
var expected_arr1 = std.ArrayList(UUID).init(allocator);
|
||||
defer expected_arr1.deinit();
|
||||
try expected_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try expected_arr1.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
|
||||
try AND(&left_arr1, &right_arr);
|
||||
try std.testing.expect(compareUUIDArray(left_arr1, expected_arr1));
|
||||
|
||||
var left_arr2 = std.ArrayList(UUID).init(allocator);
|
||||
defer left_arr2.deinit();
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
||||
try left_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
||||
|
||||
var expected_arr2 = std.ArrayList(UUID).init(allocator);
|
||||
defer expected_arr2.deinit();
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000000"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000001"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000002"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000003"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000004"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000005"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000006"));
|
||||
try expected_arr2.append(try UUID.parse("00000000-0000-0000-0000-000000000007"));
|
||||
|
||||
try OR(&left_arr2, &right_arr);
|
||||
|
||||
try std.testing.expect(compareUUIDArray(left_arr2, expected_arr2));
|
||||
}
|
||||
|
||||
fn containUUID(arr: std.ArrayList(UUID), value: UUID) bool {
|
||||
return for (arr.items) |elem| {
|
||||
if (value.compare(elem)) break true;
|
||||
} else false;
|
||||
}
|
||||
|
||||
fn compareUUIDArray(arr1: std.ArrayList(UUID), arr2: std.ArrayList(UUID)) bool {
|
||||
if (arr1.items.len != arr2.items.len) {
|
||||
std.debug.print("Not same array len when comparing UUID. arr1: {d} arr2: {d}\n", .{ arr1.items.len, arr2.items.len });
|
||||
return false;
|
||||
}
|
||||
|
||||
for (0..arr1.items.len) |i| {
|
||||
if (!containUUID(arr2, arr1.items[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
test "ADD" {
|
||||
try testParsing("ADD User (name = 'Bob', email='bob@email.com', age=55, scores=[ 1 ], friends=[])");
|
||||
try testParsing("ADD User (name = 'Bob', email='bob@email.com', age=55, scores=[ 1 ], friends=[])");
|
||||
@ -941,13 +803,13 @@ test "Specific query" {
|
||||
}
|
||||
|
||||
test "Synthax error" {
|
||||
try expectParsingError("GRAB {}", Parser.ZiQlParserError.StructNotFound);
|
||||
try expectParsingError("GRAB User {qwe = 'qwe'}", Parser.ZiQlParserError.MemberNotFound);
|
||||
try expectParsingError("ADD User (name='Bob')", Parser.ZiQlParserError.MemberMissing);
|
||||
try expectParsingError("GRAB User {name='Bob'", Parser.ZiQlParserError.SynthaxError);
|
||||
try expectParsingError("GRAB User {age = 50 name='Bob'}", Parser.ZiQlParserError.SynthaxError);
|
||||
try expectParsingError("GRAB User {age <14 AND (age>55}", Parser.ZiQlParserError.SynthaxError);
|
||||
try expectParsingError("GRAB User {name < 'Hello'}", Parser.ZiQlParserError.ConditionError);
|
||||
try expectParsingError("GRAB {}", ZiQlParserError.StructNotFound);
|
||||
try expectParsingError("GRAB User {qwe = 'qwe'}", ZiQlParserError.MemberNotFound);
|
||||
try expectParsingError("ADD User (name='Bob')", ZiQlParserError.MemberMissing);
|
||||
try expectParsingError("GRAB User {name='Bob'", ZiQlParserError.SynthaxError);
|
||||
try expectParsingError("GRAB User {age = 50 name='Bob'}", ZiQlParserError.SynthaxError);
|
||||
try expectParsingError("GRAB User {age <14 AND (age>55}", ZiQlParserError.SynthaxError);
|
||||
try expectParsingError("GRAB User {name < 'Hello'}", ZiQlParserError.ConditionError);
|
||||
}
|
||||
|
||||
fn testParsing(source: [:0]const u8) !void {
|
||||
@ -964,7 +826,7 @@ fn testParsing(source: [:0]const u8) !void {
|
||||
try parser.parse();
|
||||
}
|
||||
|
||||
fn expectParsingError(source: [:0]const u8, err: Parser.ZiQlParserError) !void {
|
||||
fn expectParsingError(source: [:0]const u8, err: ZiQlParserError) !void {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
const path = try allocator.dupe(u8, "ZipponDB");
|
||||
|
Loading…
x
Reference in New Issue
Block a user