ZipponDB/src/cliParser.zig

152 lines
6.4 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const DataEngine = @import("fileEngine.zig").FileEngine;
const cliTokenizer = @import("tokenizers/cli.zig").Tokenizer;
const cliToken = @import("tokenizers/cli.zig").Token;
const ziqlTokenizer = @import("tokenizers/ziql.zig").Tokenizer;
const ziqlToken = @import("tokenizers/ziql.zig").Token;
const ziqlParser = @import("ziqlParser.zig").Parser;
const stdout = std.io.getStdOut().writer();
fn send(comptime format: []const u8, args: anytype) void {
stdout.print(format, args) catch |err| {
std.log.err("Can't send: {any}", .{err});
stdout.print("\x03\n", .{}) catch {};
};
stdout.print("\x03\n", .{}) catch {};
}
pub fn main() !void {
// TODO: Use an environment variable for the path of the DB
checkAndCreateDirectories();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer {
switch (gpa.deinit()) {
.ok => std.log.debug("No memory leak baby !\n", .{}),
.leak => std.log.debug("We fucked it up bro...\n", .{}),
}
}
const line_buf = try allocator.alloc(u8, 1024 * 50);
defer allocator.free(line_buf);
// TODO: Use a State to prevent first_token and second_token
while (true) {
std.debug.print("> ", .{});
const line = try std.io.getStdIn().reader().readUntilDelimiterOrEof(line_buf, '\n');
if (line) |line_str| {
const time_initial = std.time.microTimestamp();
const null_term_line_str = try allocator.dupeZ(u8, line_str[0..line_str.len]);
defer allocator.free(null_term_line_str);
var cliToker = cliTokenizer.init(null_term_line_str);
const command_token = cliToker.next();
switch (command_token.tag) {
.keyword_run => {
const query_token = cliToker.next();
switch (query_token.tag) {
.string_literal => {
const null_term_query_str = try allocator.dupeZ(u8, line_str[query_token.loc.start + 1 .. query_token.loc.end - 1]);
defer allocator.free(null_term_query_str);
try runCommand(null_term_query_str);
},
.keyword_help => send("The run command will take a ZiQL query between \" and run it. eg: run \"GRAB User\"\n", .{}),
else => send("After command run, need a string of a query, eg: \"GRAB User\"\n", .{}),
}
},
.keyword_schema => {
const second_token = cliToker.next();
switch (second_token.tag) {
.keyword_describe => send("{s}\n", .{ // TODO: Change that to use the SchemaEngine
\\User (
\\ name: str,
\\ email: str,
\\)
\\Message (
\\ content: str,
\\)
}),
.keyword_init => { // Maybe rename that in init now that I dont build binary anymore
const data_engine = DataEngine.init(allocator, null);
try data_engine.initDataFolder();
},
.keyword_help => {
send("{s}", .{
\\Here are all available options to use with the schema command:
\\
\\describe Print the schema use by the current engine.
\\build Build a new engine using a schema file. Args => filename: str, path of schema file to use. Default 'schema.zipponschema'.
\\
});
},
else => std.debug.print("schema available options: describe, build & help\n", .{}),
}
},
.keyword_help => {
send("{s}", .{
\\Welcome to ZipponDB!
\\
\\run To run a query. Args => query: str, the query to execute.
\\schema Build a new engine and print current schema.
\\quit To stop the process without saving
\\dump Create a new folder with all data as copy. Args => foldername: str, the name of the folder.
\\bump Replace current data with a previous dump. Args => foldername: str, the name of the folder.
\\
});
},
.keyword_quit => break,
.eof => {},
else => send("Command need to start with a keyword, including: run, schema, help and quit\n", .{}),
}
const time_final = std.time.microTimestamp();
const duration = time_final - time_initial;
std.debug.print("Time: {d:.2}ms\n", .{@as(f64, @floatFromInt(duration)) / 1000.0});
}
}
}
pub fn runCommand(null_term_query_str: [:0]const u8) !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var toker = ziqlTokenizer.init(null_term_query_str);
var parser = ziqlParser.init(allocator, &toker);
defer parser.deinit();
try parser.parse();
}
fn checkAndCreateDirectories() void {
const cwd = std.fs.cwd();
cwd.makeDir("ZipponDB") catch |err| switch (err) {
error.PathAlreadyExists => {},
else => @panic("Error other than path already exists when trying to create the ZipponDB directory.\n"),
};
cwd.makeDir("ZipponDB/DATA") catch |err| switch (err) {
error.PathAlreadyExists => {},
else => @panic("Error other than path already exists when trying to create the DATA directory.\n"),
};
cwd.makeDir("ZipponDB/BACKUP") catch |err| switch (err) {
error.PathAlreadyExists => {},
else => @panic("Error other than path already exists when trying to create the ENGINE directory.\n"),
};
cwd.makeDir("ZipponDB/LOG") catch |err| switch (err) {
error.PathAlreadyExists => {},
else => @panic("Error other than path already exists when trying to create the ENGINE directory.\n"),
};
}