#include "qVar.h"
#include "qEval.h"
qVar variables;
qEval evaluator;
char* concat(const char* input) {
if (input == NULL) return NULL; // Return if input is NULL
char* result = (char*)malloc(sizeof(char) * 16); // Initial buffer size
if (result == NULL) return NULL; // Return if memory allocation failed
int resultSize = 16;
int resultIndex = 0;
bool inQuotes = false;
bool escapeNext = false; // To handle escaped characters within quotes
bool skipNextPlus = false; // To skip '+' right after quotes
for (int i = 0; input[i] != '\0'; ++i) {
char current = input[i];
if (current == '\\' && inQuotes) { // Handle escape character only within quotes
escapeNext = true;
continue;
}
if (current == '"' && !escapeNext) { // Toggle quote state unless it's escaped
inQuotes = !inQuotes;
skipNextPlus = true; // Enable skipping '+' after ending quotes
continue;
}
if (inQuotes) {
if (escapeNext && current == '"') {
result[resultIndex++] = current; // Include the escaped quote
} else if (!escapeNext) {
result[resultIndex++] = current; // Add current character to result
}
escapeNext = false; // Reset the escape flag after processing
} else {
// Skip '+' if it's the first character after a closing quote
if (current == '+' && skipNextPlus) {
skipNextPlus = false; // Reset flag after skipping
continue;
}
skipNextPlus = false; // Reset flag for other characters
if ((isdigit(current) || current == '-' || current == '.') && (resultIndex > 0 || !skipNextPlus)) {
result[resultIndex++] = current;
}
}
// Ensure enough buffer size, realloc if necessary
if (resultIndex == resultSize - 1) {
resultSize *= 2;
char* temp = (char*)realloc(result, resultSize);
if (temp == NULL) {
free(result);
return NULL;
}
result = temp;
}
}
result[resultIndex] = '\0'; // Null-terminate the string
return result;
}
void parse(const char *str) {
bool inQuotes = false;
int nestingLevel = 0;
size_t startIndex = 0; // Start index of the current token
int blockStart = 0;
int blockEnd = 0;
for (size_t i = 0; str[i] != '\0'; i++) {
char ch = str[i];
if (ch == '\"') {
inQuotes = !inQuotes; // Toggle inQuotes flag
} else if (ch == '{') {
if (nestingLevel == 0) { blockStart = i - startIndex + 1; }
nestingLevel++;
} else if (ch == '}') {
nestingLevel--;
if (nestingLevel == 0) { blockEnd = i - startIndex; }
}
if ((ch == ';' || ch == '}') && nestingLevel == 0 && !inQuotes) {
// Store the token
const size_t tokenSize = i - startIndex + 2; // Include space for the delimiter if it's the detected delimiter
char token[tokenSize];
for (size_t j = 0; j < tokenSize - 1; j++) { // Exclude the delimiter if it's not the detected delimiter
token[j] = str[startIndex + j];
}
if (ch == ';') { // Only add delimiter if it's the detected delimiter
token[tokenSize - 2] = ';'; // Add the delimiter
}
token[tokenSize - 1] = '\0'; // Null terminator
if (ch == '}' || ch == ';') { parseLine(token, blockStart, blockEnd); } // parse line without block
// Update startIndex for the next token
startIndex = i + 1;
blockStart = 0; blockEnd = 0;
}
}
}
void parseKeyword(const char *line, int blockStart, int blockEnd) {
// Extract keyword
const char *start = line;
while (*start == ' ') { start++; }
const char *end = start;
while (*end != '\0' && ((*end >= 'a' && *end <= 'z') || (*end >= 'A' && *end <= 'Z') || (*end >= '0' && *end <= '9') || *end == '.')) { end++; }
size_t keywordLen = end - start;
char keyword[keywordLen + 1]; // +1 for null terminator
for (size_t i = 0; i < keywordLen; i++) {
keyword[i] = start[i];
}
keyword[keywordLen] = '\0'; // Null-terminate the string
// Extract block
size_t blockLen = blockEnd - blockStart;
char block[blockLen + 1];
if (blockLen > 0) {
for (size_t i = 0; i < blockLen; i++) {
block[i] = line[blockStart + i];
}
}
block[blockLen] = '\0'; // Null-terminate the string
// Extract parameters
const char *paramStart = strchr(line, '(');
//const char *paramEnd = strchr(line, ')');
const char * paramEnd; // Parameter end
if ( blockStart != NULL ) { paramEnd = strrchrrBefore(line, ')', blockStart); }
else { paramEnd = strrchr(line, ')'); }
size_t len = 0;
if (paramStart && paramEnd && paramStart < paramEnd) {
len = paramEnd - paramStart - 1;
}
char parameters[len + 1]; // +1 for null terminator
if (len > 0) {
for (size_t i = 0; i < len; i++) {
parameters[i] = paramStart[i + 1];
}
}
parameters[len] = '\0'; // Null-terminate the string
// Handle internal keywords
if (strcmp(keyword, "if") == 0) {
//parseIf(line, blockStart, blockEnd);
} else if (strcmp(keyword, "else") == 0) {
//parseElse(line, blockStart, blockEnd);
} else if (strcmp(keyword, "while") == 0) {
//parseWhile(line, blockStart, blockEnd);
} else if (strcmp(keyword, "for") == 0) {
//parseFor(line, blockStart, blockEnd);
} else if (strcmp(keyword, "print") == 0) {
keywordPrint(parameters);
}
//if ( strcmp(keyword, "print") == 0 ) { keywordPrint(parameters); }
//Serial.println("[" + String(keyword) + "][" +String(parameters)+"]["+String(block)+"]");
}
const char *strrchrrBefore(const char *array, char target, int position) {
const char *lastOccurrence = NULL;
// Iterate backward from the specified position
for (int i = position - 1; i >= 0; i--) {
if (array[i] == target) {
// Store the address of the last occurrence of the target character
lastOccurrence = &array[i];
return lastOccurrence; // Return the address of the last occurrence
}
}
return NULL; // Return NULL if not found
}
// Function to split a character array based on a delimiter
// Delimiter is assumed to be a single character
// Ignores delimiters within quoted sections and escapes quotes within quotes
int split(char* input, char** output, char delimiter, int max_splits) {
int split_count = 0;
bool in_quotes = false;
char* ptr = input;
output[split_count++] = ptr; // First substring always starts from the beginning
while (*ptr != '\0' && split_count < max_splits) {
if (*ptr == '\"') {
in_quotes = !in_quotes; // Toggle quoted section status
}
if (*ptr == delimiter && !in_quotes) {
*ptr = '\0'; // Replace delimiter with null terminator
output[split_count++] = ptr + 1; // Next substring starts after the delimiter
}
ptr++;
}
return split_count; // Return the number of splits
}
void keywordPrint ( const char * parameters ) {
Serial.println(parameters);
}
void parseLine(const char *line, int blockStart, int blockEnd) {
// A line should either start with a keyword or a variable assignment, we need to detect which we are dealing with here
const char *equal = strchr(line, '=');
const char *parenOpen = strchr(line, '(');
const char *parenClose = strchr(line, ')');
const char *quote = strchr(line, '"');
// Variable assignment will meet these criteria
// 1. Contains equal sign
// 2. if parenthesis exist, equal sign preceeds it
// 3. if quote exist, equal sign preceeds it
if (equal && (!parenOpen || parenOpen > equal) && (!quote || quote > equal)) {
parseAssignment(line);
} else {
// Try and parse a keyword
parseKeyword(line, blockStart, blockEnd);
}
}
void parseAssignment(const char *line) {
// Implementation of parseAssignment
// Extract target
const char *start = line;
while (*start == ' ') { start++; }
const char *end = start;
while (*end != '\0' && ((*end >= 'a' && *end <= 'z') || (*end >= 'A' && *end <= 'Z') || (*end >= '0' && *end <= '9') || *end == '.')) { end++; }
size_t targetLen = end - start;
char target[targetLen + 1]; // +1 for null terminator
for (size_t i = 0; i < targetLen; i++) {
target[i] = start[i];
}
target[targetLen] = '\0'; // Null-terminate the string
// Extract assignment
const char *assignmentStart = strchr(line, '=')+1;
const char *assignmentEnd = strchr(line, ';');
size_t len = 0;
if (assignmentStart && assignmentEnd && assignmentStart < assignmentEnd) {
len = assignmentEnd - assignmentStart - 1;
}
bool assignmentType = 0; // 0 is numeric, 1 is alphanumeric
char assignment[len + 1]; // +1 for null terminator
if (len > 0) {
for (size_t i = 0; i < len; i++) {
assignment[i] = assignmentStart[i + 1];
if ( assignment[i] == '"' || assignment[i] == '\'' ) { assignmentType = 1; }
}
}
assignment[len] = '\0'; // Null-terminate the string
// char * assignmentr = variables.replaceVariablesWithValues(assignment);
//Serial.println(assignmentr);
if ( assignmentType == 0 ) { // perform numeric assignment
char* result = evaluator.eval(assignment);
// double r = evaluator.eval(assignment);
// char* result = new char[20]; // assuming the maximum length of the result is 20 characters
// dtostrf(r, 1, 2, result);
//variables.stripTrailingZeros(result);
variables.set(target, result);
free(result);
} else { // perform alphanumeric assignments
char * result = concat(assignment);
//stripUnescapedQuotes(assignment);
variables.set(target, result);
free(result);
}
//Serial.println(assignmentType);
//Serial.println(assignment);
//free(assignmentr);
}
// Function to strip all unescaped single and double quotes from a char array
void stripUnescapedQuotes(char* str) {
int writeIndex = 0; // Index to write to
bool escaped = false; // Flag to track whether the current character is escaped
for (int readIndex = 0; str[readIndex] != '\0'; ++readIndex) {
char current = str[readIndex];
if (escaped) {
// Previous character was a backslash
escaped = false; // Reset escaped status
str[writeIndex++] = current; // Always write the escaped character
} else if (current == '\\') {
// Current character is a backslash, possibly escaping the next character
escaped = true; // Set escaped flag for the next iteration
str[writeIndex++] = current; // Write the backslash to the output
} else if (current == '\'' || current == '\"') {
// It's an unescaped quote, skip it
continue;
} else {
// Regular character, just copy
str[writeIndex++] = current;
}
}
str[writeIndex] = '\0'; // Null-terminate the modified string
}
void setup() {
Serial.begin(500000);
Serial.println("Heap: " + String(ESP.getFreeHeap()));
const char testString[] = "var1 = \"this is a test\" + \" concatenation!\";"
"var2 = \"another test!\";"
"var2 = 21999;"
"var3 = 12.34*54123.5123;"
"function(test) { Serial.println(1); fun2() { test(); } }"
"if ( a > b ) { Serial.print(a); }"
"keyword_text ( \"this is some input\", 4, 20 );"
"Serial.print (\"This is a test, cool eh?\");"
"runMe { test; }"
"again(test,(test+test), 123);";
unsigned long start = micros();
parse(testString);
Serial.println(String(micros() - start));
Serial.println("Heap: " + String(ESP.getFreeHeap()));
variables.list();
//const char *t = variables.get("var3");
std::string t = std::string(variables.get("var3"));
Serial.println(t.c_str());
}
void loop() {
// Nothing here
}