/** * Cloned from https://github.com/coderthetyler/pbg.git * in commit 5b61964c84c56c3ad824b240ca078eba98bc9ca7 * * A small patch was applied to fix some memory leaks * To see it: * git clone https://github.com/coderthetyler/pbg.git && cd pbg * git checkout 5b61964c84c56c3ad824b240ca078eba98bc9ca7 * diff */ #include "pbg.h" #include #include #include /***************************** * * * LOCAL STRUCTURE DIRECTORY * * * *****************************/ /* LITERAL REPRESENTATIONS */ typedef struct { double _val; } pbg_lt_number; /* PBG_LT_NUMBER */ typedef struct { unsigned int _YYYY; /* year */ unsigned int _MM; /* month */ unsigned int _DD; /* day */ } pbg_lt_date; /* PBG_LT_DATE */ typedef char pbg_lt_string; /* PBG_LT_STRING */ /* ERROR REPRESENTATIONS */ typedef struct { int _arity; /* Number of arguments given to operator. */ pbg_field_type _type; /* Type of operator involved in error. */ } pbg_op_arity_err; /* PBG_ERR_OP_ARITY. */ typedef struct { char* _msg; /* Description of syntax error. */ char* _str; /* String in which error occurred. */ int _i; /* Index of error in string. */ } pbg_syntax_err; /* PBG_ERR_SYNTAX */ typedef struct { char* _field; /* String representing the unknown type. */ int _n; /* Length of field. */ } pbg_unknown_type_err; /* PBG_ERR_UNKNOWN_TYPE */ /**************************** * * * LOCAL FUNCTION DIRECTORY * * * ****************************/ /* ERROR MANAGEMENT */ void pbg_err_init(pbg_error* err, pbg_error_type type, int line, char* file, int size, void* data); void pbg_err_alloc(pbg_error* err, int line, char* file); void pbg_err_unknown_type(pbg_error* err, int line, char* file, char* field, int n); void pbg_err_syntax(pbg_error* err, int line, char* file, char* str, int i, char* msg); void pbg_err_op_arity(pbg_error* err, int line, char* file, pbg_field_type type, int arity); void pbg_err_state(pbg_error* err, int line, char* file, char* msg); void pbg_err_op_arg_type(pbg_error* err, int line, char* file, char* msg); char* pbg_error_str(pbg_error_type type); char* pbg_field_type_str(pbg_field_type type); /* FIELD MANAGEMENT */ pbg_field* pbg_field_get(pbg_expr* e, int index); void pbg_field_free(pbg_field* field); int pbg_store_constant(pbg_expr* e, pbg_field field); int pbg_store_variable(pbg_expr* e, pbg_field field); /* FIELD CREATION TOOLKIT */ pbg_field pbg_field_init(pbg_field_type type, int size, void* data); pbg_field pbg_parse_op(pbg_error* err, pbg_field_type type, int numchildren); pbg_field pbg_parse_var(pbg_error* err, char* str, int n); pbg_field pbg_parse_date(pbg_error* err, char* str, int n); pbg_field pbg_parse_number(pbg_error* err, char* str, int n); pbg_field pbg_parse_string(pbg_error* err, char* str, int n); /* FIELD PARSING TOOLKIT */ int pbg_check_op_arity(pbg_field_type type, int numargs); /* FIELD EVALUATION TOOLKIT */ int pbg_evaluate_r(pbg_expr* e, pbg_error* err, pbg_field* field); int pbg_evaluate_op_not(pbg_expr* e, pbg_error* err, pbg_field* field); int pbg_evaluate_op_and(pbg_expr* e, pbg_error* err, pbg_field* field); int pbg_evaluate_op_or(pbg_expr* e, pbg_error* err, pbg_field* field); int pbg_evaluate_op_exst(pbg_expr* e, pbg_error* err, pbg_field* field); int pbg_evaluate_op_eq(pbg_expr* e, pbg_error* err, pbg_field* field); int pbg_evaluate_op_neq(pbg_expr* e, pbg_error* err, pbg_field* field); int pbg_evaluate_op_order(pbg_expr* e, pbg_error* err, pbg_field* field); int pbg_evaluate_op_type(pbg_expr* e, pbg_error* err, pbg_field* field); /* JANITORIAL FUNCTIONS */ /* No local functions. */ /* CONVERSION & CHECKING TOOLKIT */ pbg_field_type pbg_gettype(char* str, int n); int pbg_istypedate(char* str, int n); int pbg_istypenumber(char* str, int n); int pbg_istypebool(char* str, int n); int pbg_istypestring(char* str, int n); int pbg_istrue(char* str, int n); int pbg_isfalse(char* str, int n); int pbg_isvar(char* str, int n); int pbg_isnumber(char* str, int n); int pbg_isstring(char* str, int n); int pbg_isdate(char* str, int n); void pbg_tonumber(pbg_lt_number* ptr, char* str, int n); void pbg_todate(pbg_lt_date* ptr, char* str, int n); int pbg_cmpnumber(pbg_lt_number* n1, pbg_lt_number* n2); int pbg_cmpdate(pbg_lt_date* d1, pbg_lt_date* d2); int pbg_cmpstring(pbg_lt_string* s1, pbg_lt_string* s2, int n); int pbg_type_isbool(pbg_field_type type); int pbg_type_isop(pbg_field_type type); /* HELPER FUNCTIONS */ int pbg_isdigit(char c); int pbg_iswhitespace(char c); /********************** * * * ERROR CONSTRUCTION * * * **********************/ void pbg_error_print(pbg_error* err) { pbg_op_arity_err* arity; pbg_syntax_err* syntax; pbg_unknown_type_err* utype; if(err->_type == PBG_ERR_NONE) return; printf("error %s at %s:%d", pbg_error_str(err->_type), err->_file, err->_line); switch(err->_type) { case PBG_ERR_OP_ARG_TYPE: case PBG_ERR_STATE: printf(": %s", (char*) err->_data); break; case PBG_ERR_OP_ARITY: arity = (pbg_op_arity_err*) err->_data; printf(": operator %s cannot take %d arguments!", pbg_field_type_str(arity->_type), arity->_arity); break; case PBG_ERR_SYNTAX: syntax = (pbg_syntax_err*) err->_data; printf(": %s -> %s", (char*) syntax->_msg, syntax->_str+syntax->_i); break; case PBG_ERR_UNKNOWN_TYPE: utype = (pbg_unknown_type_err*) err->_data; printf(": failed to recognize %s (%d bytes)\n", (char*)utype->_field, utype->_n); break; default: break; } printf("\n"); } void pbg_err_init(pbg_error* err, pbg_error_type type, int line, char* file, int size, void* data) { err->_type = type; err->_line = line; err->_file = file; err->_int = size; err->_data = data; } void pbg_err_alloc(pbg_error* err, int line, char* file) { pbg_err_init(err, PBG_ERR_ALLOC, line, file, 0, NULL); } void pbg_err_unknown_type(pbg_error* err, int line, char* file, char* field, int n) { pbg_unknown_type_err* data; data = malloc(n * sizeof(char)); if(data == NULL) { pbg_err_alloc(err, __LINE__, __FILE__); /* gah. */ return; } data->_field = field; data->_n = n; pbg_err_init(err, PBG_ERR_UNKNOWN_TYPE, line, file, n, data); } void pbg_err_syntax(pbg_error* err, int line, char* file, char* str, int i, char* msg) { pbg_syntax_err* data; int size; data = malloc(size = sizeof(pbg_syntax_err)); if(data == NULL) { pbg_err_alloc(err, __LINE__, __FILE__); /* unfortunate. */ return; } data->_msg = msg; data->_str = str; data->_i = i; pbg_err_init(err, PBG_ERR_SYNTAX, line, file, size, data); } void pbg_err_op_arity(pbg_error* err, int line, char* file, pbg_field_type type, int arity) { pbg_op_arity_err* data; int size; data = malloc(size = sizeof(pbg_op_arity_err)); if(data == NULL) { pbg_err_alloc(err, __LINE__, __FILE__); /* unfortunate. */ return; } data->_arity = arity; data->_type = type; pbg_err_init(err, PBG_ERR_OP_ARITY, line, file, size, data); } void pbg_err_state(pbg_error* err, int line, char* file, char* msg) { pbg_err_init(err, PBG_ERR_STATE, line, file, 0, msg); } void pbg_err_op_arg_type(pbg_error* err, int line, char* file, char* msg) { pbg_err_init(err, PBG_ERR_OP_ARG_TYPE, line, file, 0, msg); } void pbg_error_free(pbg_error* err) { if(err->_int != 0) free(err->_data); } /******************** * * * FIELD MANAGEMENT * * * ********************/ /** * This function returns the field identified by the given index. Constant fields * are identified by positive indices starting at 1. Variable fields are * identified by negative indices starting at -1. * @param e PBG expression to get field from. * @param index Index of the field to get. * @return Pointer to the pbg_field in e specified by the index, * NULL if index is 0. */ pbg_field* pbg_field_get(pbg_expr* e, int index) { if(index < 0) return e->_variables - (index+1); if(index > 0) return e->_constants + (index-1); return NULL; } /** * Free's the single pbg_field pointed to by the specified pointer. * @param field pbg_field to free. */ void pbg_field_free(pbg_field* field) { if(field->_data != NULL) free(field->_data); } /** * This function stores the given constant field in the AST. Constant fields are * indexed using positive values starting at 1. * @param e Abstract expression tree to store field in. * @param field Field to store. * @return a positive index if successful, * 0 otherwise. */ int pbg_store_constant(pbg_expr* e, pbg_field field) { int fieldi; if(field._type == PBG_NULL) return 0; fieldi = 1 + e->_numconst++; *pbg_field_get(e, fieldi) = field; return fieldi; } /** * This function stores the given variable field in the AST. Variable fields are * indexed using negative values starting at -1. * @param e Abstract expression tree to store field in. * @param field Field to store. * @return a negative index if successful, * 0 otherwise. */ int pbg_store_variable(pbg_expr* e, pbg_field field) { int fieldi; fieldi = -(1 + e->_numvars++); *pbg_field_get(e, fieldi) = field; return fieldi; } /************************** * * * FIELD CREATION TOOLKIT * * * **************************/ pbg_field pbg_make_date(int year, int month, int day) { int size; pbg_lt_date* data; data = malloc(size = sizeof(pbg_lt_date)); data->_YYYY = year; data->_MM = month; data->_DD = day; return pbg_field_init(PBG_LT_DATE, size, data); } pbg_field pbg_make_bool(int truth) { return pbg_field_init(truth ? PBG_LT_TRUE : PBG_LT_FALSE, 0, NULL); } pbg_field pbg_make_number(double value) { int size; pbg_lt_number* data; data = malloc(size = sizeof(pbg_lt_number)); data->_val = value; return pbg_field_init(PBG_LT_NUMBER, size, data); } pbg_field pbg_make_string(char* str) { int size, n; pbg_lt_string* data; n = strlen(str); data = malloc(size = n * sizeof(pbg_lt_string)); memcpy(data, str, n); return pbg_field_init(PBG_LT_STRING, size, data); } pbg_field pbg_make_null(void) { return pbg_field_init(PBG_NULL, 0, NULL); } /** * Create a new pbg_field with the given arguments. * @param type Type of the field. * @param size Size of the field, however that is measured. * @param data Data to store in the field. * @return the new pbg_field. */ pbg_field pbg_field_init(pbg_field_type type, int size, void* data) { pbg_field field; field._type = type; field._int = size; field._data = data; return field; } /** * Makes a pbg_field representing the given operator type with the specified * number of child fields. * @param err Used to store error, if any. * @param type Type of the operator. * @param argc Number of arguments. * @return the new pbg_field. */ pbg_field pbg_parse_op(pbg_error* err, pbg_field_type type, int argc) { void* data; data = malloc(argc * sizeof(int)); if(data == NULL) pbg_err_alloc(err, __LINE__, __FILE__); return pbg_field_init(type, argc, data); } /** * Makes a field representing a VAR. Attempts to parse the given string as a * VAR. If an error occurs during conversion, then err will be initialized with * the relevant error. * @param err Used to store error, if any. * @param str String to parse as a VAR. * @param n Length of str. * @return a VAR field if successful, a NULL field otherwise. */ pbg_field pbg_parse_var(pbg_error* err, char* str, int n) { int size; void* data; data = malloc(size = (n-2) * sizeof(char)); if(data == NULL) pbg_err_alloc(err, __LINE__, __FILE__); else memcpy(data, str+1, n-2); return pbg_field_init(PBG_LT_VAR, size, data); } /** * Makes a field representing a DATE. Attempts to parse the given string as a * DATE. If an error occurs during conversion, then err will be initialized with * the relevant error. * @param err Used to store error, if any. * @param str String to parse as a DATE. * @param n Length of str. * @return a DATE field if successful, a NULL field otherwise. */ pbg_field pbg_parse_date(pbg_error* err, char* str, int n) { int size; pbg_lt_date* data; data = malloc(size = sizeof(pbg_lt_date)); if(data == NULL) pbg_err_alloc(err, __LINE__, __FILE__); else pbg_todate(data, str, n); return pbg_field_init(PBG_LT_DATE, size, data); } /** * Makes a field representing a NUMBER. Attempts to parse the given string as a * NUMBER. If an error occurs during conversion, then err will be initialized * with the relevant error. * @param err Used to store error, if any. * @param str String to parse as a NUMBER. * @param n Length of str. * @return a NUMBER field if successful, a NULL field otherwise. */ pbg_field pbg_parse_number(pbg_error* err, char* str, int n) { int size; pbg_lt_number* data; data = malloc(size = sizeof(pbg_lt_number)); if(data == NULL) pbg_err_alloc(err, __LINE__, __FILE__); else pbg_tonumber(data, str, n); return pbg_field_init(PBG_LT_NUMBER, size, data); } /** * Makes a field representing a STRING. Attempts to parse the given string as a * STRING. If an error occurs during conversion, then err will be initialized * with the relevant error. * @param err Used to store error, if any. * @param str String to parse as a STRING. * @param n Length of str. * @return a STRING field if successful, a NULL field otherwise. */ pbg_field pbg_parse_string(pbg_error* err, char* str, int n) { int size; pbg_lt_string* data; data = malloc(size = (n-2) * sizeof(pbg_lt_string)); if(data == NULL) pbg_err_alloc(err, __LINE__, __FILE__); else memcpy(data, str+1, n-2); return pbg_field_init(PBG_LT_STRING, size, data); } /************************* * * * FIELD PARSING TOOLKIT * * * *************************/ /** * Checks if the operator can legally take the specified number of arguments. * This function encodes the rules for operator arity and should be modified if * a new operator is added. * @param type Type of operator. * @param numargs Number of arguments to operator. * @return 1 if the number of arguments can be legally given to the operator, * 0 if not or if type does not refer to an operator. */ int pbg_check_op_arity(pbg_field_type type, int numargs) { int arity = 0; /* Positive arity specifies "exact arity," i.e. the number of arguments * must be exact. Negative arity specifies a "minimum arity," i.e. the * minimum number of arguments needed. */ switch(type) { case PBG_OP_NOT: arity = 1; break; case PBG_OP_AND: arity = -2; break; case PBG_OP_OR: arity = -2; break; case PBG_OP_EQ: arity = -2; break; case PBG_OP_LT: arity = 2; break; case PBG_OP_GT: arity = 2; break; case PBG_OP_EXST: arity = -1; break; case PBG_OP_NEQ: arity = 2; break; case PBG_OP_LTE: arity = 2; break; case PBG_OP_GTE: arity = 2; break; case PBG_OP_TYPE: arity = -2; break; default: return 0; } if((arity > 0 && numargs != arity) || (-arity > 0 && numargs < -arity)) return 0; return 1; } void pbg_parse(pbg_expr* e, pbg_error* err, char* str) { pbg_parse_n(e, err, str, strlen(str)); } void pbg_parse_n(pbg_expr* e, pbg_error* err, char* str, int n) { int i; int numfields, numvars, numclosings; int depth, maxdepth, reachedend; int instring, invar; int* stack, stacksz; int* groupsz, groupi; int opened; int* fields, *lengths, fieldi; int* closings, closingi; int numconstant, numvariable; pbg_field_type type; int* children; int id = 0; int start, len; /* Always start with a clean error! */ pbg_err_init(err, PBG_ERR_NONE, 0, NULL, 0, NULL); /* Set to NULL to allow for pbg_free to check if needing free. */ e->_constants = NULL; e->_variables = NULL; /* These are initialized to 0 as they are used as counters for the number * of each type of field created. In the end they should be equal to the * associated local variables here. */ e->_numconst = 0; e->_numvars = 0; /******************************************************************* * FIRST PASS * * 1 Count number of groups, fields, and variables. * * 2 Identify depth of the tree. * * 3 Ensure group, string, and variable formatting are correct. * *******************************************************************/ numfields = numvars = numclosings = 0; depth = reachedend = maxdepth = 0; instring = invar = 0; for(i = 0; i < n; i++) { /* Ignore whitespaces. */ if(pbg_iswhitespace(str[i])) continue; /* Open a new group. */ if(str[i] == '(') { depth++, maxdepth++; /* Close current group. */ }else if(str[i] == ')') { numclosings++, depth--; if(depth < 0 || (depth == 0 && reachedend)) break; if(depth == 0 && !reachedend) reachedend = i; /* Process a new field. */ }else{ /* It's a string! */ if(str[i] == '\'') { instring = 1; do i++; while(i != n && !(str[i] == '\'' && str[i-1] != '\\')); if(i != n) instring = 0; /* It's a variable! */ }else if(str[i] == '[') { invar = 1, numvars++; do i++; while(i != n && !(str[i] == ']' && str[i-1] != '\\')); if(i != n) invar = 0; /* It's literally anything else! */ }else while(i != n-1 && !pbg_iswhitespace(str[i+1]) && str[i+1] != '[' && str[i+1] != '(' && str[i+1] != ')') i++; numfields++; } } /* Check if there aren't any fields. */ if(numfields == 0) { pbg_err_syntax(err, __LINE__, __FILE__, str, 0, "No fields in expression."); return; } /* Check if there are too many closing parentheses. */ if(depth < 0) { pbg_err_syntax(err, __LINE__, __FILE__, str, i, "Too many closing parentheses."); return; } /* Check if there are not enough closing parentheses. */ if(depth != 0) { pbg_err_syntax(err, __LINE__, __FILE__, str, 0, "Too few closing parentheses."); return; } /* Check if there are multiple (possible) expressions. */ if(depth == 0 && reachedend && i != n) { pbg_err_syntax(err, __LINE__, __FILE__, str, reachedend, "Too many opening parentheses yield multiple expressions."); return; } /* Check if string is left unclosed. */ if(instring) { pbg_err_syntax(err, __LINE__, __FILE__, str, instring, "Unclosed string."); return; } /* Check if variable is left unclosed. */ if(invar) { pbg_err_syntax(err, __LINE__, __FILE__, str, invar, "Unclosed variable."); return; } /******************************************************************* * SECOND PASS * * 1 Compute size of each group. * * 2 Identify starting index & length of each group. * * 3 Identify index of each group closing. * *******************************************************************/ /* Ensure we have a stack for TRUE/FALSE standalone literals. */ if(maxdepth == 0) maxdepth = 1; /* Use a stack to identify number of fields in each group. */ stack = malloc(2*maxdepth * sizeof(int)); /* Use to record number of fields in each group. Notice that the number of * groups is equal to the number of closings. */ groupsz = calloc(numclosings+1, sizeof(int)); /* Allocate space to record field starting positions & lengths as well as * the positions of group closings. */ fields = (int*) malloc(numfields * sizeof(int)); lengths = (int*) malloc(numfields * sizeof(int)); closings = (int*) malloc((numclosings+1) * sizeof(int)); /* Compute sizes of constant and variable arrays. */ numconstant = numfields - numvars; numvariable = numvars; /* Allocate space for constant and variable field arrays. */ e->_constants = (pbg_field*) malloc(numconstant * sizeof(pbg_field)); e->_variables = (pbg_field*) malloc(numvariable * sizeof(pbg_field)); /* Ensure we got all of the memory we need. */ if(stack == NULL || groupsz == NULL || fields == NULL || lengths == NULL || closings == NULL || e->_constants == NULL || e->_variables == NULL) { free(stack); free(groupsz); free(fields); free(lengths); free(closings); pbg_err_alloc(err, __LINE__, __FILE__); pbg_free(e); return; } /* Do the work! */ opened = stacksz = groupi = closingi = fieldi = 0; stacksz = 1; stack[0] = 0; for(i = 0; i < n; i++) { /* Ignore whitespaces. */ if(pbg_iswhitespace(str[i])) continue; /* Open a new group. Push it onto the stack. */ if(str[i] == '(') { opened = 1; if(groupi != 0) { groupsz[stack[stacksz-1]]++; stack[stacksz++] = groupi; } groupi++; /* Close current group. Pop group it off of the stack. */ }else if(str[i] == ')') { closings[closingi++] = i; stacksz--; /* Process a new field. */ }else{ /* Save index of the field. */ fields[fieldi] = i; /* It's a string! */ if(str[i] == '\'') { do i++; while(i != n && !(str[i] == '\'' && str[i-1] != '\\')); /* It's a variable! */ }else if(str[i] == '[') { do i++; while(i != n && !(str[i] == ']' && str[i-1] != '\\')); /* It's literally anything else! */ }else while(i != n-1 && !pbg_iswhitespace(str[i+1]) && str[i+1] != '[' && str[i+1] != '(' && str[i+1] != ')') i++; /* Compute length of the field. */ lengths[fieldi] = i - fields[fieldi] + 1; /* Identify type of field. */ type = pbg_gettype(str+fields[fieldi], lengths[fieldi]); /* Ensure opener is operator, and no other field is an operator. */ if(opened != pbg_type_isop(type) || (opened = 0)) { pbg_err_syntax(err, __LINE__, __FILE__, str, fields[fieldi], "Field ordering not respected."); free(stack); free(groupsz); free(fields); free(lengths); free(closings); pbg_free(e); return; } /* Add field to current group. */ groupsz[stack[stacksz-1]]++, fieldi++; } } closings[closingi] = n; /****************************** * THIRD PASS * * 1 Build the final tree. * ******************************/ /* Alias simple stack operations for more readable code. */ #define pbg_stack_inputcnt stack[2*(stacksz-1)+1] #define pbg_stack_fieldid stack[2*(stacksz-1)] children = NULL; stacksz = groupi = closingi = 0; for(fieldi = 0; fieldi < numfields; fieldi++) { /* Alias field start and field length for easier use. */ start = fields[fieldi]; len = lengths[fieldi]; /* Parsed all inputs to current operator. Pop it from the stack. */ if(start > closings[closingi]) { closingi++; /* Pop from the stack. */ stacksz--; id = pbg_stack_fieldid; /* Restore list of children from parent operator. */ children = pbg_field_get(e, id)->_data; } /* Identify type of field. */ type = pbg_gettype(str+start, len); /* It's an operator! Push it onto the stack. */ if(pbg_type_isop(type)) { /* Add operator to the tree. */ id = pbg_store_constant(e, pbg_parse_op(err, type, groupsz[groupi]-1)); /* Enforce operator arity. */ if(pbg_check_op_arity(type, groupsz[groupi]-1) == 0) { pbg_err_op_arity(err, __LINE__, __FILE__, type, groupsz[groupi]-1); id = 0; } groupi++; /* Check for errors when adding operator to tree. */ if(id == 0) break; /* Add this operator as a child of the parent operator, if any. */ if(children != NULL) children[pbg_stack_inputcnt++] = id; /* Push the operator onto the stack. */ stacksz++; pbg_stack_fieldid = id; pbg_stack_inputcnt = 0; /* Replace children list with that of the new operator. */ children = pbg_field_get(e, id)->_data; /* It's a literal! */ }else{ /* It's a variable. */ if(type == PBG_LT_VAR) id = pbg_store_variable(e, pbg_parse_var(err, str+start, len)); /* It's a date. */ else if(type == PBG_LT_DATE) id = pbg_store_constant(e, pbg_parse_date(err, str+start, len)); /* It's a number. */ else if(type == PBG_LT_NUMBER) id = pbg_store_constant(e, pbg_parse_number(err, str+start, len)); /* It's a string. */ else if(type == PBG_LT_STRING) id = pbg_store_constant(e, pbg_parse_string(err, str+start, len)); /* It's a simple field. */ else if(type == PBG_LT_TRUE || type == PBG_LT_FALSE || type == PBG_LT_TP_DATE || type == PBG_LT_TP_BOOL || type == PBG_LT_TP_NUMBER || type == PBG_LT_TP_STRING) id = pbg_store_constant(e, pbg_field_init(type, 0, NULL)); /* It's an error... */ else { pbg_err_unknown_type(err, __LINE__, __FILE__, str+start, n); id = 0; } /* Check for errors when adding literal to tree. */ if(id == 0) break; /* Add this literal as a child of the parent operator, if any. */ if(children != NULL) children[pbg_stack_inputcnt++] = id; } } /* Free expression if a parse error occurred. */ if(id == 0) pbg_free(e); /* Clean up! */ free(stack), free(groupsz); free(fields), free(lengths), free(closings); /* Do not perform sanity checks if an error occurred. */ if(pbg_iserror(err)) return; /* Sanity check: verify we parsed everything we expected. */ if(e->_numconst != numconstant || e->_numvars != numvariable) { pbg_err_state(err, __LINE__, __FILE__, "Not all fields were parsed?"); return; } } /**************************** * * * FIELD EVALUATION TOOLKIT * * * ****************************/ int pbg_evaluate_op_not(pbg_expr* e, pbg_error* err, pbg_field* field) { int child0, result; child0 = ((int*)field->_data)[0]; result = pbg_evaluate_r(e, err, pbg_field_get(e, child0)); if(result == PBG_ERROR) return PBG_ERROR; /* Pass error through. */ return result == PBG_TRUE ? PBG_FALSE : PBG_TRUE; } int pbg_evaluate_op_and(pbg_expr* e, pbg_error* err, pbg_field* field) { int i, size, childi, result; size = field->_int; for(i = 0; i < size; i++) { childi = ((int*)field->_data)[i]; result = pbg_evaluate_r(e, err, pbg_field_get(e, childi)); if(result == PBG_ERROR) return PBG_ERROR; /* Pass error through. */ if(result == PBG_FALSE) return PBG_FALSE; } return PBG_TRUE; } int pbg_evaluate_op_or(pbg_expr* e, pbg_error* err, pbg_field* field) { int i, childi, result; for(i = 0; i < field->_int; i++) { childi = ((int*)field->_data)[i]; result = pbg_evaluate_r(e, err, pbg_field_get(e, childi)); if(result == PBG_ERROR) return PBG_ERROR; /* Pass error through. */ if(result == PBG_TRUE) return PBG_TRUE; } return PBG_FALSE; } int pbg_evaluate_op_exst(pbg_expr* e, pbg_error* err, pbg_field* field) { int i, childi; PBG_UNUSED(err); for(i = 0; i < field->_int; i++) { childi = ((int*)field->_data)[i]; if(pbg_field_get(e, childi)->_type == PBG_NULL) return PBG_FALSE; } return PBG_TRUE; } int pbg_evaluate_op_eq(pbg_expr* e, pbg_error* err, pbg_field* field) { int i, child0, childi, result; pbg_field* c0, *ci; PBG_UNUSED(err); /* Ensure type and size of all children are identical. */ child0 = ((int*)field->_data)[0]; c0 = pbg_field_get(e, child0); if(c0->_type == PBG_NULL) { pbg_err_op_arg_type(err, __LINE__, __FILE__, "NULL input given to EQ operator."); return PBG_ERROR; } /* We have a bunch of BOOLs! Evaluate them. */ if(pbg_type_isbool(c0->_type)) { result = pbg_evaluate_r(e, err, c0); for(i = 1; i < field->_int; i++) { childi = ((int*)field->_data)[i]; ci = pbg_field_get(e, childi); if(ci->_type == PBG_NULL) { pbg_err_op_arg_type(err, __LINE__, __FILE__, "NULL input given to EQ operator."); return PBG_ERROR; } if(result != pbg_evaluate_r(e, err, ci)) return PBG_FALSE; } return PBG_TRUE; /* We don't have a bunch of BOOLs! Do standard equality test. */ }else{ for(i = 1; i < field->_int; i++) { childi = ((int*)field->_data)[i]; ci = pbg_field_get(e, childi); if(ci->_type == PBG_NULL) { pbg_err_op_arg_type(err, __LINE__, __FILE__, "NULL input given to EQ operator."); return PBG_ERROR; } if(ci->_int != c0->_int || ci->_type != c0->_type) return PBG_FALSE; /* Ensure each data byte is identical. */ if(memcmp(ci->_data, c0->_data, c0->_int) != 0) return PBG_FALSE; } return PBG_TRUE; } } int pbg_evaluate_op_neq(pbg_expr* e, pbg_error* err, pbg_field* field) { int child0, child1; pbg_field* c0, *c1; PBG_UNUSED(err); child0 = ((int*)field->_data)[0], child1 = ((int*)field->_data)[1]; c0 = pbg_field_get(e, child0), c1 = pbg_field_get(e, child1); if(c0->_type == PBG_NULL || c1->_type == PBG_NULL) { pbg_err_op_arg_type(err, __LINE__, __FILE__, "NULL input given to NEQ operator."); return PBG_ERROR; } /* We have two BOOLs! Evaluate them, and check if they are different. */ if(pbg_type_isbool(c0->_type) && pbg_type_isbool(c1->_type)) return (pbg_evaluate_r(e, err, c0) != pbg_evaluate_r(e, err, c1)) ? PBG_TRUE : PBG_FALSE; /* We don't have a bunch of BOOLs! Do standard difference check. */ else return (c1->_type != c0->_type || c1->_int != c0->_int || memcmp(c1->_data, c0->_data, c0->_int)) ? PBG_TRUE : PBG_FALSE; } int pbg_evaluate_op_order(pbg_expr* e, pbg_error* err, pbg_field* field) { int result; int child0, child1; pbg_field* c0, *c1; child0 = ((int*)field->_data)[0], child1 = ((int*)field->_data)[1]; c0 = pbg_field_get(e, child0), c1 = pbg_field_get(e, child1); if(c0->_type == PBG_NULL || c1->_type == PBG_NULL) { pbg_err_op_arg_type(err, __LINE__, __FILE__, "NULL input given to comparison operator."); return PBG_ERROR; } result = -2; /* Both are NUMBERs. */ if(c0->_type == PBG_LT_NUMBER && c1->_type == PBG_LT_NUMBER) result = pbg_cmpnumber(c0->_data, c1->_data); /* Both are DATEs. */ if(c0->_type == PBG_LT_DATE && c1->_type == PBG_LT_DATE) result = pbg_cmpdate(c0->_data, c1->_data); /* Both are STRINGs. */ if(c0->_type == PBG_LT_STRING && c1->_type == PBG_LT_STRING) result = pbg_cmpstring(c0->_data, c1->_data, c0->_int); /* Both are BOOLs. */ if(pbg_type_isbool(c0->_type) && pbg_type_isbool(c1->_type)) result = pbg_evaluate_r(e, err, c0) - pbg_evaluate_r(e, err, c1); /* Check if mismatched or invalid types. */ if(result == -2) { pbg_err_op_arg_type(err, __LINE__, __FILE__, "Unknown input type to comparison operator"); return PBG_ERROR; /* Compare results according to type of comparison operator. */ }else{ if(field->_type == PBG_OP_LT) return result < 0 ? PBG_TRUE : PBG_FALSE; if(field->_type == PBG_OP_GT) return result > 0 ? PBG_TRUE : PBG_FALSE; if(field->_type == PBG_OP_LTE) return result <= 0 ? PBG_TRUE : PBG_FALSE; if(field->_type == PBG_OP_GTE) return result >= 0 ? PBG_TRUE : PBG_FALSE; pbg_err_state(err, __LINE__, __FILE__, "Unknown result from select comparison function."); return PBG_ERROR; } } int pbg_evaluate_op_type(pbg_expr* e, pbg_error* err, pbg_field* field) { int i, child0, childi; pbg_field* c0, *ci; pbg_field_type type; child0 = ((int*)field->_data)[0]; c0 = pbg_field_get(e, child0); type = c0->_type; /* Ensure the first argument is a type literal. */ if(type < PBG_MIN_LT_TP || type > PBG_MAX_LT_TP) { pbg_err_op_arg_type(err, __LINE__, __FILE__, "First input to TYPE operator must be a type literal."); return PBG_ERROR; } /* Verify types of all trailing arguments. */ for(i = 1; i < field->_int; i++) { childi = ((int*)field->_data)[i]; ci = pbg_field_get(e, childi); if(type == PBG_LT_TP_BOOL && !pbg_type_isbool(ci->_type)) return PBG_FALSE; if(type == PBG_LT_TP_DATE && ci->_type != PBG_LT_DATE) return PBG_FALSE; if(type == PBG_LT_TP_NUMBER && ci->_type != PBG_LT_NUMBER) return PBG_FALSE; if(type == PBG_LT_TP_STRING && ci->_type != PBG_LT_STRING) return PBG_FALSE; } return PBG_TRUE; } int pbg_evaluate_r(pbg_expr* e, pbg_error* err, pbg_field* field) { if(pbg_type_isbool(field->_type)) { switch(field->_type) { case PBG_OP_NOT: return pbg_evaluate_op_not(e, err, field); case PBG_OP_AND: return pbg_evaluate_op_and(e, err, field); case PBG_OP_OR: return pbg_evaluate_op_or(e, err, field); case PBG_OP_EXST: return pbg_evaluate_op_exst(e, err, field); case PBG_OP_EQ: return pbg_evaluate_op_eq(e, err, field); case PBG_OP_NEQ: return pbg_evaluate_op_neq(e, err, field); case PBG_OP_LT: case PBG_OP_GT: case PBG_OP_LTE: case PBG_OP_GTE: return pbg_evaluate_op_order(e, err, field); case PBG_OP_TYPE: return pbg_evaluate_op_type(e, err, field); case PBG_LT_TRUE: return PBG_TRUE; case PBG_LT_FALSE: return PBG_FALSE; default: pbg_err_state(err, __LINE__, __FILE__, "Unsupported operation."); } } pbg_err_state(err, __LINE__, __FILE__, "Cannot evaluate a non-BOOL value."); return PBG_ERROR; } int pbg_evaluate(pbg_expr* e, pbg_error* err, pbg_field (*dict)(char*, int)) { int i, result; pbg_field* newvars, *var, *oldvars; /* Always start with a clean error! */ pbg_err_init(err, PBG_ERR_NONE, 0, NULL, 0, NULL); /* Variable resolution. Lookup every variable in provided dictionary. */ newvars = (pbg_field*) malloc(e->_numvars * sizeof(pbg_field)); if(newvars == NULL) { pbg_err_alloc(err, __LINE__, __FILE__); return PBG_ERROR; } for(i = 0; i < e->_numvars; i++) { var = e->_variables+i; newvars[i] = dict((char*)(var->_data), var->_int); } /* Swap out variable literals with dictionary equivalents. */ oldvars = e->_variables; e->_variables = newvars; /* Evaluate expression! */ result = pbg_evaluate_r(e, err, e->_constants); /* Restore old variable literal array. */ e->_variables = oldvars; /* Clean up malloc'd memory. */ for(i = 0; i < e->_numvars; i++) pbg_field_free(newvars+i); free(newvars); /* Done! */ return result; } /************************ * * * JANITORIAL FUNCTIONS * * * ************************/ void pbg_free(pbg_expr* e) { int i; /* Free individual constant fields. Some do not have _data malloc'd. */ for(i = e->_numconst-1; i >= 0; i--) pbg_field_free(e->_constants+i); /* Free individual variable fields. All have _data malloc'd. */ for(i = 0; i < e->_numvars; i++) pbg_field_free(e->_variables+i); /* Free internal field arrays. */ if(e->_constants != NULL) { free(e->_constants); e->_constants = NULL; } if(e->_variables != NULL) { free(e->_variables); e->_variables = NULL; } } /********************************* * * * CONVERSION & CHECKING TOOLKIT * * * *********************************/ /** * Translates the given pbg_field_type to a human-readable string. * @param type PBG field type to translate. * @return String representation of the given field type. */ char* pbg_field_type_str(pbg_field_type type) { switch(type) { case PBG_LT_TRUE: return "PBG_LT_TRUE"; case PBG_LT_FALSE: return "PBG_LT_FALSE"; case PBG_LT_NUMBER: return "PBG_LT_NUMBER"; case PBG_LT_STRING: return "PBG_LT_STRING"; case PBG_LT_DATE: return "PBG_LT_DATE"; case PBG_LT_VAR: return "PBG_LT_VAR"; case PBG_OP_NOT: return "PBG_OP_NOT"; case PBG_OP_AND: return "PBG_OP_AND"; case PBG_OP_OR: return "PBG_OP_OR"; case PBG_OP_EQ: return "PBG_OP_EQ"; case PBG_OP_LT: return "PBG_OP_LT"; case PBG_OP_GT: return "PBG_OP_GT"; case PBG_OP_EXST: return "PBG_OP_EXST"; case PBG_OP_NEQ: return "PBG_OP_NEQ"; case PBG_OP_LTE: return "PBG_OP_LTE"; case PBG_OP_GTE: return "PBG_OP_GTE"; case PBG_LT_TP_DATE: return "PBG_LT_TP_DATE"; case PBG_LT_TP_BOOL: return "PBG_LT_TP_BOOL"; case PBG_LT_TP_NUMBER: return "PBG_LT_TP_NUMBER"; case PBG_LT_TP_STRING: return "PBG_LT_TP_STRING"; default: return "PBG_NULL"; } } /** * Translates the given pbg_error_type to a human-readable string. * @param type PBG error type to translate. * @return String representation of the given error type. */ char* pbg_error_str(pbg_error_type type) { switch(type) { case PBG_ERR_NONE: return "PBG_ERR_NONE"; case PBG_ERR_ALLOC: return "PBG_ERR_ALLOC"; case PBG_ERR_STATE: return "PBG_ERR_STATE"; case PBG_ERR_SYNTAX: return "PBG_ERR_SYNTAX"; case PBG_ERR_UNKNOWN_TYPE: return "PBG_ERR_UNKNOWN_TYPE"; case PBG_ERR_OP_ARITY: return "PBG_ERR_OP_ARITY"; case PBG_ERR_OP_ARG_TYPE: return "PBG_ERR_OP_ARG_TYPE"; } return "PBG_ERR_???"; } int pbg_iserror(pbg_error* err) { return err->_type != PBG_ERR_NONE; } pbg_field_type pbg_gettype(char* str, int n) { /* Is it a literal? */ if(pbg_istrue(str, n)) return PBG_LT_TRUE; if(pbg_isfalse(str, n)) return PBG_LT_FALSE; if(pbg_isnumber(str, n)) return PBG_LT_NUMBER; if(pbg_isstring(str, n)) return PBG_LT_STRING; if(pbg_isdate(str, n)) return PBG_LT_DATE; if(pbg_isvar(str, n)) return PBG_LT_VAR; if(pbg_istypedate(str, n)) return PBG_LT_TP_DATE; if(pbg_istypebool(str, n)) return PBG_LT_TP_BOOL; if(pbg_istypenumber(str, n)) return PBG_LT_TP_NUMBER; if(pbg_istypestring(str, n)) return PBG_LT_TP_STRING; /* Is it an operator? */ if(n == 1) { if(str[0] == '!') return PBG_OP_NOT; if(str[0] == '&') return PBG_OP_AND; if(str[0] == '|') return PBG_OP_OR; if(str[0] == '=') return PBG_OP_EQ; if(str[0] == '<') return PBG_OP_LT; if(str[0] == '>') return PBG_OP_GT; if(str[0] == '?') return PBG_OP_EXST; if(str[0] == '@') return PBG_OP_TYPE; } if(n == 2) { if(str[0] == '!' && str[1] == '=') return PBG_OP_NEQ; if(str[0] == '<' && str[1] == '=') return PBG_OP_LTE; if(str[0] == '>' && str[1] == '=') return PBG_OP_GTE; } /* It isn't anything! */ return PBG_NULL; } int pbg_istypedate(char* str, int n) { return n == 4 && str[0]=='D' && str[1]=='A' && str[2]=='T' && str[3]=='E'; } int pbg_istypenumber(char* str, int n) { return n == 6 && str[0]=='N' && str[1]=='U' && str[2]=='M' && str[3]=='B' && str[4]=='E' && str[5]=='R'; } int pbg_istypebool(char* str, int n) { return n == 4 && str[0]=='B' && str[1]=='O' && str[2]=='O' && str[3]=='L'; } int pbg_istypestring(char* str, int n) { return n == 6 && str[0]=='S' && str[1]=='T' && str[2]=='R' && str[3]=='I' && str[4]=='N' && str[5]=='G'; } int pbg_istrue(char* str, int n) { return n == 4 && str[0]=='T' && str[1]=='R' && str[2]=='U' && str[3]=='E'; } int pbg_isfalse(char* str, int n) { return n == 5 && str[0]=='F' && str[1]=='A' && str[2]=='L' && str[3]=='S' && str[4]=='E'; } int pbg_isnumber(char* str, int n) { int i; /* Start at the beginning of the string. */ i = 0; /* Check if negative or positive */ if(str[i] == '-' || str[i] == '+') i++; /* Otherwise, ensure first character is a digit. */ else if(!pbg_isdigit(str[i])) return 0; /* Parse everything before the dot. */ if(str[i] != '0' && pbg_isdigit(str[i])) { while(i != n && pbg_isdigit(str[i])) i++; if(i != n && !pbg_isdigit(str[i]) && str[i] != '.') return 0; }else if(str[i] == '0') { if(++i != n && !(str[i] == '.' || str[i] == 'e' || str[i] == 'E')) return 0; } /* Parse everything after the dot. */ if(str[i] == '.') { /* Last character must be a digit. */ if(i++ == n-1) return 0; /* Exhaust all digits. */ while(i != n && pbg_isdigit(str[i])) i++; if(i != n && !pbg_isdigit(str[i]) && str[i] != 'e' && str[i] != 'E') return 0; } /* Parse everything after the exponent. */ if(str[i] == 'e' || str[i] == 'E') { /* Last character must be a digit. */ if(i++ == n-1) return 0; /* Parse positive or negative sign. */ if(str[i] == '-' || str[i] == '+') i++; /* Exhaust all digits. */ while(i != n && pbg_isdigit(str[i])) i++; if(i != n && !pbg_isdigit(str[i])) return 0; } /* Probably a number! */ return 1; } void pbg_tonumber(pbg_lt_number* ptr, char* str, int n) { PBG_UNUSED(n); ptr->_val = atof(str); } int pbg_cmpnumber(pbg_lt_number* n1, pbg_lt_number* n2) { if(n1->_val < n2->_val) return -1; if(n1->_val > n2->_val) return 1; return 0; } int pbg_cmpdate(pbg_lt_date* n1, pbg_lt_date* n2) { if(n1->_YYYY < n2->_YYYY) return -1; if(n1->_YYYY > n2->_YYYY) return 1; if(n1->_MM < n2->_MM) return -1; if(n1->_MM > n2->_MM) return 1; if(n1->_DD < n2->_DD) return -1; if(n1->_DD > n2->_DD) return 1; return 0; } int pbg_cmpstring(pbg_lt_string* s1, pbg_lt_string* s2, int n) { return strncmp(s1, s2, n); } int pbg_isvar(char* str, int n) { return str[0] == '[' && str[n-1] == ']'; } int pbg_isstring(char* str, int n) { return str[0] == '\'' && str[n-1] == '\''; } int pbg_isdate(char* str, int n) { return n == 10 && pbg_isdigit(str[0]) && pbg_isdigit(str[1]) && pbg_isdigit(str[2]) && pbg_isdigit(str[3]) && str[4] == '-' && pbg_isdigit(str[5]) && pbg_isdigit(str[6]) && str[7] == '-' && pbg_isdigit(str[8]) && pbg_isdigit(str[9]); } void pbg_todate(pbg_lt_date* ptr, char* str, int n) { if(n != 10) return; ptr->_YYYY = (str[0]-'0')*1000 + (str[1]-'0')*100 + (str[2]-'0')*10 + (str[3]-'0'); ptr->_MM = (str[5]-'0')*10 + (str[6]-'0'); ptr->_DD = (str[8]-'0')*10 + (str[9]-'0'); } /** * Checks if the given type is an operator, TRUE, or FALSE. Useful for checking * if both arguments will have a valid return value from pbg_evaluate_r. * @param type Type to check. * @return 1 if the given type is TRUE, FALSE, or an operator, * 0 otherwise. */ int pbg_type_isbool(pbg_field_type type) { return type == PBG_LT_TRUE || type == PBG_LT_FALSE || (type < PBG_MAX_OP && type > PBG_MIN_OP); } /** * Checks if the given type is an operator. * @param type Type to check. * @return 1 if the given type is an operator, * 0 otherwise. */ int pbg_type_isop(pbg_field_type type) { return type > PBG_MIN_OP && type < PBG_MAX_OP; } /******************** * * * HELPER FUNCTIONS * * * ********************/ /** * Checks if the given character is a digit. * @param c Character to check. */ int pbg_isdigit(char c) { return c >= '0' && c <= '9'; } /** * Checks if the given character is whitespace. * @param c Character to check. */ int pbg_iswhitespace(char c) { return c==' ' || c=='\t' || c=='\n'; }