Moved benchmark and test to src. Implemented array without testing yet
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
const std = @import("std");
|
||||
const dtype = @import("dtype");
|
||||
const DBEngine = @import("cli/core.zig");
|
||||
const ZipponError = @import("error").ZipponError;
|
||||
|
||||
const names = [_][]const u8{ "Alice", "Bob", "Charlie", "Dave", "Eve" };
|
||||
const emails = [_][]const u8{ "alice@email.com", "bob@email.com", "charlie@email.com", "dave@email.com", "eve@email.com" };
|
||||
const dates = [_][]const u8{ "2000/01/01", "1954/04/02", "1998/01/21", "1977/12/31" };
|
||||
const times = [_][]const u8{ "12:04", "20:45:11", "03:11:13", "03:00:01.0152" };
|
||||
const datetimes = [_][]const u8{ "2000/01/01-12:04", "1954/04/02-20:45:11", "1998/01/21-03:11:13", "1977/12/31-03:00:01.0153" };
|
||||
const ages = [_][]const u8{ "15", "11", "53", "34", "96", "64" };
|
||||
const address = [_][]const u8{ "01 rue Notre Dame", "11 street St Bob", "Route to Heaven bis", "Here", "Not here", "Maybe here" };
|
||||
|
||||
pub const std_options = std.Options{
|
||||
.log_level = .info,
|
||||
.logFn = myLog,
|
||||
};
|
||||
|
||||
const NUMBER_OF_RUN: usize = 10;
|
||||
var date_buffer: [64]u8 = undefined;
|
||||
var date_fa = std.heap.FixedBufferAllocator.init(&date_buffer);
|
||||
const date_allocator = date_fa.allocator();
|
||||
|
||||
pub fn myLog(
|
||||
comptime message_level: std.log.Level,
|
||||
comptime scope: @Type(.enum_literal),
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
const level_txt = comptime message_level.asText();
|
||||
const prefix = if (scope == .default) " - " else "(" ++ @tagName(scope) ++ ") - ";
|
||||
|
||||
const potential_file: ?std.fs.File = std.fs.cwd().openFile("benchmarkDB/LOG/log", .{ .mode = .write_only }) catch null;
|
||||
|
||||
if (potential_file) |file| {
|
||||
date_fa.reset();
|
||||
const now = @import("dtype").DateTime.now();
|
||||
var date_format_buffer = std.ArrayList(u8).init(date_allocator);
|
||||
defer date_format_buffer.deinit();
|
||||
now.format("YYYY/MM/DD-HH:mm:ss.SSSS", date_format_buffer.writer()) catch return;
|
||||
|
||||
file.seekFromEnd(0) catch return;
|
||||
const writer = file.writer();
|
||||
|
||||
writer.print("{s}{s}Time: {s} - ", .{ level_txt, prefix, date_format_buffer.items }) catch return;
|
||||
writer.print(format, args) catch return;
|
||||
writer.writeByte('\n') catch return;
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{ .safety = true }){};
|
||||
defer {
|
||||
const deinit_status = gpa.deinit();
|
||||
switch (deinit_status) {
|
||||
.leak => @panic("Oupsy"),
|
||||
.ok => {},
|
||||
}
|
||||
}
|
||||
|
||||
try benchmark(gpa.allocator());
|
||||
}
|
||||
|
||||
test "benchmark" {
|
||||
const allocator = std.testing.allocator;
|
||||
try benchmark(allocator);
|
||||
}
|
||||
|
||||
// Maybe I can make it a test to use the testing alloc
|
||||
pub fn benchmark(allocator: std.mem.Allocator) !void {
|
||||
const to_test = [_]usize{ 5_000, 100_000, 1_000_000, 10_000_000 };
|
||||
var line_buffer: [1024 * 1024]u8 = undefined;
|
||||
for (to_test) |users_count| {
|
||||
var db_engine = DBEngine.init(allocator, "benchmarkDB", "schema/benchmark");
|
||||
defer db_engine.deinit();
|
||||
|
||||
// Empty db
|
||||
{
|
||||
var null_term_query_str = try std.fmt.bufPrintZ(&line_buffer, "DELETE User {{}}", .{});
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
|
||||
null_term_query_str = try std.fmt.bufPrintZ(&line_buffer, "DELETE Category {{}}", .{});
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
|
||||
null_term_query_str = try std.fmt.bufPrintZ(&line_buffer, "DELETE Item {{}}", .{});
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
|
||||
null_term_query_str = try std.fmt.bufPrintZ(&line_buffer, "DELETE Order {{}}", .{});
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
}
|
||||
|
||||
// Populate with random dummy value
|
||||
{
|
||||
std.debug.print("\n=====================================\n\n", .{});
|
||||
var prng = std.Random.DefaultPrng.init(0);
|
||||
const rng = prng.random();
|
||||
|
||||
// Category
|
||||
{
|
||||
const null_term_query_str = try std.fmt.bufPrintZ(&line_buffer, "ADD Category (name = 'Book') ('Food') ('Toy') ('Other')", .{});
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
}
|
||||
|
||||
// Item
|
||||
{
|
||||
// TODO: Cache some filter. Here I end up parse Category everytime
|
||||
const null_term_query_str = try std.fmt.bufPrintZ(
|
||||
&line_buffer, // I dont like 'category = {name='Book'}'. Maybe att a IS keyword ?
|
||||
\\ADD Item
|
||||
\\(name='Book1', price=12.45, category = {{name='Book'}})
|
||||
\\(name='Book2', price=10.45, category = {{name='Book'}})
|
||||
\\(name='Book3', price=12.45, category = {{name='Book'}})
|
||||
\\(name='Book4', price=2.00, category = {{name='Book'}})
|
||||
\\(name='Book5', price=59.99, category = {{name='Book'}})
|
||||
\\(name='Book6', price=10.45, category = {{name='Book'}})
|
||||
\\(name='Book7', price=10.45, category = {{name='Book'}})
|
||||
\\
|
||||
\\(name='Food1', price=1.45, category = {{name='Food'}})
|
||||
\\(name='Food2', price=1.45, category = {{name='Food'}})
|
||||
\\(name='Food3', price=1.45, category = {{name='Food'}})
|
||||
\\(name='Food4', price=1.45, category = {{name='Food'}})
|
||||
\\(name='Food6', price=1.45, category = {{name='Food'}})
|
||||
\\(name='Food7', price=1.45, category = {{name='Food'}})
|
||||
\\(name='Food8', price=1.45, category = {{name='Food'}})
|
||||
\\
|
||||
\\(name='Toy1', price=10.45, category = {{name='Toy'}})
|
||||
\\(name='Toy2', price=4.45, category = {{name='Toy'}})
|
||||
\\(name='Toy3', price=6.45, category = {{name='Toy'}})
|
||||
\\(name='Toy4', price=1.45, category = {{name='Toy'}})
|
||||
\\
|
||||
\\(name='Other', price=0.99, category = {{name='Other'}})
|
||||
,
|
||||
.{},
|
||||
);
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
}
|
||||
|
||||
// User
|
||||
{
|
||||
std.debug.print("Populating with {d} users.\n", .{users_count});
|
||||
const populate_start_time = std.time.nanoTimestamp();
|
||||
|
||||
var array = std.ArrayList(u8).init(allocator);
|
||||
defer array.deinit();
|
||||
var writer = array.writer();
|
||||
|
||||
try writer.print(
|
||||
"ADD User (name = '{s}', email='{s}', age={s}, address='{s}')",
|
||||
.{
|
||||
names[rng.uintAtMost(usize, names.len - 1)],
|
||||
emails[rng.uintAtMost(usize, emails.len - 1)],
|
||||
ages[rng.uintAtMost(usize, ages.len - 1)],
|
||||
address[rng.uintAtMost(usize, address.len - 1)],
|
||||
},
|
||||
);
|
||||
|
||||
for (0..users_count - 1) |_| {
|
||||
try writer.print(
|
||||
"('{s}', '{s}', {s}, '{s}')",
|
||||
.{
|
||||
names[rng.uintAtMost(usize, names.len - 1)],
|
||||
emails[rng.uintAtMost(usize, emails.len - 1)],
|
||||
ages[rng.uintAtMost(usize, ages.len - 1)],
|
||||
address[rng.uintAtMost(usize, address.len - 1)],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const null_term_query_str = try std.fmt.allocPrintZ(allocator, "{s}", .{array.items});
|
||||
defer allocator.free(null_term_query_str);
|
||||
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
|
||||
const populate_end_time = std.time.nanoTimestamp();
|
||||
const populate_duration = @as(f64, @floatFromInt(populate_end_time - populate_start_time)) / 1e9;
|
||||
|
||||
std.debug.print("Populate duration: {d:.6} seconds\n\n", .{populate_duration});
|
||||
}
|
||||
|
||||
// Order
|
||||
if (false) {
|
||||
// Linked array not yet implemented and array manipulation not tested
|
||||
const null_term_query_str = try std.fmt.bufPrintZ(
|
||||
&line_buffer, // I dont like 'category = {name='Book'}'. Maybe att a IS keyword ?
|
||||
\\ADD Order (from={{}}, at=NOW, items={{name IN ['Food1', 'Food2']}}, quantity=[5 22])
|
||||
,
|
||||
.{},
|
||||
);
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
}
|
||||
|
||||
var buffer = std.ArrayList(u8).init(allocator);
|
||||
defer buffer.deinit();
|
||||
try db_engine.file_engine.writeDbMetrics(&buffer);
|
||||
std.debug.print("{s}\n", .{buffer.items});
|
||||
std.debug.print("--------------------------------------\n\n", .{});
|
||||
}
|
||||
|
||||
if (false) {
|
||||
for (db_engine.schema_engine.struct_array) |sstruct| {
|
||||
const mb: f64 = @as(f64, @floatFromInt(sstruct.uuid_file_index.arena.queryCapacity())) / 1024.0 / 1024.0;
|
||||
std.debug.print("Sstruct: {s}\n", .{sstruct.name});
|
||||
std.debug.print("Memory: {d:.2}Mb\n", .{mb});
|
||||
std.debug.print("Count: {d}\n\n", .{sstruct.uuid_file_index.map.count()});
|
||||
std.debug.print("--------------------------------------\n\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
// Run query
|
||||
{
|
||||
var read_time: f64 = 0;
|
||||
var read_write_time: f64 = 0;
|
||||
const queries = [_][]const u8{
|
||||
"GRAB User {}",
|
||||
"GRAB User {name='asd'}",
|
||||
"GRAB User [1] {}",
|
||||
"GRAB User [name] {}",
|
||||
"GRAB User {name = 'Charlie'}",
|
||||
"GRAB Category {}",
|
||||
"GRAB Item {}",
|
||||
"GRAB Order {}",
|
||||
"GRAB Order [from, items, quantity, at] {}",
|
||||
"DELETE User {}",
|
||||
};
|
||||
|
||||
// Run benchmarks
|
||||
for (queries, 0..) |query, j| {
|
||||
var time_buff: [NUMBER_OF_RUN]f64 = undefined;
|
||||
for (0..NUMBER_OF_RUN) |i| {
|
||||
const start_time = std.time.nanoTimestamp();
|
||||
|
||||
// Execute the query here
|
||||
const null_term_query_str = try std.fmt.bufPrintZ(&line_buffer, "{s}", .{query});
|
||||
db_engine.runQuery(null_term_query_str);
|
||||
|
||||
const end_time = std.time.nanoTimestamp();
|
||||
time_buff[i] = @as(f64, @floatFromInt(end_time - start_time)) / 1e6;
|
||||
}
|
||||
std.debug.print(
|
||||
"Query: \t{s}\nTime: \t{d:>6.2} ± {d:<6.2}ms | Min {d:>8.2}ms | Max {d:>8.2}ms\n\n",
|
||||
.{ query, mean(time_buff), std_dev(time_buff), min(time_buff), max(time_buff) },
|
||||
);
|
||||
|
||||
if (j == 0) read_write_time = mean(time_buff);
|
||||
if (j == 1) read_time = mean(time_buff);
|
||||
}
|
||||
std.debug.print(
|
||||
"Read: \t{d:.0} Entity/second\t*Include small condition\n",
|
||||
.{@as(f64, @floatFromInt(users_count)) / (read_time / 1000)},
|
||||
);
|
||||
std.debug.print(
|
||||
"Write: \t{d:.0} Entity/second\n",
|
||||
.{@as(f64, @floatFromInt(users_count)) / ((read_write_time - read_time) / 1000)},
|
||||
);
|
||||
}
|
||||
}
|
||||
std.debug.print("=====================================\n\n", .{});
|
||||
}
|
||||
|
||||
fn min(array: [NUMBER_OF_RUN]f64) f64 {
|
||||
var current_min: f64 = 999999999999;
|
||||
for (array) |value| {
|
||||
if (value < current_min) current_min = value;
|
||||
}
|
||||
return current_min;
|
||||
}
|
||||
|
||||
fn max(array: [NUMBER_OF_RUN]f64) f64 {
|
||||
var current_max: f64 = 0;
|
||||
for (array) |value| {
|
||||
if (value > current_max) current_max = value;
|
||||
}
|
||||
return current_max;
|
||||
}
|
||||
|
||||
fn mean(array: [NUMBER_OF_RUN]f64) f64 {
|
||||
var total: f64 = 0;
|
||||
for (array) |value| {
|
||||
total += value;
|
||||
}
|
||||
return total / @as(f64, @floatFromInt(NUMBER_OF_RUN));
|
||||
}
|
||||
|
||||
fn variance(array: [NUMBER_OF_RUN]f64) f64 {
|
||||
const m = mean(array);
|
||||
var square_diff: f64 = 0;
|
||||
for (array) |value| {
|
||||
square_diff += (value - m) * (value - m);
|
||||
}
|
||||
return square_diff / @as(f64, @floatFromInt(NUMBER_OF_RUN));
|
||||
}
|
||||
|
||||
fn std_dev(array: [NUMBER_OF_RUN]f64) f64 {
|
||||
const vari = variance(array);
|
||||
return @sqrt(vari);
|
||||
}
|
||||
@@ -69,6 +69,18 @@ pub const ConditionValue = union(enum) {
|
||||
link: *std.AutoHashMap(UUID, void),
|
||||
link_array: *std.AutoHashMap(UUID, void),
|
||||
|
||||
pub fn init(dtype: DataType, value: []const u8) ConditionValue {
|
||||
return switch (dtype) {
|
||||
.int => ConditionValue.initInt(value),
|
||||
.float => ConditionValue.initFloat(value),
|
||||
.bool => ConditionValue.initBool(value),
|
||||
.date => ConditionValue.initDate(value),
|
||||
.time => ConditionValue.initTime(value),
|
||||
.datetime => ConditionValue.initDateTime(value),
|
||||
.str => ConditionValue.initStr(value[1 .. value.len - 1]),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initInt(value: []const u8) ConditionValue {
|
||||
return ConditionValue{ .int = s2t.parseInt(value) };
|
||||
}
|
||||
@@ -102,32 +114,24 @@ pub const ConditionValue = union(enum) {
|
||||
}
|
||||
|
||||
// Array
|
||||
pub fn initArrayInt(allocator: std.mem.Allocator, value: []const u8) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .int_array = s2t.parseArrayInt(allocator, value) catch return ZipponError.ParsingValueError };
|
||||
pub fn initArrayInt(value: []const i32) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .int_array = value };
|
||||
}
|
||||
|
||||
pub fn initArrayFloat(allocator: std.mem.Allocator, value: []const u8) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .float_array = s2t.parseArrayFloat(allocator, value) catch return ZipponError.ParsingValueError };
|
||||
pub fn initArrayFloat(value: []const f64) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .float_array = value };
|
||||
}
|
||||
|
||||
pub fn initArrayStr(allocator: std.mem.Allocator, value: []const u8) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .str_array = s2t.parseArrayStr(allocator, value) catch return ZipponError.ParsingValueError };
|
||||
pub fn initArrayStr(value: []const []const u8) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .str_array = value };
|
||||
}
|
||||
|
||||
pub fn initArrayBool(allocator: std.mem.Allocator, value: []const u8) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .bool_array = s2t.parseArrayBool(allocator, value) catch return ZipponError.ParsingValueError };
|
||||
pub fn initArrayBool(value: []const bool) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .bool_array = value };
|
||||
}
|
||||
|
||||
pub fn initArrayDate(allocator: std.mem.Allocator, value: []const u8) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .unix_array = s2t.parseArrayDateUnix(allocator, value) catch return ZipponError.ParsingValueError };
|
||||
}
|
||||
|
||||
pub fn initArrayTime(allocator: std.mem.Allocator, value: []const u8) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .unix_array = s2t.parseArrayTimeUnix(allocator, value) catch return ZipponError.ParsingValueError };
|
||||
}
|
||||
|
||||
pub fn initArrayDateTime(allocator: std.mem.Allocator, value: []const u8) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .unix_array = s2t.parseArrayDatetimeUnix(allocator, value) catch return ZipponError.ParsingValueError };
|
||||
pub fn initArrayUnix(value: []const u64) ZipponError!ConditionValue {
|
||||
return ConditionValue{ .unix_array = value };
|
||||
}
|
||||
|
||||
pub fn initLink(value: *std.AutoHashMap(UUID, void)) ConditionValue {
|
||||
@@ -308,13 +312,28 @@ pub const Filter = struct {
|
||||
else => unreachable,
|
||||
},
|
||||
|
||||
// TODO: Add stuff to compare array, now condition with value as int, operator as in and to compare as []int should exist
|
||||
.in => switch (condition.data_type) {
|
||||
.link => condition.value.link.contains(UUID{ .bytes = row_value.UUID }),
|
||||
.int => in(i32, row_value.Int, condition.value.int_array),
|
||||
.float => in(f64, row_value.Float, condition.value.float_array),
|
||||
.str => inStr(row_value.Str, condition.value.str_array),
|
||||
.bool => in(bool, row_value.Bool, condition.value.bool_array),
|
||||
.date => in(u64, row_value.Unix, condition.value.unix_array),
|
||||
.time => in(u64, row_value.Unix, condition.value.unix_array),
|
||||
.datetime => in(u64, row_value.Unix, condition.value.unix_array),
|
||||
else => unreachable,
|
||||
},
|
||||
|
||||
.not_in => switch (condition.data_type) {
|
||||
.link => !condition.value.link.contains(UUID{ .bytes = row_value.UUID }),
|
||||
.int => !in(i32, row_value.Int, condition.value.int_array),
|
||||
.float => !in(f64, row_value.Float, condition.value.float_array),
|
||||
.str => !inStr(row_value.Str, condition.value.str_array),
|
||||
.bool => !in(bool, row_value.Bool, condition.value.bool_array),
|
||||
.date => !in(u64, row_value.Unix, condition.value.unix_array),
|
||||
.time => !in(u64, row_value.Unix, condition.value.unix_array),
|
||||
.datetime => !in(u64, row_value.Unix, condition.value.unix_array),
|
||||
else => unreachable,
|
||||
},
|
||||
};
|
||||
@@ -343,6 +362,16 @@ pub const Filter = struct {
|
||||
.empty => std.debug.print("Empty", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
fn in(comptime T: type, value: T, array: []const T) bool {
|
||||
for (array) |v| if (v == value) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn inStr(value: []const u8, array: []const []const u8) bool {
|
||||
for (array) |v| if (std.mem.eql(u8, v, value)) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
test "Evaluate" {
|
||||
|
||||
@@ -8,7 +8,7 @@ const DataType = dtype.DataType;
|
||||
const DateTime = dtype.DateTime;
|
||||
const UUID = dtype.UUID;
|
||||
|
||||
// TODO: Move this from FileEngine
|
||||
// TODO: Move this outside of FileEngine and make it faster
|
||||
|
||||
const ZipponError = @import("error").ZipponError;
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ pub fn parse(self: *Parser, allocator: Allocator, struct_array: *std.ArrayList(S
|
||||
},
|
||||
.eof => state = .end,
|
||||
else => {
|
||||
std.debug.print("{s}\n", .{self.toker.getTokenSlice(token)});
|
||||
return printError(
|
||||
"Error parsing schema: Expected a struct name",
|
||||
ZipponError.SynthaxError,
|
||||
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Parser = @import("ziql/parser.zig");
|
||||
const Tokenizer = @import("ziql/tokenizer.zig").Tokenizer;
|
||||
const DBEngine = @import("cli/core.zig");
|
||||
const ZipponError = @import("error").ZipponError;
|
||||
|
||||
const DB = struct {
|
||||
path: []const u8,
|
||||
schema: []const u8,
|
||||
};
|
||||
|
||||
test "Synthax error" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try expectParsingError(db, "ADD User (name = 'Bob', email='bob@email.com', age=-55, scores=[ 1 ], best_friend=7db1f06d-a5a7-4917-8cc6-4d490191c9c1, bday=2000/01/01, a_time=12:04:54.8741, last_order=2000/01/01-12:45)", ZipponError.SynthaxError);
|
||||
try expectParsingError(db, "GRAB {}", ZipponError.StructNotFound);
|
||||
try expectParsingError(db, "GRAB User {qwe = 'qwe'}", ZipponError.MemberNotFound);
|
||||
try expectParsingError(db, "ADD User (name='Bob')", ZipponError.MemberMissing);
|
||||
try expectParsingError(db, "GRAB User {name='Bob'", ZipponError.SynthaxError);
|
||||
try expectParsingError(db, "GRAB User {age = 50 name='Bob'}", ZipponError.SynthaxError);
|
||||
try expectParsingError(db, "GRAB User {age <14 AND (age>55}", ZipponError.SynthaxError);
|
||||
try expectParsingError(db, "GRAB User {name < 'Hello'}", ZipponError.ConditionError);
|
||||
}
|
||||
|
||||
test "Clear" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "DELETE User {}");
|
||||
}
|
||||
|
||||
test "ADD" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "ADD User (name = 'Bob', email='bob@email.com', age=55, scores=[ 1 ], best_friend=none, friends=none, bday=2000/01/01, a_time=12:04, last_order=2000/01/01-12:45)");
|
||||
try testParsing(db, "ADD User (name = 'Bob', email='bob@email.com', age=55, scores=[ 666 123 331 ], best_friend=none, friends=none, bday=2000/11/01, a_time=12:04:54, last_order=2000/01/01-12:45)");
|
||||
try testParsing(db, "ADD User (name = 'Bob', email='bob@email.com', age=-55, scores=[ 33 ], best_friend=none, friends=none, bday=2000/01/04, a_time=12:04:54.8741, last_order=2000/01/01-12:45)");
|
||||
try testParsing(db, "ADD User (name = 'Boba', email='boba@email.com', age=20, scores=[ ], best_friend=none, friends=none, bday=2000/06/06, a_time=04:04:54.8741, last_order=2000/01/01-12:45)");
|
||||
|
||||
try testParsing(db, "ADD User (name = 'Bob', email='bob@email.com', age=-55, scores=[ 1 ], best_friend={name='Bob'}, friends=none, bday=2000/01/01, a_time=12:04:54.8741, last_order=2000/01/01-12:45)");
|
||||
try testParsing(db, "ADD User (name = 'Bou', email='bob@email.com', age=66, scores=[ 1 ], best_friend={name = 'Boba'}, friends={name = 'Bob'}, bday=2000/01/01, a_time=02:04:54.8741, last_order=2000/01/01-12:45)");
|
||||
try testParsing(db, "ADD User (name = 'Bobibou', email='bob@email.com', age=66, scores=[ 1 ], best_friend={name = 'Boba'}, friends=[1]{name = 'Bob'}, bday=2000/01/01, a_time=02:04:54.8741, last_order=2000/01/01-12:45)");
|
||||
|
||||
try testParsing(db, "GRAB User {}");
|
||||
}
|
||||
|
||||
test "ADD batch" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "ADD User (name = 'ewq', email='ewq@email.com', age=22, scores=[ ], best_friend=none, friends=none, bday=2000/01/01, a_time=12:04, last_order=2000/01/01-12:45) (name = 'Roger', email='roger@email.com', age=10, scores=[ 1 11 111 123 562345 123451234 34623465234 12341234 ], best_friend=none, friends=none, bday=2000/01/01, a_time=12:04, last_order=2000/01/01-12:45)");
|
||||
try testParsing(db, "ADD User (name = 'qwe', email='qwe@email.com', age=57, scores=[ ], best_friend=none, friends=none, bday=2000/01/01, a_time=12:04, last_order=2000/01/01-12:45) ('Rodrigo', 'bob@email.com', 55, [ 1 ], {name = 'qwe'}, none, 2000/01/01, 12:04, 2000/01/01-12:45)");
|
||||
|
||||
try testParsing(db, "GRAB User [name, best_friend] {name = 'Rodrigo'}");
|
||||
try testParsing(db, "GRAB User {}");
|
||||
}
|
||||
|
||||
test "GRAB filter with string" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User {name = 'Bob'}");
|
||||
try testParsing(db, "GRAB User {name != 'Brittany Rogers'}");
|
||||
}
|
||||
|
||||
test "GRAB with additional data" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User [1] {age < 18}");
|
||||
try testParsing(db, "GRAB User [id, name] {age < 18}");
|
||||
try testParsing(db, "GRAB User [100; name, age] {age < 18}");
|
||||
}
|
||||
|
||||
test "UPDATE" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "UPDATE User [1] {name = 'Bob'} TO (email='new@gmail.com')");
|
||||
try testParsing(db, "GRAB User {}");
|
||||
}
|
||||
|
||||
test "GRAB filter with int" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User {age = 18}");
|
||||
try testParsing(db, "GRAB User {age > -18}");
|
||||
try testParsing(db, "GRAB User {age < 18}");
|
||||
try testParsing(db, "GRAB User {age <= 18}");
|
||||
try testParsing(db, "GRAB User {age >= 18}");
|
||||
try testParsing(db, "GRAB User {age != 18}");
|
||||
}
|
||||
|
||||
test "GRAB filter with date" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User {bday > 2000/01/01}");
|
||||
try testParsing(db, "GRAB User {a_time < 08:00}");
|
||||
try testParsing(db, "GRAB User {last_order > 2000/01/01-12:45}");
|
||||
}
|
||||
|
||||
test "Specific query" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User");
|
||||
try testParsing(db, "GRAB User {}");
|
||||
try testParsing(db, "GRAB User [1]");
|
||||
}
|
||||
|
||||
test "UPDATE relationship" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "UPDATE User [1] {name='Bob'} TO (best_friend = {name='Boba'} )");
|
||||
try testParsing(db, "GRAB User {}");
|
||||
}
|
||||
|
||||
test "GRAB Relationship Filter" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User {best_friend IN {name = 'Bob'}}");
|
||||
try testParsing(db, "GRAB User {best_friend IN {name = 'Boba'}}");
|
||||
}
|
||||
|
||||
test "GRAB Relationship AdditionalData" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User [name, friends] {}");
|
||||
try testParsing(db, "GRAB User [name, best_friend] {}");
|
||||
}
|
||||
|
||||
test "GRAB Relationship Sub AdditionalData" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User [name, friends [name]] {}");
|
||||
try testParsing(db, "GRAB User [name, best_friend [name, friends [age]]] {}");
|
||||
}
|
||||
|
||||
test "GRAB Relationship AdditionalData Filtered" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User [2; name, best_friend] {name = 'Bob'}");
|
||||
try testParsing(db, "GRAB User [2; name, best_friend] {best_friend IN {}}");
|
||||
try testParsing(db, "GRAB User [2; name, best_friend] {best_friend !IN {}}");
|
||||
}
|
||||
|
||||
test "GRAB name IN" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "GRAB User {name IN ['Bob' 'Bobinou']}");
|
||||
}
|
||||
|
||||
test "GRAB Relationship dot" {
|
||||
// DO I add this ? I'm not sure about this feature
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
// try testParsing(db, "GRAB User.best_friend {}");
|
||||
// try testParsing(db, "GRAB User.best_friend.best_friend {}");
|
||||
// try testParsing(db, "GRAB User.best_friend.posts {}");
|
||||
// try testParsing(db, "GRAB User.best_friend.posts.comments {}");
|
||||
try testParsing(db, "GRAB User [1] {}");
|
||||
}
|
||||
|
||||
test "DELETE" {
|
||||
const db = DB{ .path = "test1", .schema = "schema/test" };
|
||||
try testParsing(db, "DELETE User {}");
|
||||
}
|
||||
|
||||
test "3 struct base" {
|
||||
const db = DB{ .path = "test2", .schema = "schema/test-3struct" };
|
||||
try testParsing(db, "DELETE User {}");
|
||||
try testParsing(db, "DELETE Post {}");
|
||||
try testParsing(db, "ADD User (name = 'Bob', email='bob@email.com', age=55, friends=none, posts=none, comments=none, bday=2000/01/01)");
|
||||
try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={}, comments=none)");
|
||||
try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={}, comments=none)");
|
||||
try testParsing(db, "GRAB Post [id, text, at, from [id, name]] {}");
|
||||
}
|
||||
|
||||
test "3 struct both side" {
|
||||
const db = DB{ .path = "test2", .schema = "schema/test-3struct" };
|
||||
try testParsing(db, "DELETE User {}");
|
||||
try testParsing(db, "DELETE Post {}");
|
||||
try testParsing(db, "ADD User (name = 'Bob', email='bob@email.com', age=55, friends=none, posts=none, comments=none, bday=2000/01/01)");
|
||||
try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from=none, comments=none)");
|
||||
//try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={}, comments=none) -> new_post -> UPDATE User {} TO (posts APPEND new_post)");
|
||||
// try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={} APPEND TO posts, comments=none)"); Maybe I can use that to be like the above query
|
||||
// ADD Post (text = 'Hello every body', at=NOW, from={} TO last_post, comments=none) And this for a single link
|
||||
// try testParsing(db, "ADD Post (text = 'Hello every body', at=NOW, from={} APPEND TO [posts, last_post], comments=none)"); Can be an array to add it to multiple list
|
||||
// last_post is replaced instead of append
|
||||
try testParsing(db, "GRAB Post [id, text, at, from [id, name]] {}");
|
||||
try testParsing(db, "GRAB User [id, name] {}");
|
||||
}
|
||||
|
||||
fn testParsing(db: DB, source: [:0]const u8) !void {
|
||||
const allocator = std.testing.allocator;
|
||||
var db_engine = DBEngine.init(allocator, db.path, db.schema);
|
||||
defer db_engine.deinit();
|
||||
|
||||
var parser = Parser.init(
|
||||
&db_engine.file_engine,
|
||||
&db_engine.schema_engine,
|
||||
);
|
||||
|
||||
std.debug.print("Running: {s}\n", .{source});
|
||||
try parser.parse(allocator, source);
|
||||
std.debug.print("\n\n-------------------------------\n\n", .{});
|
||||
}
|
||||
|
||||
fn expectParsingError(db: DB, source: [:0]const u8, err: ZipponError) !void {
|
||||
const allocator = std.testing.allocator;
|
||||
var db_engine = DBEngine.init(allocator, db.path, db.schema);
|
||||
defer db_engine.deinit();
|
||||
|
||||
var parser = Parser.init(
|
||||
&db_engine.file_engine,
|
||||
&db_engine.schema_engine,
|
||||
);
|
||||
|
||||
std.debug.print("Running: {s}\n", .{source});
|
||||
try std.testing.expectError(err, parser.parse(allocator, source));
|
||||
std.debug.print("\n\n-------------------------------\n\n", .{});
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const BORDER = "=" ** 80;
|
||||
|
||||
const log = std.log.scoped(.cli);
|
||||
pub const std_options = std.Options{
|
||||
.logFn = myLog,
|
||||
};
|
||||
|
||||
pub fn myLog(
|
||||
comptime message_level: std.log.Level,
|
||||
comptime scope: @Type(.enum_literal),
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) void {
|
||||
_ = message_level;
|
||||
_ = scope;
|
||||
if (true) {
|
||||
std.debug.print(format, args);
|
||||
std.debug.print("\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
// use in custom panic handler
|
||||
var current_test: ?[]const u8 = null;
|
||||
|
||||
pub fn main() !void {
|
||||
var mem: [8192]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&mem);
|
||||
|
||||
const allocator = fba.allocator();
|
||||
|
||||
const env = Env.init(allocator);
|
||||
defer env.deinit(allocator);
|
||||
|
||||
var slowest = SlowTracker.init(allocator, 5);
|
||||
defer slowest.deinit();
|
||||
|
||||
var pass: usize = 0;
|
||||
var fail: usize = 0;
|
||||
var skip: usize = 0;
|
||||
var leak: usize = 0;
|
||||
|
||||
const printer = Printer.init();
|
||||
printer.fmt("\r\x1b[0K", .{}); // beginning of line and clear to end of line
|
||||
|
||||
for (builtin.test_functions) |t| {
|
||||
if (isSetup(t)) {
|
||||
t.func() catch |err| {
|
||||
printer.status(.fail, "\nsetup \"{s}\" failed: {}\n", .{ t.name, err });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (builtin.test_functions) |t| {
|
||||
if (isSetup(t) or isTeardown(t)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var status = Status.pass;
|
||||
slowest.startTiming();
|
||||
|
||||
const is_unnamed_test = isUnnamed(t);
|
||||
if (env.filter) |f| {
|
||||
if (!is_unnamed_test and std.mem.indexOf(u8, t.name, f) == null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const friendly_name = blk: {
|
||||
const name = t.name;
|
||||
var it = std.mem.splitScalar(u8, name, '.');
|
||||
while (it.next()) |value| {
|
||||
if (std.mem.eql(u8, value, "test")) {
|
||||
const rest = it.rest();
|
||||
break :blk if (rest.len > 0) rest else name;
|
||||
}
|
||||
}
|
||||
break :blk name;
|
||||
};
|
||||
|
||||
current_test = friendly_name;
|
||||
std.testing.allocator_instance = .{};
|
||||
const result = t.func();
|
||||
current_test = null;
|
||||
|
||||
const ns_taken = slowest.endTiming(friendly_name);
|
||||
|
||||
if (std.testing.allocator_instance.deinit() == .leak) {
|
||||
leak += 1;
|
||||
printer.status(.fail, "\n{s}\n\"{s}\" - Memory Leak\n{s}\n", .{ BORDER, friendly_name, BORDER });
|
||||
}
|
||||
|
||||
if (result) |_| {
|
||||
pass += 1;
|
||||
} else |err| switch (err) {
|
||||
error.SkipZigTest => {
|
||||
skip += 1;
|
||||
status = .skip;
|
||||
},
|
||||
else => {
|
||||
status = .fail;
|
||||
fail += 1;
|
||||
printer.status(.fail, "\n{s}\n\"{s}\" - {s}\n{s}\n", .{ BORDER, friendly_name, @errorName(err), BORDER });
|
||||
if (@errorReturnTrace()) |trace| {
|
||||
std.debug.dumpStackTrace(trace.*);
|
||||
}
|
||||
if (env.fail_first) {
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if (env.verbose) {
|
||||
const ms = @as(f64, @floatFromInt(ns_taken)) / 1_000_000.0;
|
||||
printer.status(status, "{s} ({d:.2}ms)\n", .{ friendly_name, ms });
|
||||
} else {
|
||||
printer.status(status, ".", .{});
|
||||
}
|
||||
}
|
||||
|
||||
for (builtin.test_functions) |t| {
|
||||
if (isTeardown(t)) {
|
||||
t.func() catch |err| {
|
||||
printer.status(.fail, "\nteardown \"{s}\" failed: {}\n", .{ t.name, err });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const total_tests = pass + fail;
|
||||
const status = if (fail == 0) Status.pass else Status.fail;
|
||||
printer.status(status, "\n{d} of {d} test{s} passed\n", .{ pass, total_tests, if (total_tests != 1) "s" else "" });
|
||||
if (skip > 0) {
|
||||
printer.status(.skip, "{d} test{s} skipped\n", .{ skip, if (skip != 1) "s" else "" });
|
||||
}
|
||||
if (leak > 0) {
|
||||
printer.status(.fail, "{d} test{s} leaked\n", .{ leak, if (leak != 1) "s" else "" });
|
||||
}
|
||||
printer.fmt("\n", .{});
|
||||
try slowest.display(printer);
|
||||
printer.fmt("\n", .{});
|
||||
std.posix.exit(if (fail == 0) 0 else 1);
|
||||
}
|
||||
|
||||
const Printer = struct {
|
||||
out: std.fs.File.Writer,
|
||||
|
||||
fn init() Printer {
|
||||
return .{
|
||||
.out = std.io.getStdErr().writer(),
|
||||
};
|
||||
}
|
||||
|
||||
fn fmt(self: Printer, comptime format: []const u8, args: anytype) void {
|
||||
std.fmt.format(self.out, format, args) catch unreachable;
|
||||
}
|
||||
|
||||
fn status(self: Printer, s: Status, comptime format: []const u8, args: anytype) void {
|
||||
const color = switch (s) {
|
||||
.pass => "\x1b[32m",
|
||||
.fail => "\x1b[31m",
|
||||
.skip => "\x1b[33m",
|
||||
else => "",
|
||||
};
|
||||
const out = self.out;
|
||||
out.writeAll(color) catch @panic("writeAll failed?!");
|
||||
std.fmt.format(out, format, args) catch @panic("std.fmt.format failed?!");
|
||||
self.fmt("\x1b[0m", .{});
|
||||
}
|
||||
};
|
||||
|
||||
const Status = enum {
|
||||
pass,
|
||||
fail,
|
||||
skip,
|
||||
text,
|
||||
};
|
||||
|
||||
const SlowTracker = struct {
|
||||
const SlowestQueue = std.PriorityDequeue(TestInfo, void, compareTiming);
|
||||
max: usize,
|
||||
slowest: SlowestQueue,
|
||||
timer: std.time.Timer,
|
||||
|
||||
fn init(allocator: Allocator, count: u32) SlowTracker {
|
||||
const timer = std.time.Timer.start() catch @panic("failed to start timer");
|
||||
var slowest = SlowestQueue.init(allocator, {});
|
||||
slowest.ensureTotalCapacity(count) catch @panic("OOM");
|
||||
return .{
|
||||
.max = count,
|
||||
.timer = timer,
|
||||
.slowest = slowest,
|
||||
};
|
||||
}
|
||||
|
||||
const TestInfo = struct {
|
||||
ns: u64,
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
fn deinit(self: SlowTracker) void {
|
||||
self.slowest.deinit();
|
||||
}
|
||||
|
||||
fn startTiming(self: *SlowTracker) void {
|
||||
self.timer.reset();
|
||||
}
|
||||
|
||||
fn endTiming(self: *SlowTracker, test_name: []const u8) u64 {
|
||||
var timer = self.timer;
|
||||
const ns = timer.lap();
|
||||
|
||||
var slowest = &self.slowest;
|
||||
|
||||
if (slowest.count() < self.max) {
|
||||
// Capacity is fixed to the # of slow tests we want to track
|
||||
// If we've tracked fewer tests than this capacity, than always add
|
||||
slowest.add(TestInfo{ .ns = ns, .name = test_name }) catch @panic("failed to track test timing");
|
||||
return ns;
|
||||
}
|
||||
|
||||
{
|
||||
// Optimization to avoid shifting the dequeue for the common case
|
||||
// where the test isn't one of our slowest.
|
||||
const fastest_of_the_slow = slowest.peekMin() orelse unreachable;
|
||||
if (fastest_of_the_slow.ns > ns) {
|
||||
// the test was faster than our fastest slow test, don't add
|
||||
return ns;
|
||||
}
|
||||
}
|
||||
|
||||
// the previous fastest of our slow tests, has been pushed off.
|
||||
_ = slowest.removeMin();
|
||||
slowest.add(TestInfo{ .ns = ns, .name = test_name }) catch @panic("failed to track test timing");
|
||||
return ns;
|
||||
}
|
||||
|
||||
fn display(self: *SlowTracker, printer: Printer) !void {
|
||||
var slowest = self.slowest;
|
||||
const count = slowest.count();
|
||||
printer.fmt("Slowest {d} test{s}: \n", .{ count, if (count != 1) "s" else "" });
|
||||
while (slowest.removeMinOrNull()) |info| {
|
||||
const ms = @as(f64, @floatFromInt(info.ns)) / 1_000_000.0;
|
||||
printer.fmt(" {d:.2}ms\t{s}\n", .{ ms, info.name });
|
||||
}
|
||||
}
|
||||
|
||||
fn compareTiming(context: void, a: TestInfo, b: TestInfo) std.math.Order {
|
||||
_ = context;
|
||||
return std.math.order(a.ns, b.ns);
|
||||
}
|
||||
};
|
||||
|
||||
const Env = struct {
|
||||
verbose: bool,
|
||||
fail_first: bool,
|
||||
filter: ?[]const u8,
|
||||
|
||||
fn init(allocator: Allocator) Env {
|
||||
return .{
|
||||
.verbose = readEnvBool(allocator, "TEST_VERBOSE", true),
|
||||
.fail_first = readEnvBool(allocator, "TEST_FAIL_FIRST", false),
|
||||
.filter = readEnv(allocator, "TEST_FILTER"),
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: Env, allocator: Allocator) void {
|
||||
if (self.filter) |f| {
|
||||
allocator.free(f);
|
||||
}
|
||||
}
|
||||
|
||||
fn readEnv(allocator: Allocator, key: []const u8) ?[]const u8 {
|
||||
const v = std.process.getEnvVarOwned(allocator, key) catch |err| {
|
||||
if (err == error.EnvironmentVariableNotFound) {
|
||||
return null;
|
||||
}
|
||||
std.log.warn("failed to get env var {s} due to err {}", .{ key, err });
|
||||
return null;
|
||||
};
|
||||
return v;
|
||||
}
|
||||
|
||||
fn readEnvBool(allocator: Allocator, key: []const u8, deflt: bool) bool {
|
||||
const value = readEnv(allocator, key) orelse return deflt;
|
||||
defer allocator.free(value);
|
||||
return std.ascii.eqlIgnoreCase(value, "true");
|
||||
}
|
||||
};
|
||||
|
||||
fn isUnnamed(t: std.builtin.TestFn) bool {
|
||||
const marker = ".test_";
|
||||
const test_name = t.name;
|
||||
const index = std.mem.indexOf(u8, test_name, marker) orelse return false;
|
||||
_ = std.fmt.parseInt(u32, test_name[index + marker.len ..], 10) catch return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
fn isSetup(t: std.builtin.TestFn) bool {
|
||||
return std.mem.endsWith(u8, t.name, "tests:beforeAll");
|
||||
}
|
||||
|
||||
fn isTeardown(t: std.builtin.TestFn) bool {
|
||||
return std.mem.endsWith(u8, t.name, "tests:afterAll");
|
||||
}
|
||||
@@ -67,7 +67,6 @@ 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");
|
||||
|
||||
toker: *Tokenizer = undefined,
|
||||
file_engine: *FileEngine,
|
||||
|
||||
@@ -71,11 +71,21 @@ pub fn parseCondition(
|
||||
|
||||
.expect_operation => {
|
||||
condition.operation = try self.parseComparisonOperator(token);
|
||||
if (condition.operation == .in) condition.data_type = switch (condition.data_type) {
|
||||
.int => .int_array,
|
||||
.float => .float_array,
|
||||
.str => .str_array,
|
||||
.bool => .bool_array,
|
||||
.date => .date_array,
|
||||
.time => .time_array,
|
||||
.datetime => .datetime_array,
|
||||
else => condition.data_type,
|
||||
};
|
||||
log.debug("Condition operation {any}\n", .{condition.operation});
|
||||
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;
|
||||
},
|
||||
@@ -87,3 +97,87 @@ pub fn parseCondition(
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
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, .int_array, .float_array, .str_array, .bool_array, .time_array, .date_array, .datetime_array => {},
|
||||
else => unreachable,
|
||||
},
|
||||
|
||||
.not_in => switch (condition.data_type) {
|
||||
.link, .int_array, .float_array, .str_array, .bool_array, .time_array, .date_array, .datetime_array => {},
|
||||
else => unreachable,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+312
-136
@@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const dtype = @import("dtype");
|
||||
const s2t = dtype.s2t;
|
||||
const UUID = dtype.UUID;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Token = @import("../tokenizer.zig").Token;
|
||||
@@ -12,9 +13,10 @@ const ZipponError = @import("error").ZipponError;
|
||||
|
||||
var buff: [1024]u8 = undefined;
|
||||
|
||||
var zero_map_buf: [1024 * 4]u8 = undefined;
|
||||
var zero_map_buf: [200]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&zero_map_buf);
|
||||
var zero_map = std.AutoHashMap(UUID, void).init(fba.allocator());
|
||||
var empty_map = std.AutoHashMap(UUID, void).init(fba.allocator());
|
||||
|
||||
const Self = @import("../parser.zig");
|
||||
|
||||
@@ -23,148 +25,324 @@ pub fn initZeroMap() ZipponError!void {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
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
|
||||
|
||||
if (data_type.is_array()) switch (token.tag) {
|
||||
.l_bracket => token.* = self.toker.next(),
|
||||
else => return printError(
|
||||
"Error: expecting [ to start array.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
),
|
||||
};
|
||||
|
||||
// Check if the all next tokens are the right one
|
||||
if (expected_tag) |tag| {
|
||||
if (data_type.is_array()) {
|
||||
token.* = try self.checkTokensInArray(tag);
|
||||
switch (data_type) {
|
||||
.self => unreachable,
|
||||
|
||||
.int => if (token.tag != .int_literal) {
|
||||
return printError(
|
||||
"Error: Wrong type. Expected: int.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
} else {
|
||||
if (token.tag != tag) {
|
||||
const msg = std.fmt.bufPrint(&buff, "Error: Wrong type. Expected: {any}", .{tag}) catch return ZipponError.MemoryError;
|
||||
return printError(
|
||||
msg,
|
||||
return ConditionValue.initInt(self.toker.buffer[start_index..token.loc.end]);
|
||||
},
|
||||
|
||||
.float => if (token.tag != .float_literal) {
|
||||
return printError(
|
||||
"Error: Wrong type. Expected: float.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
} else {
|
||||
return ConditionValue.initFloat(self.toker.buffer[start_index..token.loc.end]);
|
||||
},
|
||||
|
||||
.str => if (token.tag != .string_literal) {
|
||||
return printError(
|
||||
"Error: Wrong type. Expected: string.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
} else {
|
||||
return ConditionValue.initStr(self.toker.buffer[start_index..token.loc.end]);
|
||||
},
|
||||
|
||||
.bool => if (token.tag != .bool_literal_true and token.tag != .bool_literal_false) {
|
||||
return printError(
|
||||
"Error: Wrong type. Expected: bool.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
} else {
|
||||
return ConditionValue.initBool(self.toker.buffer[start_index..token.loc.end]);
|
||||
},
|
||||
|
||||
.date => if (token.tag != .date_literal and token.tag != .keyword_now) {
|
||||
return printError(
|
||||
"Error: Wrong type. Expected: date.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
} else {
|
||||
return ConditionValue.initDate(self.toker.buffer[start_index..token.loc.end]);
|
||||
},
|
||||
|
||||
.time => if (token.tag != .time_literal and token.tag != .keyword_now) {
|
||||
return printError(
|
||||
"Error: Wrong type. Expected: time.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
} else {
|
||||
return ConditionValue.initTime(self.toker.buffer[start_index..token.loc.end]);
|
||||
},
|
||||
|
||||
.datetime => if (token.tag != .date_literal and token.tag != .datetime_literal and token.tag != .keyword_now) {
|
||||
return printError(
|
||||
"Error: Wrong type. Expected: datetime.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
} else {
|
||||
return ConditionValue.initDateTime(self.toker.buffer[start_index..token.loc.end]);
|
||||
},
|
||||
|
||||
.int_array => {
|
||||
var array = std.ArrayList(i32).init(allocator);
|
||||
errdefer array.deinit();
|
||||
|
||||
var first = true;
|
||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
||||
if (!first) {
|
||||
if (token.tag != .comma) return printError(
|
||||
"Error: Expected comma.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
token.* = self.toker.next();
|
||||
} else first = false;
|
||||
|
||||
if (token.tag != .int_literal) return printError(
|
||||
"Error: Wrong type. Expected int.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
array.append(s2t.parseInt(self.toker.getTokenSlice(token.*))) catch return ZipponError.MemoryError;
|
||||
}
|
||||
}
|
||||
} 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]),
|
||||
return try ConditionValue.initArrayInt(array.toOwnedSlice() catch return ZipponError.MemoryError);
|
||||
},
|
||||
|
||||
.float_array => {
|
||||
var array = std.ArrayList(f64).init(allocator);
|
||||
errdefer array.deinit();
|
||||
|
||||
var first = true;
|
||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
||||
if (!first) {
|
||||
if (token.tag != .comma) return printError(
|
||||
"Error: Expected comma.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
token.* = self.toker.next();
|
||||
} else first = false;
|
||||
|
||||
if (token.tag != .float_literal) return printError(
|
||||
"Error: Wrong type. Expected float.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
array.append(s2t.parseFloat(self.toker.getTokenSlice(token.*))) catch return ZipponError.MemoryError;
|
||||
}
|
||||
|
||||
return try ConditionValue.initArrayFloat(array.toOwnedSlice() catch return ZipponError.MemoryError);
|
||||
},
|
||||
|
||||
.bool_array => {
|
||||
var array = std.ArrayList(bool).init(allocator);
|
||||
errdefer array.deinit();
|
||||
|
||||
var first = true;
|
||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
||||
if (!first) {
|
||||
if (token.tag != .comma) return printError(
|
||||
"Error: Expected comma.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
token.* = self.toker.next();
|
||||
} else first = false;
|
||||
|
||||
if (token.tag != .bool_literal_false and token.tag != .bool_literal_true) return printError(
|
||||
"Error: Wrong type. Expected bool.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
array.append(s2t.parseBool(self.toker.getTokenSlice(token.*))) catch return ZipponError.MemoryError;
|
||||
}
|
||||
|
||||
return try ConditionValue.initArrayBool(array.toOwnedSlice() catch return ZipponError.MemoryError);
|
||||
},
|
||||
|
||||
.str_array => {
|
||||
var array = std.ArrayList([]const u8).init(allocator);
|
||||
errdefer array.deinit();
|
||||
|
||||
var first = true;
|
||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
||||
if (!first) {
|
||||
if (token.tag != .comma) return printError(
|
||||
"Error: Expected comma.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
token.* = self.toker.next();
|
||||
} else first = false;
|
||||
|
||||
if (token.tag != .string_literal) return printError(
|
||||
"Error: Wrong type. Expected str.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
array.append(self.toker.getTokenSlice(token.*)) catch return ZipponError.MemoryError;
|
||||
}
|
||||
|
||||
return try ConditionValue.initArrayStr(array.toOwnedSlice() catch return ZipponError.MemoryError);
|
||||
},
|
||||
|
||||
.date_array => {
|
||||
var array = std.ArrayList(u64).init(allocator);
|
||||
errdefer array.deinit();
|
||||
|
||||
var first = true;
|
||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
||||
if (!first) {
|
||||
if (token.tag != .comma) return printError(
|
||||
"Error: Expected comma.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
token.* = self.toker.next();
|
||||
} else first = false;
|
||||
|
||||
if (token.tag != .date_literal and token.tag != .keyword_now) return printError(
|
||||
"Error: Wrong type. Expected date.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
array.append(s2t.parseDate(self.toker.getTokenSlice(token.*)).toUnix()) catch return ZipponError.MemoryError;
|
||||
}
|
||||
|
||||
return try ConditionValue.initArrayUnix(array.toOwnedSlice() catch return ZipponError.MemoryError);
|
||||
},
|
||||
|
||||
.time_array => {
|
||||
var array = std.ArrayList(u64).init(allocator);
|
||||
errdefer array.deinit();
|
||||
|
||||
var first = true;
|
||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
||||
if (!first) {
|
||||
if (token.tag != .comma) return printError(
|
||||
"Error: Expected comma.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
token.* = self.toker.next();
|
||||
} else first = false;
|
||||
|
||||
if (token.tag != .time_literal and token.tag != .keyword_now) return printError(
|
||||
"Error: Wrong type. Expected time.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
array.append(s2t.parseTime(self.toker.getTokenSlice(token.*)).toUnix()) catch return ZipponError.MemoryError;
|
||||
}
|
||||
|
||||
return try ConditionValue.initArrayUnix(array.toOwnedSlice() catch return ZipponError.MemoryError);
|
||||
},
|
||||
|
||||
.datetime_array => {
|
||||
var array = std.ArrayList(u64).init(allocator);
|
||||
errdefer array.deinit();
|
||||
|
||||
var first = true;
|
||||
while (token.tag != .r_bracket) : (token.* = self.toker.next()) {
|
||||
if (!first) {
|
||||
if (token.tag != .comma) return printError(
|
||||
"Error: Expected comma.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
token.* = self.toker.next();
|
||||
} else first = false;
|
||||
|
||||
if (token.tag != .datetime_literal and token.tag != .date_literal and token.tag != .keyword_now) return printError(
|
||||
"Error: Wrong type. Expected datetime.",
|
||||
ZipponError.SynthaxError,
|
||||
self.toker.buffer,
|
||||
token.loc.start,
|
||||
token.loc.end,
|
||||
);
|
||||
array.append(s2t.parseDatetime(self.toker.getTokenSlice(token.*)).toUnix()) catch return ZipponError.MemoryError;
|
||||
}
|
||||
|
||||
return try ConditionValue.initArrayUnix(array.toOwnedSlice() catch return ZipponError.MemoryError);
|
||||
},
|
||||
|
||||
.link => switch (token.tag) {
|
||||
.keyword_none => {
|
||||
_ = self.toker.next();
|
||||
@@ -239,12 +417,11 @@ pub fn parseConditionValue(self: Self, allocator: Allocator, struct_name: []cons
|
||||
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);
|
||||
return ConditionValue.initArrayLink(&empty_map);
|
||||
},
|
||||
.l_brace, .l_bracket => {
|
||||
var filter: ?Filter = null;
|
||||
@@ -292,6 +469,5 @@ pub fn parseConditionValue(self: Self, allocator: Allocator, struct_name: []cons
|
||||
token.loc.end,
|
||||
),
|
||||
},
|
||||
.self => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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("parser.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,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user