Missing from previous commit
This commit is contained in:
parent
a9c39d9aa5
commit
154a146678
@ -5,7 +5,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
const FileEngine = @import("../file/core.zig");
|
const FileEngine = @import("../file/core.zig");
|
||||||
const SchemaEngine = @import("../schema/core.zig");
|
const SchemaEngine = @import("../schema/core.zig");
|
||||||
const ThreadEngine = @import("../thread/engine.zig");
|
const ThreadEngine = @import("../thread/engine.zig");
|
||||||
const ziqlParser = @import("../parser/core.zig");
|
const ziqlParser = @import("../ziql/parser.zig");
|
||||||
const setLogPath = @import("../main.zig").setLogPath;
|
const setLogPath = @import("../main.zig").setLogPath;
|
||||||
const log = std.log.scoped(.cli);
|
const log = std.log.scoped(.cli);
|
||||||
|
|
||||||
|
@ -1,398 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const config = @import("config");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const Token = @import("../tokenizer.zig").Token;
|
|
||||||
const AdditionalData = @import("../../dataStructure/additionalData.zig").AdditionalData;
|
|
||||||
const printError = @import("../../utils.zig").printError;
|
|
||||||
|
|
||||||
const ZipponError = @import("error").ZipponError;
|
|
||||||
|
|
||||||
const Self = @import("../core.zig");
|
|
||||||
|
|
||||||
/// When this function is call, next token should be [
|
|
||||||
/// Check if an int is here -> check if ; is here -> check if member is here -> check if [ is here -> loop
|
|
||||||
pub fn parseAdditionalData(
|
|
||||||
self: Self,
|
|
||||||
allocator: Allocator,
|
|
||||||
additional_data: *AdditionalData,
|
|
||||||
struct_name: []const u8,
|
|
||||||
) ZipponError!void {
|
|
||||||
var token = self.toker.next();
|
|
||||||
var keep_next = false;
|
|
||||||
var state: Self.State = .expect_limit;
|
|
||||||
var last_member: []const u8 = undefined;
|
|
||||||
|
|
||||||
while (state != .end) : ({
|
|
||||||
token = if ((!keep_next) and (state != .end)) self.toker.next() else token;
|
|
||||||
keep_next = false;
|
|
||||||
if (config.PRINT_STATE) std.debug.print("parseAdditionalData: {any}\n", .{state});
|
|
||||||
}) switch (state) {
|
|
||||||
.expect_limit => switch (token.tag) {
|
|
||||||
.int_literal => {
|
|
||||||
additional_data.limit = std.fmt.parseInt(usize, self.toker.getTokenSlice(token), 10) catch {
|
|
||||||
return printError(
|
|
||||||
"Error while transforming limit into a integer.",
|
|
||||||
ZipponError.ParsingValueError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
state = .expect_semicolon_OR_right_bracket;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
state = .expect_member;
|
|
||||||
keep_next = true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_semicolon_OR_right_bracket => switch (token.tag) {
|
|
||||||
.semicolon => state = .expect_member,
|
|
||||||
.r_bracket => state = .end,
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expect ';' or ']'.",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_member => switch (token.tag) {
|
|
||||||
.identifier => {
|
|
||||||
if (!(self.schema_engine.isMemberNameInStruct(struct_name, self.toker.getTokenSlice(token)) catch {
|
|
||||||
return printError(
|
|
||||||
"Struct not found.",
|
|
||||||
ZipponError.StructNotFound,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
})) {
|
|
||||||
return printError(
|
|
||||||
"Member not found in struct.",
|
|
||||||
ZipponError.MemberNotFound,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
try additional_data.addMember(
|
|
||||||
self.toker.getTokenSlice(token),
|
|
||||||
try self.schema_engine.memberName2DataIndex(struct_name, self.toker.getTokenSlice(token)),
|
|
||||||
);
|
|
||||||
last_member = self.toker.getTokenSlice(token);
|
|
||||||
|
|
||||||
state = .expect_comma_OR_r_bracket_OR_l_bracket;
|
|
||||||
},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected a member name.",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_comma_OR_r_bracket_OR_l_bracket => switch (token.tag) {
|
|
||||||
.comma => state = .expect_member,
|
|
||||||
.r_bracket => state = .end,
|
|
||||||
.l_bracket => {
|
|
||||||
const sstruct = try self.schema_engine.structName2SchemaStruct(struct_name);
|
|
||||||
try parseAdditionalData(
|
|
||||||
self,
|
|
||||||
allocator,
|
|
||||||
&additional_data.childrens.items[additional_data.childrens.items.len - 1].additional_data,
|
|
||||||
sstruct.links.get(last_member).?,
|
|
||||||
);
|
|
||||||
state = .expect_comma_OR_r_bracket;
|
|
||||||
},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected , or ] or [",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_comma_OR_r_bracket => switch (token.tag) {
|
|
||||||
.comma => state = .expect_member,
|
|
||||||
.r_bracket => state = .end,
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected , or ]",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const Token = @import("../tokenizer.zig").Token;
|
|
||||||
const ComparisonOperator = @import("../../dataStructure/filter.zig").ComparisonOperator;
|
|
||||||
const printError = @import("../../utils.zig").printError;
|
|
||||||
|
|
||||||
const ZipponError = @import("error").ZipponError;
|
|
||||||
|
|
||||||
const Self = @import("../core.zig");
|
|
||||||
|
|
||||||
pub fn parseComparisonOperator(
|
|
||||||
self: Self,
|
|
||||||
token: Token,
|
|
||||||
) ZipponError!ComparisonOperator {
|
|
||||||
return switch (token.tag) {
|
|
||||||
.equal => .equal, // =
|
|
||||||
.angle_bracket_left => .inferior, // <
|
|
||||||
.angle_bracket_right => .superior, // >
|
|
||||||
.angle_bracket_left_equal => .inferior_or_equal, // <=
|
|
||||||
.angle_bracket_right_equal => .superior_or_equal, // >=
|
|
||||||
.bang_equal => .different, // !=
|
|
||||||
.keyword_in => .in,
|
|
||||||
.keyword_not_in => .not_in,
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected condition. Including < > <= >= = !=",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const config = @import("config");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const Token = @import("../tokenizer.zig").Token;
|
|
||||||
const Condition = @import("../../dataStructure/filter.zig").Condition;
|
|
||||||
const printError = @import("../../utils.zig").printError;
|
|
||||||
const log = std.log.scoped(.ziqlParser);
|
|
||||||
|
|
||||||
const ZipponError = @import("error").ZipponError;
|
|
||||||
|
|
||||||
const Self = @import("../core.zig");
|
|
||||||
|
|
||||||
/// Parse to get a Condition. Which is a struct that is use by the FileEngine to retreive data.
|
|
||||||
/// In the query, it is this part name = 'Bob' or age <= 10
|
|
||||||
pub fn parseCondition(
|
|
||||||
self: Self,
|
|
||||||
allocator: Allocator,
|
|
||||||
token_ptr: *Token,
|
|
||||||
struct_name: []const u8,
|
|
||||||
) ZipponError!Condition {
|
|
||||||
var keep_next = false;
|
|
||||||
var state: Self.State = .expect_member;
|
|
||||||
var token = token_ptr.*;
|
|
||||||
var member_name: []const u8 = undefined;
|
|
||||||
|
|
||||||
var condition = Condition{};
|
|
||||||
|
|
||||||
while (state != .end) : ({
|
|
||||||
token = if (!keep_next) self.toker.next() else token;
|
|
||||||
keep_next = false;
|
|
||||||
if (config.PRINT_STATE) std.debug.print("parseCondition: {any}\n", .{state});
|
|
||||||
}) switch (state) {
|
|
||||||
.expect_member => switch (token.tag) {
|
|
||||||
.identifier => {
|
|
||||||
if (!(self.schema_engine.isMemberNameInStruct(struct_name, self.toker.getTokenSlice(token)) catch {
|
|
||||||
return printError(
|
|
||||||
"Error: Struct not found.",
|
|
||||||
ZipponError.StructNotFound,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
})) {
|
|
||||||
return printError(
|
|
||||||
"Error: Member not part of struct.",
|
|
||||||
ZipponError.MemberNotFound,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
condition.data_type = self.schema_engine.memberName2DataType(
|
|
||||||
struct_name,
|
|
||||||
self.toker.getTokenSlice(token),
|
|
||||||
) catch return ZipponError.MemberNotFound;
|
|
||||||
condition.data_index = self.schema_engine.memberName2DataIndex(
|
|
||||||
struct_name,
|
|
||||||
self.toker.getTokenSlice(token),
|
|
||||||
) catch return ZipponError.MemberNotFound;
|
|
||||||
member_name = self.toker.getTokenSlice(token);
|
|
||||||
state = .expect_operation;
|
|
||||||
},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected member name.",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_operation => {
|
|
||||||
condition.operation = try self.parseComparisonOperator(token);
|
|
||||||
state = .expect_value;
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_value => {
|
|
||||||
log.debug("Parse condition value of member {s}", .{member_name});
|
|
||||||
condition.value = try self.parseConditionValue(allocator, struct_name, member_name, condition.data_type, &token);
|
|
||||||
state = .end;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
try self.checkConditionValidity(condition, token);
|
|
||||||
|
|
||||||
return condition;
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const dtype = @import("dtype");
|
|
||||||
const config = @import("config");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const Filter = @import("../../dataStructure/filter.zig").Filter;
|
|
||||||
const printError = @import("../../utils.zig").printError;
|
|
||||||
|
|
||||||
const ZipponError = @import("error").ZipponError;
|
|
||||||
|
|
||||||
const Self = @import("../core.zig");
|
|
||||||
|
|
||||||
/// Take an array of UUID and populate it with what match what is between {}
|
|
||||||
/// Main is to know if between {} or (), main is true if between {}, otherwise between () inside {}
|
|
||||||
pub fn parseFilter(
|
|
||||||
self: Self,
|
|
||||||
allocator: Allocator,
|
|
||||||
struct_name: []const u8,
|
|
||||||
is_sub: bool,
|
|
||||||
) ZipponError!Filter {
|
|
||||||
var filter = try Filter.init(allocator);
|
|
||||||
errdefer filter.deinit();
|
|
||||||
|
|
||||||
var keep_next = false;
|
|
||||||
var token = self.toker.next();
|
|
||||||
var state: Self.State = .expect_condition;
|
|
||||||
|
|
||||||
while (state != .end) : ({
|
|
||||||
token = if (keep_next) token else self.toker.next();
|
|
||||||
keep_next = false;
|
|
||||||
if (config.PRINT_STATE) std.debug.print("parseFilter: {any}\n", .{state});
|
|
||||||
}) switch (state) {
|
|
||||||
.expect_condition => switch (token.tag) {
|
|
||||||
.r_brace => {
|
|
||||||
if (!is_sub) {
|
|
||||||
state = .end;
|
|
||||||
} else {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected ) not }",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.r_paren => {
|
|
||||||
if (is_sub) {
|
|
||||||
state = .end;
|
|
||||||
} else {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected } not )",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.l_paren => {
|
|
||||||
var sub_filter = try parseFilter(self, allocator, struct_name, true);
|
|
||||||
filter.addSubFilter(&sub_filter);
|
|
||||||
token = self.toker.last();
|
|
||||||
keep_next = true;
|
|
||||||
state = .expect_ANDOR_OR_end;
|
|
||||||
},
|
|
||||||
.identifier => {
|
|
||||||
const condition = try self.parseCondition(allocator, &token, struct_name);
|
|
||||||
try filter.addCondition(condition);
|
|
||||||
token = self.toker.last();
|
|
||||||
keep_next = true;
|
|
||||||
state = .expect_ANDOR_OR_end;
|
|
||||||
},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected ( or condition.",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_ANDOR_OR_end => switch (token.tag) {
|
|
||||||
.r_brace => {
|
|
||||||
if (!is_sub) {
|
|
||||||
state = .end;
|
|
||||||
} else {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected ) not }",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.r_paren => {
|
|
||||||
if (is_sub) {
|
|
||||||
state = .end;
|
|
||||||
} else {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected } not )",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.keyword_and => {
|
|
||||||
try filter.addLogicalOperator(.AND);
|
|
||||||
state = .expect_condition;
|
|
||||||
},
|
|
||||||
.keyword_or => {
|
|
||||||
try filter.addLogicalOperator(.OR);
|
|
||||||
state = .expect_condition;
|
|
||||||
},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected AND, OR, or }",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.end => {},
|
|
||||||
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const config = @import("config");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const ConditionValue = @import("../../dataStructure/filter.zig").ConditionValue;
|
|
||||||
const printError = @import("../../utils.zig").printError;
|
|
||||||
|
|
||||||
const ZipponError = @import("error").ZipponError;
|
|
||||||
|
|
||||||
const Self = @import("../core.zig");
|
|
||||||
|
|
||||||
/// Take the tokenizer and return a map of the ADD action.
|
|
||||||
/// Keys are the member name and value are the string of the value in the query. E.g. 'Adrien' or '10'
|
|
||||||
/// Entry token need to be (
|
|
||||||
pub fn parseNewData(
|
|
||||||
self: Self,
|
|
||||||
allocator: Allocator,
|
|
||||||
map: *std.StringHashMap(ConditionValue),
|
|
||||||
struct_name: []const u8,
|
|
||||||
order: *std.ArrayList([]const u8),
|
|
||||||
) !void {
|
|
||||||
var token = self.toker.next();
|
|
||||||
var keep_next = false;
|
|
||||||
var reordering: bool = false;
|
|
||||||
var member_name: []const u8 = undefined;
|
|
||||||
var state: Self.State = .expect_member_OR_value;
|
|
||||||
var i: usize = 0;
|
|
||||||
|
|
||||||
while (state != .end) : ({
|
|
||||||
token = if (!keep_next) self.toker.next() else token;
|
|
||||||
keep_next = false;
|
|
||||||
if (config.PRINT_STATE) std.debug.print("parseNewData: {any}\n", .{state});
|
|
||||||
}) switch (state) {
|
|
||||||
.expect_member_OR_value => switch (token.tag) {
|
|
||||||
.identifier => {
|
|
||||||
if (!reordering) {
|
|
||||||
order.*.clearRetainingCapacity();
|
|
||||||
reordering = true;
|
|
||||||
}
|
|
||||||
member_name = self.toker.getTokenSlice(token);
|
|
||||||
if (!(self.schema_engine.isMemberNameInStruct(struct_name, member_name) catch {
|
|
||||||
return ZipponError.StructNotFound;
|
|
||||||
})) return printError(
|
|
||||||
"Member not found in struct.",
|
|
||||||
ZipponError.MemberNotFound,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
order.*.append(allocator.dupe(u8, member_name) catch return ZipponError.MemoryError) catch return ZipponError.MemoryError;
|
|
||||||
state = .expect_equal;
|
|
||||||
},
|
|
||||||
.string_literal,
|
|
||||||
.int_literal,
|
|
||||||
.float_literal,
|
|
||||||
.date_literal,
|
|
||||||
.time_literal,
|
|
||||||
.datetime_literal,
|
|
||||||
.bool_literal_true,
|
|
||||||
.bool_literal_false,
|
|
||||||
.uuid_literal,
|
|
||||||
.l_bracket,
|
|
||||||
.l_brace,
|
|
||||||
.keyword_none,
|
|
||||||
.keyword_now,
|
|
||||||
=> {
|
|
||||||
member_name = order.items[i];
|
|
||||||
i += 1;
|
|
||||||
keep_next = true;
|
|
||||||
state = .expect_new_value;
|
|
||||||
},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected member name.",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_equal => switch (token.tag) {
|
|
||||||
// TODO: Implement stuff to manipulate array like APPEND or REMOVE
|
|
||||||
.equal => state = .expect_new_value,
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected =",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_new_value => {
|
|
||||||
const data_type = self.schema_engine.memberName2DataType(struct_name, member_name) catch return ZipponError.StructNotFound;
|
|
||||||
map.put(member_name, try self.parseConditionValue(allocator, struct_name, member_name, data_type, &token)) catch return ZipponError.MemoryError;
|
|
||||||
if (data_type == .link or data_type == .link_array) {
|
|
||||||
token = self.toker.last_token;
|
|
||||||
keep_next = true;
|
|
||||||
}
|
|
||||||
state = .expect_comma_OR_end;
|
|
||||||
},
|
|
||||||
|
|
||||||
.expect_comma_OR_end => switch (token.tag) {
|
|
||||||
.r_paren => state = .end,
|
|
||||||
.comma => state = .expect_member_OR_value,
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expect , or )",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,289 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const dtype = @import("dtype");
|
|
||||||
const UUID = dtype.UUID;
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const Token = @import("../tokenizer.zig").Token;
|
|
||||||
const Filter = @import("../../dataStructure/filter.zig").Filter;
|
|
||||||
const ConditionValue = @import("../../dataStructure/filter.zig").ConditionValue;
|
|
||||||
const AdditionalData = @import("../../dataStructure/additionalData.zig").AdditionalData;
|
|
||||||
const printError = @import("../../utils.zig").printError;
|
|
||||||
|
|
||||||
const ZipponError = @import("error").ZipponError;
|
|
||||||
|
|
||||||
const Self = @import("../core.zig");
|
|
||||||
|
|
||||||
/// To run just after a condition like = or > or >= to get the corresponding ConditionValue that you need to compare
|
|
||||||
pub fn parseConditionValue(self: Self, allocator: Allocator, struct_name: []const u8, member_name: []const u8, data_type: dtype.DataType, token: *Token) ZipponError!ConditionValue {
|
|
||||||
const start_index = token.loc.start;
|
|
||||||
const expected_tag: ?Token.Tag = switch (data_type) {
|
|
||||||
.int => .int_literal,
|
|
||||||
.float => .float_literal,
|
|
||||||
.str => .string_literal,
|
|
||||||
.self => .uuid_literal,
|
|
||||||
.int_array => .int_literal,
|
|
||||||
.float_array => .float_literal,
|
|
||||||
.str_array => .string_literal,
|
|
||||||
.bool, .bool_array, .link, .link_array, .date, .time, .datetime, .date_array, .time_array, .datetime_array => null, // handle separately
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check if the all next tokens are the right one
|
|
||||||
if (expected_tag) |tag| {
|
|
||||||
if (data_type.is_array()) {
|
|
||||||
token.* = try self.checkTokensInArray(tag);
|
|
||||||
} else {
|
|
||||||
if (token.tag != tag) {
|
|
||||||
return printError(
|
|
||||||
"Error: Wrong type", // TODO: Print the expected type
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else switch (data_type) {
|
|
||||||
.bool => if (token.tag != .bool_literal_true and token.tag != .bool_literal_false) {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected bool",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.bool_array => {
|
|
||||||
token.* = self.toker.next();
|
|
||||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
|
||||||
if (token.tag != .bool_literal_true and token.tag != .bool_literal_false) {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected bool or ]",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.date => if (token.tag != .date_literal and token.tag != .keyword_now) {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected date",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.date_array => {
|
|
||||||
token.* = self.toker.next();
|
|
||||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
|
||||||
if (token.tag != .date_literal and token.tag != .keyword_now) {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected date",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.time => if (token.tag != .time_literal and token.tag != .keyword_now) {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected time",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.time_array => {
|
|
||||||
token.* = self.toker.next();
|
|
||||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
|
||||||
if (token.tag != .time_literal and token.tag != .keyword_now) {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected time",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.datetime => if (token.tag != .datetime_literal and token.tag != .keyword_now) {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected datetime",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
.datetime_array => {
|
|
||||||
token.* = self.toker.next();
|
|
||||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
|
||||||
if (token.tag != .datetime_literal and token.tag != .keyword_now) {
|
|
||||||
return printError(
|
|
||||||
"Error: Expected datetime",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.link, .link_array => {},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
|
|
||||||
// And finally create the ConditionValue
|
|
||||||
switch (data_type) {
|
|
||||||
.int => return ConditionValue.initInt(self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.float => return ConditionValue.initFloat(self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.str => return ConditionValue.initStr(self.toker.buffer[start_index + 1 .. token.loc.end - 1]),
|
|
||||||
.date => return ConditionValue.initDate(self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.time => return ConditionValue.initTime(self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.datetime => return ConditionValue.initDateTime(self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.bool => return ConditionValue.initBool(self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.int_array => return try ConditionValue.initArrayInt(allocator, self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.str_array => return try ConditionValue.initArrayStr(allocator, self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.bool_array => return try ConditionValue.initArrayBool(allocator, self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.float_array => return try ConditionValue.initArrayFloat(allocator, self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.date_array => return try ConditionValue.initArrayDate(allocator, self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.time_array => return try ConditionValue.initArrayTime(allocator, self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.datetime_array => return try ConditionValue.initArrayDateTime(allocator, self.toker.buffer[start_index..token.loc.end]),
|
|
||||||
.link => switch (token.tag) {
|
|
||||||
.keyword_none => { // TODO: Stop creating a map if empty, can be null or something. Or maybe just keep one map link that in memory, so I dont create it everytime
|
|
||||||
const map = allocator.create(std.AutoHashMap(UUID, void)) catch return ZipponError.MemoryError;
|
|
||||||
map.* = std.AutoHashMap(UUID, void).init(allocator);
|
|
||||||
map.put(dtype.Zero, {}) catch return ZipponError.MemoryError;
|
|
||||||
_ = self.toker.next();
|
|
||||||
return ConditionValue.initLink(map);
|
|
||||||
},
|
|
||||||
.uuid_literal => {
|
|
||||||
const uuid = UUID.parse(self.toker.buffer[start_index..token.loc.end]) catch return ZipponError.InvalidUUID;
|
|
||||||
if (!self.schema_engine.isUUIDExist(struct_name, uuid)) return printError(
|
|
||||||
"Error: UUID do not exist in database.",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
|
|
||||||
const map = allocator.create(std.AutoHashMap(UUID, void)) catch return ZipponError.MemoryError;
|
|
||||||
map.* = std.AutoHashMap(UUID, void).init(allocator);
|
|
||||||
map.put(uuid, {}) catch return ZipponError.MemoryError;
|
|
||||||
_ = self.toker.next();
|
|
||||||
return ConditionValue.initLink(map);
|
|
||||||
},
|
|
||||||
.l_brace, .l_bracket => {
|
|
||||||
var filter: ?Filter = null;
|
|
||||||
defer if (filter != null) filter.?.deinit();
|
|
||||||
|
|
||||||
var additional_data_arena = std.heap.ArenaAllocator.init(allocator);
|
|
||||||
defer additional_data_arena.deinit();
|
|
||||||
var additional_data = AdditionalData.init(additional_data_arena.allocator());
|
|
||||||
|
|
||||||
if (token.tag == .l_bracket) {
|
|
||||||
try self.parseAdditionalData(allocator, &additional_data, struct_name);
|
|
||||||
token.* = self.toker.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
additional_data.limit = 1;
|
|
||||||
|
|
||||||
const link_sstruct = try self.schema_engine.linkedStructName(struct_name, member_name);
|
|
||||||
if (token.tag == .l_brace) filter = try self.parseFilter(
|
|
||||||
allocator,
|
|
||||||
link_sstruct.name,
|
|
||||||
false,
|
|
||||||
) else return printError(
|
|
||||||
"Error: Expected filter",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
|
|
||||||
filter = switch (filter.?.root.*) {
|
|
||||||
.empty => null,
|
|
||||||
else => filter,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Here I have the filter and additionalData
|
|
||||||
const map = allocator.create(std.AutoHashMap(UUID, void)) catch return ZipponError.MemoryError;
|
|
||||||
map.* = std.AutoHashMap(UUID, void).init(allocator);
|
|
||||||
try self.file_engine.populateVoidUUIDMap(
|
|
||||||
link_sstruct.name,
|
|
||||||
filter,
|
|
||||||
map,
|
|
||||||
&additional_data,
|
|
||||||
);
|
|
||||||
return ConditionValue.initLink(map);
|
|
||||||
},
|
|
||||||
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected uuid or none",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
.link_array => switch (token.tag) {
|
|
||||||
.keyword_none => {
|
|
||||||
const map = allocator.create(std.AutoHashMap(UUID, void)) catch return ZipponError.MemoryError;
|
|
||||||
map.* = std.AutoHashMap(UUID, void).init(allocator);
|
|
||||||
_ = self.toker.next();
|
|
||||||
return ConditionValue.initArrayLink(map);
|
|
||||||
},
|
|
||||||
.l_brace, .l_bracket => {
|
|
||||||
var filter: ?Filter = null;
|
|
||||||
defer if (filter != null) filter.?.deinit();
|
|
||||||
|
|
||||||
var additional_data_arena = std.heap.ArenaAllocator.init(allocator);
|
|
||||||
defer additional_data_arena.deinit();
|
|
||||||
var additional_data = AdditionalData.init(additional_data_arena.allocator());
|
|
||||||
|
|
||||||
if (token.tag == .l_bracket) {
|
|
||||||
try self.parseAdditionalData(allocator, &additional_data, struct_name);
|
|
||||||
token.* = self.toker.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
const link_sstruct = try self.schema_engine.linkedStructName(struct_name, member_name);
|
|
||||||
if (token.tag == .l_brace) filter = try self.parseFilter(allocator, link_sstruct.name, false) else return printError(
|
|
||||||
"Error: Expected filter",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
|
|
||||||
filter = switch (filter.?.root.*) {
|
|
||||||
.empty => null,
|
|
||||||
else => filter,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Here I have the filter and additionalData
|
|
||||||
const map = allocator.create(std.AutoHashMap(UUID, void)) catch return ZipponError.MemoryError;
|
|
||||||
map.* = std.AutoHashMap(UUID, void).init(allocator);
|
|
||||||
try self.file_engine.populateVoidUUIDMap(
|
|
||||||
struct_name,
|
|
||||||
filter,
|
|
||||||
map,
|
|
||||||
&additional_data,
|
|
||||||
);
|
|
||||||
return ConditionValue.initArrayLink(map);
|
|
||||||
},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Expected uuid or none",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
.self => unreachable,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,447 +0,0 @@
|
|||||||
// From https://github.com/ziglang/zig/blob/master/lib/std/zig/tokenizer.zig
|
|
||||||
const std = @import("std");
|
|
||||||
const Loc = @import("../dataStructure/loc.zig");
|
|
||||||
|
|
||||||
pub const Token = struct {
|
|
||||||
tag: Tag,
|
|
||||||
loc: Loc,
|
|
||||||
|
|
||||||
pub const keywords = std.StaticStringMap(Tag).initComptime(.{
|
|
||||||
.{ "GRAB", .keyword_grab },
|
|
||||||
.{ "UPDATE", .keyword_update },
|
|
||||||
.{ "DELETE", .keyword_delete },
|
|
||||||
.{ "ADD", .keyword_add },
|
|
||||||
.{ "IN", .keyword_in },
|
|
||||||
.{ "AND", .keyword_and },
|
|
||||||
.{ "OR", .keyword_or },
|
|
||||||
.{ "TO", .keyword_to },
|
|
||||||
.{ "NONE", .keyword_none },
|
|
||||||
.{ "NOW", .keyword_now },
|
|
||||||
.{ "APPEND", .keyword_append },
|
|
||||||
.{ "POP", .keyword_pop },
|
|
||||||
.{ "REMOVE", .keyword_remove },
|
|
||||||
.{ "REMOVEAT", .keyword_remove_at },
|
|
||||||
.{ "grab", .keyword_grab },
|
|
||||||
.{ "update", .keyword_update },
|
|
||||||
.{ "delete", .keyword_delete },
|
|
||||||
.{ "add", .keyword_add },
|
|
||||||
.{ "in", .keyword_in },
|
|
||||||
.{ "and", .keyword_and },
|
|
||||||
.{ "or", .keyword_or },
|
|
||||||
.{ "to", .keyword_to },
|
|
||||||
.{ "none", .keyword_none },
|
|
||||||
.{ "true", .bool_literal_true },
|
|
||||||
.{ "false", .bool_literal_false },
|
|
||||||
.{ "now", .keyword_now },
|
|
||||||
});
|
|
||||||
|
|
||||||
pub fn getKeyword(bytes: []const u8) ?Tag {
|
|
||||||
return keywords.get(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Tag = enum {
|
|
||||||
eof,
|
|
||||||
invalid,
|
|
||||||
|
|
||||||
keyword_grab,
|
|
||||||
keyword_update,
|
|
||||||
keyword_delete,
|
|
||||||
keyword_add,
|
|
||||||
keyword_in,
|
|
||||||
keyword_not_in,
|
|
||||||
keyword_and,
|
|
||||||
keyword_or,
|
|
||||||
keyword_to,
|
|
||||||
keyword_none,
|
|
||||||
keyword_now,
|
|
||||||
keyword_append,
|
|
||||||
keyword_pop,
|
|
||||||
keyword_remove,
|
|
||||||
keyword_remove_at,
|
|
||||||
|
|
||||||
string_literal,
|
|
||||||
int_literal,
|
|
||||||
float_literal,
|
|
||||||
date_literal,
|
|
||||||
time_literal,
|
|
||||||
datetime_literal,
|
|
||||||
bool_literal_true,
|
|
||||||
bool_literal_false,
|
|
||||||
uuid_literal,
|
|
||||||
identifier,
|
|
||||||
equal,
|
|
||||||
bang, // !
|
|
||||||
pipe, // |
|
|
||||||
l_paren, // (
|
|
||||||
r_paren, // )
|
|
||||||
l_bracket, // [
|
|
||||||
r_bracket, // ]
|
|
||||||
l_brace, // {
|
|
||||||
r_brace, // }
|
|
||||||
semicolon, // ;
|
|
||||||
comma, // ,
|
|
||||||
angle_bracket_left, // <
|
|
||||||
angle_bracket_right, // >
|
|
||||||
angle_bracket_left_equal, // <=
|
|
||||||
angle_bracket_right_equal, // >=
|
|
||||||
equal_angle_bracket_right, // =>
|
|
||||||
period, // .
|
|
||||||
bang_equal, // !=
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Tokenizer = struct {
|
|
||||||
buffer: [:0]const u8,
|
|
||||||
index: usize,
|
|
||||||
last_token: Token = undefined,
|
|
||||||
|
|
||||||
pub fn getTokenSlice(self: *Tokenizer, token: Token) []const u8 {
|
|
||||||
return self.buffer[token.loc.start..token.loc.end];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last(self: Tokenizer) Token {
|
|
||||||
return self.last_token;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(buffer: [:0]const u8) Tokenizer {
|
|
||||||
// Skip the UTF-8 BOM if present.
|
|
||||||
return .{
|
|
||||||
.buffer = buffer,
|
|
||||||
.index = if (std.mem.startsWith(u8, buffer, "\xEF\xBB\xBF")) 3 else 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const State = enum {
|
|
||||||
start,
|
|
||||||
invalid,
|
|
||||||
string_literal,
|
|
||||||
date_literal,
|
|
||||||
time_literal,
|
|
||||||
uuid_literal,
|
|
||||||
identifier,
|
|
||||||
equal,
|
|
||||||
bang,
|
|
||||||
angle_bracket_left,
|
|
||||||
angle_bracket_right,
|
|
||||||
string_literal_backslash,
|
|
||||||
float,
|
|
||||||
int,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn next(self: *Tokenizer) Token {
|
|
||||||
var state: State = .start;
|
|
||||||
var result: Token = .{
|
|
||||||
.tag = undefined,
|
|
||||||
.loc = .{
|
|
||||||
.start = self.index,
|
|
||||||
.end = undefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
while (true) : (self.index += 1) {
|
|
||||||
const c = self.buffer[self.index];
|
|
||||||
switch (state) {
|
|
||||||
.start => switch (c) {
|
|
||||||
0 => {
|
|
||||||
if (self.index == self.buffer.len) return .{
|
|
||||||
.tag = .eof,
|
|
||||||
.loc = .{
|
|
||||||
.start = self.index,
|
|
||||||
.end = self.index,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
state = .invalid;
|
|
||||||
},
|
|
||||||
' ', '\n', '\t', '\r' => {
|
|
||||||
result.loc.start = self.index + 1;
|
|
||||||
},
|
|
||||||
'\'' => {
|
|
||||||
state = .string_literal;
|
|
||||||
result.tag = .string_literal;
|
|
||||||
},
|
|
||||||
'a'...'z', 'A'...'Z', '_' => {
|
|
||||||
state = .identifier;
|
|
||||||
result.tag = .identifier;
|
|
||||||
},
|
|
||||||
'=' => {
|
|
||||||
state = .equal;
|
|
||||||
},
|
|
||||||
'!' => {
|
|
||||||
state = .bang;
|
|
||||||
},
|
|
||||||
'|' => {
|
|
||||||
result.tag = .pipe;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
'(' => {
|
|
||||||
result.tag = .l_paren;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
')' => {
|
|
||||||
result.tag = .r_paren;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
'[' => {
|
|
||||||
result.tag = .l_bracket;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
']' => {
|
|
||||||
result.tag = .r_bracket;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
';' => {
|
|
||||||
result.tag = .semicolon;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
',' => {
|
|
||||||
result.tag = .comma;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
'<' => {
|
|
||||||
state = .angle_bracket_left;
|
|
||||||
},
|
|
||||||
'>' => {
|
|
||||||
state = .angle_bracket_right;
|
|
||||||
},
|
|
||||||
'{' => {
|
|
||||||
result.tag = .l_brace;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
'}' => {
|
|
||||||
result.tag = .r_brace;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
'.' => {
|
|
||||||
state = .float;
|
|
||||||
result.tag = .float_literal;
|
|
||||||
},
|
|
||||||
'0'...'9', '-' => {
|
|
||||||
state = .int;
|
|
||||||
result.tag = .int_literal;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
state = .invalid;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.invalid => {
|
|
||||||
// TODO make a better invalid handler
|
|
||||||
@panic("Unknow char!!!");
|
|
||||||
},
|
|
||||||
|
|
||||||
.identifier => switch (c) {
|
|
||||||
'a'...'z', 'A'...'Z', '_', '0'...'9' => continue,
|
|
||||||
'-' => {
|
|
||||||
state = .uuid_literal;
|
|
||||||
result.tag = .uuid_literal;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |tag| {
|
|
||||||
result.tag = tag;
|
|
||||||
} else {
|
|
||||||
result.tag = .identifier;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.string_literal => switch (c) {
|
|
||||||
0 => {
|
|
||||||
if (self.index != self.buffer.len) {
|
|
||||||
state = .invalid;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result.tag = .invalid;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
'\n' => {
|
|
||||||
result.tag = .invalid;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
'\\' => {
|
|
||||||
state = .string_literal_backslash;
|
|
||||||
},
|
|
||||||
'\'' => {
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
0x01...0x09, 0x0b...0x1f, 0x7f => {
|
|
||||||
state = .invalid;
|
|
||||||
},
|
|
||||||
else => continue,
|
|
||||||
},
|
|
||||||
|
|
||||||
.string_literal_backslash => switch (c) {
|
|
||||||
0, '\n' => {
|
|
||||||
result.tag = .invalid;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
state = .string_literal;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.bang => switch (c) {
|
|
||||||
'=' => {
|
|
||||||
result.tag = .bang_equal;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
'I' => {
|
|
||||||
if (self.buffer.len > self.index + 1 and self.buffer[self.index + 1] == 'N') {
|
|
||||||
result.tag = .keyword_not_in;
|
|
||||||
self.index += 2; // Skip 'I' and 'N'
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
result.tag = .bang;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
result.tag = .bang;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.equal => switch (c) {
|
|
||||||
'>' => {
|
|
||||||
result.tag = .equal_angle_bracket_right;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
result.tag = .equal;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.angle_bracket_left => switch (c) {
|
|
||||||
'=' => {
|
|
||||||
result.tag = .angle_bracket_left_equal;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
result.tag = .angle_bracket_left;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.angle_bracket_right => switch (c) {
|
|
||||||
'=' => {
|
|
||||||
result.tag = .angle_bracket_right_equal;
|
|
||||||
self.index += 1;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
result.tag = .angle_bracket_right;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.int => switch (c) {
|
|
||||||
'.' => {
|
|
||||||
state = .float;
|
|
||||||
result.tag = .float_literal;
|
|
||||||
},
|
|
||||||
'a'...'z', '-' => {
|
|
||||||
state = .uuid_literal;
|
|
||||||
result.tag = .uuid_literal;
|
|
||||||
},
|
|
||||||
'/' => {
|
|
||||||
state = .date_literal;
|
|
||||||
result.tag = .date_literal;
|
|
||||||
},
|
|
||||||
':' => {
|
|
||||||
state = .time_literal;
|
|
||||||
result.tag = .time_literal;
|
|
||||||
},
|
|
||||||
'_', '0'...'9' => continue,
|
|
||||||
else => break,
|
|
||||||
},
|
|
||||||
|
|
||||||
.float => switch (c) {
|
|
||||||
'_', '0'...'9' => {
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.date_literal => switch (c) {
|
|
||||||
'-' => {
|
|
||||||
state = .time_literal;
|
|
||||||
result.tag = .datetime_literal;
|
|
||||||
},
|
|
||||||
'0'...'9', '/' => continue,
|
|
||||||
else => break,
|
|
||||||
},
|
|
||||||
|
|
||||||
.time_literal => switch (c) {
|
|
||||||
'0'...'9', ':', '.' => continue,
|
|
||||||
else => break,
|
|
||||||
},
|
|
||||||
|
|
||||||
.uuid_literal => switch (c) {
|
|
||||||
'0'...'9', 'a'...'z', '-' => continue,
|
|
||||||
else => break,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.loc.end = self.index;
|
|
||||||
self.last_token = result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test "keywords" {
|
|
||||||
try testTokenize("GRAB UPDATE ADD DELETE IN", &.{ .keyword_grab, .keyword_update, .keyword_add, .keyword_delete, .keyword_in });
|
|
||||||
}
|
|
||||||
|
|
||||||
test "basic query" {
|
|
||||||
try testTokenize("GRAB User {}", &.{ .keyword_grab, .identifier, .l_brace, .r_brace });
|
|
||||||
try testTokenize("GRAB User { name = 'Adrien'}", &.{ .keyword_grab, .identifier, .l_brace, .identifier, .equal, .string_literal, .r_brace });
|
|
||||||
try testTokenize("GRAB User { age = 1.5}", &.{ .keyword_grab, .identifier, .l_brace, .identifier, .equal, .float_literal, .r_brace });
|
|
||||||
try testTokenize("GRAB User { admin = true}", &.{ .keyword_grab, .identifier, .l_brace, .identifier, .equal, .bool_literal_true, .r_brace });
|
|
||||||
try testTokenize("GRAB User [1; name] {}", &.{ .keyword_grab, .identifier, .l_bracket, .int_literal, .semicolon, .identifier, .r_bracket, .l_brace, .r_brace });
|
|
||||||
try testTokenize("GRAB User{}|ASCENDING name|", &.{ .keyword_grab, .identifier, .l_brace, .r_brace, .pipe, .identifier, .identifier, .pipe });
|
|
||||||
try testTokenize("DELETE User[1]{name='Adrien'}|ASCENDING name, age|", &.{ .keyword_delete, .identifier, .l_bracket, .int_literal, .r_bracket, .l_brace, .identifier, .equal, .string_literal, .r_brace, .pipe, .identifier, .identifier, .comma, .identifier, .pipe });
|
|
||||||
}
|
|
||||||
|
|
||||||
test "basic date" {
|
|
||||||
try testTokenize("1a5527af-88fb-48c1-8d5c-49c9b73c2379", &.{.uuid_literal});
|
|
||||||
try testTokenize("1998/01/21", &.{.date_literal});
|
|
||||||
try testTokenize("17:55:31.0000", &.{.time_literal});
|
|
||||||
try testTokenize("1998/01/21-17:55:31.0000", &.{.datetime_literal});
|
|
||||||
}
|
|
||||||
|
|
||||||
test "not in keyword" {
|
|
||||||
try testTokenize("!IN", &.{.keyword_not_in});
|
|
||||||
try testTokenize("!IN(", &.{ .keyword_not_in, .l_paren });
|
|
||||||
try testTokenize("!Ind", &.{ .bang, .identifier });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn testTokenize(source: [:0]const u8, expected_token_tags: []const Token.Tag) !void {
|
|
||||||
var tokenizer = Tokenizer.init(source);
|
|
||||||
for (expected_token_tags) |expected_token_tag| {
|
|
||||||
const token = tokenizer.next();
|
|
||||||
try std.testing.expectEqual(expected_token_tag, token.tag);
|
|
||||||
}
|
|
||||||
// Last token should always be eof, even when the last token was invalid,
|
|
||||||
// in which case the tokenizer is in an invalid state, which can only be
|
|
||||||
// recovered by opinionated means outside the scope of this implementation.
|
|
||||||
const last_token = tokenizer.next();
|
|
||||||
try std.testing.expectEqual(Token.Tag.eof, last_token.tag);
|
|
||||||
try std.testing.expectEqual(source.len, last_token.loc.start);
|
|
||||||
try std.testing.expectEqual(source.len, last_token.loc.end);
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const config = @import("config");
|
|
||||||
const Allocator = std.mem.Allocator;
|
|
||||||
const Token = @import("tokenizer.zig").Token;
|
|
||||||
const Condition = @import("../dataStructure/filter.zig").Condition;
|
|
||||||
const printError = @import("../utils.zig").printError;
|
|
||||||
|
|
||||||
const ZipponError = @import("error").ZipponError;
|
|
||||||
|
|
||||||
const Self = @import("core.zig");
|
|
||||||
|
|
||||||
/// Check if all token in an array is of one specific type
|
|
||||||
pub fn checkTokensInArray(self: Self, tag: Token.Tag) ZipponError!Token {
|
|
||||||
var token = self.toker.next();
|
|
||||||
while (token.tag != .r_bracket) : (token = self.toker.next()) {
|
|
||||||
if (token.tag != tag) return printError(
|
|
||||||
"Error: Wrong type.",
|
|
||||||
ZipponError.SynthaxError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Will check if what is compared is ok, like comparing if a string is superior to another string is not for example.
|
|
||||||
pub fn checkConditionValidity(
|
|
||||||
self: Self,
|
|
||||||
condition: Condition,
|
|
||||||
token: Token,
|
|
||||||
) ZipponError!void {
|
|
||||||
switch (condition.operation) {
|
|
||||||
.equal => switch (condition.data_type) {
|
|
||||||
.int, .float, .str, .bool, .date, .time, .datetime => {},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Only int, float, str, bool, date, time, datetime can be compare with =",
|
|
||||||
ZipponError.ConditionError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.different => switch (condition.data_type) {
|
|
||||||
.int, .float, .str, .bool, .date, .time, .datetime => {},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Only int, float, str, bool, date, time, datetime can be compare with !=",
|
|
||||||
ZipponError.ConditionError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.superior_or_equal => switch (condition.data_type) {
|
|
||||||
.int, .float, .date, .time, .datetime => {},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Only int, float, date, time, datetime can be compare with >=",
|
|
||||||
ZipponError.ConditionError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.superior => switch (condition.data_type) {
|
|
||||||
.int, .float, .date, .time, .datetime => {},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Only int, float, date, time, datetime can be compare with >",
|
|
||||||
ZipponError.ConditionError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.inferior_or_equal => switch (condition.data_type) {
|
|
||||||
.int, .float, .date, .time, .datetime => {},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Only int, float, date, time, datetime can be compare with <=",
|
|
||||||
ZipponError.ConditionError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.inferior => switch (condition.data_type) {
|
|
||||||
.int, .float, .date, .time, .datetime => {},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Only int, float, date, time, datetime can be compare with <",
|
|
||||||
ZipponError.ConditionError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.in => switch (condition.data_type) {
|
|
||||||
.link => {},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Only link can be compare with IN.",
|
|
||||||
ZipponError.ConditionError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
.not_in => switch (condition.data_type) {
|
|
||||||
.link => {},
|
|
||||||
else => return printError(
|
|
||||||
"Error: Only link can be compare with !IN.",
|
|
||||||
ZipponError.ConditionError,
|
|
||||||
self.toker.buffer,
|
|
||||||
token.loc.start,
|
|
||||||
token.loc.end,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user