Compare commits
7 Commits
0dfc657f23
...
c16b062267
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c16b062267 | ||
|
|
7d939a49dd | ||
|
|
b7cbc64277 | ||
|
|
5d0b36d0f0 | ||
|
|
7ad26ba427 | ||
|
|
765f2bc673 | ||
|
|
2d26c59df6 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,3 +7,5 @@ CMakeFiles
|
|||||||
Testing
|
Testing
|
||||||
.cache
|
.cache
|
||||||
Makefile
|
Makefile
|
||||||
|
cmake_install.cmake
|
||||||
|
CMakeCache.txt
|
||||||
|
|||||||
4
.vscode/c_cpp_properties.json
vendored
4
.vscode/c_cpp_properties.json
vendored
@@ -7,10 +7,6 @@
|
|||||||
"${workspaceFolder}/clib"
|
"${workspaceFolder}/clib"
|
||||||
],
|
],
|
||||||
"defines": [],
|
"defines": [],
|
||||||
"macFrameworkPath": [
|
|
||||||
"/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/System/Library/Frameworks"
|
|
||||||
],
|
|
||||||
"compilerPath": "/usr/bin/clang",
|
|
||||||
"cStandard": "c17",
|
"cStandard": "c17",
|
||||||
"intelliSenseMode": "macos-clang-x64",
|
"intelliSenseMode": "macos-clang-x64",
|
||||||
"configurationProvider": "ms-vscode.cmake-tools"
|
"configurationProvider": "ms-vscode.cmake-tools"
|
||||||
|
|||||||
@@ -14,16 +14,20 @@ CsvParser::CsvParser(bool skip_hdr, char field_sep, char quote_ch, char line_sep
|
|||||||
}
|
}
|
||||||
|
|
||||||
MlValue CsvParser::parseCSV(const std::string &csvSource) {
|
MlValue CsvParser::parseCSV(const std::string &csvSource) {
|
||||||
int linesRead = 0;
|
constexpr size_t INITIAL_PARSED_ROWS_SIZE = 128;
|
||||||
|
constexpr size_t INITIAL_COLUMNS_SIZE = 32;
|
||||||
|
constexpr size_t ROWS_READ_FOR_SIZE_ESTIMATION = 16;
|
||||||
|
|
||||||
|
size_t linesRead = 0;
|
||||||
bool inQuote(false);
|
bool inQuote(false);
|
||||||
bool newLine(false);
|
bool newLine(false);
|
||||||
std::string field;
|
std::string field;
|
||||||
|
|
||||||
std::vector<MlValue> parsed_data;
|
std::vector<MlValue> parsed_rows;
|
||||||
parsed_data.reserve(128); // TODO introduce constant here
|
parsed_rows.reserve(INITIAL_PARSED_ROWS_SIZE);
|
||||||
|
|
||||||
std::vector<MlValue> line;
|
std::vector<MlValue> line;
|
||||||
line.reserve(32); // TODO introduce constant here
|
line.reserve(INITIAL_COLUMNS_SIZE);
|
||||||
|
|
||||||
std::string::const_iterator aChar = csvSource.begin();
|
std::string::const_iterator aChar = csvSource.begin();
|
||||||
std::string::const_iterator aEnd = csvSource.end();
|
std::string::const_iterator aEnd = csvSource.end();
|
||||||
@@ -45,14 +49,14 @@ MlValue CsvParser::parseCSV(const std::string &csvSource) {
|
|||||||
} else {
|
} else {
|
||||||
if (!newLine) {
|
if (!newLine) {
|
||||||
line.push_back(ivalualize(field));
|
line.push_back(ivalualize(field));
|
||||||
add_line(line, parsed_data);
|
add_row(line, parsed_rows);
|
||||||
field.clear();
|
field.clear();
|
||||||
line.clear();
|
line.clear();
|
||||||
linesRead++;
|
linesRead++;
|
||||||
if (linesRead == 16) {
|
if (linesRead == ROWS_READ_FOR_SIZE_ESTIMATION) {
|
||||||
size_t linesEstimation = csvSource.size() / (std::distance(csvSource.begin(), aChar) / linesRead);
|
size_t linesEstimation = csvSource.size() / (std::distance(csvSource.begin(), aChar) / linesRead);
|
||||||
if (linesEstimation > parsed_data.capacity())
|
if (linesEstimation > parsed_rows.capacity())
|
||||||
parsed_data.reserve(linesEstimation);
|
parsed_rows.reserve(linesEstimation);
|
||||||
}
|
}
|
||||||
newLine = true;
|
newLine = true;
|
||||||
}
|
}
|
||||||
@@ -68,18 +72,18 @@ MlValue CsvParser::parseCSV(const std::string &csvSource) {
|
|||||||
if (!field.empty())
|
if (!field.empty())
|
||||||
line.push_back(ivalualize(field));
|
line.push_back(ivalualize(field));
|
||||||
|
|
||||||
add_line(line, parsed_data);
|
add_row(line, parsed_rows);
|
||||||
|
|
||||||
return parsed_data;
|
return parsed_rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CsvParser::add_line(const std::vector<MlValue> &line, std::vector<MlValue> &lines) {
|
void CsvParser::add_row(const std::vector<MlValue> &columns, std::vector<MlValue> &rows) {
|
||||||
if (skip_header && !header_skiped) {
|
if (skip_header && !header_skiped) {
|
||||||
header_skiped = true;
|
header_skiped = true;
|
||||||
} else {
|
} else {
|
||||||
if (!line.empty())
|
if (!columns.empty())
|
||||||
lines.emplace_back(line);
|
rows.emplace_back(columns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public:
|
|||||||
MlValue parseCSV(const std::string &csvSource);
|
MlValue parseCSV(const std::string &csvSource);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void add_line(const std::vector<MlValue> &line, std::vector<MlValue> &lines);
|
void add_row(const std::vector<MlValue> &columns, std::vector<MlValue> &rows);
|
||||||
|
|
||||||
static MlValue ivalualize(const std::string &value) ;
|
static MlValue ivalualize(const std::string &value) ;
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
std::string
|
std::string mini_sprintf_format(bool left_align, bool sign, bool space_on_left, bool padding_by_zero,
|
||||||
mini_sprintf_format(bool left_align, bool sign, bool space_on_left, bool padding_by_zero, int width, int precision,
|
int width, int precision,
|
||||||
int length, char specifier, const MlValue &value) {
|
int length, char specifier, const MlValue &value) {
|
||||||
std::string s;
|
std::string s;
|
||||||
std::ostringstream stream_str; // PERF simpler solution..without string stream
|
std::ostringstream stream_str; // PERF string append should be faster
|
||||||
bool is_positive = false;
|
bool is_positive = false;
|
||||||
|
|
||||||
if (specifier == 's') {
|
if (specifier == 's') {
|
||||||
@@ -47,7 +47,7 @@ mini_sprintf_format(bool left_align, bool sign, bool space_on_left, bool padding
|
|||||||
stream_str << std::setprecision(precision);
|
stream_str << std::setprecision(precision);
|
||||||
|
|
||||||
stream_str << dval;
|
stream_str << dval;
|
||||||
s = stream_str.str(); // TODO ??
|
s = stream_str.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width > -1 && s.size() < width) {
|
if (width > -1 && s.size() < width) {
|
||||||
@@ -172,7 +172,7 @@ std::string mini_sprintf(const std::string &format_str, const std::vector<MlValu
|
|||||||
// specifier
|
// specifier
|
||||||
if (si >= format_str.end())
|
if (si >= format_str.end())
|
||||||
return output_str; // invalid end of string
|
return output_str; // invalid end of string
|
||||||
if (*si == 'i' || *si == 'd' || *si == 'f' || *si == 's' || *si == 'c') { // TODO more specifiers
|
if (*si == 'i' || *si == 'd' || *si == 'f' || *si == 's' || *si == 'c') { // TODO more specifiers
|
||||||
std::string s = mini_sprintf_format(left_align, sign, space_on_left, padding_by_zero, width,
|
std::string s = mini_sprintf_format(left_align, sign, space_on_left, padding_by_zero, width,
|
||||||
precision, length, *si, parameters[arg_position]);
|
precision, length, *si, parameters[arg_position]);
|
||||||
arg_position++;
|
arg_position++;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -16,8 +15,6 @@
|
|||||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET
|
||||||
|
|
||||||
|
|
||||||
// TODO user streams instead of printf's
|
|
||||||
|
|
||||||
std::pair<int, std::string> HttpClient::doRequest(const std::string &method, const std::string &url, const std::map<std::string, std::string> &headers, const std::string &request_body) {
|
std::pair<int, std::string> HttpClient::doRequest(const std::string &method, const std::string &url, const std::map<std::string, std::string> &headers, const std::string &request_body) {
|
||||||
// split url to parts
|
// split url to parts
|
||||||
parseURL(url);
|
parseURL(url);
|
||||||
@@ -39,7 +36,6 @@ std::pair<int, std::string> HttpClient::doRequest(const std::string &method, con
|
|||||||
return std::make_pair(403, "");
|
return std::make_pair(403, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// get headers
|
// get headers
|
||||||
std::string::size_type position = ssl_read_packet.find("\r\n\r\n");
|
std::string::size_type position = ssl_read_packet.find("\r\n\r\n");
|
||||||
if (position == std::string::npos)
|
if (position == std::string::npos)
|
||||||
@@ -198,7 +194,7 @@ int HttpClient::sslRequest(const std::string &server_name, const std::string &re
|
|||||||
int s;
|
int s;
|
||||||
s = socket(AF_INET, SOCK_STREAM, 0);
|
s = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
printf("sslRequest, error creating socket.\n");
|
std::cerr << "HttpClient::sslRequest, error creating socket" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +209,7 @@ int HttpClient::sslRequest(const std::string &server_name, const std::string &re
|
|||||||
|
|
||||||
// connect to server
|
// connect to server
|
||||||
if (connect(s, (struct sockaddr *) &sa, socklen)) {
|
if (connect(s, (struct sockaddr *) &sa, socklen)) {
|
||||||
printf("sslRequest, error connecting to server.\n");
|
std::cerr << "HttpClient::sslRequest, error connecting to server" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +220,7 @@ int HttpClient::sslRequest(const std::string &server_name, const std::string &re
|
|||||||
SSL_CTX *ctx = SSL_CTX_new(meth);
|
SSL_CTX *ctx = SSL_CTX_new(meth);
|
||||||
ssl = SSL_new(ctx);
|
ssl = SSL_new(ctx);
|
||||||
if (!ssl) {
|
if (!ssl) {
|
||||||
printf("sslRequest, error creating SSL.\n");
|
std::cerr << "HttpClient::sslRequest, error creating SSL" << std::endl;
|
||||||
logSSL();
|
logSSL();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -235,23 +231,23 @@ int HttpClient::sslRequest(const std::string &server_name, const std::string &re
|
|||||||
|
|
||||||
int err = SSL_connect(ssl);
|
int err = SSL_connect(ssl);
|
||||||
if (err <= 0) {
|
if (err <= 0) {
|
||||||
printf("sslRequest, error creating SSL connection. err=%x\n", err);
|
std::cerr << "HttpClient::sslRequest, error creating SSL connection. " << err << std::endl;
|
||||||
logSSL();
|
logSSL();
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// log cipher
|
// log cipher
|
||||||
// printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
|
// std::cerr << "HttpClient::sslRequest, SSL connection using" << SSL_get_cipher (ssl) << std::endl;
|
||||||
// showCerts(ssl);
|
// showCerts(ssl);
|
||||||
|
|
||||||
|
|
||||||
// send request
|
// send request
|
||||||
// printf ("SSL sending request %s\n", request.c_str());
|
// std::cerr << "HttpClient::sslRequest, SSL sending request: " << request << std::endl;
|
||||||
|
|
||||||
int written_bytes = sslSendPacket(request);
|
int written_bytes = sslSendPacket(request);
|
||||||
if (written_bytes != request.length()) {
|
if (written_bytes != request.length()) {
|
||||||
printf("sslRequest, error sending request\n");
|
std::cerr << "HttpClient::sslRequest, error sending request" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,15 +271,15 @@ void HttpClient::showCerts(SSL* ssl) {
|
|||||||
|
|
||||||
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
|
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
|
||||||
if ( cert != NULL ) {
|
if ( cert != NULL ) {
|
||||||
printf("Server certificates:\n");
|
std::cerr << "HttpClient::showCerts, Server certificates: " << std::endl;
|
||||||
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
||||||
printf("Subject: %s\n", line);
|
std::cerr << "HttpClient::showCerts, Subject: " << line << std::endl;
|
||||||
free(line); /* free the malloc'ed string */
|
free(line); /* free the malloc'ed string */
|
||||||
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
||||||
printf("Issuer: %s\n", line);
|
std::cerr << "HttpClient::showCerts, Issuer: " << line << std::endl;
|
||||||
free(line); /* free the malloc'ed string */
|
free(line); /* free the malloc'ed string */
|
||||||
X509_free(cert); /* free the malloc'ed certificate copy */
|
X509_free(cert); /* free the malloc'ed certificate copy */
|
||||||
} else {
|
} else {
|
||||||
printf("No certificates.\n");
|
std::cerr << "HttpClient::showCerts, No certificates." << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
|
|
||||||
class HttpClient {
|
class HttpClient {
|
||||||
// TODO at this moment only https is implemented
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
|||||||
44
debug.lsp
44
debug.lsp
@@ -1,7 +1,5 @@
|
|||||||
(include "/usr/local/var/mlisp/terminal.lsp")
|
(include "/usr/local/var/mlisp/terminal.lsp")
|
||||||
|
|
||||||
; TODO better env formating
|
|
||||||
; TODO time parse function
|
|
||||||
|
|
||||||
|
|
||||||
(defn cron::sleep-to-next-minute ()
|
(defn cron::sleep-to-next-minute ()
|
||||||
@@ -9,9 +7,6 @@
|
|||||||
|
|
||||||
|
|
||||||
(defn cron::eval-time-spec (val time_spec)
|
(defn cron::eval-time-spec (val time_spec)
|
||||||
(do
|
|
||||||
;(print "DEBUG" "cron::eval-time-spec" val time_spec)
|
|
||||||
(def r
|
|
||||||
(cond
|
(cond
|
||||||
((nil? time_spec)
|
((nil? time_spec)
|
||||||
#t) ; no time_spec -> it's true
|
#t) ; no time_spec -> it's true
|
||||||
@@ -25,10 +20,7 @@
|
|||||||
(#t
|
(#t
|
||||||
nil) ; TODO exception
|
nil) ; TODO exception
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(print "val:" val "time_spec:" time_spec "result" r)
|
|
||||||
r
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
(defn cron::add-job (name time_spec job)
|
(defn cron::add-job (name time_spec job)
|
||||||
@@ -55,10 +47,17 @@
|
|||||||
(set! bool (and bool (cron::eval-time-spec time_day (find-val-in-list time_spec "day"))))
|
(set! bool (and bool (cron::eval-time-spec time_day (find-val-in-list time_spec "day"))))
|
||||||
(set! bool (and bool (cron::eval-time-spec time_month (find-val-in-list time_spec "month"))))
|
(set! bool (and bool (cron::eval-time-spec time_month (find-val-in-list time_spec "month"))))
|
||||||
(set! bool (and bool (cron::eval-time-spec time_year (find-val-in-list time_spec "year"))))
|
(set! bool (and bool (cron::eval-time-spec time_year (find-val-in-list time_spec "year"))))
|
||||||
(print "time-to-run test" bool)
|
; (print "time-to-run test" bool)
|
||||||
bool
|
bool
|
||||||
))
|
))
|
||||||
|
|
||||||
|
(defn cron::run-job (name code)
|
||||||
|
(do
|
||||||
|
;(print "running job:" name "->" code)
|
||||||
|
(def run_code (+ "(thread-create " (display code) ")"))
|
||||||
|
(eval (parse run_code))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
(defn cron::start ()
|
(defn cron::start ()
|
||||||
(do
|
(do
|
||||||
@@ -66,17 +65,17 @@
|
|||||||
(print "cron daemon started, waiting" wait_time "miliseconds for first run")
|
(print "cron daemon started, waiting" wait_time "miliseconds for first run")
|
||||||
; (thread-sleep wait_time)
|
; (thread-sleep wait_time)
|
||||||
(while (not cron::stop)
|
(while (not cron::stop)
|
||||||
(def now (get-universal-time))
|
(def now (get-universal-time))
|
||||||
(for e cron::entries
|
(for e cron::entries
|
||||||
(def job_name (first e))
|
(def job_name (first e))
|
||||||
(def time_spec (second e))
|
(def time_spec (second e))
|
||||||
|
(def job_code (third e))
|
||||||
|
|
||||||
; (print "DEBUG" "checking" job_name "time_spec" time_spec)
|
; (print "\n\n")
|
||||||
(if (cron::time-to-run now time_spec)
|
(def is_for_run (cron::time-to-run now time_spec))
|
||||||
(do
|
; (print "DEBUG" job_name "checked" is_for_run)
|
||||||
;(print "running job:" job_name "code:" (third e))
|
(if is_for_run
|
||||||
(thread-create (eval (third e)))
|
(cron::run-job job_name job_code)
|
||||||
)
|
|
||||||
; (print "not running" job_name)
|
; (print "not running" job_name)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -99,19 +98,20 @@
|
|||||||
|
|
||||||
(cron::add-job "every_min_print"
|
(cron::add-job "every_min_print"
|
||||||
'(("minute" "*"))
|
'(("minute" "*"))
|
||||||
'(print "code: every_min_print"))
|
'(print "executing: every_min_print"))
|
||||||
|
; '(do (sleep 3) (print "executing: every_min_print")))
|
||||||
|
|
||||||
(cron::add-job "every_min_between_00_and_05"
|
(cron::add-job "every_min_between_00_and_05"
|
||||||
'(("minute" "00-05"))
|
'(("minute" "00-05"))
|
||||||
'(print "code: every_min_between_00_and_05"))
|
'(print "executing: every_min_between_00_and_05"))
|
||||||
|
|
||||||
(cron::add-job "13:20_working_day"
|
(cron::add-job "13:20_working_day"
|
||||||
'(("weekday" "1-5")("hour" "13")("minute" "20"))
|
'(("weekday" "1-5")("hour" "13")("minute" "20"))
|
||||||
'(print "code: 13:20_working_day"))
|
'(print "executing: 13:20_working_day"))
|
||||||
|
|
||||||
(cron::add-job "15:20_working_day"
|
(cron::add-job "15:20_working_day"
|
||||||
'(("weekday" "1-5")("hour" "15")("minute" "20"))
|
'(("weekday" "1-5")("hour" "15")("minute" "20"))
|
||||||
'(print "code: 15:20_working_day"))
|
'(print "executing: 15:20_working_day"))
|
||||||
|
|
||||||
|
|
||||||
(cron::start)
|
(cron::start)
|
||||||
|
|||||||
@@ -113,7 +113,7 @@
|
|||||||
|`start-of-year datetime`|Returns epoch time of start of year|`>>> (start-of-year (str-to-date "2021-05-13 10:32:12" "%Y-%m-%d %H:%M:%S")) => 1609459200`|Date and time|
|
|`start-of-year datetime`|Returns epoch time of start of year|`>>> (start-of-year (str-to-date "2021-05-13 10:32:12" "%Y-%m-%d %H:%M:%S")) => 1609459200`|Date and time|
|
||||||
|`end-of-year datetime`|Returns epoch time of end of year|`>>> (end-of-year (str-to-date "2021-05-13 10:32:12" "%Y-%m-%d %H:%M:%S")) => 1640995199`|Date and time|
|
|`end-of-year datetime`|Returns epoch time of end of year|`>>> (end-of-year (str-to-date "2021-05-13 10:32:12" "%Y-%m-%d %H:%M:%S")) => 1640995199`|Date and time|
|
||||||
|`decode-universal-time time`|Returns the decoded time represented by the given universal time. Returns list with (second, minute, hour, day, month, year, day-of-the-week)|`>>> (decode-universal-time 0) => (0 0 0 1 1 1970 4)`|Date and time|
|
|`decode-universal-time time`|Returns the decoded time represented by the given universal time. Returns list with (second, minute, hour, day, month, year, day-of-the-week)|`>>> (decode-universal-time 0) => (0 0 0 1 1 1970 4)`|Date and time|
|
||||||
|`(debug ..)`|TODO add me|TODO add me|IO|
|
|`(debug ..)`|Returns string representation of passed params|`>>> (debug '(1 2 3)) => "(1 2 3)"`|IO|
|
||||||
|`(display ..)`|Displays passed parameters|`>>> (display '(1 2 3)) => "(1 2 3)"`|IO|
|
|`(display ..)`|Displays passed parameters|`>>> (display '(1 2 3)) => "(1 2 3)"`|IO|
|
||||||
|`(string-replace source substr replacement)`|Replace a substring with a replacement string in a source string|`>>> (string-replace "abcdefg" "de" "DE") => "abcDEfg"`|String manipulation|
|
|`(string-replace source substr replacement)`|Replace a substring with a replacement string in a source string|`>>> (string-replace "abcdefg" "de" "DE") => "abcDEfg"`|String manipulation|
|
||||||
|`(string-replace-re source substr replacement)`|Replace a substring regex with a replacement string in a source string|`>>> (string-replace-re "there is a subsequence in the string" "\\b(sub)([^ ]*)" "sub-$2") => "there is a sub-sequence in the string"`|String manipulation|
|
|`(string-replace-re source substr replacement)`|Replace a substring regex with a replacement string in a source string|`>>> (string-replace-re "there is a subsequence in the string" "\\b(sub)([^ ]*)" "sub-$2") => "there is a sub-sequence in the string"`|String manipulation|
|
||||||
@@ -177,4 +177,4 @@
|
|||||||
|`(thread-sleep milisecons)`|Sleeps thread for given amount of miliseconds||Threading|
|
|`(thread-sleep milisecons)`|Sleeps thread for given amount of miliseconds||Threading|
|
||||||
|`(threads-join)`|Wait for all running threads to finish||Threading|
|
|`(threads-join)`|Wait for all running threads to finish||Threading|
|
||||||
|`(try block catch_block [finally_block])`|Evaluates block and if an exception is thrown, evaluates catch_block.Eventually in both cases evals finally_block. Return evaluated last expression from block if no exception or last expression from catch_block, if exception is thrown from block. Variable ml-exception in catch block is available with astring describing exception||Exceptions|
|
|`(try block catch_block [finally_block])`|Evaluates block and if an exception is thrown, evaluates catch_block.Eventually in both cases evals finally_block. Return evaluated last expression from block if no exception or last expression from catch_block, if exception is thrown from block. Variable ml-exception in catch block is available with astring describing exception||Exceptions|
|
||||||
|`(throw-exception exp_desc)`|Throws an exception with exp_desc describing what happened ||Exceptions|
|
|`(throw exp_desc)`|Throws an exception with exp_desc describing what happened. exp_desc will be evaluated a returned as string|`>>> (throw (+ 1 2))\n3`|Exceptions|
|
||||||
18
ml.cpp
18
ml.cpp
@@ -1239,7 +1239,6 @@ MlValue read_url(std::vector<MlValue> args, MlEnvironment &env) {
|
|||||||
MlValue parse_json(std::vector<MlValue> args, MlEnvironment &env) {
|
MlValue parse_json(std::vector<MlValue> args, MlEnvironment &env) {
|
||||||
eval_args(args, env);
|
eval_args(args, env);
|
||||||
|
|
||||||
// TODO add support for more params specifying options
|
|
||||||
if (args.size() != 1)
|
if (args.size() != 1)
|
||||||
throw MlError(MlValue("parse-json", parse_json), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
throw MlError(MlValue("parse-json", parse_json), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||||
|
|
||||||
@@ -1247,10 +1246,9 @@ MlValue parse_json(std::vector<MlValue> args, MlEnvironment &env) {
|
|||||||
std::string err;
|
std::string err;
|
||||||
auto json = json11::Json::parse(str, err);
|
auto json = json11::Json::parse(str, err);
|
||||||
|
|
||||||
if (!err.empty()) {
|
if (!err.empty())
|
||||||
// TODO handle error
|
|
||||||
return MlValue::string("ERROR json parsing: " + err);
|
return MlValue::string("ERROR json parsing: " + err);
|
||||||
}
|
|
||||||
|
|
||||||
return json.ivalualize();
|
return json.ivalualize();
|
||||||
}
|
}
|
||||||
@@ -1973,9 +1971,10 @@ MlValue thread_create(std::vector<MlValue> args, MlEnvironment &env) {
|
|||||||
MlValue acc = arg.eval(env);
|
MlValue acc = arg.eval(env);
|
||||||
} catch (const MlError &e) {
|
} catch (const MlError &e) {
|
||||||
std::cerr << "thread_create exception: " << e.description() << std::endl;
|
std::cerr << "thread_create exception: " << e.description() << std::endl;
|
||||||
|
throw;
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << "thread_create exception: " << e.what() << std::endl;
|
std::cerr << "thread_create exception: " << e.what() << std::endl;
|
||||||
throw e;
|
throw;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2058,10 +2057,12 @@ MlValue try_block(std::vector<MlValue> args, MlEnvironment &env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MlValue throw_exception(std::vector<MlValue> args, MlEnvironment &env) {
|
MlValue throw_exception(std::vector<MlValue> args, MlEnvironment &env) {
|
||||||
|
eval_args(args, env);
|
||||||
|
|
||||||
if (args.size() != 1)
|
if (args.size() != 1)
|
||||||
throw MlError(MlValue("throw", throw_exception), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
throw MlError(MlValue("throw", throw_exception), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||||
|
|
||||||
throw std::runtime_error(args[0].as_string());
|
throw std::runtime_error(args[0].cast_to_string().as_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
MlValue usql(std::vector<MlValue> args, MlEnvironment &env) {
|
MlValue usql(std::vector<MlValue> args, MlEnvironment &env) {
|
||||||
@@ -2356,10 +2357,10 @@ int main(int argc, char *argv[]) {
|
|||||||
run(read_file_contents(file), env);
|
run(read_file_contents(file), env);
|
||||||
// sheebang
|
// sheebang
|
||||||
} else if (cmdOptionExists(argv, argv + argc, "-run")) {
|
} else if (cmdOptionExists(argv, argv + argc, "-run")) {
|
||||||
for (auto & file : getCmdOption(argv, argc, "-run")) { // TODO check only one file is specified ??
|
for (auto & file : getCmdOption(argv, argc, "-run")) {
|
||||||
std::string file_content = read_file_contents(file);
|
std::string file_content = read_file_contents(file);
|
||||||
if (file_content.find("#!") == 0) // shebang ?
|
if (file_content.find("#!") == 0) // shebang ?
|
||||||
file_content.erase(0, file_content.find('\n') + 1); // TODO mac osx newline??
|
file_content.erase(0, file_content.find('\n') + 1);
|
||||||
|
|
||||||
run(file_content, env);
|
run(file_content, env);
|
||||||
}
|
}
|
||||||
@@ -2376,7 +2377,6 @@ int main(int argc, char *argv[]) {
|
|||||||
std::cerr << e.description() << std::endl;
|
std::cerr << e.description() << std::endl;
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
std::cerr << MlPerfMon::instance().callstack() << e.what() << std::endl;
|
std::cerr << MlPerfMon::instance().callstack() << e.what() << std::endl;
|
||||||
MlPerfMon::instance().clear_callstack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
10
ml_util.cpp
10
ml_util.cpp
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
const std::vector<std::string> commands {
|
const std::vector<std::string> commands {
|
||||||
"eval", "type", "parse", "do", "if", "for", "while", "scope", "quote", "defn",
|
"eval", "type", "parse", "do", "if", "for", "while", "scope", "quote", "defn",
|
||||||
@@ -17,12 +18,11 @@ const std::vector<std::string> commands {
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::string get_history_file_dir() {
|
std::string get_history_file_dir() {
|
||||||
// TODO not portable and in function
|
std::string filename{".ml_history.txt"};
|
||||||
std::string file{"/.ml_history.txt"};
|
|
||||||
const char *t = std::getenv("HOME");
|
const char *t = std::getenv("HOME");
|
||||||
|
|
||||||
if (t == nullptr) return "/tmp/" + file;
|
if (t == nullptr) return std::filesystem::temp_directory_path() / filename;
|
||||||
else return std::string{t} + "/" + file;
|
else return std::filesystem::path(std::string{t}) /filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
MlEnvironment * repl_env = nullptr;
|
MlEnvironment * repl_env = nullptr;
|
||||||
@@ -68,7 +68,7 @@ void completion(const char *buf, linenoiseCompletions *lc) {
|
|||||||
std::string token = str.substr(pos+1);
|
std::string token = str.substr(pos+1);
|
||||||
std::string begining = str.substr(0, pos+1);
|
std::string begining = str.substr(0, pos+1);
|
||||||
|
|
||||||
// TODO optimize not to get all lambdas, but those beginning with token
|
// PERF optimize not to get all lambdas, but those beginning with token
|
||||||
std::vector<std::string> lambdas = repl_env->get_lambdas_list();
|
std::vector<std::string> lambdas = repl_env->get_lambdas_list();
|
||||||
lambdas.insert(end(lambdas), begin(commands), end(commands));
|
lambdas.insert(end(lambdas), begin(commands), end(commands));
|
||||||
|
|
||||||
|
|||||||
@@ -83,19 +83,22 @@
|
|||||||
; return 1 when list contains item otherwise nil
|
; return 1 when list contains item otherwise nil
|
||||||
(defn member (lst itm)
|
(defn member (lst itm)
|
||||||
(do
|
(do
|
||||||
; TODO check if is empty list
|
(if (list? lst)
|
||||||
(def found_index -1)
|
(do
|
||||||
(def i 0)
|
(def found_index -1)
|
||||||
(def lst_len (len lst))
|
(def i 0)
|
||||||
|
(def lst_len (len lst))
|
||||||
|
|
||||||
(while (and (< i lst_len) (= found_index -1))
|
(while (and (< i lst_len) (= found_index -1))
|
||||||
(if (= itm (index lst i))
|
(if (= itm (index lst i))
|
||||||
(set! found_index i)
|
(set! found_index i)
|
||||||
(set! i (+ i 1))
|
(set! i (+ i 1))
|
||||||
))
|
))
|
||||||
|
|
||||||
(if (!= -1 found_index)
|
(if (!= -1 found_index)
|
||||||
#t
|
#t
|
||||||
|
nil)
|
||||||
|
)
|
||||||
nil)
|
nil)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,17 @@ namespace usql {
|
|||||||
|
|
||||||
// TOOD handle premature eof
|
// TOOD handle premature eof
|
||||||
|
|
||||||
|
std::string column_type_name(const ColumnType type) {
|
||||||
|
if (type == ColumnType::integer_type) return "integer_type";
|
||||||
|
if (type == ColumnType::float_type) return "float_type";
|
||||||
|
if (type == ColumnType::varchar_type) return "varchar_type";
|
||||||
|
if (type == ColumnType::date_type) return "date_type";
|
||||||
|
if (type == ColumnType::bool_type) return "bool_type";
|
||||||
|
|
||||||
|
throw Exception("invalid column type: " + (int)type);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Parser::Parser() {
|
Parser::Parser() {
|
||||||
m_lexer = Lexer{};
|
m_lexer = Lexer{};
|
||||||
}
|
}
|
||||||
@@ -433,39 +444,39 @@ namespace usql {
|
|||||||
|
|
||||||
// function call
|
// function call
|
||||||
if (token_typcol == TokenType::identifier && m_lexer.nextTokenType() == TokenType::open_paren) {
|
if (token_typcol == TokenType::identifier && m_lexer.nextTokenType() == TokenType::open_paren) {
|
||||||
std::string function_name = m_lexer.consumeToken(TokenType::identifier).token_string;
|
std::string function_name = m_lexer.consumeToken(TokenType::identifier).token_string;
|
||||||
std::vector<std::unique_ptr<Node>> pars;
|
std::vector<std::unique_ptr<Node>> pars;
|
||||||
|
|
||||||
m_lexer.skipToken(TokenType::open_paren);
|
m_lexer.skipToken(TokenType::open_paren);
|
||||||
while (m_lexer.tokenType() != TokenType::close_paren && m_lexer.tokenType() != TokenType::eof) {
|
while (m_lexer.tokenType() != TokenType::close_paren && m_lexer.tokenType() != TokenType::eof) {
|
||||||
pars.push_back(parse_expression());
|
pars.push_back(parse_expression());
|
||||||
m_lexer.skipTokenOptional(TokenType::comma);
|
m_lexer.skipTokenOptional(TokenType::comma);
|
||||||
}
|
}
|
||||||
m_lexer.skipToken(TokenType::close_paren);
|
m_lexer.skipToken(TokenType::close_paren);
|
||||||
return std::make_unique<FunctionNode>(function_name, std::move(pars));
|
return std::make_unique<FunctionNode>(function_name, std::move(pars));
|
||||||
}
|
}
|
||||||
|
|
||||||
// numbers and strings
|
// numbers and strings
|
||||||
std::string tokenString = m_lexer.consumeToken().token_string;
|
std::string tokenString = m_lexer.consumeToken().token_string;
|
||||||
|
|
||||||
if (token_typcol == TokenType::int_number)
|
if (token_typcol == TokenType::int_number)
|
||||||
return std::make_unique<IntValueNode>(std::stoi(tokenString));
|
return std::make_unique<IntValueNode>(std::stoi(tokenString));
|
||||||
if (token_typcol == TokenType::double_number)
|
if (token_typcol == TokenType::double_number)
|
||||||
return std::make_unique<DoubleValueNode>(std::stod(tokenString));
|
return std::make_unique<DoubleValueNode>(std::stod(tokenString));
|
||||||
if (token_typcol == TokenType::string_literal)
|
if (token_typcol == TokenType::string_literal)
|
||||||
return std::make_unique<StringValueNode>(tokenString);
|
return std::make_unique<StringValueNode>(tokenString);
|
||||||
|
|
||||||
// db column
|
// db column
|
||||||
if (token_typcol == TokenType::identifier)
|
if (token_typcol == TokenType::identifier)
|
||||||
return std::make_unique<DatabaseValueNode>(tokenString);
|
return std::make_unique<DatabaseValueNode>(tokenString);
|
||||||
|
|
||||||
// null
|
// null
|
||||||
if (token_typcol == TokenType::keyword_null)
|
if (token_typcol == TokenType::keyword_null)
|
||||||
return std::make_unique<NullValueNode>();
|
return std::make_unique<NullValueNode>();
|
||||||
|
|
||||||
// true / false
|
// true / false
|
||||||
if (token_typcol == TokenType::keyword_true || token_typcol == TokenType::keyword_false)
|
if (token_typcol == TokenType::keyword_true || token_typcol == TokenType::keyword_false)
|
||||||
return std::make_unique<BooleanValueNode>(token_typcol == TokenType::keyword_true);
|
return std::make_unique<BooleanValueNode>(token_typcol == TokenType::keyword_true);
|
||||||
|
|
||||||
// token * for count(*)
|
// token * for count(*)
|
||||||
if (token_typcol == TokenType::multiply)
|
if (token_typcol == TokenType::multiply)
|
||||||
@@ -529,4 +540,3 @@ namespace usql {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
140
usql/parser.h
140
usql/parser.h
@@ -21,6 +21,9 @@ enum class ColumnType {
|
|||||||
bool_type
|
bool_type
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string column_type_name(const ColumnType type);
|
||||||
|
|
||||||
|
|
||||||
enum class NodeType {
|
enum class NodeType {
|
||||||
true_node,
|
true_node,
|
||||||
null_value,
|
null_value,
|
||||||
@@ -51,6 +54,7 @@ enum class NodeType {
|
|||||||
error
|
error
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Node {
|
struct Node {
|
||||||
NodeType node_type;
|
NodeType node_type;
|
||||||
|
|
||||||
@@ -58,7 +62,7 @@ struct Node {
|
|||||||
virtual ~Node() = default;
|
virtual ~Node() = default;
|
||||||
|
|
||||||
virtual void dump() const {
|
virtual void dump() const {
|
||||||
std::cout << "type: Node" << std::endl;
|
std::cout << "type: Node" << (int)node_type << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -115,19 +119,76 @@ struct ColDefNode : Node {
|
|||||||
null(nullable) {}
|
null(nullable) {}
|
||||||
|
|
||||||
void dump() const override {
|
void dump() const override {
|
||||||
std::cout << "type: ColDefNode, name: " << name << ", type: " << (int)type << " TODO add more" << std::endl;
|
std::cout << "type: ColDefNode, name: " << name << ", type: " << column_type_name(type) << ", order: " << order << ", length: " << length << ", null: " << null << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FunctionNode : Node {
|
struct FunctionNode : Node {
|
||||||
std::string function; // TODO use enum
|
|
||||||
|
enum class Type {
|
||||||
|
to_string,
|
||||||
|
to_date,
|
||||||
|
date_add,
|
||||||
|
pp,
|
||||||
|
lower,
|
||||||
|
upper,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
count
|
||||||
|
};
|
||||||
|
|
||||||
|
static Type get_function(const std::string &str) {
|
||||||
|
if (str=="to_string") return Type::to_string;
|
||||||
|
if (str=="to_date") return Type::to_date;
|
||||||
|
if (str=="date_add") return Type::date_add;
|
||||||
|
if (str=="pp") return Type::pp;
|
||||||
|
if (str=="lower") return Type::lower;
|
||||||
|
if (str=="upper") return Type::upper;
|
||||||
|
if (str=="min") return Type::min;
|
||||||
|
if (str=="max") return Type::max;
|
||||||
|
if (str=="count") return Type::count;
|
||||||
|
|
||||||
|
throw Exception("invalid function: " + str);
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string function_name(const Type type) {
|
||||||
|
if (type == Type::to_string) return "to_string";
|
||||||
|
if (type == Type::to_date) return "to_date";
|
||||||
|
if (type == Type::date_add) return "date_add";
|
||||||
|
if (type == Type::pp) return "pp";
|
||||||
|
if (type == Type::lower) return "lower";
|
||||||
|
if (type == Type::upper) return "upper";
|
||||||
|
if (type == Type::min) return "min";
|
||||||
|
if (type == Type::max) return "max";
|
||||||
|
if (type == Type::count) return "count";
|
||||||
|
|
||||||
|
throw Exception("invalid function: " + (int)type);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Type function;
|
||||||
std::vector<std::unique_ptr<Node>> params;
|
std::vector<std::unique_ptr<Node>> params;
|
||||||
|
|
||||||
FunctionNode(std::string func_name, std::vector<std::unique_ptr<Node>> pars) :
|
FunctionNode(std::string func_name, std::vector<std::unique_ptr<Node>> pars) :
|
||||||
Node(NodeType::function), function(std::move(func_name)), params(std::move(pars)) {}
|
Node(NodeType::function), function(get_function(func_name)), params(std::move(pars)) {}
|
||||||
|
|
||||||
|
bool is_agg_function() {
|
||||||
|
return (function == Type::count || function == Type::min || function == Type::max);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
friend std::ostream &operator<<(std::ostream &output, const Type &t ) {
|
||||||
|
output << function_name(t);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
void dump() const override {
|
void dump() const override {
|
||||||
std::cout << "type: FunctionNode, function: " << function << " TODO add more" << std::endl;
|
std::cout << "type: FunctionNode, function: " << function_name(function) << "(";
|
||||||
|
for(int i = 0; i < params.size(); i++){
|
||||||
|
if (i > 0) std::cout << ",";
|
||||||
|
params[i]->dump();
|
||||||
|
}
|
||||||
|
std::cout << ")" << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -325,12 +386,17 @@ struct CreateTableNode : Node {
|
|||||||
Node(NodeType::create_table), table_name(std::move(name)), cols_defs(std::move(defs)) {}
|
Node(NodeType::create_table), table_name(std::move(name)), cols_defs(std::move(defs)) {}
|
||||||
|
|
||||||
void dump() const override {
|
void dump() const override {
|
||||||
std::cout << "type: CreateTableNode, table_name: " << table_name << "TODO complete me" << std::endl;
|
std::cout << "type: CreateTableNode, table_name: " << table_name << "(";
|
||||||
|
for(int i = 0; i < cols_defs.size(); i++) {
|
||||||
|
if (i > 0) std::cout << ",";
|
||||||
|
cols_defs[i].dump();
|
||||||
|
}
|
||||||
|
std::cout << ")" << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InsertIntoTableNode : Node {
|
struct InsertIntoTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::vector<DatabaseValueNode> cols_names;
|
std::vector<DatabaseValueNode> cols_names;
|
||||||
std::vector<std::unique_ptr<Node>> cols_values;
|
std::vector<std::unique_ptr<Node>> cols_values;
|
||||||
|
|
||||||
@@ -338,29 +404,53 @@ struct InsertIntoTableNode : Node {
|
|||||||
Node(NodeType::insert_into), table_name(std::move(name)), cols_names(std::move(names)), cols_values(std::move(values)) {}
|
Node(NodeType::insert_into), table_name(std::move(name)), cols_names(std::move(names)), cols_values(std::move(values)) {}
|
||||||
|
|
||||||
void dump() const override {
|
void dump() const override {
|
||||||
std::cout << "type: InsertIntoTableNode, table_name: " << table_name << "TODO complete me" << std::endl;
|
std::cout << "type: InsertIntoTableNode, table_name: " << table_name << "(";
|
||||||
|
for(int i = 0; i < cols_names.size(); i++) {
|
||||||
|
if (i > 0) std::cout << ",";
|
||||||
|
cols_names[i].dump();
|
||||||
|
}
|
||||||
|
std::cout << ") values (";
|
||||||
|
for(int i = 0; i < cols_values.size(); i++) {
|
||||||
|
if (i > 0) std::cout << ",";
|
||||||
|
cols_values[i]->dump();
|
||||||
|
}
|
||||||
|
std::cout << ")" << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SelectFromTableNode : Node {
|
struct SelectFromTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::unique_ptr<std::vector<SelectColNode>> cols_names;
|
std::unique_ptr<std::vector<SelectColNode>> cols_names;
|
||||||
std::unique_ptr<Node> where;
|
std::unique_ptr<Node> where;
|
||||||
std::vector<ColOrderNode> order_by;
|
std::vector<ColOrderNode> order_by;
|
||||||
OffsetLimitNode offset_limit;
|
OffsetLimitNode offset_limit;
|
||||||
bool distinct;
|
bool distinct;
|
||||||
|
|
||||||
SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause, std::vector<ColOrderNode> orderby, OffsetLimitNode offlim, bool distinct_):
|
SelectFromTableNode(std::string name, std::unique_ptr<std::vector<SelectColNode>> names, std::unique_ptr<Node> where_clause, std::vector<ColOrderNode> orderby, OffsetLimitNode offlim, bool distinct_):
|
||||||
Node(NodeType::select_from), table_name(std::move(name)), cols_names(std::move(names)), where(std::move(where_clause)), order_by(std::move(orderby)), offset_limit(std::move(offlim)), distinct(distinct_) {}
|
Node(NodeType::select_from), table_name(std::move(name)), cols_names(std::move(names)), where(std::move(where_clause)), order_by(std::move(orderby)), offset_limit(std::move(offlim)), distinct(distinct_) {}
|
||||||
|
|
||||||
void dump() const override {
|
void dump() const override {
|
||||||
std::cout << "type: SelectFromTableNode, table_name: " << table_name << "TODO complete me" << std::endl;
|
std::cout << "type: SelectFromTableNode, table_name: " << table_name;
|
||||||
|
std::cout << "colums: ";
|
||||||
|
for(int i = 0; i < cols_names->size(); i++) {
|
||||||
|
if (i > 0) std::cout << ",";
|
||||||
|
cols_names->operator[](i).dump();
|
||||||
|
}
|
||||||
|
std::cout << "where: ";
|
||||||
where->dump();
|
where->dump();
|
||||||
|
std::cout << "offset,limit: ";
|
||||||
|
for(int i = 0; i < order_by.size(); i++) {
|
||||||
|
if (i > 0) std::cout << ",";
|
||||||
|
order_by[i].dump();
|
||||||
|
}
|
||||||
|
std::cout << "offset,limit: ";
|
||||||
|
offset_limit.dump();
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreateTableAsSelectNode : Node {
|
struct CreateTableAsSelectNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::unique_ptr<Node> select_table;
|
std::unique_ptr<Node> select_table;
|
||||||
|
|
||||||
CreateTableAsSelectNode(std::string name, std::unique_ptr<Node> table) :
|
CreateTableAsSelectNode(std::string name, std::unique_ptr<Node> table) :
|
||||||
@@ -373,10 +463,10 @@ struct CreateTableAsSelectNode : Node {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct UpdateTableNode : Node {
|
struct UpdateTableNode : Node {
|
||||||
std::string table_name;
|
std::string table_name;
|
||||||
std::vector<DatabaseValueNode> cols_names;
|
std::vector<DatabaseValueNode> cols_names;
|
||||||
std::vector<std::unique_ptr<Node>> values;
|
std::vector<std::unique_ptr<Node>> values;
|
||||||
std::unique_ptr<Node> where;
|
std::unique_ptr<Node> where;
|
||||||
|
|
||||||
UpdateTableNode(std::string name, std::vector<DatabaseValueNode> names, std::vector<std::unique_ptr<Node>> vals,
|
UpdateTableNode(std::string name, std::vector<DatabaseValueNode> names, std::vector<std::unique_ptr<Node>> vals,
|
||||||
std::unique_ptr<Node> where_clause) :
|
std::unique_ptr<Node> where_clause) :
|
||||||
@@ -384,8 +474,16 @@ struct UpdateTableNode : Node {
|
|||||||
where(std::move(where_clause)) {}
|
where(std::move(where_clause)) {}
|
||||||
|
|
||||||
void dump() const override {
|
void dump() const override {
|
||||||
std::cout << "type: UpdateTableNode, table_name: " << table_name << "TODO complete me" << std::endl;
|
std::cout << "type: UpdateTableNode, table_name: " << table_name << " set ";
|
||||||
|
for(int i = 0; i < cols_names.size(); i++) {
|
||||||
|
if (i > 0) std::cout << ",";
|
||||||
|
cols_names[i].dump();
|
||||||
|
std::cout << " = ";
|
||||||
|
values[i]->dump();
|
||||||
|
}
|
||||||
|
std::cout << " where: ";
|
||||||
where->dump();
|
where->dump();
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -431,8 +529,10 @@ struct DeleteFromTableNode : Node {
|
|||||||
Node(NodeType::delete_from), table_name(std::move(name)), where(std::move(where_clause)) {}
|
Node(NodeType::delete_from), table_name(std::move(name)), where(std::move(where_clause)) {}
|
||||||
|
|
||||||
void dump() const override {
|
void dump() const override {
|
||||||
std::cout << "type: DeleteFromTableNode, table_name: " << table_name << std::endl;
|
std::cout << "type: DeleteFromTableNode, table_name: " << table_name;
|
||||||
|
std::cout << "where: ";
|
||||||
where->dump();
|
where->dump();
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] bool is_visible() const { return m_visible; };
|
[[nodiscard]] bool is_visible() const { return m_visible; };
|
||||||
void set_visible() { m_visible = true; };
|
void set_visible() { m_visible = true; };
|
||||||
void set_deleted() { m_visible = true; };
|
void set_deleted() { m_visible = false; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_visible;
|
bool m_visible;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
|
||||||
namespace usql {
|
namespace usql {
|
||||||
|
|
||||||
struct Table {
|
struct Table {
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ std::unique_ptr<ValueNode> USql::eval_value_node(Table *table, Row &row, Node *n
|
|||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::eval_database_value_node(Table *table, Row &row, Node *node) {
|
std::unique_ptr<ValueNode> USql::eval_database_value_node(Table *table, Row &row, Node *node) {
|
||||||
auto *dvl = static_cast<DatabaseValueNode *>(node);
|
auto *dvl = static_cast<DatabaseValueNode *>(node);
|
||||||
ColDefNode col_def = table->get_column_def( dvl->col_name); // TODO optimize it to just get this def once
|
ColDefNode col_def = table->get_column_def(dvl->col_name); // TODO optimize it to just get this def once
|
||||||
ColValue &db_value = row[col_def.order];
|
ColValue &db_value = row[col_def.order];
|
||||||
|
|
||||||
if (db_value.isNull())
|
if (db_value.isNull())
|
||||||
@@ -176,18 +176,17 @@ std::unique_ptr<ValueNode> USql::eval_function_value_node(Table *table, Row &row
|
|||||||
if (evaluatedPars.empty() || evaluatedPars[0]->isNull())
|
if (evaluatedPars.empty() || evaluatedPars[0]->isNull())
|
||||||
return std::make_unique<NullValueNode>();
|
return std::make_unique<NullValueNode>();
|
||||||
|
|
||||||
// TODO use some enum
|
if (fnc->function == FunctionNode::Type::lower) return lower_function(evaluatedPars);
|
||||||
if (fnc->function == "lower") return lower_function(evaluatedPars);
|
if (fnc->function == FunctionNode::Type::upper) return upper_function(evaluatedPars);
|
||||||
if (fnc->function == "upper") return upper_function(evaluatedPars);
|
if (fnc->function == FunctionNode::Type::to_date) return to_date_function(evaluatedPars);
|
||||||
if (fnc->function == "to_date") return to_date_function(evaluatedPars);
|
if (fnc->function == FunctionNode::Type::to_string) return to_string_function(evaluatedPars);
|
||||||
if (fnc->function == "to_string") return to_string_function(evaluatedPars);
|
if (fnc->function == FunctionNode::Type::date_add) return date_add_function(evaluatedPars);
|
||||||
if (fnc->function == "date_add") return date_add_function(evaluatedPars);
|
if (fnc->function == FunctionNode::Type::pp) return pp_function(evaluatedPars);
|
||||||
if (fnc->function == "pp") return pp_function(evaluatedPars);
|
if (fnc->function == FunctionNode::Type::count) return count_function(agg_func_value, evaluatedPars);
|
||||||
if (fnc->function == "count") return count_function(agg_func_value, evaluatedPars);
|
if (fnc->function == FunctionNode::Type::max) return max_function(evaluatedPars, col_def_node, agg_func_value);
|
||||||
if (fnc->function == "max") return max_function(evaluatedPars, col_def_node, agg_func_value);
|
if (fnc->function == FunctionNode::Type::min) return min_function(evaluatedPars, col_def_node, agg_func_value);
|
||||||
if (fnc->function == "min") return min_function(evaluatedPars, col_def_node, agg_func_value);
|
|
||||||
|
|
||||||
throw Exception("invalid function: " + fnc->function);
|
throw Exception("invalid function: " + FunctionNode::function_name(fnc->function));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ void USql::select_row(SelectFromTableNode &where_node,
|
|||||||
|
|
||||||
Row *rslt_row = nullptr;
|
Row *rslt_row = nullptr;
|
||||||
|
|
||||||
// when aggregate functions in rslt_table only one row exists
|
// when aggregate functions in rslt_table only one row exists
|
||||||
if (is_aggregated && !rslt_table->empty())
|
if (is_aggregated && !rslt_table->empty())
|
||||||
rslt_row = &rslt_table->m_rows[0];
|
rslt_row = &rslt_table->m_rows[0];
|
||||||
else
|
else
|
||||||
@@ -108,24 +108,21 @@ void USql::select_row(SelectFromTableNode &where_node,
|
|||||||
rslt_row->setColumnValue((ColDefNode *) &rslt_tbl_col_defs[idx], col_value);
|
rslt_row->setColumnValue((ColDefNode *) &rslt_tbl_col_defs[idx], col_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// for aggregate is validated more than needed
|
||||||
// for aggregate is validated more than needed
|
rslt_table->commit_row(*rslt_row);
|
||||||
rslt_table->commit_row(*rslt_row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool USql::check_for_aggregate_only_functions(SelectFromTableNode &node, size_t result_cols_cnt) {
|
bool USql::check_for_aggregate_only_functions(SelectFromTableNode &node, size_t result_cols_cnt) {
|
||||||
size_t aggregate_funcs = 0;
|
size_t aggregate_funcs = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < node.cols_names->size(); i++) {
|
for (size_t i = 0; i < node.cols_names->size(); i++) {
|
||||||
SelectColNode * col_node = &node.cols_names->operator[](i);
|
SelectColNode * col_node = &node.cols_names->operator[](i);
|
||||||
if (col_node->value->node_type == NodeType::function) {
|
if (col_node->value->node_type == NodeType::function && ((FunctionNode *)col_node->value.get())->is_agg_function())
|
||||||
auto func_node = static_cast<FunctionNode *>(col_node->value.get());
|
aggregate_funcs++;
|
||||||
if (func_node->function == "count" || func_node->function == "min" || func_node->function == "max")
|
|
||||||
aggregate_funcs++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// check whether aggregates are not present or all columns are aggregates
|
// check whether aggregates are not present or all columns are aggregates
|
||||||
if (aggregate_funcs > 0 && aggregate_funcs != result_cols_cnt) {
|
if (aggregate_funcs > 0 && aggregate_funcs != result_cols_cnt) {
|
||||||
throw Exception("aggregate functions with no aggregates");
|
throw Exception("aggregate functions mixed with no aggregate functions in select clause");
|
||||||
}
|
}
|
||||||
|
|
||||||
return aggregate_funcs > 0;
|
return aggregate_funcs > 0;
|
||||||
@@ -229,20 +226,20 @@ std::tuple<int, ColDefNode> USql::get_node_definition(Table *table, Node * node,
|
|||||||
} else if (node->node_type == NodeType::function) {
|
} else if (node->node_type == NodeType::function) {
|
||||||
auto func_node = static_cast<FunctionNode *>(node);
|
auto func_node = static_cast<FunctionNode *>(node);
|
||||||
|
|
||||||
if (func_node->function == "to_string") {
|
if (func_node->function == FunctionNode::Type::to_string) {
|
||||||
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 32, true};
|
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 32, true};
|
||||||
return std::make_tuple(-1, col_def);
|
return std::make_tuple(FUNCTION_CALL, col_def);
|
||||||
} else if (func_node->function == "to_date") {
|
} else if (func_node->function == FunctionNode::Type::to_date) {
|
||||||
ColDefNode col_def = ColDefNode{col_name, ColumnType::integer_type, col_order, 1, true};
|
ColDefNode col_def = ColDefNode{col_name, ColumnType::integer_type, col_order, 1, true};
|
||||||
return std::make_tuple(-1, col_def);
|
return std::make_tuple(FUNCTION_CALL, col_def);
|
||||||
} else if (func_node->function == "pp") {
|
} else if (func_node->function == FunctionNode::Type::pp) {
|
||||||
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 10, true};
|
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 10, true};
|
||||||
return std::make_tuple(-1, col_def);
|
return std::make_tuple(FUNCTION_CALL, col_def);
|
||||||
} else if (func_node->function == "lower" || func_node->function == "upper") {
|
} else if (func_node->function == FunctionNode::Type::lower || func_node->function == FunctionNode::Type::upper) {
|
||||||
// TODO get length, use get_db_column_definition
|
// TODO get length, use get_db_column_definition
|
||||||
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 256, true};
|
ColDefNode col_def = ColDefNode{col_name, ColumnType::varchar_type, col_order, 256, true};
|
||||||
return std::make_tuple(-1, col_def);
|
return std::make_tuple(FUNCTION_CALL, col_def);
|
||||||
} else if (func_node->function == "min" || func_node->function == "max") {
|
} else if (func_node->function == FunctionNode::Type::min || func_node->function == FunctionNode::Type::max) {
|
||||||
auto col_type= ColumnType::float_type;
|
auto col_type= ColumnType::float_type;
|
||||||
size_t col_len = 1;
|
size_t col_len = 1;
|
||||||
auto & v = func_node->params[0];
|
auto & v = func_node->params[0];
|
||||||
@@ -252,10 +249,10 @@ std::tuple<int, ColDefNode> USql::get_node_definition(Table *table, Node * node,
|
|||||||
col_len = src_col_def.length;
|
col_len = src_col_def.length;
|
||||||
}
|
}
|
||||||
ColDefNode col_def = ColDefNode{col_name, col_type, col_order, col_len, true};
|
ColDefNode col_def = ColDefNode{col_name, col_type, col_order, col_len, true};
|
||||||
return std::make_tuple(-1, col_def);
|
return std::make_tuple(FUNCTION_CALL, col_def);
|
||||||
} else if (func_node->function == "count") {
|
} else if (func_node->function == FunctionNode::Type::count) {
|
||||||
ColDefNode col_def = ColDefNode{col_name, ColumnType::integer_type, col_order, 1, true};
|
ColDefNode col_def = ColDefNode{col_name, ColumnType::integer_type, col_order, 1, true};
|
||||||
return std::make_tuple(-1, col_def);
|
return std::make_tuple(FUNCTION_CALL, col_def);
|
||||||
}
|
}
|
||||||
throw Exception("Unsupported function");
|
throw Exception("Unsupported function");
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ std::unique_ptr<ValueNode> USql::to_string_function(const std::vector<std::uniqu
|
|||||||
long date = evaluatedPars[0]->getDateValue();
|
long date = evaluatedPars[0]->getDateValue();
|
||||||
std::string format = evaluatedPars[1]->getStringValue();
|
std::string format = evaluatedPars[1]->getStringValue();
|
||||||
std::string formatted_date = date_to_string(date, format);
|
std::string formatted_date = date_to_string(date, format);
|
||||||
|
|
||||||
return std::make_unique<StringValueNode>(formatted_date);
|
return std::make_unique<StringValueNode>(formatted_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ std::unique_ptr<ValueNode> USql::to_date_function(const std::vector<std::unique_
|
|||||||
std::string date = evaluatedPars[0]->getStringValue();
|
std::string date = evaluatedPars[0]->getStringValue();
|
||||||
std::string format = evaluatedPars[1]->getStringValue();
|
std::string format = evaluatedPars[1]->getStringValue();
|
||||||
long epoch_time = string_to_date(date, format);
|
long epoch_time = string_to_date(date, format);
|
||||||
|
|
||||||
return std::make_unique<IntValueNode>(epoch_time); // No DateValueNode for now
|
return std::make_unique<IntValueNode>(epoch_time); // No DateValueNode for now
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ std::unique_ptr<ValueNode> USql::date_add_function(const std::vector<std::unique
|
|||||||
std::string part = evaluatedPars[2]->getStringValue();
|
std::string part = evaluatedPars[2]->getStringValue();
|
||||||
|
|
||||||
long new_date = add_to_date(datetime, quantity, part);
|
long new_date = add_to_date(datetime, quantity, part);
|
||||||
|
|
||||||
return std::make_unique<IntValueNode>(new_date); // No DateValueNode for now
|
return std::make_unique<IntValueNode>(new_date); // No DateValueNode for now
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,51 +37,57 @@ std::unique_ptr<ValueNode> USql::date_add_function(const std::vector<std::unique
|
|||||||
std::unique_ptr<ValueNode> USql::upper_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
std::unique_ptr<ValueNode> USql::upper_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
std::string str = evaluatedPars[0]->getStringValue();
|
std::string str = evaluatedPars[0]->getStringValue();
|
||||||
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) -> unsigned char { return toupper(c); });
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) -> unsigned char { return toupper(c); });
|
||||||
|
|
||||||
return std::make_unique<StringValueNode>(str);
|
return std::make_unique<StringValueNode>(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::lower_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
std::unique_ptr<ValueNode> USql::lower_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
std::string str = evaluatedPars[0]->getStringValue();
|
std::string str = evaluatedPars[0]->getStringValue();
|
||||||
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
||||||
|
|
||||||
return std::make_unique<StringValueNode>(str);
|
return std::make_unique<StringValueNode>(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ValueNode> USql::pp_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
std::unique_ptr<ValueNode> USql::pp_function(const std::vector<std::unique_ptr<ValueNode>> &evaluatedPars) {
|
||||||
|
constexpr auto k_num_format_rpad = 10;
|
||||||
|
constexpr auto k_num_format_maxlen = 20;
|
||||||
|
|
||||||
auto &parsed_value = evaluatedPars[0];
|
auto &parsed_value = evaluatedPars[0];
|
||||||
|
|
||||||
if (parsed_value->node_type == NodeType::int_value || parsed_value->node_type == NodeType::float_value) {
|
if (parsed_value->node_type == NodeType::int_value || parsed_value->node_type == NodeType::float_value) {
|
||||||
std::string format = evaluatedPars.size() > 1 ? evaluatedPars[1]->getStringValue() : "";
|
std::string format = evaluatedPars.size() > 1 ? evaluatedPars[1]->getStringValue() : "";
|
||||||
char buf[20] {0}; // TODO constant here
|
char buf[k_num_format_maxlen] {0};
|
||||||
double value = parsed_value->getDoubleValue();
|
double value = parsed_value->getDoubleValue();
|
||||||
|
|
||||||
if (format == "100%")
|
if (format == "100%")
|
||||||
std::snprintf(buf, 20, "%.2f%%", value);
|
std::snprintf(buf, k_num_format_maxlen, "%.2f%%", value);
|
||||||
else if (format == "%.2f")
|
else if (format == "%.2f")
|
||||||
std::snprintf(buf, 20, "%.2f", value);
|
std::snprintf(buf, k_num_format_maxlen, "%.2f", value);
|
||||||
else if (value >= 1000000000000)
|
else if (value >= 1000000000000)
|
||||||
std::snprintf(buf, 20, "%7.2fT", value/1000000000000);
|
std::snprintf(buf, k_num_format_maxlen, "%7.2fT", value/1000000000000);
|
||||||
else if (value >= 1000000000)
|
else if (value >= 1000000000)
|
||||||
std::sprintf(buf, "%7.2fB", value/1000000000);
|
std::snprintf(buf, k_num_format_maxlen, "%7.2fB", value/1000000000);
|
||||||
else if (value >= 1000000)
|
else if (value >= 1000000)
|
||||||
std::snprintf(buf, 20, "%7.2fM", value/1000000);
|
std::snprintf(buf, k_num_format_maxlen, "%7.2fM", value/1000000);
|
||||||
else if (value >= 100000)
|
else if (value >= 100000)
|
||||||
std::snprintf(buf, 20, "%7.2fM", value/100000); // 0.12M
|
std::snprintf(buf, k_num_format_maxlen, "%7.2fM", value/100000); // 0.12M
|
||||||
else if (value <= -1000000000000)
|
else if (value <= -1000000000000)
|
||||||
std::snprintf(buf, 20, "%7.2fT", value/1000000000000);
|
std::snprintf(buf, k_num_format_maxlen, "%7.2fT", value/1000000000000);
|
||||||
else if (value <= -1000000000)
|
else if (value <= -1000000000)
|
||||||
std::snprintf(buf, 20, "%7.2fB", value/1000000000);
|
std::snprintf(buf, k_num_format_maxlen, "%7.2fB", value/1000000000);
|
||||||
else if (value <= -1000000)
|
else if (value <= -1000000)
|
||||||
std::snprintf(buf, 20, "%7.2fM", value/1000000);
|
std::snprintf(buf, k_num_format_maxlen, "%7.2fM", value/1000000);
|
||||||
else if (value <= -100000)
|
else if (value <= -100000)
|
||||||
std::snprintf(buf, 20, "%7.2fM", value/100000); // 0.12M
|
std::snprintf(buf, k_num_format_maxlen, "%7.2fM", value/100000); // 0.12M
|
||||||
else if (value == 0)
|
else if (value == 0)
|
||||||
buf[0]='0';
|
buf[0]='0';
|
||||||
else
|
else
|
||||||
return std::make_unique<StringValueNode>(parsed_value->getStringValue().substr(0, 10));
|
return std::make_unique<StringValueNode>(parsed_value->getStringValue().substr(0, k_num_format_rpad));
|
||||||
// TODO introduce constant for 10
|
|
||||||
std::string s {buf};
|
std::string s {buf};
|
||||||
return std::make_unique<StringValueNode>(string_padd(s.erase(s.find_last_not_of(' ')+1), 10, ' ', false));
|
return std::make_unique<StringValueNode>(string_padd(s.erase(s.find_last_not_of(' ') + 1), k_num_format_rpad, ' ', false));
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<StringValueNode>(parsed_value->getStringValue());
|
return std::make_unique<StringValueNode>(parsed_value->getStringValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user