diff --git a/.gitignore b/.gitignore index 4e7c853..b491bbd 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,4 @@ testing build .vscode *-bin -Icon -test/* -test +Icon diff --git a/src/aux/aux_c_format.cpp b/src/aux/aux_c_format.cpp index dc1dfa7..14b2cfb 100644 --- a/src/aux/aux_c_format.cpp +++ b/src/aux/aux_c_format.cpp @@ -1,388 +1,329 @@ /* This file contains auxiliary functions that turn LDPL identifiers / * expressions into C / C++ identifiers / expressions */ - #include "../ldpl.h" - // +---------------------------------------------+ // | TODO: comment and format this file properly | // +---------------------------------------------+ // Given a full variable (with accesses and everything, like foo:0:'hi there' or // bar) returns the C++ representation of said variable in order to be accessed. -string get_c_variable(compiler_state &state, string &variable) -{ - // We want to get two things in order to create the correct C++ representation - // of a 'full variable': the variable name and all the indexess we are trying - // to access that correspond to this particular variable (because we could - // have foo number list list and bar number list and try to access - // foo:bar:0:1. In this case, we would be interested in getting 'foo', 'bar:0' - // and '1'. - string var_name; - vector indexes; - // We use the split_vector function to get these values into our variables. - split_vector(variable, var_name, indexes, state); - // We 'fix' the variable name, turning all characters not accepted by C++ into - // LDPL codes that C++ does accept. - var_name = fix_identifier(var_name, true, state); - // If split_vector didn't return any indexes, then we are dealing with a - // scalar variable. We just return the C++ variable name and we are done. - if (indexes.empty()) - return var_name; - // If our indexes vector is not empty, however, we recreate the correct C++ - // container access, with one dimension for each value in our indexes vector. - for (size_t i = 0; i < indexes.size(); ++i) - var_name += '[' + get_c_expression(state, indexes[i]) + ']'; - // Once we are done, we return the variable name. - return var_name; +string get_c_variable(compiler_state &state, string &variable) { + // We want to get two things in order to create the correct C++ representation + // of a 'full variable': the variable name and all the indexess we are trying + // to access that correspond to this particular variable (because we could + // have foo number list list and bar number list and try to access + // foo:bar:0:1. In this case, we would be interested in getting 'foo', 'bar:0' + // and '1'. + string var_name; + vector indexes; + // We use the split_vector function to get these values into our variables. + split_vector(variable, var_name, indexes, state); + // We 'fix' the variable name, turning all characters not accepted by C++ into + // LDPL codes that C++ does accept. + var_name = fix_identifier(var_name, true, state); + // If split_vector didn't return any indexes, then we are dealing with a + // scalar variable. We just return the C++ variable name and we are done. + if (indexes.empty()) return var_name; + // If our indexes vector is not empty, however, we recreate the correct C++ + // container access, with one dimension for each value in our indexes vector. + for (size_t i = 0; i < indexes.size(); ++i) + var_name += '[' + get_c_expression(state, indexes[i]) + ']'; + // Once we are done, we return the variable name. + return var_name; } -string get_c_expression(compiler_state &state, string &expression) -{ - if (is_scalar_variable(expression, state) || - variable_exists(expression, state)) - return get_c_variable(state, expression); - is_number(expression); // We fix the expression should it be a number literal - return expression; +string get_c_expression(compiler_state &state, string &expression) { + if (is_scalar_variable(expression, state) || + variable_exists(expression, state)) + return get_c_variable(state, expression); + is_number(expression); // We fix the expression should it be a number literal + return expression; } // text must be a TEXT or a TEXT variable -string get_c_char_array(compiler_state &state, string &text) -{ - if (is_txt_var(text, state)) - return get_c_variable(state, text) + ".c_str()"; - return text; +string get_c_char_array(compiler_state &state, string &text) { + if (is_txt_var(text, state)) + return get_c_variable(state, text) + ".str_rep().c_str()"; + return text; } -string get_c_string(compiler_state &state, string &expression) -{ - string c_expression = get_c_expression(state, expression); - if (is_num_expr(expression, state)) - return "to_ldpl_string(" + c_expression + ")"; - return c_expression; +string get_c_string(compiler_state &state, string &expression) { + string c_expression = get_c_expression(state, expression); + if (is_num_expr(expression, state)) + return "to_ldpl_string(" + c_expression + ")"; + return c_expression; } -string get_c_number(compiler_state &state, string &expression) -{ - string c_expression = get_c_expression(state, expression); - if (is_txt_expr(expression, state)) - return "to_number(" + c_expression + ")"; - return c_expression; +string get_c_number(compiler_state &state, string &expression) { + string c_expression = get_c_expression(state, expression); + if (is_txt_expr(expression, state)) return "to_number(" + c_expression + ")"; + return c_expression; } // Returns [ERROR] if invalid condtion, otherwise returns C++ condition -string get_c_condition(compiler_state &state, vector tokens) -{ - unsigned int ct = 0; - string condition = get_c_condition(state, tokens, ct); - if (ct < tokens.size()) - return "[ERROR]"; - return condition; +string get_c_condition(compiler_state &state, vector tokens) { + unsigned int ct = 0; + string condition = get_c_condition(state, tokens, ct); + if (ct < tokens.size()) return "[ERROR]"; + return condition; } -#define MATCH(x) \ - if (ct < tokens.size() && tokens[ct] == x) \ - ct++; \ - else \ - return "[ERROR]"; +#define MATCH(x) \ + if (ct < tokens.size() && tokens[ct] == x) \ + ct++; \ + else \ + return "[ERROR]"; string get_c_condition(compiler_state &state, vector tokens, - unsigned int &ct) -{ - if (ct >= tokens.size()) + unsigned int &ct) { + if (ct >= tokens.size()) return "[ERROR]"; + string condition; + if (tokens[ct] == "(") { + MATCH("("); + condition = get_c_condition(state, tokens, ct); + if (condition == "[ERROR]") return condition; + MATCH(")"); + condition = "(" + condition + ")"; + } else { + string first_value = tokens[ct]; + string second_value; + string rel_op; + ct++; // We validate the token after we get the second value + if (tokens[ct] == "IS") { + MATCH("IS"); + if (tokens[ct] == "EQUAL") { + MATCH("EQUAL"); + MATCH("TO"); + rel_op = "EQUAL TO"; + } else if (tokens[ct] == "NOT") { + MATCH("NOT"); + MATCH("EQUAL"); + MATCH("TO"); + rel_op = "NOT EQUAL TO"; + } else if (tokens[ct] == "GREATER") { + MATCH("GREATER"); + MATCH("THAN"); + if (ct + 1 < tokens.size() && tokens[ct + 1] == "EQUAL") { + // We check the next token instead of the curent one + // because "OR" could be a variable after "GREATER THAN" + MATCH("OR"); + MATCH("EQUAL"); + MATCH("TO"); + rel_op = "GREATER THAN OR EQUAL TO"; + } else { + rel_op = "GREATER THAN"; + } + } else if (tokens[ct] == "LESS") { + MATCH("LESS"); + MATCH("THAN"); + if (ct + 1 < tokens.size() && tokens[ct + 1] == "EQUAL") { + // We check the next token instead of the curent one + // because "OR" could be a variable after "LESS THAN" + MATCH("OR"); + MATCH("EQUAL"); + MATCH("TO"); + rel_op = "LESS THAN OR EQUAL TO"; + } else { + rel_op = "LESS THAN"; + } + } else { return "[ERROR]"; - string condition; - if (tokens[ct] == "(") - { - MATCH("("); - condition = get_c_condition(state, tokens, ct); - if (condition == "[ERROR]") - return condition; - MATCH(")"); - condition = "(" + condition + ")"; - } - else - { - string first_value = tokens[ct]; - string second_value; - string rel_op; - ct++; // We validate the token after we get the second value - if (tokens[ct] == "IS") - { - MATCH("IS"); - if (tokens[ct] == "EQUAL") - { - MATCH("EQUAL"); - MATCH("TO"); - rel_op = "EQUAL TO"; - } - else if (tokens[ct] == "NOT") - { - MATCH("NOT"); - MATCH("EQUAL"); - MATCH("TO"); - rel_op = "NOT EQUAL TO"; - } - else if (tokens[ct] == "GREATER") - { - MATCH("GREATER"); - MATCH("THAN"); - if (ct + 1 < tokens.size() && tokens[ct + 1] == "EQUAL") - { - // We check the next token instead of the curent one - // because "OR" could be a variable after "GREATER THAN" - MATCH("OR"); - MATCH("EQUAL"); - MATCH("TO"); - rel_op = "GREATER THAN OR EQUAL TO"; - } - else - { - rel_op = "GREATER THAN"; - } - } - else if (tokens[ct] == "LESS") - { - MATCH("LESS"); - MATCH("THAN"); - if (ct + 1 < tokens.size() && tokens[ct + 1] == "EQUAL") - { - // We check the next token instead of the curent one - // because "OR" could be a variable after "LESS THAN" - MATCH("OR"); - MATCH("EQUAL"); - MATCH("TO"); - rel_op = "LESS THAN OR EQUAL TO"; - } - else - { - rel_op = "LESS THAN"; - } - } - else - { - return "[ERROR]"; - } + } - second_value = tokens[ct]; - ++ct; + second_value = tokens[ct]; + ++ct; - string type; - if (is_num_expr(first_value, state) && is_num_expr(second_value, state)) - type = "NUMBER"; - else if (is_txt_expr(first_value, state) && - is_txt_expr(second_value, state)) - type = "TEXT"; - else if (is_num_map(first_value, state) && - is_num_map(second_value, state)) - type = "NUMBER MAP"; - else if (is_txt_map(first_value, state) && - is_txt_map(second_value, state)) - type = "TEXT MAP"; - else if (is_num_list(first_value, state) && - is_num_list(second_value, state)) - type = "NUMBER LIST"; - else if (is_txt_list(first_value, state) && - is_txt_list(second_value, state)) - type = "TEXT LIST"; - else if (is_list_list(first_value, state) && - is_list_list(second_value, state) && - variable_type(first_value, state) == - variable_type(second_value, state)) - type = "LIST LIST"; - else if (is_map_map(first_value, state) && - is_map_map(second_value, state) && - variable_type(first_value, state) == - variable_type(second_value, state)) - type = "MAP MAP"; - else - return "[ERROR]"; + string type; + if (is_num_expr(first_value, state) && is_num_expr(second_value, state)) + type = "NUMBER"; + else if (is_txt_expr(first_value, state) && + is_txt_expr(second_value, state)) + type = "TEXT"; + else if (is_num_map(first_value, state) && + is_num_map(second_value, state)) + type = "NUMBER MAP"; + else if (is_txt_map(first_value, state) && + is_txt_map(second_value, state)) + type = "TEXT MAP"; + else if (is_num_list(first_value, state) && + is_num_list(second_value, state)) + type = "NUMBER LIST"; + else if (is_txt_list(first_value, state) && + is_txt_list(second_value, state)) + type = "TEXT LIST"; + else if (is_list_list(first_value, state) && + is_list_list(second_value, state) && + variable_type(first_value, state) == + variable_type(second_value, state)) + type = "LIST LIST"; + else if (is_map_map(first_value, state) && + is_map_map(second_value, state) && + variable_type(first_value, state) == + variable_type(second_value, state)) + type = "MAP MAP"; + else + return "[ERROR]"; - first_value = get_c_expression(state, first_value); - second_value = get_c_expression(state, second_value); - if (type == "NUMBER") - { - if (rel_op == "EQUAL TO") - condition = "num_equal(" + first_value + ", " + second_value + ")"; - else if (rel_op == "NOT EQUAL TO") - condition = "!num_equal(" + first_value + ", " + second_value + ")"; - else if (rel_op == "GREATER THAN") - condition = first_value + " > " + second_value; - else if (rel_op == "LESS THAN") - condition = first_value + " < " + second_value; - else if (rel_op == "GREATER THAN OR EQUAL TO") - condition = "(" + first_value + " > " + second_value + - " || num_equal(" + first_value + ", " + second_value + - "))"; - else - condition = "(" + first_value + " < " + second_value + - " || num_equal(" + first_value + ", " + second_value + - "))"; - } - else if (type == "TEXT") - { - if (rel_op == "EQUAL TO") - condition = "str_cmp(" + first_value + ", " + second_value + ") == 0"; - else if (rel_op == "NOT EQUAL TO") - condition = "str_cmp(" + first_value + ", " + second_value + ") != 0"; - else if (rel_op == "GREATER THAN") - condition = "str_cmp(" + first_value + ", " + second_value + ") > 0"; - else if (rel_op == "LESS THAN") - condition = "str_cmp(" + first_value + ", " + second_value + ") < 0"; - else if (rel_op == "GREATER THAN OR EQUAL TO") - condition = "str_cmp(" + first_value + ", " + second_value + ") >= 0"; - else if (rel_op == "LESS THAN OR EQUAL TO") - condition = "str_cmp(" + first_value + ", " + second_value + ") <= 0"; - else - return "[ERROR]"; - } - else - { - first_value += ".inner_collection"; - second_value += ".inner_collection"; - if (rel_op == "EQUAL TO") - condition = first_value + " == " + second_value; - else if (rel_op == "NOT EQUAL TO") - condition = first_value + " != " + second_value; - else // >, >, <= and >= are only valid in NUMBER and TEXT - return "[ERROR]"; - } - } + first_value = get_c_expression(state, first_value); + second_value = get_c_expression(state, second_value); + if (type == "NUMBER") { + if (rel_op == "EQUAL TO") + condition = "num_equal(" + first_value + ", " + second_value + ")"; + else if (rel_op == "NOT EQUAL TO") + condition = "!num_equal(" + first_value + ", " + second_value + ")"; + else if (rel_op == "GREATER THAN") + condition = first_value + " > " + second_value; + else if (rel_op == "LESS THAN") + condition = first_value + " < " + second_value; + else if (rel_op == "GREATER THAN OR EQUAL TO") + condition = "(" + first_value + " > " + second_value + + " || num_equal(" + first_value + ", " + second_value + + "))"; + else + condition = "(" + first_value + " < " + second_value + + " || num_equal(" + first_value + ", " + second_value + + "))"; + } else if (type == "TEXT") { + if (rel_op == "EQUAL TO") + condition = "str_cmp(" + first_value + ", " + second_value + ") == 0"; + else if (rel_op == "NOT EQUAL TO") + condition = "str_cmp(" + first_value + ", " + second_value + ") != 0"; + else if (rel_op == "GREATER THAN") + condition = "str_cmp(" + first_value + ", " + second_value + ") > 0"; + else if (rel_op == "LESS THAN") + condition = "str_cmp(" + first_value + ", " + second_value + ") < 0"; + else if (rel_op == "GREATER THAN OR EQUAL TO") + condition = "str_cmp(" + first_value + ", " + second_value + ") >= 0"; + else if (rel_op == "LESS THAN OR EQUAL TO") + condition = "str_cmp(" + first_value + ", " + second_value + ") <= 0"; else - { - if (tokens[ct] == "IN") - { - MATCH("IN"); - rel_op = "IN"; - } - else if (tokens[ct] == "NOT") - { - MATCH("NOT"); - MATCH("IN"); - rel_op = "NOT IN"; - } + return "[ERROR]"; + } else { + first_value += ".inner_collection"; + second_value += ".inner_collection"; + if (rel_op == "EQUAL TO") + condition = first_value + " == " + second_value; + else if (rel_op == "NOT EQUAL TO") + condition = first_value + " != " + second_value; + else // >, >, <= and >= are only valid in NUMBER and TEXT + return "[ERROR]"; + } + } else { + if (tokens[ct] == "IN") { + MATCH("IN"); + rel_op = "IN"; + } else if (tokens[ct] == "NOT") { + MATCH("NOT"); + MATCH("IN"); + rel_op = "NOT IN"; + } - second_value = tokens[ct]; - ++ct; - string type; - if (is_num_expr(first_value, state) && is_num_list(second_value, state)) - type = "NUM-IN-NUM-LIST"; - else if (is_num_expr(first_value, state) && - is_txt_list(second_value, state)) - type = "NUM-IN-TEXT-LIST"; - else if (is_txt_expr(first_value, state) && - is_num_list(second_value, state)) - type = "TEXT-IN-NUM-LIST"; - else if (is_txt_expr(first_value, state) && - is_txt_list(second_value, state)) - type = "TEXT-IN-TEXT-LIST"; - else if (is_num_expr(first_value, state) && is_map(second_value, state)) - type = "NUM-IN-MAP"; - else if (is_string(first_value) && is_map(second_value, state)) - type = "STR-IN-MAP"; - else if (is_txt_var(first_value, state) && is_map(second_value, state)) - type = "TEXTVAR-IN-MAP"; - else - return "[ERROR]"; + second_value = tokens[ct]; + ++ct; + string type; + if (is_num_expr(first_value, state) && is_num_list(second_value, state)) + type = "NUM-IN-NUM-LIST"; + else if (is_num_expr(first_value, state) && + is_txt_list(second_value, state)) + type = "NUM-IN-TEXT-LIST"; + else if (is_txt_expr(first_value, state) && + is_num_list(second_value, state)) + type = "TEXT-IN-NUM-LIST"; + else if (is_txt_expr(first_value, state) && + is_txt_list(second_value, state)) + type = "TEXT-IN-TEXT-LIST"; + else if (is_num_expr(first_value, state) && is_map(second_value, state)) + type = "NUM-IN-MAP"; + else if (is_string(first_value) && is_map(second_value, state)) + type = "STR-IN-MAP"; + else if (is_txt_var(first_value, state) && is_map(second_value, state)) + type = "TEXTVAR-IN-MAP"; + else + return "[ERROR]"; - first_value = get_c_expression(state, first_value); - second_value = get_c_expression(state, second_value); - if (type == "NUM-IN-NUM-LIST") - { - if (rel_op == "IN") - condition = "find(" + second_value + ".inner_collection.begin(), " + - second_value + ".inner_collection.end(), (ldpl_number) " + - first_value + ") != " + second_value + - ".inner_collection.end()"; - else if (rel_op == "NOT IN") - condition = "find(" + second_value + ".inner_collection.begin(), " + - second_value + ".inner_collection.end(), (ldpl_number) " + - first_value + ") == " + second_value + - ".inner_collection.end()"; - } - else if (type == "TEXT-IN-NUM-LIST") - { - if (rel_op == "IN") - condition = "find(" + second_value + ".inner_collection.begin(), " + - second_value + ".inner_collection.end(), to_number(" + - first_value + ")) != " + second_value + - ".inner_collection.end()"; - else if (rel_op == "NOT IN") - condition = "find(" + second_value + ".inner_collection.begin(), " + - second_value + ".inner_collection.end(), to_number(" + - first_value + ")) == " + second_value + - ".inner_collection.end()"; - } - else if (type == "NUM-IN-TEXT-LIST") - { - if (rel_op == "IN") - condition = "find(" + second_value + ".inner_collection.begin(), " + - second_value + - ".inner_collection.end(), to_ldpl_string(" + first_value + - ")) != " + second_value + ".inner_collection.end()"; - else if (rel_op == "NOT IN") - condition = "find(" + second_value + ".inner_collection.begin(), " + - second_value + - ".inner_collection.end(), to_ldpl_string(" + first_value + - ")) == " + second_value + ".inner_collection.end()"; - } - else if (type == "TEXT-IN-TEXT-LIST") - { - if (rel_op == "IN") - condition = "find(" + second_value + ".inner_collection.begin(), " + - second_value + ".inner_collection.end(), " + first_value + - ") != " + second_value + ".inner_collection.end()"; - else if (rel_op == "NOT IN") - condition = "find(" + second_value + ".inner_collection.begin(), " + - second_value + ".inner_collection.end(), " + first_value + - ") == " + second_value + ".inner_collection.end()"; - } - else if (type == "NUM-IN-MAP") - { - if (rel_op == "IN") - condition = second_value + ".inner_collection.find(to_ldpl_string(" + - first_value + ")) != " + second_value + - ".inner_collection.end()"; - else if (rel_op == "NOT IN") - condition = second_value + ".inner_collection.find(to_ldpl_string(" + - first_value + ")) == " + second_value + - ".inner_collection.end()"; - } - else if (type == "STR-IN-MAP") - { - if (rel_op == "IN") - condition = second_value + ".inner_collection.find(" + first_value + - ") != " + second_value + ".inner_collection.end()"; - else if (rel_op == "NOT IN") - condition = second_value + ".inner_collection.find(" + first_value + - ") == " + second_value + ".inner_collection.end()"; - } - else if (type == "TEXTVAR-IN-MAP") - { - if (rel_op == "IN") - condition = second_value + ".inner_collection.find(" + first_value + - ") != " + second_value + - ".inner_collection.end()"; - else if (rel_op == "NOT IN") - condition = second_value + ".inner_collection.find(" + first_value + - ") == " + second_value + - ".inner_collection.end()"; - } - else - return "[ERROR]"; - } + first_value = get_c_expression(state, first_value); + second_value = get_c_expression(state, second_value); + if (type == "NUM-IN-NUM-LIST") { + if (rel_op == "IN") + condition = "find(" + second_value + ".inner_collection.begin(), " + + second_value + ".inner_collection.end(), (ldpl_number) " + + first_value + ") != " + second_value + + ".inner_collection.end()"; + else if (rel_op == "NOT IN") + condition = "find(" + second_value + ".inner_collection.begin(), " + + second_value + ".inner_collection.end(), (ldpl_number) " + + first_value + ") == " + second_value + + ".inner_collection.end()"; + } else if (type == "TEXT-IN-NUM-LIST") { + if (rel_op == "IN") + condition = "find(" + second_value + ".inner_collection.begin(), " + + second_value + ".inner_collection.end(), to_number(" + + first_value + ")) != " + second_value + + ".inner_collection.end()"; + else if (rel_op == "NOT IN") + condition = "find(" + second_value + ".inner_collection.begin(), " + + second_value + ".inner_collection.end(), to_number(" + + first_value + ")) == " + second_value + + ".inner_collection.end()"; + } else if (type == "NUM-IN-TEXT-LIST") { + if (rel_op == "IN") + condition = "find(" + second_value + ".inner_collection.begin(), " + + second_value + + ".inner_collection.end(), to_ldpl_string(" + first_value + + ")) != " + second_value + ".inner_collection.end()"; + else if (rel_op == "NOT IN") + condition = "find(" + second_value + ".inner_collection.begin(), " + + second_value + + ".inner_collection.end(), to_ldpl_string(" + first_value + + ")) == " + second_value + ".inner_collection.end()"; + } else if (type == "TEXT-IN-TEXT-LIST") { + if (rel_op == "IN") + condition = "find(" + second_value + ".inner_collection.begin(), " + + second_value + ".inner_collection.end(), " + first_value + + ") != " + second_value + ".inner_collection.end()"; + else if (rel_op == "NOT IN") + condition = "find(" + second_value + ".inner_collection.begin(), " + + second_value + ".inner_collection.end(), " + first_value + + ") == " + second_value + ".inner_collection.end()"; + } else if (type == "NUM-IN-MAP") { + if (rel_op == "IN") + condition = second_value + ".inner_collection.find(to_ldpl_string(" + + first_value + ").str_rep()) != " + second_value + + ".inner_collection.end()"; + else if (rel_op == "NOT IN") + condition = second_value + ".inner_collection.find(to_ldpl_string(" + + first_value + ").str_rep()) == " + second_value + + ".inner_collection.end()"; + } else if (type == "STR-IN-MAP") { + if (rel_op == "IN") + condition = second_value + ".inner_collection.find(" + first_value + + ") != " + second_value + ".inner_collection.end()"; + else if (rel_op == "NOT IN") + condition = second_value + ".inner_collection.find(" + first_value + + ") == " + second_value + ".inner_collection.end()"; + } else if (type == "TEXTVAR-IN-MAP") { + if (rel_op == "IN") + condition = second_value + ".inner_collection.find(" + first_value + + ".str_rep()) != " + second_value + + ".inner_collection.end()"; + else if (rel_op == "NOT IN") + condition = second_value + ".inner_collection.find(" + first_value + + ".str_rep()) == " + second_value + + ".inner_collection.end()"; + } else + return "[ERROR]"; + } - if (ct < tokens.size() && (tokens[ct] == "AND" || tokens[ct] == "OR")) - { - if (tokens[ct] == "AND") - condition += " && "; - else if (tokens[ct] == "OR") - condition += " || "; - ++ct; - string next_condition = get_c_condition(state, tokens, ct); - if (next_condition == "[ERROR]") - return next_condition; - condition += next_condition; - } + if (ct < tokens.size() && (tokens[ct] == "AND" || tokens[ct] == "OR")) { + if (tokens[ct] == "AND") + condition += " && "; + else if (tokens[ct] == "OR") + condition += " || "; + ++ct; + string next_condition = get_c_condition(state, tokens, ct); + if (next_condition == "[ERROR]") return next_condition; + condition += next_condition; } - return condition; + } + return condition; } \ No newline at end of file diff --git a/src/aux/aux_code.cpp b/src/aux/aux_code.cpp index 93022e5..548dea1 100644 --- a/src/aux/aux_code.cpp +++ b/src/aux/aux_code.cpp @@ -36,7 +36,7 @@ void add_call_code(string &subprocedure, vector ¶meters, // C++ doen't allow passing literals in reference parameters, we create // vars for them string literal_paramater_var = state.new_literal_parameter_var(); - state.add_code((is_number(parameters[i]) ? "ldpl_number " : "string ") + + state.add_code((is_number(parameters[i]) ? "ldpl_number " : "chText ") + literal_paramater_var + " = " + parameters[i] + ";", state.where); code += literal_paramater_var; diff --git a/src/aux/aux_compile_line.cpp b/src/aux/aux_compile_line.cpp index bd942ed..7d915a2 100644 --- a/src/aux/aux_compile_line.cpp +++ b/src/aux/aux_compile_line.cpp @@ -6,1365 +6,1211 @@ // +---------------------------------------------+ // Compiles line per line -void compile_line(vector &tokens, compiler_state &state) -{ - // include - if (line_like("INCLUDE $string", tokens, state)) - { - if (state.section_state != 0) - badcode( - "you can only use the INCLUDE statement before the DATA and " - "PROCEDURE sections", - state.where); - else - { - string file_to_compile = tokens[1].substr(1, tokens[1].size() - 2); - string separators = "/"; +void compile_line(vector &tokens, compiler_state &state) { + // include + if (line_like("INCLUDE $string", tokens, state)) { + if (state.section_state != 0) + badcode( + "you can only use the INCLUDE statement before the DATA and " + "PROCEDURE sections", + state.where); + else { + string file_to_compile = tokens[1].substr(1, tokens[1].size() - 2); + string separators = "/"; #if defined(_WIN32) - separators += "\\"; + separators += "\\"; #endif - size_t last_sep = state.where.current_file.find_last_of(separators); - code_location old_location = state.where; - if (last_sep != string::npos) - file_to_compile = state.where.current_file.substr(0, last_sep) + "/" + - file_to_compile; - load_and_compile(file_to_compile, state); - state.section_state = 0; - state.where = old_location; - } - return; - } + size_t last_sep = state.where.current_file.find_last_of(separators); + code_location old_location = state.where; + if (last_sep != string::npos) + file_to_compile = state.where.current_file.substr(0, last_sep) + "/" + + file_to_compile; + load_and_compile(file_to_compile, state); + state.section_state = 0; + state.where = old_location; + } + return; + } - // extension (INCLUDE but for c++ extensions) - if (line_like("EXTENSION $string", tokens, state)) - { - if (state.section_state != 0) - badcode( - "you can only use the EXTENSION statement before the DATA and " - "PROCEDURE sections", - state.where); - else - { - string file_to_add = tokens[1].substr(1, tokens[1].size() - 2); - string separators = "/"; + // extension (INCLUDE but for c++ extensions) + if (line_like("EXTENSION $string", tokens, state)) { + if (state.section_state != 0) + badcode( + "you can only use the EXTENSION statement before the DATA and " + "PROCEDURE sections", + state.where); + else { + string file_to_add = tokens[1].substr(1, tokens[1].size() - 2); + string separators = "/"; #if defined(_WIN32) - separators += "\\"; + separators += "\\"; #endif - size_t last_sep = state.where.current_file.find_last_of(separators); - if (last_sep != string::npos) - file_to_add = - state.where.current_file.substr(0, last_sep) + "/" + file_to_add; - extensions.push_back(file_to_add); - } - return; - } + size_t last_sep = state.where.current_file.find_last_of(separators); + if (last_sep != string::npos) + file_to_add = + state.where.current_file.substr(0, last_sep) + "/" + file_to_add; + extensions.push_back(file_to_add); + } + return; + } - // extension flags (for the C++ compiler) - if (line_like("FLAG $string", tokens, state)) - { - if (state.section_state != 0) - badcode( - "you can only use the FLAG statement before the DATA and PROCEDURE " - "sections", - state.where); - else - { - string flag = tokens[1].substr(1, tokens[1].size() - 2); - extension_flags.push_back(flag); - } - return; - } - // os-specific extension flags - if (line_like("FLAG $name $string", tokens, state)) - { - if (state.section_state != 0) - badcode( - "you can only use the FLAG statement before the DATA and PROCEDURE " - "sections", - state.where); - else - { - if (tokens[1] == current_os()) - { - string flag = tokens[2].substr(1, tokens[2].size() - 2); - extension_flags.push_back(flag); - } - } - return; - } + // extension flags (for the C++ compiler) + if (line_like("FLAG $string", tokens, state)) { + if (state.section_state != 0) + badcode( + "you can only use the FLAG statement before the DATA and PROCEDURE " + "sections", + state.where); + else { + string flag = tokens[1].substr(1, tokens[1].size() - 2); + extension_flags.push_back(flag); + } + return; + } + // os-specific extension flags + if (line_like("FLAG $name $string", tokens, state)) { + if (state.section_state != 0) + badcode( + "you can only use the FLAG statement before the DATA and PROCEDURE " + "sections", + state.where); + else { + if (tokens[1] == current_os()) { + string flag = tokens[2].substr(1, tokens[2].size() - 2); + extension_flags.push_back(flag); + } + } + return; + } - // Sections - if (line_like("DATA:", tokens, state)) - { - if (state.section_state == 1) - badcode("Duplicate DATA section declaration", state.where); - if (state.section_state >= 2) - badcode("DATA section declaration within PROCEDURE section", state.where); - state.section_state = 1; - return; - } - if (line_like("PROCEDURE:", tokens, state)) - { - if (state.section_state == 2) - badcode("Duplicate PROCEDURE section declaration", state.where); - if (state.current_subprocedure != "" && state.section_state >= 3) - open_subprocedure_code(state); - state.section_state = 2; - return; - } - if (line_like("PARAMETERS:", tokens, state)) - { - if (state.current_subprocedure == "") - badcode("PARAMETERS section outside subprocedure", state.where); - if (state.section_state == 4) - badcode("Duplicate PARAMETERS section declaration", state.where); - if (state.section_state == 1) - badcode("PARAMETERS section declaration within LOCAL DATA section", - state.where); - if (state.section_state == 2) - badcode("PARAMETERS section declaration within PROCEDURE section", - state.where); - state.section_state = 4; - return; - } - if (line_like("LOCAL DATA:", tokens, state)) - { - if (state.current_subprocedure == "") - badcode("LOCAL DATA section outside subprocedure", state.where); - if (state.section_state == 1) - badcode("Duplicate LOCAL DATA section declaration", state.where); - if (state.section_state == 2) - badcode("LOCAL DATA section declaration within PROCEDURE section", - state.where); - state.section_state = 1; - open_subprocedure_code(state); - return; - } + // Sections + if (line_like("DATA:", tokens, state)) { + if (state.section_state == 1) + badcode("Duplicate DATA section declaration", state.where); + if (state.section_state >= 2) + badcode("DATA section declaration within PROCEDURE section", state.where); + state.section_state = 1; + return; + } + if (line_like("PROCEDURE:", tokens, state)) { + if (state.section_state == 2) + badcode("Duplicate PROCEDURE section declaration", state.where); + if (state.current_subprocedure != "" && state.section_state >= 3) + open_subprocedure_code(state); + state.section_state = 2; + return; + } + if (line_like("PARAMETERS:", tokens, state)) { + if (state.current_subprocedure == "") + badcode("PARAMETERS section outside subprocedure", state.where); + if (state.section_state == 4) + badcode("Duplicate PARAMETERS section declaration", state.where); + if (state.section_state == 1) + badcode("PARAMETERS section declaration within LOCAL DATA section", + state.where); + if (state.section_state == 2) + badcode("PARAMETERS section declaration within PROCEDURE section", + state.where); + state.section_state = 4; + return; + } + if (line_like("LOCAL DATA:", tokens, state)) { + if (state.current_subprocedure == "") + badcode("LOCAL DATA section outside subprocedure", state.where); + if (state.section_state == 1) + badcode("Duplicate LOCAL DATA section declaration", state.where); + if (state.section_state == 2) + badcode("LOCAL DATA section declaration within PROCEDURE section", + state.where); + state.section_state = 1; + open_subprocedure_code(state); + return; + } - // Variable Declaration - if (line_like("$name IS $anything", tokens, - state)) // If it's a variable declaration - { - string extern_keyword = ""; // C++ extern keyword to prepend to the type - // (empty if not EXTERNAL) - vector type_number; // All data types in LDPL have a list of - // numbers that represent its type - string assign_default; // Default Value to asign to the variable - size_t i = - 2; // i is used to check all the words after 'IS' and thus starts in 2. - bool valid_type = true; // Used to check if it's a valid tipe - if (tokens[i] == "EXTERNAL" && - state.current_subprocedure == - "") - { // EXTERNAL is only valid in DATA section (not in LOCAL DATA) - state.externals[tokens[0]] = - true; // Add it to the list of external variables - extern_keyword = "extern "; // Set the prepended keyword to 'extern' - ++i; // Check the next word. - } - if (tokens[i] == "NUMBER") - { // If it's a number... - type_number.push_back(1); // Then the type number is 1 - if (extern_keyword == "") - assign_default = " = 0"; // if its not an external variable, set a - // default value for it. - } - else if (tokens[i] == - "TEXT") - { // If we are dealing with a text variable... - type_number.push_back(2); // The type number is 2 - if (extern_keyword == "") - assign_default = " = \"\""; // And if it's not external, we set it to a - // default value. - } - else if ((tokens[i] == "MAP" || tokens[i] == "LIST") && - tokens.size() > i && tokens[i + 1] == "OF") - { - // nested 'of' syntax, ex: map of list of number - while (valid_type && i < tokens.size()) - { - if (i % 2 == 1) - { - if (tokens[i] != "OF") - valid_type = false; - } - else if (i % 2 == 0) - { - if (tokens[i] == "MAP" || (tokens[i] == "MAPS" && i > 0)) - { - type_number.push_back(4); - } - else if (tokens[i] == "LIST" || (tokens[i] == "LISTS" && i > 0)) - { - type_number.push_back(3); - } - else if (tokens.size() - 1 > i) - { - // text and number must be the final type listed - valid_type = false; - } - else if (tokens[i] == "TEXT" || tokens[i] == "TEXTS") - { - type_number.push_back(2); - } - else if (tokens[i] == "NUMBER" || tokens[i] == "NUMBERS") - { - type_number.push_back(1); - } - else - { - valid_type = false; - } - } - else - { - valid_type = false; - } - ++i; - } - reverse(begin(type_number), end(type_number)); + // Variable Declaration + if (line_like("$name IS $anything", tokens, + state)) // If it's a variable declaration + { + string extern_keyword = ""; // C++ extern keyword to prepend to the type + // (empty if not EXTERNAL) + vector type_number; // All data types in LDPL have a list of + // numbers that represent its type + string assign_default; // Default Value to asign to the variable + size_t i = + 2; // i is used to check all the words after 'IS' and thus starts in 2. + bool valid_type = true; // Used to check if it's a valid tipe + if (tokens[i] == "EXTERNAL" && + state.current_subprocedure == + "") { // EXTERNAL is only valid in DATA section (not in LOCAL DATA) + state.externals[tokens[0]] = + true; // Add it to the list of external variables + extern_keyword = "extern "; // Set the prepended keyword to 'extern' + ++i; // Check the next word. + } + if (tokens[i] == "NUMBER") { // If it's a number... + type_number.push_back(1); // Then the type number is 1 + if (extern_keyword == "") + assign_default = " = 0"; // if its not an external variable, set a + // default value for it. + } else if (tokens[i] == + "TEXT") { // If we are dealing with a text variable... + type_number.push_back(2); // The type number is 2 + if (extern_keyword == "") + assign_default = " = \"\""; // And if it's not external, we set it to a + // default value. + } else if ((tokens[i] == "MAP" || tokens[i] == "LIST") && + tokens.size() > i && tokens[i + 1] == "OF") { + // nested 'of' syntax, ex: map of list of number + while (valid_type && i < tokens.size()) { + if (i % 2 == 1) { + if (tokens[i] != "OF") valid_type = false; + } else if (i % 2 == 0) { + if (tokens[i] == "MAP" || (tokens[i] == "MAPS" && i > 0)) { + type_number.push_back(4); + } else if (tokens[i] == "LIST" || (tokens[i] == "LISTS" && i > 0)) { + type_number.push_back(3); + } else if (tokens.size() - 1 > i) { + // text and number must be the final type listed + valid_type = false; + } else if (tokens[i] == "TEXT" || tokens[i] == "TEXTS") { + type_number.push_back(2); + } else if (tokens[i] == "NUMBER" || tokens[i] == "NUMBERS") { + type_number.push_back(1); + } else { + valid_type = false; + } + } else { + valid_type = false; } + ++i; + } + reverse(begin(type_number), end(type_number)); + } else { + valid_type = false; // If its not a NUMBER, a TEXT or a collection of + // these data types + } // then it's not a valid LDPL data type. + ++i; // Move to the next keyword. + while (valid_type && + i < tokens.size()) { // If up to this point we got a valid data + // type, we check for containers. + assign_default = + ""; // Collections are not initialized with any default values. + if (tokens[i] == "MAP" || + tokens[i] == "VECTOR") { // If its a MAP (aka VECTOR) + type_number.push_back(4); // We add the MAP type (4) to its type list + } else if (tokens[i] == "LIST") { // If its a LIST + type_number.push_back(3); // We add the LIST type (3) to its type list + } else { // If the container is not a VECTOR nor a MAP + valid_type = false; // then it's not a valid data type. + } + ++i; // Move to the next keyword. + } + if (valid_type && i >= tokens.size() - 1) { + if (state.section_state != 1 && state.section_state != 4) + badcode( + "Variable declaration outside DATA, PARAMETERS or LOCAL DATA " + "section", + state.where); + if (state.variables[state.current_subprocedure].count(tokens[0]) > 0) + badcode("Duplicate declaration for variable \"" + tokens[0] + "\"", + state.where); + state.variables[state.current_subprocedure][tokens[0]] = type_number; + if (state.section_state == 1) { // DATA or LOCAL DATA + string identifier = fix_identifier(tokens[0], true, state); + string type = state.get_c_type(type_number); + string code = + extern_keyword + type + " " + identifier + assign_default + ";"; + if (state.current_subprocedure == "") // DATA + state.add_var_code(code); else - { - valid_type = false; // If its not a NUMBER, a TEXT or a collection of - // these data types - } // then it's not a valid LDPL data type. - ++i; // Move to the next keyword. - while (valid_type && - i < tokens.size()) - { // If up to this point we got a valid data - // type, we check for containers. - assign_default = - ""; // Collections are not initialized with any default values. - if (tokens[i] == "MAP" || - tokens[i] == "VECTOR") - { // If its a MAP (aka VECTOR) - type_number.push_back(4); // We add the MAP type (4) to its type list - } - else if (tokens[i] == "LIST") - { // If its a LIST - type_number.push_back(3); // We add the LIST type (3) to its type list - } - else - { // If the container is not a VECTOR nor a MAP - valid_type = false; // then it's not a valid data type. - } - ++i; // Move to the next keyword. - } - if (valid_type && i >= tokens.size() - 1) - { - if (state.section_state != 1 && state.section_state != 4) - badcode( - "Variable declaration outside DATA, PARAMETERS or LOCAL DATA " - "section", - state.where); - if (state.variables[state.current_subprocedure].count(tokens[0]) > 0) - badcode("Duplicate declaration for variable \"" + tokens[0] + "\"", - state.where); - state.variables[state.current_subprocedure][tokens[0]] = type_number; - if (state.section_state == 1) - { // DATA or LOCAL DATA - string identifier = fix_identifier(tokens[0], true, state); - string type = state.get_c_type(type_number); - string code = - extern_keyword + type + " " + identifier + assign_default + ";"; - if (state.current_subprocedure == "") // DATA - state.add_var_code(code); - else - state.add_code(code, state.where); // LOCAL DATA - } - else // PARAMETERS - state.subprocedures[state.current_subprocedure].emplace_back(tokens[0]); - return; - } + state.add_code(code, state.where); // LOCAL DATA + } else // PARAMETERS + state.subprocedures[state.current_subprocedure].emplace_back(tokens[0]); + return; } + } - // SUB-PROCEDURE Declaration - if (line_like("SUB-PROCEDURE $name", tokens, state) || - line_like("SUB $name", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("SUB-PROCEDURE declaration outside PROCEDURE section", - state.where); - if (is_subprocedure(tokens[1], state)) - badcode("Duplicate declaration for SUB-PROCEDURE \"" + tokens[1] + "\"", - state.where); - if (state.closing_subprocedure()) - badcode("SUB-PROCEDURE declaration inside SUB-PROCEDURE", state.where); - else if (state.closing_if()) - badcode("SUB-PROCEDURE declaration inside IF", state.where); - else if (state.closing_loop()) - badcode("SUB-PROCEDURE declaration inside WHILE or FOR", state.where); - state.section_state = 3; - state.open_subprocedure(tokens[1]); - state.subprocedures.emplace(tokens[1], vector()); - return; - } - if (line_like("EXTERNAL SUB-PROCEDURE $external", tokens, state) || - line_like("EXTERNAL SUB $external", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("EXTERNAL SUB-PROCEDURE declaration outside PROCEDURE section", - state.where); - if (state.closing_subprocedure()) - badcode("SUB-PROCEDURE declaration inside SUB-PROCEDURE", state.where); - else if (state.closing_if()) - badcode("SUB-PROCEDURE declaration inside IF", state.where); - else if (state.closing_loop()) - badcode("SUB-PROCEDURE declaration inside WHILE or FOR", state.where); - else - state.open_subprocedure(tokens[2]); - // C++ Code - state.add_code("void " + fix_external_identifier(tokens[2], false) + "(){", - state.where); - return; - } - if (line_like("END SUB-PROCEDURE", tokens, state) || - line_like("END SUB", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("END SUB-PROCEDURE outside PROCEDURE section", state.where); - if (!state.closing_subprocedure()) - badcode("END SUB-PROCEDURE without SUB-PROCEDURE", state.where); - // C++ Code - state.add_code("return;}", state.where); - // Cierro la subrutina - state.close_subprocedure(); - return; - } + // SUB-PROCEDURE Declaration + if (line_like("SUB-PROCEDURE $name", tokens, state) || + line_like("SUB $name", tokens, state)) { + if (!in_procedure_section(state)) + badcode("SUB-PROCEDURE declaration outside PROCEDURE section", + state.where); + if (is_subprocedure(tokens[1], state)) + badcode("Duplicate declaration for SUB-PROCEDURE \"" + tokens[1] + "\"", + state.where); + if (state.closing_subprocedure()) + badcode("SUB-PROCEDURE declaration inside SUB-PROCEDURE", state.where); + else if (state.closing_if()) + badcode("SUB-PROCEDURE declaration inside IF", state.where); + else if (state.closing_loop()) + badcode("SUB-PROCEDURE declaration inside WHILE or FOR", state.where); + state.section_state = 3; + state.open_subprocedure(tokens[1]); + state.subprocedures.emplace(tokens[1], vector()); + return; + } + if (line_like("EXTERNAL SUB-PROCEDURE $external", tokens, state) || + line_like("EXTERNAL SUB $external", tokens, state)) { + if (!in_procedure_section(state)) + badcode("EXTERNAL SUB-PROCEDURE declaration outside PROCEDURE section", + state.where); + if (state.closing_subprocedure()) + badcode("SUB-PROCEDURE declaration inside SUB-PROCEDURE", state.where); + else if (state.closing_if()) + badcode("SUB-PROCEDURE declaration inside IF", state.where); + else if (state.closing_loop()) + badcode("SUB-PROCEDURE declaration inside WHILE or FOR", state.where); + else + state.open_subprocedure(tokens[2]); + // C++ Code + state.add_code("void " + fix_external_identifier(tokens[2], false) + "(){", + state.where); + return; + } + if (line_like("END SUB-PROCEDURE", tokens, state) || + line_like("END SUB", tokens, state)) { + if (!in_procedure_section(state)) + badcode("END SUB-PROCEDURE outside PROCEDURE section", state.where); + if (!state.closing_subprocedure()) + badcode("END SUB-PROCEDURE without SUB-PROCEDURE", state.where); + // C++ Code + state.add_code("return;}", state.where); + // Cierro la subrutina + state.close_subprocedure(); + return; + } - // Control Flow Statements - if (line_like("STORE $expression IN $var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("STORE statement outside PROCEDURE section", state.where); - // C++ Code - string rhand; - if (is_num_var(tokens[3], state)) - rhand = get_c_number(state, tokens[1]); + // Control Flow Statements + if (line_like("STORE $expression IN $var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("STORE statement outside PROCEDURE section", state.where); + // C++ Code + string rhand; + if (is_num_var(tokens[3], state)) + rhand = get_c_number(state, tokens[1]); + else + rhand = get_c_string(state, tokens[1]); + state.add_code(get_c_variable(state, tokens[3]) + " = " + rhand + ";", + state.where); + return; + } + if (line_like("IF $condition THEN", tokens, state)) { + string condition = get_c_condition( + state, vector(tokens.begin() + 1, tokens.end() - 1)); + if (condition != "[ERROR]") { + if (!in_procedure_section(state)) + badcode("IF outside PROCEDURE section", state.where); + // C++ Code + state.open_if(); + state.add_code("if (" + condition + "){", state.where); + return; + } + } + if (line_like("ELSE IF $condition THEN", tokens, state)) { + string condition = get_c_condition( + state, vector(tokens.begin() + 2, tokens.end() - 1)); + if (condition != "[ERROR]") { + if (!in_procedure_section(state)) + badcode("ELSE IF outside PROCEDURE section", state.where); + if (!state.closing_if()) badcode("ELSE IF without IF", state.where); + // C++ Code + state.add_code("} else if (" + condition + "){", state.where); + return; + } + } + if (line_like("ELSE", tokens, state)) { + if (!in_procedure_section(state)) + badcode("ELSE outside PROCEDURE section", state.where); + if (!state.closing_if()) badcode("ELSE without IF", state.where); + // C++ Code + state.open_else(); + state.add_code("}else{", state.where); + return; + } + if (line_like("END IF", tokens, state) || + line_like("END-IF", tokens, state)) { + if (!in_procedure_section(state)) + badcode("END IF outside PROCEDURE section", state.where); + if (!state.closing_if() && !state.closing_else()) + badcode("END IF without IF", state.where); + // C++ Code + state.close_if(); + state.add_code("}", state.where); + return; + } + if (line_like("WHILE $condition DO", tokens, state)) { + string condition = get_c_condition( + state, vector(tokens.begin() + 1, tokens.end() - 1)); + if (condition != "[ERROR]") { + if (!in_procedure_section(state)) + badcode("WHILE outside PROCEDURE section", state.where); + // C++ Code + state.open_loop(); + state.add_code("while (" + condition + "){", state.where); + return; + } + } + if (line_like("REPEAT", tokens, state)) { + if (!in_procedure_section(state)) + badcode("REPEAT outside PROCEDURE section", state.where); + if (!state.closing_loop()) + badcode("REPEAT without WHILE or FOR", state.where); + // C++ Code + state.close_loop(); + state.add_code("}", state.where); + return; + } + if (line_like("FOR $num-var FROM $num-expr TO $num-expr STEP $num-expr DO", + tokens, state)) { + if (!in_procedure_section(state)) + badcode("FOR outside PROCEDURE section", state.where); + state.open_loop(); + string var = get_c_variable(state, tokens[1]); + string from = get_c_expression(state, tokens[3]); + string to = get_c_expression(state, tokens[5]); + string step = get_c_expression(state, tokens[7]); + string init = var + " = " + from; + string condition = + step + " >= 0 ? " + var + " < " + to + " : " + var + " > " + to; + string increment = var + " += " + step; + // C++ Code + state.add_code("for (" + init + "; " + condition + "; " + increment + ") {", + state.where); + return; + } + if (line_like("FOR EACH $anyVar IN $collection DO", tokens, state)) { + if (!in_procedure_section(state)) + badcode("FOR EACH outside PROCEDURE section", state.where); + vector iteration_type = variable_type(tokens[2], state); + vector collected_type = variable_type(tokens[4], state); + unsigned int collection_type = collected_type.back(); // LIST or MAP + collected_type.pop_back(); + if (collection_type == 3 && iteration_type != collected_type) + badcode("FOR EACH iteration variable type doesn't match LIST type", + state.where); + else if (collection_type == 4 && iteration_type != vector{2}) + badcode("FOR EACH iteration variable type must be TEXT on MAP iteration", + state.where); + state.open_loop(); + // C Code + string range_var = state.new_range_var(); + string collection = get_c_variable(state, tokens[4]) + ".inner_collection"; + string iteration_var = range_var; + if (collection_type == 4) { + iteration_var += ".first"; + } + state.add_code("for (auto& " + range_var + " : " + collection + ") {", + state.where); + state.add_code( + get_c_variable(state, tokens[2]) + " = " + iteration_var + ";", + state.where); + return; + } + if (line_like("BREAK", tokens, state)) { + if (!in_procedure_section(state)) + badcode("BREAK outside PROCEDURE section", state.where); + if (state.open_loops == 0) + badcode("BREAK without WHILE or FOR", state.where); + // C++ Code + state.add_code("break;", state.where); + return; + } + if (line_like("CONTINUE", tokens, state)) { + if (!in_procedure_section(state)) + badcode("CONTINUE outside PROCEDURE section", state.where); + if (state.open_loops == 0) + badcode("CONTINUE without WHILE or FOR", state.where); + // C++ Code + state.add_code("continue;", state.where); + return; + } + if (line_like("CALL EXTERNAL $external", tokens, state)) { + if (!in_procedure_section(state)) + badcode("CALL EXTERNAL outside PROCEDURE section", state.where); + state.add_code(fix_external_identifier(tokens[2], false) + "();", + state.where); + // prototype of function defined in extension + state.add_var_code("void " + fix_external_identifier(tokens[2], false) + + "();"); + return; + } + if (line_like("CALL SUB-PROCEDURE $anything", tokens, state) || + line_like("CALL $anything", tokens, state)) { + size_t i = 1; + if (tokens[i] == "SUB-PROCEDURE") i++; + string subprocedure = tokens[i]; + // Valid options: No WITH or WITH with at least one paramter + if (i == tokens.size() - 1 || + (i < tokens.size() - 2 && tokens[i + 1] == "WITH")) { + if (!in_procedure_section(state)) + badcode("CALL outside PROCEDURE section", state.where); + vector parameters( + i != tokens.size() - 1 ? tokens.begin() + i + 2 : tokens.end(), + tokens.end()); + vector> types; + for (string ¶meter : parameters) { + if (is_number(parameter)) + types.push_back({1}); + else if (is_string(parameter)) + types.push_back({2}); + else if (variable_exists(parameter, state)) + types.push_back(variable_type(parameter, state)); else - rhand = get_c_string(state, tokens[1]); - state.add_code(get_c_variable(state, tokens[3]) + " = " + rhand + ";", - state.where); - return; - } - if (line_like("IF $condition THEN", tokens, state)) - { - string condition = get_c_condition( - state, vector(tokens.begin() + 1, tokens.end() - 1)); - if (condition != "[ERROR]") - { - if (!in_procedure_section(state)) - badcode("IF outside PROCEDURE section", state.where); - // C++ Code - state.open_if(); - state.add_code("if (" + condition + "){", state.where); - return; - } - } - if (line_like("ELSE IF $condition THEN", tokens, state)) - { - string condition = get_c_condition( - state, vector(tokens.begin() + 2, tokens.end() - 1)); - if (condition != "[ERROR]") - { - if (!in_procedure_section(state)) - badcode("ELSE IF outside PROCEDURE section", state.where); - if (!state.closing_if()) - badcode("ELSE IF without IF", state.where); - // C++ Code - state.add_code("} else if (" + condition + "){", state.where); - return; - } - } - if (line_like("ELSE", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("ELSE outside PROCEDURE section", state.where); - if (!state.closing_if()) - badcode("ELSE without IF", state.where); - // C++ Code - state.open_else(); - state.add_code("}else{", state.where); - return; - } - if (line_like("END IF", tokens, state) || - line_like("END-IF", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("END IF outside PROCEDURE section", state.where); - if (!state.closing_if() && !state.closing_else()) - badcode("END IF without IF", state.where); - // C++ Code - state.close_if(); - state.add_code("}", state.where); - return; - } - if (line_like("WHILE $condition DO", tokens, state)) - { - string condition = get_c_condition( - state, vector(tokens.begin() + 1, tokens.end() - 1)); - if (condition != "[ERROR]") - { - if (!in_procedure_section(state)) - badcode("WHILE outside PROCEDURE section", state.where); - // C++ Code - state.open_loop(); - state.add_code("while (" + condition + "){", state.where); - return; - } - } - if (line_like("REPEAT", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("REPEAT outside PROCEDURE section", state.where); - if (!state.closing_loop()) - badcode("REPEAT without WHILE or FOR", state.where); - // C++ Code - state.close_loop(); - state.add_code("}", state.where); - return; - } - if (line_like("FOR $num-var FROM $num-expr TO $num-expr STEP $num-expr DO", - tokens, state)) - { - if (!in_procedure_section(state)) - badcode("FOR outside PROCEDURE section", state.where); - state.open_loop(); - string var = get_c_variable(state, tokens[1]); - string from = get_c_expression(state, tokens[3]); - string to = get_c_expression(state, tokens[5]); - string step = get_c_expression(state, tokens[7]); - string init = var + " = " + from; - string condition = - step + " >= 0 ? " + var + " < " + to + " : " + var + " > " + to; - string increment = var + " += " + step; - // C++ Code - state.add_code("for (" + init + "; " + condition + "; " + increment + ") {", - state.where); - return; - } - if (line_like("FOR EACH $anyVar IN $collection DO", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("FOR EACH outside PROCEDURE section", state.where); - vector iteration_type = variable_type(tokens[2], state); - vector collected_type = variable_type(tokens[4], state); - unsigned int collection_type = collected_type.back(); // LIST or MAP - collected_type.pop_back(); - if (collection_type == 3 && iteration_type != collected_type) - badcode("FOR EACH iteration variable type doesn't match LIST type", - state.where); - else if (collection_type == 4 && iteration_type != vector{2}) - badcode("FOR EACH iteration variable type must be TEXT on MAP iteration", - state.where); - state.open_loop(); - // C Code - string range_var = state.new_range_var(); - string collection = get_c_variable(state, tokens[4]) + ".inner_collection"; - string iteration_var = range_var; - if (collection_type == 4) - { - iteration_var += ".first"; - } - state.add_code("for (auto& " + range_var + " : " + collection + ") {", - state.where); - state.add_code( - get_c_variable(state, tokens[2]) + " = " + iteration_var + ";", - state.where); - return; - } - if (line_like("BREAK", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("BREAK outside PROCEDURE section", state.where); - if (state.open_loops == 0) - badcode("BREAK without WHILE or FOR", state.where); - // C++ Code - state.add_code("break;", state.where); - return; - } - if (line_like("CONTINUE", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("CONTINUE outside PROCEDURE section", state.where); - if (state.open_loops == 0) - badcode("CONTINUE without WHILE or FOR", state.where); - // C++ Code - state.add_code("continue;", state.where); - return; - } - if (line_like("CALL EXTERNAL $external", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("CALL EXTERNAL outside PROCEDURE section", state.where); - state.add_code(fix_external_identifier(tokens[2], false) + "();", - state.where); - // prototype of function defined in extension - state.add_var_code("void " + fix_external_identifier(tokens[2], false) + - "();"); - return; - } - if (line_like("CALL SUB-PROCEDURE $anything", tokens, state) || - line_like("CALL $anything", tokens, state)) - { - size_t i = 1; - if (tokens[i] == "SUB-PROCEDURE") - i++; - string subprocedure = tokens[i]; - // Valid options: No WITH or WITH with at least one paramter - if (i == tokens.size() - 1 || - (i < tokens.size() - 2 && tokens[i + 1] == "WITH")) - { - if (!in_procedure_section(state)) - badcode("CALL outside PROCEDURE section", state.where); - vector parameters( - i != tokens.size() - 1 ? tokens.begin() + i + 2 : tokens.end(), - tokens.end()); - vector> types; - for (string ¶meter : parameters) - { - if (is_number(parameter)) - types.push_back({1}); - else if (is_string(parameter)) - types.push_back({2}); - else if (variable_exists(parameter, state)) - types.push_back(variable_type(parameter, state)); - else - badcode("CALL with invalid parameter \"" + parameter + "\"", - state.where); - } - bool correct_types = - state.correct_subprocedure_types(subprocedure, types); - if (!is_subprocedure(subprocedure, state)) - { - if (!correct_types) - badcode("CALL parameter types don't match previous CALL", - state.where); - state.add_expected_subprocedure( - subprocedure, fix_identifier(subprocedure, false), types); - } - else - { - if (!correct_types) - badcode("CALL parameter types don't match SUB-PROCEDURE declaration", - state.where); - } - add_call_code(subprocedure, parameters, state); - return; - } - } - if (line_like("RETURN", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("RETURN outside PROCEDURE section", state.where); - if (state.current_subprocedure == "") - badcode("RETURN found outside subprocedure", state.where); - // C++ Code - state.add_code("return;", state.where); - return; - } - if (line_like("EXIT", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("EXIT outside PROCEDURE section", state.where); - // C++ Code - state.add_code("exit(0);", state.where); - return; - } - if (line_like("WAIT $num-expr MILLISECONDS", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("WAIT statement outside PROCEDURE section", state.where); + badcode("CALL with invalid parameter \"" + parameter + "\"", + state.where); + } + bool correct_types = + state.correct_subprocedure_types(subprocedure, types); + if (!is_subprocedure(subprocedure, state)) { + if (!correct_types) + badcode("CALL parameter types don't match previous CALL", + state.where); + state.add_expected_subprocedure( + subprocedure, fix_identifier(subprocedure, false), types); + } else { + if (!correct_types) + badcode("CALL parameter types don't match SUB-PROCEDURE declaration", + state.where); + } + add_call_code(subprocedure, parameters, state); + return; + } + } + if (line_like("RETURN", tokens, state)) { + if (!in_procedure_section(state)) + badcode("RETURN outside PROCEDURE section", state.where); + if (state.current_subprocedure == "") + badcode("RETURN found outside subprocedure", state.where); + // C++ Code + state.add_code("return;", state.where); + return; + } + if (line_like("EXIT", tokens, state)) { + if (!in_procedure_section(state)) + badcode("EXIT outside PROCEDURE section", state.where); + // C++ Code + state.add_code("exit(0);", state.where); + return; + } + if (line_like("WAIT $num-expr MILLISECONDS", tokens, state)) { + if (!in_procedure_section(state)) + badcode("WAIT statement outside PROCEDURE section", state.where); // C++ Code #if defined(_WIN32) - state.add_code( - "_sleep((long int)" + get_c_expression(state, tokens[1]) + ");", - state.where); + state.add_code( + "_sleep((long int)" + get_c_expression(state, tokens[1]) + ");", + state.where); #else - state.add_code( - "std::this_thread::sleep_for(std::chrono::milliseconds((long int)" + - get_c_expression(state, tokens[1]) + "));", - state.where); + state.add_code( + "std::this_thread::sleep_for(std::chrono::milliseconds((long int)" + + get_c_expression(state, tokens[1]) + "));", + state.where); #endif - return; - } - if (line_like("GOTO $label", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GOTO outside PROCEDURE section", state.where); - state.add_code("goto label_" + fix_identifier(tokens[1]) + ";", - state.where); - return; - } - if (line_like("LABEL $label", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("LABEL outside PROCEDURE section", state.where); - state.add_code("label_" + fix_identifier(tokens[1]) + ":", state.where); - return; - } - - // Arithmetic Statements - if (line_like("MODULO $num-expr BY $num-expr IN $num-var", tokens, - state)) // TODO move this into the standard library - { - if (!in_procedure_section(state)) - badcode("MODULO statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = modulo(" + - get_c_expression(state, tokens[1]) + ", " + - get_c_expression(state, tokens[3]) + ");", - state.where); - return; - } - if (line_like("RAISE $num-expr TO $num-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("RAISE statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = pow(" + - get_c_expression(state, tokens[1]) + ", " + - get_c_expression(state, tokens[3]) + ");", - state.where); - return; - } - if (line_like("LOG $num-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("LOG statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = log(" + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("SIN $num-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("SIN statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = sin(" + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("COS $num-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("COS statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = cos(" + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("TAN $num-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("TAN statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = tan(" + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("ADD $num-expr AND $num-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("ADD statement outside PROCEDURE section", state.where); - // C Code - state.add_code(get_c_variable(state, tokens[5]) + " = " + - get_c_expression(state, tokens[1]) + " + " + - get_c_expression(state, tokens[3]) + ";", - state.where); - return; - } - if (line_like("SUBTRACT $num-expr FROM $num-expr IN $num-var", tokens, - state)) - { - if (!in_procedure_section(state)) - badcode("SUBTRACT statement outside PROCEDURE section", state.where); - // C Code - state.add_code(get_c_variable(state, tokens[5]) + " = " + - get_c_expression(state, tokens[3]) + " - " + - get_c_expression(state, tokens[1]) + ";", - state.where); - return; - } - if (line_like("MULTIPLY $num-expr BY $num-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("MULTIPLY statement outside PROCEDURE section", state.where); - // C Code - state.add_code(get_c_variable(state, tokens[5]) + " = " + - get_c_expression(state, tokens[1]) + " * " + - get_c_expression(state, tokens[3]) + ";", - state.where); - return; - } - if (line_like("DIVIDE $num-expr BY $num-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("DIVIDE statement outside PROCEDURE section", state.where); - // C Code - state.add_code(get_c_variable(state, tokens[5]) + " = " + - get_c_expression(state, tokens[1]) + " / " + - get_c_expression(state, tokens[3]) + ";", - state.where); - return; - } - if (line_like("GET RANDOM IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("RANDOM outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = get_random();", - state.where); - return; - } - if (line_like("FLOOR $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("FLOOR statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[1]) + " = floor(" + - get_c_variable(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("CEIL $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("CEIL statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[1]) + " = ceil(" + - get_c_variable(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("FLOOR $num-var IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("FLOOR statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = floor(" + - get_c_variable(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("CEIL $num-var IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("CEIL statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = ceil(" + - get_c_variable(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("IN $num-var SOLVE $math", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("IN-SOLVE statement outside PROCEDURE section", state.where); - - string code = ""; - for (unsigned int i = 3; i < tokens.size(); ++i) - { - if (is_num_var(tokens[i], state)) - code += " " + get_c_variable(state, tokens[i]); - else if (is_txt_expr(tokens[i], state)) - code += " " + get_c_number(state, tokens[i]); - else - code += " " + tokens[i]; - } - state.add_code(get_c_variable(state, tokens[1]) + " =" + code + ";", - state.where); - return; - } + return; + } + if (line_like("GOTO $label", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GOTO outside PROCEDURE section", state.where); + state.add_code("goto label_" + fix_identifier(tokens[1]) + ";", + state.where); + return; + } + if (line_like("LABEL $label", tokens, state)) { + if (!in_procedure_section(state)) + badcode("LABEL outside PROCEDURE section", state.where); + state.add_code("label_" + fix_identifier(tokens[1]) + ":", state.where); + return; + } - // Text Statements - if (line_like("JOIN $expression AND $expression IN $str-var", tokens, - state)) - { - if (!in_procedure_section(state)) - badcode("JOIN statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code("join(" + get_c_string(state, tokens[1]) + ", " + - get_c_string(state, tokens[3]) + ", " + - get_c_variable(state, tokens[5]) + ");", - state.where); - return; - } - if (line_like("GET CHARACTER AT $num-expr FROM $str-expr IN $str-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET CHARACTER statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[7]) + " = charat(" + - get_c_expression(state, tokens[5]) + ", " + - get_c_expression(state, tokens[3]) + ");", - state.where); - return; - } - if (line_like("GET LENGTH OF $str-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET LENGTH OF outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = countGraphemes(" + get_c_expression(state, tokens[3]) + ");", state.where); - return; - } - if (line_like("GET ASCII CHARACTER $num-expr IN $str-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET ASCII CHARACTER statement outside PROCEDURE section", - state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = (char)(" + - get_c_expression(state, tokens[3]) + ");", - state.where); - return; - } - if (line_like("GET CHARACTER CODE OF $str-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET CHARACTER CODE OF statement outside PROCEDURE section", - state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[6]) + " = get_char_num(" + - get_c_expression(state, tokens[4]) + ");", - state.where); - return; - } - if (line_like("STORE QUOTE IN $str-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("STORE QUOTE IN statement outside PROCEDURE section", - state.where); - state.open_quote = true; - // C++ Code. More strings will get emitted - state.add_code(get_c_variable(state, tokens[3]) + " = \"\"", state.where); - return; - } - if (line_like("END QUOTE", tokens, state)) - badcode("END QUOTE statement without preceding STORE QUOTE statement", - state.where); - if (line_like("IN $str-var JOIN $display", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("IN-JOIN statement outside PROCEDURE section", state.where); - if (tokens.size() < 5) - badcode("IN-JOIN expects at least two values to join", state.where); - // C++ Code - state.add_code("joinvar = \"\";", state.where); - for (unsigned int i = 3; i < tokens.size(); ++i) - { - state.add_code( - "join(joinvar, " + get_c_string(state, tokens[i]) + ", joinvar);", - state.where); - } - state.add_code(get_c_variable(state, tokens[1]) + " = joinvar;", - state.where); - return; - } + // Arithmetic Statements + if (line_like("MODULO $num-expr BY $num-expr IN $num-var", tokens, + state)) // TODO move this into the standard library + { + if (!in_procedure_section(state)) + badcode("MODULO statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = modulo(" + + get_c_expression(state, tokens[1]) + ", " + + get_c_expression(state, tokens[3]) + ");", + state.where); + return; + } + if (line_like("RAISE $num-expr TO $num-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("RAISE statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = pow(" + + get_c_expression(state, tokens[1]) + ", " + + get_c_expression(state, tokens[3]) + ");", + state.where); + return; + } + if (line_like("LOG $num-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("LOG statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = log(" + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("SIN $num-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("SIN statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = sin(" + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("COS $num-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("COS statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = cos(" + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("TAN $num-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("TAN statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = tan(" + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("ADD $num-expr AND $num-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("ADD statement outside PROCEDURE section", state.where); + // C Code + state.add_code(get_c_variable(state, tokens[5]) + " = " + + get_c_expression(state, tokens[1]) + " + " + + get_c_expression(state, tokens[3]) + ";", + state.where); + return; + } + if (line_like("SUBTRACT $num-expr FROM $num-expr IN $num-var", tokens, + state)) { + if (!in_procedure_section(state)) + badcode("SUBTRACT statement outside PROCEDURE section", state.where); + // C Code + state.add_code(get_c_variable(state, tokens[5]) + " = " + + get_c_expression(state, tokens[3]) + " - " + + get_c_expression(state, tokens[1]) + ";", + state.where); + return; + } + if (line_like("MULTIPLY $num-expr BY $num-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("MULTIPLY statement outside PROCEDURE section", state.where); + // C Code + state.add_code(get_c_variable(state, tokens[5]) + " = " + + get_c_expression(state, tokens[1]) + " * " + + get_c_expression(state, tokens[3]) + ";", + state.where); + return; + } + if (line_like("DIVIDE $num-expr BY $num-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("DIVIDE statement outside PROCEDURE section", state.where); + // C Code + state.add_code(get_c_variable(state, tokens[5]) + " = " + + get_c_expression(state, tokens[1]) + " / " + + get_c_expression(state, tokens[3]) + ";", + state.where); + return; + } + if (line_like("GET RANDOM IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("RANDOM outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = get_random();", + state.where); + return; + } + if (line_like("FLOOR $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("FLOOR statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[1]) + " = floor(" + + get_c_variable(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("CEIL $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("CEIL statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[1]) + " = ceil(" + + get_c_variable(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("FLOOR $num-var IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("FLOOR statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = floor(" + + get_c_variable(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("CEIL $num-var IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("CEIL statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = ceil(" + + get_c_variable(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("IN $num-var SOLVE $math", tokens, state)) { + if (!in_procedure_section(state)) + badcode("IN-SOLVE statement outside PROCEDURE section", state.where); - // I/O Statements - if (line_like("DISPLAY $display", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("DISPLAY statement outside PROCEDURE section", state.where); - // C++ Code - for (unsigned int i = 1; i < tokens.size(); ++i) - { - // TODO ADD COLORS HERE - if (is_scalar_variable(tokens[i], state)) - { - state.add_code( - "cout << " + get_c_variable(state, tokens[i]) + " << flush;", - state.where); - } - else - { - state.add_code("cout << " + tokens[i] + " << flush;", state.where); - } - } - return; - } - if (line_like("ACCEPT $var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("ACCEPT statement outside PROCEDURE section", state.where); - // C++ Code - if (is_num_var(tokens[1], state)) - state.add_code(get_c_variable(state, tokens[1]) + " = input_number();", - state.where); - else - state.add_code(get_c_variable(state, tokens[1]) + " = input_string();", - state.where); - return; - } - if (line_like("EXECUTE $str-expr", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("EXECUTE outside PROCEDURE section", state.where); - // C++ Code - state.add_code("system(" + get_c_char_array(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("EXECUTE $str-expr AND STORE OUTPUT IN $str-var", tokens, - state)) - { - if (!in_procedure_section(state)) - badcode("EXECUTE outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[6]) + " = exec(" + - get_c_char_array(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("EXECUTE $str-expr AND STORE EXIT CODE IN $num-var", tokens, - state)) - { - if (!in_procedure_section(state)) - badcode("EXECUTE outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[7]) + " = (system(" + - get_c_char_array(state, tokens[1]) + ") >> 8) & 0xff;", - state.where); // shift wait() val and get lowest 2 - return; - } - if (line_like("ACCEPT $str-var UNTIL EOF", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("ACCEPT statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[1]) + " = input_until_eof();", - state.where); - return; - } - if (line_like("LOAD FILE $str-expr IN $str-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("LOAD FILE statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code("load_file(" + get_c_expression(state, tokens[2]) + ", " + - get_c_variable(state, tokens[4]) + ");", - state.where); - return; - } - if (line_like("WRITE $expression TO FILE $str-expr", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("WRITE statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code("write_file(" + get_c_expression(state, tokens[4]) + ", " + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("APPEND $expression TO FILE $str-expr", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("APPEND statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code("append_to_file(" + get_c_expression(state, tokens[4]) + - ", " + get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } + string code = ""; + for (unsigned int i = 3; i < tokens.size(); ++i) { + if (is_num_var(tokens[i], state)) + code += " " + get_c_variable(state, tokens[i]); + else if (is_txt_expr(tokens[i], state)) + code += " " + get_c_number(state, tokens[i]); + else + code += " " + tokens[i]; + } + state.add_code(get_c_variable(state, tokens[1]) + " =" + code + ";", + state.where); + return; + } - if (line_like("REPLACE $str-expr WITH $str-expr FROM $str-expr IN $str-var", - tokens, state)) - { - if (!in_procedure_section(state)) - badcode("REPLACE statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code( - get_c_variable(state, tokens[7]) + " = str_replace(" + get_c_expression(state, tokens[5]) + ", " + get_c_expression(state, tokens[1]) + ", " + get_c_expression(state, tokens[3]) + ");", - state.where); - return; - } - // deprecated - if (line_like("REPLACE $str-expr FROM $str-expr WITH $str-expr IN $str-var", - tokens, state)) - { - if (!in_procedure_section(state)) - badcode("REPLACE statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code( - get_c_variable(state, tokens[7]) + " = str_replace(" + get_c_expression(state, tokens[3]) + ", " + get_c_expression(state, tokens[1]) + ", " + get_c_expression(state, tokens[5]) + ");", - state.where); - return; - } - if (line_like("GET INDEX OF $str-expr FROM $str-expr IN $num-var", tokens, - state)) - { - if (!in_procedure_section(state)) - badcode("GET INDEX OF statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[7]) + " = utf8GetIndexOf(" + - get_c_expression(state, tokens[5]) + ", " + - get_c_expression(state, tokens[3]) + ");", - state.where); - return; - } - if (line_like("COUNT $str-expr FROM $str-expr IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("COUNT statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = utf8Count(" + - get_c_expression(state, tokens[3]) + ", " + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like( - "SUBSTRING $str-expr FROM $num-expr LENGTH $num-expr IN $str-var", - tokens, state)) - { - if (!in_procedure_section(state)) - badcode("SUBSTRING statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code("joinvar = " + get_c_expression(state, tokens[1]) + ";", - state.where); - state.add_code(get_c_variable(state, tokens[7]) + " = joinvar.substr(" + - get_c_expression(state, tokens[3]) + ", " + - get_c_expression(state, tokens[5]) + ");", - state.where); - return; - } - if (line_like("TRIM $str-expr IN $str-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("TRIM statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = trimCopy(" + get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("CONVERT $str-expr TO UPPERCASE IN $str-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("CONVERT statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = toUpperCopy(" + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("CONVERT $str-expr TO LOWERCASE IN $str-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("CONVERT statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = toLowerCopy(" + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - if (line_like("CLEAR $collection", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("CLEAR statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code( - get_c_variable(state, tokens[1]) + ".inner_collection.clear();", - state.where); - return; - } - if (line_like("COPY $collection TO $collection", tokens, state)) - { - if (variable_type(tokens[1], state) == variable_type(tokens[3], state)) - { - if (!in_procedure_section(state)) - badcode("COPY statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + ".inner_collection = " + - get_c_variable(state, tokens[1]) + - ".inner_collection;", - state.where); - return; - } - } - if (line_like("GET KEY COUNT OF $map IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET KEY COUNT statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[6]) + " = " + - get_c_variable(state, tokens[4]) + - ".inner_collection.size();", - state.where); - return; - } - if (line_like("GET KEYS OF $map IN $str-list", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET KEYS statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code("get_indices(" + get_c_variable(state, tokens[5]) + ", " + - get_c_variable(state, tokens[3]) + ");", - state.where); - return; - } - if (line_like("PUSH MAP TO $map-list", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("PUSH statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code( - get_c_variable(state, tokens[3]) + ".inner_collection.emplace_back();", - state.where); - return; - } - if (line_like("PUSH LIST TO $list-list", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("PUSH statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code( - get_c_variable(state, tokens[3]) + ".inner_collection.emplace_back();", - state.where); - return; - } - if (line_like("PUSH $expression TO $scalar-list", tokens, state)) - { - // The type of the pushed element must match the collection type - if (is_num_expr(tokens[1], state) == is_num_list(tokens[3], state)) - { - if (!in_procedure_section(state)) - badcode("PUSH statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + - ".inner_collection.push_back(" + - get_c_expression(state, tokens[1]) + ");", - state.where); - return; - } - else - { - badcode("List - Value type mismatch", state.where); - } - } - if (line_like("GET LENGTH OF $list IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET LENGTH OF (list) statement outside PROCEDURE section", - state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = " + - get_c_variable(state, tokens[3]) + - ".inner_collection.size();", - state.where); - return; - } - if (line_like("DELETE LAST ELEMENT OF $list", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("DELETE LAST ELEMENT OF statement outside PROCEDURE section", - state.where); - // C++ Code - state.add_code("if(" + get_c_variable(state, tokens[4]) + - ".inner_collection.size() > 0)", - state.where); - state.add_code( - get_c_variable(state, tokens[4]) + ".inner_collection.pop_back();", - state.where); - return; - } - if (line_like("REMOVE ELEMENT AT $num-expr FROM $list", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("REMOVE ELEMENT AT statement outside PROCEDURE section", - state.where); - // C++ Code - state.add_code("if(" + get_c_variable(state, tokens[5]) + - ".inner_collection.size() > " + - get_c_expression(state, tokens[3]) + ")", - state.where); - state.add_code( - get_c_variable(state, tokens[5]) + ".inner_collection.erase(" + - get_c_variable(state, tokens[5]) + ".inner_collection.begin() + " + - get_c_expression(state, tokens[3]) + ");", + // Text Statements + if (line_like("JOIN $expression AND $expression IN $str-var", tokens, + state)) { + if (!in_procedure_section(state)) + badcode("JOIN statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code("join(" + get_c_string(state, tokens[1]) + ", " + + get_c_string(state, tokens[3]) + ", " + + get_c_variable(state, tokens[5]) + ");", + state.where); + return; + } + if (line_like("GET CHARACTER AT $num-expr FROM $str-expr IN $str-var", tokens, + state)) { + if (!in_procedure_section(state)) + badcode("GET CHARACTER statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[7]) + " = charat(" + + get_c_expression(state, tokens[5]) + ", " + + get_c_expression(state, tokens[3]) + ");", + state.where); + return; + } + if (line_like("GET LENGTH OF $str-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET LENGTH OF outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = ((chText)" + + get_c_expression(state, tokens[3]) + ").size();", + state.where); + return; + } + if (line_like("GET ASCII CHARACTER $num-expr IN $str-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET ASCII CHARACTER statement outside PROCEDURE section", + state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = (char)(" + + get_c_expression(state, tokens[3]) + ");", + state.where); + return; + } + if (line_like("GET CHARACTER CODE OF $str-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET CHARACTER CODE OF statement outside PROCEDURE section", + state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[6]) + " = get_char_num(" + + get_c_expression(state, tokens[4]) + ");", + state.where); + return; + } + if (line_like("STORE QUOTE IN $str-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("STORE QUOTE IN statement outside PROCEDURE section", + state.where); + state.open_quote = true; + // C++ Code. More strings will get emitted + state.add_code(get_c_variable(state, tokens[3]) + " = \"\"", state.where); + return; + } + if (line_like("END QUOTE", tokens, state)) + badcode("END QUOTE statement without preceding STORE QUOTE statement", state.where); - return; - } - if (line_like("SPLIT $str-expr BY $str-expr IN $str-list", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("SPLIT statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[5]) + " = utf8_split_list(" + - get_c_expression(state, tokens[1]) + ", " + - get_c_expression(state, tokens[3]) + ");", - state.where); - return; - } - if (line_like("GET HOUR IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET HOUT statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code( - get_c_variable(state, tokens[3]) + " = localtime(&ldpl_time)->tm_hour;", - state.where); - return; - } - if (line_like("GET MINUTES IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET MINUTES statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code( - get_c_variable(state, tokens[3]) + " = localtime(&ldpl_time)->tm_min;", - state.where); - return; - } - if (line_like("GET SECONDS IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET SECONDS statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code( - get_c_variable(state, tokens[3]) + " = localtime(&ldpl_time)->tm_sec;", - state.where); - return; - } - if (line_like("GET YEAR IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET YEAR statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + - " = localtime(&ldpl_time)->tm_year + 1900;", - state.where); - return; - } - if (line_like("GET DAY IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET DAY statement outside PROCEDURE section", state.where); - // C++ Code + if (line_like("IN $str-var JOIN $display", tokens, state)) { + if (!in_procedure_section(state)) + badcode("IN-JOIN statement outside PROCEDURE section", state.where); + if (tokens.size() < 5) + badcode("IN-JOIN expects at least two values to join", state.where); + // C++ Code + state.add_code("joinvar = \"\";", state.where); + for (unsigned int i = 3; i < tokens.size(); ++i) { + state.add_code( + "join(joinvar, " + get_c_string(state, tokens[i]) + ", joinvar);", + state.where); + } + state.add_code(get_c_variable(state, tokens[1]) + " = joinvar;", + state.where); + return; + } + + // I/O Statements + if (line_like("DISPLAY $display", tokens, state)) { + if (!in_procedure_section(state)) + badcode("DISPLAY statement outside PROCEDURE section", state.where); + // C++ Code + for (unsigned int i = 1; i < tokens.size(); ++i) { + // TODO ADD COLORS HERE + if (is_scalar_variable(tokens[i], state)) { state.add_code( - get_c_variable(state, tokens[3]) + " = localtime(&ldpl_time)->tm_mday;", + "cout << " + get_c_variable(state, tokens[i]) + " << flush;", state.where); - return; - } - if (line_like("GET MONTH IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET MONTH statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + - " = localtime(&ldpl_time)->tm_mon + 1;", - state.where); - return; - } - if (line_like("GET MONTH IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET MONTH statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + - " = localtime(&ldpl_time)->tm_mon + 1;", - state.where); - return; - } - if (line_like("GET EPOCH IN $num-var", tokens, state)) - { - if (!in_procedure_section(state)) - badcode("GET EPOCH statement outside PROCEDURE section", state.where); - // C++ Code - state.add_code(get_c_variable(state, tokens[3]) + " = ldpl_time;", - state.where); - return; - } - // Custom Statements - if (line_like("CREATE STATEMENT $string EXECUTING $subprocedure", tokens, - state)) - { - if (!in_procedure_section(state)) - badcode("CREATE STATEMENT statement outside PROCEDURE section", - state.where); - if (state.closing_subprocedure()) - badcode("CREATE STATEMENT statement inside SUB-PROCEDURE", state.where); - else if (state.closing_if()) - badcode("CREATE STATEMENT statement inside IF", state.where); - else if (state.closing_loop()) - badcode("CREATE STATEMENT statement inside WHILE or FOR", state.where); - string model_line = tokens[2].substr(1, tokens[2].size() - 2); - vector model_tokens; - vector parameters = state.subprocedures[tokens[4]]; - trim(model_line); - tokenize(model_line, model_tokens, state.where, true, ' '); - size_t param_count = 0; - size_t keyword_count = 0; - string valid_keyword_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - model_line = ""; - for (string &token : model_tokens) - { - if (token == "$") - { - ++param_count; - if (param_count > parameters.size()) - break; - vector type = - state.variables[tokens[4]][parameters[param_count - 1]]; - if (type == vector{1}) - model_line += "$num-expr "; - else if (type == vector{2}) - model_line += "$str-expr "; - else - { - model_line += "$var-type-"; - for (size_t i = 0; i < type.size(); ++i) - { - model_line += to_string(type[i]); - } - model_line += " "; - } - } - else if (token.find_first_not_of(valid_keyword_chars) == string::npos) - { - ++keyword_count; - model_line += token + " "; - } - else - { - badcode("CREATE STATEMENT with invalid token \"" + token + "\"", - state.where); - } - } - if (param_count != parameters.size()) - badcode("CREATE STATEMENT parameters count doesn't match SUB-PROCEDURE", - state.where); - if (keyword_count == 0) - badcode("CREATE STATEMENT without keywords", state.where); - state.custom_statements.emplace_back(model_line, tokens[4]); - return; - } - for (pair &statement : state.custom_statements) - { - if (line_like(statement.first, tokens, state)) - { - string prefix = statement.first.substr(0, statement.first.find("$")); - if (!in_procedure_section(state)) - badcode(prefix + "statement outside PROCEDURE section", state.where); - vector model_tokens; - vector parameters; - tokenize(statement.first, model_tokens, state.where, false, ' '); - for (size_t i = 0; i < model_tokens.size(); i++) - { - if (model_tokens[i][0] == '$') - parameters.push_back(tokens[i]); - } - add_call_code(statement.second, parameters, state); - return; + } else { + state.add_code("cout << " + tokens[i] + " << flush;", state.where); + } + } + return; + } + if (line_like("ACCEPT $var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("ACCEPT statement outside PROCEDURE section", state.where); + // C++ Code + if (is_num_var(tokens[1], state)) + state.add_code(get_c_variable(state, tokens[1]) + " = input_number();", + state.where); + else + state.add_code(get_c_variable(state, tokens[1]) + " = input_string();", + state.where); + return; + } + if (line_like("EXECUTE $str-expr", tokens, state)) { + if (!in_procedure_section(state)) + badcode("EXECUTE outside PROCEDURE section", state.where); + // C++ Code + state.add_code("system(" + get_c_char_array(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("EXECUTE $str-expr AND STORE OUTPUT IN $str-var", tokens, + state)) { + if (!in_procedure_section(state)) + badcode("EXECUTE outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[6]) + " = exec(" + + get_c_char_array(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("EXECUTE $str-expr AND STORE EXIT CODE IN $num-var", tokens, + state)) { + if (!in_procedure_section(state)) + badcode("EXECUTE outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[7]) + " = (system(" + + get_c_char_array(state, tokens[1]) + ") >> 8) & 0xff;", + state.where); // shift wait() val and get lowest 2 + return; + } + if (line_like("ACCEPT $str-var UNTIL EOF", tokens, state)) { + if (!in_procedure_section(state)) + badcode("ACCEPT statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[1]) + " = input_until_eof();", + state.where); + return; + } + if (line_like("LOAD FILE $str-expr IN $str-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("LOAD FILE statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code("load_file(" + get_c_expression(state, tokens[2]) + ", " + + get_c_variable(state, tokens[4]) + ");", + state.where); + return; + } + if (line_like("WRITE $expression TO FILE $str-expr", tokens, state)) { + if (!in_procedure_section(state)) + badcode("WRITE statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code("write_file(" + get_c_expression(state, tokens[4]) + ", " + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("APPEND $expression TO FILE $str-expr", tokens, state)) { + if (!in_procedure_section(state)) + badcode("APPEND statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code("append_to_file(" + get_c_expression(state, tokens[4]) + + ", " + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + + if (line_like("REPLACE $str-expr WITH $str-expr FROM $str-expr IN $str-var", + tokens, state)) { + if (!in_procedure_section(state)) + badcode("REPLACE statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[7]) + " = str_replace(((chText)" + + get_c_expression(state, tokens[5]) + ").str_rep(), ((chText)" + + get_c_expression(state, tokens[1]) + ").str_rep(), ((chText)" + + get_c_expression(state, tokens[3]) + ").str_rep());", + state.where); + return; + } + // deprecated + if (line_like("REPLACE $str-expr FROM $str-expr WITH $str-expr IN $str-var", + tokens, state)) { + if (!in_procedure_section(state)) + badcode("REPLACE statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[7]) + " = str_replace(((chText)" + + get_c_expression(state, tokens[3]) + ").str_rep(), ((chText)" + + get_c_expression(state, tokens[1]) + ").str_rep(), ((chText)" + + get_c_expression(state, tokens[5]) + ").str_rep());", + state.where); + return; + } + if (line_like("GET INDEX OF $str-expr FROM $str-expr IN $num-var", tokens, + state)) { + if (!in_procedure_section(state)) + badcode("GET INDEX OF statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[7]) + " = utf8GetIndexOf(" + + get_c_expression(state, tokens[5]) + ", " + + get_c_expression(state, tokens[3]) + ");", + state.where); + return; + } + if (line_like("COUNT $str-expr FROM $str-expr IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("COUNT statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = utf8Count(" + + get_c_expression(state, tokens[3]) + ", " + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like( + "SUBSTRING $str-expr FROM $num-expr LENGTH $num-expr IN $str-var", + tokens, state)) { + if (!in_procedure_section(state)) + badcode("SUBSTRING statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code("joinvar = " + get_c_expression(state, tokens[1]) + ";", + state.where); + state.add_code(get_c_variable(state, tokens[7]) + " = joinvar.substr(" + + get_c_expression(state, tokens[3]) + ", " + + get_c_expression(state, tokens[5]) + ");", + state.where); + return; + } + if (line_like("TRIM $str-expr IN $str-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("TRIM statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = trimCopy(" + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("CONVERT $str-expr TO UPPERCASE IN $str-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("CONVERT statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = toUpperCopy(" + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("CONVERT $str-expr TO LOWERCASE IN $str-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("CONVERT statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = toLowerCopy(" + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } + if (line_like("CLEAR $collection", tokens, state)) { + if (!in_procedure_section(state)) + badcode("CLEAR statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[1]) + ".inner_collection.clear();", + state.where); + return; + } + if (line_like("COPY $collection TO $collection", tokens, state)) { + if (variable_type(tokens[1], state) == variable_type(tokens[3], state)) { + if (!in_procedure_section(state)) + badcode("COPY statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + ".inner_collection = " + + get_c_variable(state, tokens[1]) + + ".inner_collection;", + state.where); + return; + } + } + if (line_like("GET KEY COUNT OF $map IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET KEY COUNT statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[6]) + " = " + + get_c_variable(state, tokens[4]) + + ".inner_collection.size();", + state.where); + return; + } + if (line_like("GET KEYS OF $map IN $str-list", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET KEYS statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code("get_indices(" + get_c_variable(state, tokens[5]) + ", " + + get_c_variable(state, tokens[3]) + ");", + state.where); + return; + } + if (line_like("PUSH MAP TO $map-list", tokens, state)) { + if (!in_procedure_section(state)) + badcode("PUSH statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[3]) + ".inner_collection.emplace_back();", + state.where); + return; + } + if (line_like("PUSH LIST TO $list-list", tokens, state)) { + if (!in_procedure_section(state)) + badcode("PUSH statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[3]) + ".inner_collection.emplace_back();", + state.where); + return; + } + if (line_like("PUSH $expression TO $scalar-list", tokens, state)) { + // The type of the pushed element must match the collection type + if (is_num_expr(tokens[1], state) == is_num_list(tokens[3], state)) { + if (!in_procedure_section(state)) + badcode("PUSH statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + + ".inner_collection.push_back(" + + get_c_expression(state, tokens[1]) + ");", + state.where); + return; + } else { + badcode("List - Value type mismatch", state.where); + } + } + if (line_like("GET LENGTH OF $list IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET LENGTH OF (list) statement outside PROCEDURE section", + state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = " + + get_c_variable(state, tokens[3]) + + ".inner_collection.size();", + state.where); + return; + } + if (line_like("DELETE LAST ELEMENT OF $list", tokens, state)) { + if (!in_procedure_section(state)) + badcode("DELETE LAST ELEMENT OF statement outside PROCEDURE section", + state.where); + // C++ Code + state.add_code("if(" + get_c_variable(state, tokens[4]) + + ".inner_collection.size() > 0)", + state.where); + state.add_code( + get_c_variable(state, tokens[4]) + ".inner_collection.pop_back();", + state.where); + return; + } + if (line_like("REMOVE ELEMENT AT $num-expr FROM $list", tokens, state)) { + if (!in_procedure_section(state)) + badcode("REMOVE ELEMENT AT statement outside PROCEDURE section", + state.where); + // C++ Code + state.add_code("if(" + get_c_variable(state, tokens[5]) + + ".inner_collection.size() > " + + get_c_expression(state, tokens[3]) + ")", + state.where); + state.add_code( + get_c_variable(state, tokens[5]) + ".inner_collection.erase(" + + get_c_variable(state, tokens[5]) + ".inner_collection.begin() + " + + get_c_expression(state, tokens[3]) + ");", + state.where); + return; + } + if (line_like("SPLIT $str-expr BY $str-expr IN $str-list", tokens, state)) { + if (!in_procedure_section(state)) + badcode("SPLIT statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[5]) + " = utf8_split_list(" + + get_c_expression(state, tokens[1]) + ", " + + get_c_expression(state, tokens[3]) + ");", + state.where); + return; + } + if (line_like("GET HOUR IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET HOUT statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[3]) + " = localtime(&ldpl_time)->tm_hour;", + state.where); + return; + } + if (line_like("GET MINUTES IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET MINUTES statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[3]) + " = localtime(&ldpl_time)->tm_min;", + state.where); + return; + } + if (line_like("GET SECONDS IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET SECONDS statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[3]) + " = localtime(&ldpl_time)->tm_sec;", + state.where); + return; + } + if (line_like("GET YEAR IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET YEAR statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + + " = localtime(&ldpl_time)->tm_year + 1900;", + state.where); + return; + } + if (line_like("GET DAY IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET DAY statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code( + get_c_variable(state, tokens[3]) + " = localtime(&ldpl_time)->tm_mday;", + state.where); + return; + } + if (line_like("GET MONTH IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET MONTH statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + + " = localtime(&ldpl_time)->tm_mon + 1;", + state.where); + return; + } + if (line_like("GET MONTH IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET MONTH statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + + " = localtime(&ldpl_time)->tm_mon + 1;", + state.where); + return; + } + if (line_like("GET EPOCH IN $num-var", tokens, state)) { + if (!in_procedure_section(state)) + badcode("GET EPOCH statement outside PROCEDURE section", state.where); + // C++ Code + state.add_code(get_c_variable(state, tokens[3]) + " = ldpl_time;", + state.where); + return; + } + // Custom Statements + if (line_like("CREATE STATEMENT $string EXECUTING $subprocedure", tokens, + state)) { + if (!in_procedure_section(state)) + badcode("CREATE STATEMENT statement outside PROCEDURE section", + state.where); + if (state.closing_subprocedure()) + badcode("CREATE STATEMENT statement inside SUB-PROCEDURE", state.where); + else if (state.closing_if()) + badcode("CREATE STATEMENT statement inside IF", state.where); + else if (state.closing_loop()) + badcode("CREATE STATEMENT statement inside WHILE or FOR", state.where); + string model_line = tokens[2].substr(1, tokens[2].size() - 2); + vector model_tokens; + vector parameters = state.subprocedures[tokens[4]]; + trim(model_line); + tokenize(model_line, model_tokens, state.where, true, ' '); + size_t param_count = 0; + size_t keyword_count = 0; + string valid_keyword_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + model_line = ""; + for (string &token : model_tokens) { + if (token == "$") { + ++param_count; + if (param_count > parameters.size()) break; + vector type = + state.variables[tokens[4]][parameters[param_count - 1]]; + if (type == vector{1}) + model_line += "$num-expr "; + else if (type == vector{2}) + model_line += "$str-expr "; + else { + model_line += "$var-type-"; + for (size_t i = 0; i < type.size(); ++i) { + model_line += to_string(type[i]); + } + model_line += " "; } - } + } else if (token.find_first_not_of(valid_keyword_chars) == string::npos) { + ++keyword_count; + model_line += token + " "; + } else { + badcode("CREATE STATEMENT with invalid token \"" + token + "\"", + state.where); + } + } + if (param_count != parameters.size()) + badcode("CREATE STATEMENT parameters count doesn't match SUB-PROCEDURE", + state.where); + if (keyword_count == 0) + badcode("CREATE STATEMENT without keywords", state.where); + state.custom_statements.emplace_back(model_line, tokens[4]); + return; + } + for (pair &statement : state.custom_statements) { + if (line_like(statement.first, tokens, state)) { + string prefix = statement.first.substr(0, statement.first.find("$")); + if (!in_procedure_section(state)) + badcode(prefix + "statement outside PROCEDURE section", state.where); + vector model_tokens; + vector parameters; + tokenize(statement.first, model_tokens, state.where, false, ' '); + for (size_t i = 0; i < model_tokens.size(); i++) { + if (model_tokens[i][0] == '$') parameters.push_back(tokens[i]); + } + add_call_code(statement.second, parameters, state); + return; + } + } - badcode("Malformed statement", state.where); + badcode("Malformed statement", state.where); } \ No newline at end of file diff --git a/src/data_types/compiler_state.h b/src/data_types/compiler_state.h index b59cc99..66d04e2 100644 --- a/src/data_types/compiler_state.h +++ b/src/data_types/compiler_state.h @@ -119,7 +119,7 @@ struct compiler_state { if (number_type == 1) { c_type = "ldpl_number"; } else if (number_type == 2) { - c_type = "string"; + c_type = "chText"; } else if (number_type == 3) { c_type = "ldpl_list<" + c_type + ">"; } else if (number_type == 4) { diff --git a/src/ldpl.cpp b/src/ldpl.cpp index 659f69d..41cab74 100644 --- a/src/ldpl.cpp +++ b/src/ldpl.cpp @@ -252,7 +252,7 @@ int main(int argc, const char *argv[]) // Add default variable declaration code to the generated code state.variables[""]["ARGV"] = {2, 3}; // List of text - state.add_var_code("ldpl_list " + fix_identifier("ARGV", true) + ";"); + state.add_var_code("ldpl_list " + fix_identifier("ARGV", true) + ";"); state.variables[""]["ERRORCODE"] = {1}; // Declared in ldpl_lib.cpp state.variables[""]["ERRORTEXT"] = {2}; // Declared in ldpl_lib.cpp state.add_code("for(int i = 1; i < argc; ++i)"); @@ -306,8 +306,8 @@ int main(int argc, const char *argv[]) // If only to print the generated code (IR) was required if (show_ir) { - cout << "#include \"" << LDPLLIBLOCATION << "/ldpl_lib.cpp\"" << endl; // Add LDPL library - // TODO delete this and make the library turn into an included string at compile-time + cout << "#include \"" << LDPLLIBLOCATION << "/ldpl_lib.cpp\"" + << endl; // Add LDPL library for (string line : state.variable_code) cout << line << endl; for (string line : state.subroutine_code) @@ -320,7 +320,8 @@ int main(int argc, const char *argv[]) // Otherwise, save the generated code ofstream myfile; myfile.open("ldpl-temp.cpp"); - myfile << "#include \"" << LDPLLIBLOCATION << "/ldpl_lib.cpp\"" << endl; // Add LDPL library + myfile << "#include \"" << LDPLLIBLOCATION << "/ldpl_lib.cpp\"" + << endl; // Add LDPL library for (string line : state.variable_code) myfile << line << endl; for (string line : state.subroutine_code) diff --git a/src/ldpl.h b/src/ldpl.h index 0a923dc..6ae1e7a 100644 --- a/src/ldpl.h +++ b/src/ldpl.h @@ -1,6 +1,4 @@ /* --- STD Includes --- */ -#ifndef LDPL -#define LDPL #include #include #include @@ -13,10 +11,6 @@ #include #include -#ifndef LDPLLIBLOCATION -#define LDPLLIBLOCATION "" -#endif - /* --- Namespace Usage --- */ using namespace std; @@ -39,7 +33,7 @@ using namespace std; /* --- Global Variables --- */ bool show_ir = false; // Show internal representation? string &escape_c_quotes(string &str); -vector extensions; // C++ extensions to add to the file +vector extensions; // C++ extensions to add to the file vector extension_flags; // Flags to pass to the C++ Compiler /* --- Custom Data Types --- */ @@ -103,5 +97,4 @@ void open_subprocedure_code(compiler_state &state); void add_call_code(string &subprocedure, vector ¶meters, compiler_state &state); string current_os(); bool is_map(string &token, compiler_state &state); -void badcode(const string &msg, const code_location where); -#endif \ No newline at end of file +void badcode(const string &msg, const code_location where); \ No newline at end of file diff --git a/src/ldpl_lib/ldpl_lib.cpp b/src/ldpl_lib/ldpl_lib.cpp index 58b0db2..effc2b4 100644 --- a/src/ldpl_lib/ldpl_lib.cpp +++ b/src/ldpl_lib/ldpl_lib.cpp @@ -2,6 +2,7 @@ #include #include #include + #include #include #include @@ -11,27 +12,109 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #define NVM_FLOAT_EPSILON 0.00000001 -#define CRLF "\r\n" +#define CRLF "\n" #define ldpl_number double #define ldpl_vector ldpl_map -#define string string +#define ldpl_text chText using namespace std; +// ------------------------------------------------------- + +#ifndef CHTEXT +#define CHTEXT +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +class chText +{ +private: + vector buffer; + string stringRep; + size_t chText_get_str_utf8length(const string cstr); + void createFromString(const string &cstr); + void createFromChar(const char *cstr); + void createFromMem(const char *mem, size_t len); + +public: + size_t size() const; + bool empty() const; + size_t length() const; + string str_rep(); + chText(); + chText(const string &x); + chText(const double &f); + chText &operator=(const string &x); + chText(const char *x); + chText &operator=(const char *x); + chText(const char *x, size_t len); + chText(char x); + chText &operator=(char x); + chText operator[](size_t i) const; + string &operator[](int i); + friend ostream &operator<<(ostream &out, const chText &c); + friend chText operator+(const chText &c1, const chText &c2); + friend chText operator+(const string &c1, const chText &c2); + friend chText operator+(const chText &c1, const string &c2); + friend chText operator+(const char *c1, const chText &c2); + friend chText operator+(const chText &c1, const char *c2); + friend bool operator<(const chText &c1, const chText &c2); + friend bool operator>(const chText &c1, const chText &c2); + friend bool operator==(const chText &ch1, const chText &ch2); + friend bool operator==(const chText &ch1, const string &ch2); + friend bool operator==(const chText &ch1, const char *ch2); + friend bool operator==(const string &ch2, const chText &ch1); + friend bool operator==(const char *ch2, const chText &ch1); + friend bool operator==(const chText &ch1, const char ch2); + friend bool operator==(const char ch2, const chText &ch1); + friend bool operator!=(const chText &ch1, const chText &ch2); + bool loadFile(const string &fileName); + chText &operator+=(const chText &txt); + chText &operator+=(const string &txt); + chText &operator+=(const char *txt); + bool isAlphanumeric(); + bool isAlphanumeric(size_t from); + bool isNumber(); + double getNumber(); + chText substr(size_t from, size_t count); + chText &erase(size_t from, size_t count); + chText substr(size_t from); + int compare(size_t from, size_t count, const chText &other); + int compare(const chText &other); +}; + +ostream &operator<<(ostream &out, const chText &c); +chText operator+(const chText &c1, const chText &c2); +chText operator+(const string &c1, const chText &c2); +chText operator+(const chText &c1, const string &str); +chText operator+(const char *c1, const chText &c2); +chText operator+(const chText &c1, const char *str); +bool operator==(const chText &ch1, const chText &ch2); +bool operator==(const string &c1, const chText &ch2); +bool operator==(const chText &ch1, const string &c2); +bool operator==(const char *c1, const chText &ch2); +bool operator==(const chText &ch1, const char *c2); +bool operator==(const char c1, const chText &ch2); +bool operator==(const chText &ch1, const char c2); +bool operator<(const chText &c1, const chText &c2); +bool operator>(const chText &c1, const chText &c2); +bool operator!=(const chText &ch1, const chText &ch2); +chText to_ldpl_string(double x); +#endif + // Gets length of utf8-encoded c++ string -size_t get_str_utf8length(const string cstr) +size_t chText::chText_get_str_utf8length(const string cstr) { size_t len = cstr.size(); size_t utf8len = 0; @@ -55,8 +138,124 @@ size_t get_str_utf8length(const string cstr) } return utf8len; } +// Fills buffer with utf8-encoded c++ string +void chText::createFromString(const string &cstr) +{ + createFromMem(cstr.c_str(), cstr.size()); +} +// Fills buffer with utf8-encoded c++ string +void chText::createFromChar(const char *cstr) +{ + // If we copy the implementation we can do without the `strlen` call. + createFromMem(cstr, strlen(cstr)); +} +// Fills buffer with utf8-encoded c++ string +void chText::createFromMem(const char *cstr, size_t cstrlen) +{ + buffer.clear(); + size_t chPos = 0; + for (size_t i = 0; i < cstrlen; ++i) + { + string ch = ""; + char c = cstr[i]; + if (c >= 0 && c <= 127) + { + ch += c; + } + else if ((c & 0xE0) == 0xC0) + { + ch += c; + ch += cstr[++i]; + } + else if ((c & 0xF0) == 0xE0) + { + ch += c; + ch += cstr[++i]; + ch += cstr[++i]; + } + else if ((c & 0xF8) == 0xF0) + { + ch += c; + ch += cstr[++i]; + ch += cstr[++i]; + ch += cstr[++i]; + } + buffer.push_back(ch); + chPos++; + } +} +size_t chText::size() const { return buffer.size(); } +bool chText::empty() const { return buffer.empty(); } +size_t chText::length() const { return size(); } +string chText::str_rep() +{ + stringRep = ""; + for (size_t i = 0; i < size(); ++i) + { + stringRep += buffer[i]; + } + return stringRep; +} +// default constructor +chText::chText() {} +// conversion from string (constructor): +chText::chText(const string &x) { createFromString(x); } + +// conversion from double (constructor): +chText::chText(const double &f) +{ + std::string str = to_string(f); + str.erase(str.find_last_not_of('0') + 1, std::string::npos); + str.erase(str.find_last_not_of('.') + 1, std::string::npos); + createFromString(str); +} -/* bool loadFile(const string &fileName) +// conversion from string (assignment): +chText &chText::operator=(const string &x) +{ + createFromString(x); + return *this; +} +// conversion from char * (constructor): +chText::chText(const char *x) { createFromChar(x); } +// conversion from char * (assignment): +chText &chText::operator=(const char *x) +{ + createFromChar(x); + return *this; +} +// conversion from char * and size (constructor): +chText::chText(const char *x, size_t len) { createFromMem(x, len); } +// conversion from char (constructor): +chText::chText(char x) { createFromMem(&x, 1); } +// conversion from char (assignment): +chText &chText::operator=(char x) +{ + createFromMem(&x, 1); + return *this; +} +// [] for reading +chText chText::operator[](size_t i) const +{ + if (i >= buffer.size()) + { + cout << "Out-of-bounds index access." << endl; + exit(1); + } + chText c = buffer[i]; + return c; +} +// [] for setting +string &chText::operator[](int i) +{ + if (i >= buffer.size()) + { + cout << "Out-of-bounds index access." << endl; + exit(1); + } + return buffer[i]; +} +bool chText::loadFile(const string &fileName) { int fd = open(fileName.c_str(), O_RDONLY); if (fd == -1) @@ -78,28 +277,57 @@ size_t get_str_utf8length(const string cstr) munmap(fmmap, flen); close(fd); return true; -}*/ +} +// += operator +chText &chText::operator+=(const chText &txt) +{ + buffer.insert(buffer.end(), txt.buffer.begin(), txt.buffer.end()); + return *this; +} +// += operator +chText &chText::operator+=(const string &txt) +{ + chText c2 = txt; + buffer.insert(buffer.end(), c2.buffer.begin(), c2.buffer.end()); + return *this; +} +// += operator +chText &chText::operator+=(const char *txt) +{ + chText c2 = txt; + buffer.insert(buffer.end(), c2.buffer.begin(), c2.buffer.end()); + return *this; +} -bool isAlphanumeric(const string &str) +bool chText::isAlphanumeric() { - for (const char &c : str) - if (!isalnum(c)) - return false; + for (const string &s : buffer) + { + for (const char &c : s) + if (!isalnum(c)) + return false; + } return true; } -bool isAlphanumeric(const string &str, size_t from) +bool chText::isAlphanumeric(size_t from) { - for (size_t i = from; i < str.length(); ++i) + for (size_t i = from; i < size(); ++i) { - if (!isalnum(str[i])) - return false; + for (const char &c : buffer[i]) + if (!isalnum(c)) + return false; } return true; } -bool isNumber(const string &number) +bool chText::isNumber() { + string number = ""; + for (size_t i = 0; i < size(); ++i) + { + number += buffer[i]; + } unsigned int firstchar = 0; if (number[0] == '-') firstchar = 1; @@ -136,6 +364,7 @@ bool isNumber(const string &number) } } f_number += number.substr(i - 1); + number = f_number; return true; } else @@ -144,50 +373,57 @@ bool isNumber(const string &number) } } -double getNumber(const string &number) +double chText::getNumber() { + string number = ""; + for (size_t i = 0; i < size(); ++i) + { + number += buffer[i]; + } return stod(number); } -string substr(const string &s, size_t from, size_t count) +chText chText::substr(size_t from, size_t count) { - if (from > s.length()) - return ""; - return s.substr(from, count); + count = from + count > buffer.size() ? buffer.size() - from : count; + chText newText; + newText.buffer.insert(newText.buffer.begin(), buffer.begin() + from, + buffer.begin() + from + count); + return newText; } -/*string &string::erase(size_t from, size_t count) +chText &chText::erase(size_t from, size_t count) { buffer.erase(buffer.begin() + from, buffer.begin() + from + count); return *this; -}*/ +} -string substr(const string &s, size_t from) +chText chText::substr(size_t from) { - if (from > s.length()) - return ""; - return s.substr(); + chText newText; + newText.buffer.insert(newText.buffer.begin(), buffer.begin() + from, + buffer.end()); + return newText; } // NOTE: returns 0 on equality, -1 if the string is shorter, and 1 in any other // case. -/* int compare(const string &s, size_t from, size_t count, const string &other) +int chText::compare(size_t from, size_t count, const chText &other) { - // TODO: Wtf is the point of this // Fix count to respect the actual end of the buffer. - count = from + count > s.length() ? s.length() - from : count; + count = from + count > buffer.size() ? buffer.size() - from : count; // Compare sizes before anything else for efficiency. - if (count < other.length()) + if (count < other.buffer.size()) return -1; - if (count > other.length()) + if (count > other.buffer.size()) return 1; for (size_t i = from, j = 0; j < count; ++i, ++j) - if (s[i] != other[j]) + if (buffer[i] != other.buffer[j]) return 1; // We already know it's not shorter, see above. return 0; -}*/ +} -/*int string::compare(const string &other) +int chText::compare(const chText &other) { if (*this == other) return 0; @@ -195,7 +431,127 @@ string substr(const string &s, size_t from) return -1; else return 1; -}*/ +} + +ostream &operator<<(ostream &out, const chText &c) +{ + for (const string &s : c.buffer) + { + out << s; + } + return out; +} + +chText operator+(const chText &c1, const chText &c2) +{ + chText res = c1; + res.buffer.insert(res.buffer.end(), c2.buffer.begin(), c2.buffer.end()); + return res; +} + +chText operator+(const string &c1, const chText &c2) +{ + chText res = c1; + res.buffer.insert(res.buffer.end(), c2.buffer.begin(), c2.buffer.end()); + return res; +} + +chText operator+(const chText &c1, const string &str) +{ + chText res = c1; + chText c2 = str; + res.buffer.insert(res.buffer.end(), c2.buffer.begin(), c2.buffer.end()); + return res; +} + +chText operator+(const char *c1, const chText &c2) +{ + chText res = c1; + res.buffer.insert(res.buffer.end(), c2.buffer.begin(), c2.buffer.end()); + return res; +} + +chText operator+(const chText &c1, const char *str) +{ + chText res = c1; + chText c2 = str; + res.buffer.insert(res.buffer.end(), c2.buffer.begin(), c2.buffer.end()); + return res; +} + +bool operator==(const chText &ch1, const chText &ch2) +{ + return ch1.buffer == ch2.buffer; +} + +bool operator==(const string &c1, const chText &ch2) +{ + const chText ch1 = c1; + return ch1 == ch2; +} + +bool operator==(const chText &ch1, const string &c2) +{ + const chText ch2 = c2; + return ch1 == ch2; +} + +bool operator==(const char *c1, const chText &ch2) +{ + const chText ch1 = c1; + return ch1 == ch2; +} + +bool operator==(const chText &ch1, const char *c2) +{ + const chText ch2 = c2; + return ch1 == ch2; +} + +bool operator==(const char c1, const chText &ch2) +{ + const chText ch1 = c1; + return ch1 == ch2; +} + +bool operator==(const chText &ch1, const char c2) +{ + const chText ch2 = c2; + return ch1 == ch2; +} + +bool operator<(const chText &c1, const chText &c2) +{ + size_t max = + c1.buffer.size() > c2.buffer.size() ? c2.buffer.size() : c1.buffer.size(); + for (size_t i = 0; i < max; ++i) + { + if (c1.buffer[i] < c2.buffer[i]) + return true; + else if (c1.buffer[i] > c2.buffer[i]) + return false; + } + return c1.buffer.size() < c2.buffer.size(); +} + +bool operator>(const chText &c1, const chText &c2) +{ + size_t max = + c1.buffer.size() > c2.buffer.size() ? c2.buffer.size() : c1.buffer.size(); + for (size_t i = 0; i < max; ++i) + { + if (c1.buffer[i] > c2.buffer[i]) + return true; + else if (c1.buffer[i] < c2.buffer[i]) + return false; + } + return c1.buffer.size() > c2.buffer.size(); +} + +bool operator!=(const chText &ch1, const chText &ch2) +{ + return ch1.buffer != ch2.buffer; +} // --------------------------------------------------------------------------------------------------- @@ -203,14 +559,14 @@ string substr(const string &s, size_t from) ifstream file_loading_stream; ofstream file_writing_stream; string file_loading_line; -string joinvar; // Generic temporary use text variable (used by join but can be +chText joinvar; // Generic temporary use text variable (used by join but can be // used by any other statement as well) ldpl_number VAR_ERRORCODE = 0; -string VAR_ERRORTEXT = ""; +chText VAR_ERRORTEXT = ""; // Forward declarations -string to_ldpl_string(ldpl_number x); -string trimCopy(const string &line); +chText to_ldpl_string(ldpl_number x); +chText trimCopy(chText _line); #ifndef LDPLMAP #define LDPLMAP @@ -218,7 +574,7 @@ template struct ldpl_map { unordered_map inner_collection; - T &operator[](string i); + T &operator[](chText i); T &operator[](ldpl_number i); bool operator==(const ldpl_map &map2) const; }; @@ -231,15 +587,15 @@ bool ldpl_map::operator==(const ldpl_map &map2) const } template -T &ldpl_map::operator[](string i) +T &ldpl_map::operator[](chText i) { - return inner_collection[i]; + return inner_collection[i.str_rep()]; } template T &ldpl_map::operator[](ldpl_number i) { - return inner_collection[to_ldpl_string(i)]; + return inner_collection[to_ldpl_string(i).str_rep()]; } #ifndef LDPLLIST @@ -273,7 +629,7 @@ T &ldpl_list::operator[](ldpl_number i) } template -void get_indices(ldpl_list &dest, ldpl_vector &source) +void get_indices(ldpl_list &dest, ldpl_vector &source) { dest.inner_collection.clear(); int i = 0; @@ -302,15 +658,16 @@ ldpl_number input_number() } } -ldpl_number to_number(const string &textNumber) +ldpl_number to_number(chText textNumber) { + string a = textNumber.str_rep(); try { // This is used to disallow the use of hexadecimal and binary literals. - for (char i : textNumber) + for (char i : a) if ((i < '0' || i > '9') && i != '-' && i != '.') return 0; - ldpl_number num = stod(textNumber); + ldpl_number num = stod(a); return num; } catch (const invalid_argument &ia) @@ -346,11 +703,11 @@ bool num_equal(ldpl_number a, ldpl_number b) return fabs(a - b) < NVM_FLOAT_EPSILON; } -int str_cmp(const string &a, const string &b) +int str_cmp(chText a, chText b) { if (a == b) return 0; - if (a < b) + if (a.str_rep() < b.str_rep()) return -1; else return 1; @@ -363,13 +720,13 @@ ldpl_number modulo(ldpl_number a, ldpl_number b) return (f_a % f_b + f_b) % f_b; } -void join(const string &a, const string &b, string &c) +void join(const chText &a, const chText &b, chText &c) { c = a + b; } // https://stackoverflow.com/a/27658515 -string str_replace(const string &s, const string &find, const string &replace) +chText str_replace(string s, string find, string replace) { string result; size_t find_len = find.size(); @@ -384,9 +741,9 @@ string str_replace(const string &s, const string &find, const string &replace) return result; } -ldpl_number get_char_num(const string &chr) +ldpl_number get_char_num(chText chr) { - int length = chr.length(); + int length = chr.str_rep().size(); if (length != 1) { VAR_ERRORTEXT = @@ -397,78 +754,20 @@ ldpl_number get_char_num(const string &chr) } VAR_ERRORTEXT = ""; VAR_ERRORCODE = 0; - ldpl_number ord = (unsigned char)chr[0]; + ldpl_number ord = (unsigned char)chr.str_rep()[0]; return ord; } -string charat(const string &s, ldpl_number pos) +chText charat(const chText &s, ldpl_number pos) { - int graphemeCount = 0; - size_t i = 0; - - while (i < s.size()) - { - unsigned char c = s[i]; - size_t charLen = 0; - - if (c < 0x80) - { // 1-byte character - charLen = 1; - } - else if ((c & 0xE0) == 0xC0) - { // 2-byte character - charLen = 2; - } - else if ((c & 0xF0) == 0xE0) - { // 3-byte character - charLen = 3; - } - else if ((c & 0xF8) == 0xF0) - { // 4-byte character - charLen = 4; - } - - bool isCombiningCharacter = false; - if (charLen > 1) - { - int cp = 0; - if (charLen == 2) - { - cp = ((c & 0x1F) << 6) | (s[i + 1] & 0x3F); - } - else if (charLen == 3) - { - cp = ((c & 0x0F) << 12) | ((s[i + 1] & 0x3F) << 6) | (s[i + 2] & 0x3F); - } - else if (charLen == 4) - { - cp = ((c & 0x07) << 18) | ((s[i + 1] & 0x3F) << 12) | ((s[i + 2] & 0x3F) << 6) | (s[i + 3] & 0x3F); - } - if (cp >= 0x0300 && cp <= 0x036F) - { - isCombiningCharacter = true; - } - } - - if (!isCombiningCharacter) - { - if (graphemeCount == pos) - { - return s.substr(i, charLen); - } - graphemeCount++; - } - - i += charLen; - } - - return ""; // Return an empty string if the position is out of range + unsigned int _pos = floor(pos); + return s[pos]; } // Convert ldpl_number to LDPL string, killing trailing 0's // https://stackoverflow.com/questions/16605967/ & // https://stackoverflow.com/questions/13686482/ -string to_ldpl_string(ldpl_number x) +chText to_ldpl_string(ldpl_number x) { ostringstream out; out.precision(10); @@ -479,7 +778,13 @@ string to_ldpl_string(ldpl_number x) return str; } -string exec(const char *cmd) +#include +#include +#include +#include +#include + +chText exec(const char *cmd) { array buffer; string result; @@ -495,6 +800,8 @@ string exec(const char *cmd) return result; } +#include + ldpl_number get_random() { random_device rd; @@ -504,13 +811,13 @@ ldpl_number get_random() return r; } -string expandHomeDirectory(const string &filename) +string expandHomeDirectory(string filename) { #if defined(_WIN32) return filename; #else - string homeDir = exec("echo $HOME"); - homeDir = trimCopy(homeDir); + string homeDir = exec("echo $HOME").str_rep(); + homeDir = trimCopy(homeDir).str_rep(); string newPath = ""; for (size_t i = 0; i < filename.length(); ++i) { @@ -562,7 +869,7 @@ std::istream &getlineSafe(std::istream &is, std::string &t) } } -void load_file(const string &filename, string &destination) +void load_file(chText filename, chText &destination) { // Default to fail values. int fd = -1; @@ -574,7 +881,8 @@ void load_file(const string &filename, string &destination) VAR_ERRORTEXT = "The file '" + filename + "' couldn't be opened."; VAR_ERRORCODE = 1; - fd = open(expandHomeDirectory(filename).c_str(), O_RDONLY); + string fileName = filename.str_rep(); + fd = open(expandHomeDirectory(fileName).c_str(), O_RDONLY); if (fd == -1) goto bail; if (fstat(fd, &buf) == -1) @@ -583,7 +891,7 @@ void load_file(const string &filename, string &destination) fmap = mmap(NULL, flen, PROT_READ, MAP_SHARED, fd, 0); if (fmap == MAP_FAILED) goto bail; - destination = string((const char *)fmap, flen); + destination = chText((const char *)fmap, flen); VAR_ERRORTEXT = ""; VAR_ERRORCODE = 0; @@ -596,9 +904,9 @@ void load_file(const string &filename, string &destination) } // Used by append_ and write_. -void save_to_file(const string &filename, const string &content, ios_base::openmode mode) +void save_to_file(chText filename, chText content, ios_base::openmode mode) { - file_writing_stream.open(expandHomeDirectory(filename), mode); + file_writing_stream.open(expandHomeDirectory(filename.str_rep()), mode); if (!file_writing_stream.is_open()) { VAR_ERRORTEXT = "Could not open " + filename; @@ -617,16 +925,16 @@ void save_to_file(const string &filename, const string &content, ios_base::openm file_writing_stream.close(); } -void append_to_file(const string &filename, const string &content) +void append_to_file(chText filename, chText content) { save_to_file(filename, content, ios_base::app); } -void write_file(const string &filename, const string &content) +void write_file(chText filename, chText content) { save_to_file(filename, content, ios_base::out); } -ldpl_number utf8GetIndexOf(const string &haystack, const string &needle) +ldpl_number utf8GetIndexOf(chText haystack, chText needle) { int lenHaystack = haystack.size(); int lenNeedle = needle.size(); @@ -642,7 +950,7 @@ ldpl_number utf8GetIndexOf(const string &haystack, const string &needle) return -1; } -ldpl_number utf8Count(const string &haystack, const string &needle) +ldpl_number utf8Count(chText haystack, chText needle) { int lenHaystack = haystack.size(); int lenNeedle = needle.size(); @@ -660,33 +968,36 @@ ldpl_number utf8Count(const string &haystack, const string &needle) } // Converts string to uppercase -string toUpperCopy(string s) +chText toUpperCopy(chText str) { - std::transform(s.begin(), s.end(), s.begin(), ::toupper); - return s; + string out = str.str_rep(); + std::transform(out.begin(), out.end(), out.begin(), ::toupper); + return out; } // Converts string to lowercase -string toLowerCopy(string s) +chText toLowerCopy(chText str) { - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - return s; + string out = str.str_rep(); + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + return out; } // Removes all trailing and ending whitespace from a string -string trimCopy(const string &line) +chText trimCopy(chText _line) { + string line = _line.str_rep(); // If the string is empty - if (line.length() == 0) + if (line.size() == 0) return line; // If the string has only one character - if (line.length() == 1 && !isspace(line[0])) + if (line.size() == 1 && !isspace(line[0])) return line; // Left trim int first = 0; - for (unsigned int i = 0; i < line.length(); ++i) + for (unsigned int i = 0; i < line.size(); ++i) { if (!isspace(line[i])) { @@ -697,7 +1008,7 @@ string trimCopy(const string &line) // Right trim int last = 0; - for (unsigned int i = line.length() - 1; i >= 0; --i) + for (unsigned int i = line.size() - 1; i >= 0; --i) { if (!isspace(line[i])) { @@ -713,13 +1024,15 @@ string trimCopy(const string &line) } } - // Trim the string and return - return line.substr(first, last - first); + // Trim the string + line = line.substr(first, last - first); + + return line; } -ldpl_list utf8_split_list(const string &haystack, const string &needle) +ldpl_list utf8_split_list(chText haystack, chText needle) { - ldpl_list result; + ldpl_list result; int lenHaystack = haystack.size(); int lenNeedle = needle.size(); if (lenNeedle > 0) @@ -730,7 +1043,7 @@ ldpl_list utf8_split_list(const string &haystack, const string &needle) { if (haystack.substr(i, lenNeedle) == needle) { - string token = haystack.substr(last_start, i - last_start); + chText token = haystack.substr(last_start, i - last_start); if (token.length() > 0) { result.inner_collection.push_back(token); @@ -744,7 +1057,7 @@ ldpl_list utf8_split_list(const string &haystack, const string &needle) } } // Grab everything after the last needle - string token = haystack.substr(last_start, lenHaystack - last_start); + chText token = haystack.substr(last_start, lenHaystack - last_start); if (token.length() > 0) { result.inner_collection.push_back(token); @@ -758,64 +1071,3 @@ ldpl_list utf8_split_list(const string &haystack, const string &needle) } return result; } - -int countGraphemes(const string &str) -{ - int graphemeCount = 0; - size_t i = 0; - - while (i < str.size()) - { - unsigned char c = str[i]; - size_t charLen = 0; - - if (c < 0x80) - { // 1-byte character - charLen = 1; - } - else if ((c & 0xE0) == 0xC0) - { // 2-byte character - charLen = 2; - } - else if ((c & 0xF0) == 0xE0) - { // 3-byte character - charLen = 3; - } - else if ((c & 0xF8) == 0xF0) - { // 4-byte character - charLen = 4; - } - - // Check for combining characters (this is a simplified check) - bool isCombiningCharacter = false; - if (charLen > 1) - { - int cp = 0; - if (charLen == 2) - { - cp = ((c & 0x1F) << 6) | (str[i + 1] & 0x3F); - } - else if (charLen == 3) - { - cp = ((c & 0x0F) << 12) | ((str[i + 1] & 0x3F) << 6) | (str[i + 2] & 0x3F); - } - else if (charLen == 4) - { - cp = ((c & 0x07) << 18) | ((str[i + 1] & 0x3F) << 12) | ((str[i + 2] & 0x3F) << 6) | (str[i + 3] & 0x3F); - } - if (cp >= 0x0300 && cp <= 0x036F) - { - isCombiningCharacter = true; - } - } - - if (!isCombiningCharacter) - { - graphemeCount++; - } - - i += charLen; - } - - return graphemeCount; -} \ No newline at end of file