ZipponDB/src/parser/core.zig

399 lines
14 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const FileEngine = @import("../file/core.zig");
const SchemaEngine = @import("../schema/core.zig");
const Tokenizer = @import("tokenizer.zig").Tokenizer;
const dtype = @import("dtype");
const UUID = dtype.UUID;
const Filter = @import("../dataStructure/filter.zig").Filter;
const Condition = @import("../dataStructure/filter.zig").Condition;
const ConditionValue = @import("../dataStructure/filter.zig").ConditionValue;
const ComparisonOperator = @import("../dataStructure/filter.zig").ComparisonOperator;
const AdditionalData = @import("../dataStructure/additionalData.zig").AdditionalData;
const AdditionalDataMember = @import("../dataStructure/additionalData.zig").AdditionalDataMember;
const send = @import("../utils.zig").send;
const printError = @import("../utils.zig").printError;
const ZipponError = @import("error").ZipponError;
const PRINT_STATE = @import("config").PRINT_STATE;
const log = std.log.scoped(.ziqlParser);
pub const State = enum {
start,
invalid,
end,
// Endpoint
parse_new_data_and_add_data,
filter_and_send,
filter_and_update,
filter_and_delete,
// For the main parse function
expect_struct_name,
expect_filter,
parse_additional_data,
expect_filter_or_additional_data,
expect_new_data,
expect_right_arrow,
// For the additional data parser
expect_limit,
expect_semicolon_OR_right_bracket,
expect_member,
expect_comma_OR_r_bracket_OR_l_bracket,
expect_comma_OR_r_bracket,
// For the filter parser
expect_condition,
expect_operation, // Operations are = != < <= > >=
expect_value,
expect_ANDOR_OR_end,
expect_right_uuid_array,
// For the new data
expect_member_OR_value,
expect_equal,
expect_new_value,
expect_comma_OR_end,
add_member_to_map,
add_array_to_map,
};
pub const Self = @This();
pub usingnamespace @import("parts/comparison.zig");
pub usingnamespace @import("parts/condition.zig");
pub usingnamespace @import("parts/newData.zig");
pub usingnamespace @import("parts/value.zig");
pub usingnamespace @import("parts/filter.zig");
pub usingnamespace @import("parts/additionalData.zig");
pub usingnamespace @import("utils.zig");
var toker: Tokenizer = undefined;
toker: *Tokenizer = undefined,
file_engine: *FileEngine,
schema_engine: *SchemaEngine,
pub fn init(file_engine: *FileEngine, schema_engine: *SchemaEngine) Self {
return Self{
.file_engine = file_engine,
.schema_engine = schema_engine,
};
}
pub fn parse(self: *Self, buffer: [:0]const u8) ZipponError!void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
toker = Tokenizer.init(buffer);
self.toker = &toker;
var state: State = .start;
var additional_data = AdditionalData.init(allocator);
var struct_name: []const u8 = undefined;
var action: enum { GRAB, ADD, UPDATE, DELETE } = undefined;
var token = self.toker.next();
var keep_next = false; // Use in the loop to prevent to get the next token when continue. Just need to make it true and it is reset at every loop
while (state != State.end) : ({
token = if (!keep_next) self.toker.next() else token;
keep_next = false;
if (PRINT_STATE) std.debug.print("parse: {any}\n", .{state});
}) switch (state) {
.start => switch (token.tag) {
.keyword_grab => {
action = .GRAB;
state = .expect_struct_name;
},
.keyword_add => {
action = .ADD;
state = .expect_struct_name;
},
.keyword_update => {
action = .UPDATE;
state = .expect_struct_name;
},
.keyword_delete => {
action = .DELETE;
state = .expect_struct_name;
},
else => return printError(
"Error: Expected action keyword. Available: GRAB ADD DELETE UPDATE",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.expect_struct_name => {
// Check if the struct name is in the schema
struct_name = self.toker.getTokenSlice(token);
if (token.tag != .identifier) return printError(
"Error: Missing struct name.",
ZipponError.StructNotFound,
self.toker.buffer,
token.loc.start,
token.loc.end,
);
if (!self.schema_engine.isStructNameExists(struct_name)) return printError(
"Error: struct name not found in schema.",
ZipponError.StructNotFound,
self.toker.buffer,
token.loc.start,
token.loc.end,
);
switch (action) {
.ADD => state = .expect_new_data,
else => state = .expect_filter_or_additional_data,
}
},
.expect_filter_or_additional_data => {
keep_next = true;
switch (token.tag) {
.l_bracket => state = .parse_additional_data,
.l_brace, .eof => state = switch (action) {
.GRAB => .filter_and_send,
.UPDATE => .filter_and_update,
.DELETE => .filter_and_delete,
else => unreachable,
},
else => return printError(
"Error: Expect [ for additional data or { for a filter",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
}
},
.parse_additional_data => {
try self.parseAdditionalData(allocator, &additional_data, struct_name);
state = switch (action) {
.GRAB => .filter_and_send,
.UPDATE => .filter_and_update,
.DELETE => .filter_and_delete,
else => unreachable,
};
},
.filter_and_send => switch (token.tag) {
.l_brace => {
var filter = try self.parseFilter(allocator, struct_name, false);
defer filter.deinit();
const json_string = try self.file_engine.parseEntities(struct_name, filter, &additional_data, allocator);
send("{s}", .{json_string});
state = .end;
},
.eof => {
const json_string = try self.file_engine.parseEntities(struct_name, null, &additional_data, allocator);
send("{s}", .{json_string});
state = .end;
},
else => return printError(
"Error: Expected filter.",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
// TODO: Optimize so it doesnt use parseFilter but just parse the file and directly check the condition. Here I end up parsing 2 times.
.filter_and_update => switch (token.tag) {
.l_brace => {
var filter = try self.parseFilter(allocator, struct_name, false);
defer filter.deinit();
token = self.toker.last();
if (token.tag != .keyword_to) return printError(
"Error: Expected TO",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
);
token = self.toker.next();
if (token.tag != .l_paren) return printError(
"Error: Expected (",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
);
const sstruct = try self.schema_engine.structName2SchemaStruct(struct_name);
var members = std.ArrayList([]const u8).init(allocator);
defer members.deinit();
members.appendSlice(sstruct.members[1..]) catch return ZipponError.MemoryError;
var data_map = std.StringHashMap(ConditionValue).init(allocator);
defer data_map.deinit();
try self.parseNewData(allocator, &data_map, struct_name, &members);
var buff = std.ArrayList(u8).init(allocator);
defer buff.deinit();
try self.file_engine.updateEntities(struct_name, filter, data_map, &buff.writer(), &additional_data);
send("{s}", .{buff.items});
state = .end;
},
.keyword_to => {
token = self.toker.next();
if (token.tag != .l_paren) return printError(
"Error: Expected (",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
);
const sstruct = try self.schema_engine.structName2SchemaStruct(struct_name);
var members = std.ArrayList([]const u8).init(allocator);
defer members.deinit();
members.appendSlice(sstruct.members[1..]) catch return ZipponError.MemoryError;
var data_map = std.StringHashMap(ConditionValue).init(allocator);
defer data_map.deinit();
try self.parseNewData(allocator, &data_map, struct_name, &members);
var buff = std.ArrayList(u8).init(allocator);
defer buff.deinit();
try self.file_engine.updateEntities(struct_name, null, data_map, &buff.writer(), &additional_data);
send("{s}", .{buff.items});
state = .end;
},
else => return printError(
"Error: Expected filter or TO.",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.filter_and_delete => switch (token.tag) {
.l_brace => {
var filter = try self.parseFilter(allocator, struct_name, false);
defer filter.deinit();
var buff = std.ArrayList(u8).init(allocator);
defer buff.deinit();
try self.file_engine.deleteEntities(struct_name, filter, &buff.writer(), &additional_data);
send("{s}", .{buff.items});
state = .end;
},
.eof => {
var buff = std.ArrayList(u8).init(allocator);
defer buff.deinit();
try self.file_engine.deleteEntities(struct_name, null, &buff.writer(), &additional_data);
send("{s}", .{buff.items});
state = .end;
},
else => return printError(
"Error: Expected filter.",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.expect_new_data => switch (token.tag) {
.l_paren => {
keep_next = true;
state = .parse_new_data_and_add_data;
},
else => return printError(
"Error: Expected new data starting with (",
ZipponError.SynthaxError,
self.toker.buffer,
token.loc.start,
token.loc.end,
),
},
.parse_new_data_and_add_data => {
const sstruct = try self.schema_engine.structName2SchemaStruct(struct_name);
var order = std.ArrayList([]const u8).init(allocator);
defer order.deinit();
order.appendSlice(sstruct.members[1..]) catch return ZipponError.MemoryError;
var buff = std.ArrayList(u8).init(allocator);
defer buff.deinit();
buff.writer().writeAll("[") catch return ZipponError.WriteError;
var maps = std.ArrayList(std.StringHashMap(ConditionValue)).init(allocator);
defer maps.deinit();
var local_arena = std.heap.ArenaAllocator.init(allocator);
defer local_arena.deinit();
const local_allocator = arena.allocator();
var data_map = std.StringHashMap(ConditionValue).init(allocator);
defer data_map.deinit();
while (true) { // I could multithread that as it do take a long time for big benchmark
data_map.clearRetainingCapacity();
try self.parseNewData(local_allocator, &data_map, struct_name, &order);
var error_message_buffer = std.ArrayList(u8).init(local_allocator);
defer error_message_buffer.deinit();
const error_message_buffer_writer = error_message_buffer.writer();
error_message_buffer_writer.writeAll("Error missing: ") catch return ZipponError.WriteError;
if (!(self.schema_engine.checkIfAllMemberInMap(struct_name, &data_map, &error_message_buffer) catch {
return ZipponError.StructNotFound;
})) {
_ = error_message_buffer.pop();
_ = error_message_buffer.pop();
return printError(
error_message_buffer.items,
ZipponError.MemberMissing,
self.toker.buffer,
token.loc.start,
token.loc.end,
);
}
maps.append(data_map.cloneWithAllocator(local_allocator) catch return ZipponError.MemoryError) catch return ZipponError.MemoryError;
if (maps.items.len >= 1_000) {
try self.file_engine.addEntity(struct_name, maps.items, &buff.writer());
maps.clearRetainingCapacity();
_ = local_arena.reset(.retain_capacity);
}
token = self.toker.last_token;
if (token.tag == .l_paren) continue;
break;
}
try self.file_engine.addEntity(struct_name, maps.items, &buff.writer());
buff.writer().writeAll("]") catch return ZipponError.WriteError;
send("{s}", .{buff.items});
state = .end;
},
else => unreachable,
};
}