/* Parse a placeholder from a format string */ Placeholder* Placeholder_parse(const char** expr) { if(**expr != '@') { RAISE(badChar(**expr), true); } /* Move past '@' */ (*expr)++; unsigned index = 0; if(isdigit(**expr)) { /* Like @1v or @4@ */ char* end; errno = 0; /* Read the number after the '@' */ index = (unsigned)strtoul(*expr, &end, 10); if(errno != 0 || index == 0 || *expr == end) { /* An error occurred (EINVAL, ERANGE) */ RAISE(syntaxError("Invalid number in placeholder"), true); } /* Advance past the number */ *expr = end; } PLACETYPE type = getPlaceholderType(**expr); if(type == PH_ERR) { RAISE(badChar(**expr), true); } (*expr)++; return Placeholder_new(type, index); }
void validateQuotedLocal() { ++finger; while(*finger != '"') { if(*finger == '\\') { ++finger; if(!*finger) fail("unexpected end-of-string in quoted local part"); else if(badChar(*finger)) fail("illegal escaped character in quoted local part"); } else if(!*finger) fail("unexpected end-of-string in quoted local part"); else if(badQuotedChar(*finger)) fail("illegal character in quoted local part (may need escaping)"); ++finger; } ++finger; if(!*finger) fail("address had quoted local part but no domain (reached end-of-string)"); else if(*finger != '@') fail("quoted local part was not followed by @"); ++finger; }
static Value* parseToken(const char** expr, parser_cb* cb) { Value* ret; char* token = nextToken(expr); if(token == NULL) { return ValErr(badChar(**expr)); } trimSpaces(expr); if(**expr == '(') { (*expr)++; ArgList* arglist = ArgList_parse(expr, ',', ')', cb); if(arglist == NULL) { /* Parse error occurred and has already been raised */ free(token); return ValErr(ignoreError()); } ret = ValCall(FuncCall_create(token, arglist)); } else { ret = ValVar(token); } free(token); return ret; }
static Value* parseNum(const char** expr) { Value* ret; char* end1; char* end2; errno = 0; double dbl = strtod(*expr, &end1); if(errno != 0 || *expr == end1) { /* An error occurred (EINVAL, ERANGE) */ end1 = NULL; } long long ll = strtoll(*expr, &end2, 10); if(errno != 0 || *expr == end2) { /* An error occurred (EINVAL, ERANGE) */ end2 = NULL; } if(end1 > end2) { /* Must be a double because more of the string was parsed as double than long long */ ret = ValReal(dbl); *expr = end1; } else if(end2 != NULL) { /* Must be an integer */ ret = ValInt(ll); *expr = end2; } else { /* Both failed to convert the data */ ret = ValErr(badChar(**expr)); } return ret; }
void validateValue(char const * _value, char const * _label) { value = finger = _value; label = _label; while(*finger) { if(badChar(*finger)) fail("illegal character"); ++finger; } }
void escapeQuoted(char const * in, StringBuffer & out, char const * _label) { value = finger = in; label = _label; while(*finger) { if(badChar(*finger)) fail("illegal character"); else if((*finger == '"') || (*finger == '\\')) { if(finger>in) out.append(finger-in, in); out.append('\\'); in = finger; } ++finger; } if(finger>in) out.append(finger-in, in); }
int main(int argc, char* argv[]) { Context* ctx = Context_new(); register_math(ctx); for(nextLine(); !feof(stdin); nextLine()) { /* Strip trailing newline */ char* end; if((end = strchr(line, '\n')) != NULL) *end = '\0'; if((end = strchr(line, '\r')) != NULL) *end = '\0'; if((end = strchr(line, '#')) != NULL) *end = '\0'; const char* p = line; /* Get verbosity level */ int verbose = 0; while(p[0] == '?') { verbose++; p++; } trimSpaces(&p); if(*p == '~') { /* Variable deletion */ p++; char* name = nextToken(&p); if(name == NULL) { /* '~~~' means reset interpreter */ if(p[0] == '~' && p[1] == '~') { /* Wipe out context */ Context_free(ctx); ctx = Context_new(); register_math(ctx); continue; } if(*p == '\0') { RAISE(earlyEnd()); continue; } RAISE(badChar(*p)); continue; } Context_del(ctx, name); free(name); continue; } /* Parse the user's input */ Expression* expr = Expression_parse(&p); /* Print expression depending on verbosity */ Expression_print(expr, ctx, verbose); /* Error? Go to next loop iteration */ if(Expression_didError(expr)) { Expression_free(expr); continue; } /* Evaluate expression */ Value* result = Expression_eval(expr, ctx); Expression_free(expr); /* Print result */ Value_print(result, ctx); Value_free(result); } Context_free(ctx); return 0; }
/* By default, the '@' character is illegal */ Value* _default_cb(const char** expr, void* data) { return ValErr(badChar(**expr)); }
Value* Value_parse(const char** expr, char sep, char end, parser_cb* cb) { Value* val; BINTYPE op = BIN_UNK; BinOp* tree = NULL; BinOp* prev; while(1) { /* Get next value */ val = Value_next(expr, end, cb); /* Error parsing next value? */ if(val->type == VAL_ERR) { if(tree) { BinOp_free(tree); } return val; } /* End of input? */ if(val->type == VAL_END) { if(tree) { BinOp_free(tree); } return val; } /* Special case: negative value */ if(val->type == VAL_NEG) { Value_free(val); BinOp* cur = BinOp_new(BIN_MUL, ValInt(-1), NULL); if(tree) { prev->b = ValExpr(cur); } else { tree = cur; } prev = cur; continue; } /* Get next operator if it exists */ op = BinOp_nextType(expr, sep, end); /* Invalid operator? Return syntax error */ if(op == BIN_UNK) { /* Exit gracefully and return error */ if(tree) { BinOp_free(tree); } Value_free(val); return ValErr(badChar(**expr)); } /* End of the statement? */ else if(op == BIN_END) { /* Only skip end character if there's only one value to parse */ if(!sep && **expr && **expr == end) { (*expr)++; } /* If there was only one value, return it */ if(!tree) { return val; } /* Otherwise, place the final value into the tree and break out of the parse loop */ prev->b = val; break; } /* Tree not yet begun? Initialize it! */ if(tree == NULL) { tree = BinOp_new(op, val, NULL); prev = tree; } else { /* Tree already started, so add to it */ treeAddValue(&tree, &prev, op, val); } } return ValExpr(tree); }
VERBOSITY getVerbosity(const char** str) { VERBOSITY ret = 0; if(**str != '?') { return 0; } /* Move past the '?' */ (*str)++; bool again = true; while(again) { switch(**str) { #define ADD_V(lvl) do { \ if(ret & V_##lvl) { \ RAISE(syntaxError("Specified " #lvl " verbosity more than once."), false); \ return V_ERR; \ } \ ret |= V_##lvl; \ } while(0) case VC_REPR: ADD_V(REPR); break; case VC_PRETTY: ADD_V(PRETTY); /* Pretty implies repr */ ret |= V_REPR; break; case VC_WRAP: ADD_V(WRAP); break; case VC_TREE: ADD_V(TREE); break; case VC_XML: ADD_V(XML); break; case ' ': case '\t': /* Verbosity command ended by whitespace only */ again = false; break; default: RAISE(badChar(**str), false); return V_ERR; } #undef ADD_V (*str)++; } /* For slight backwards compatibility and for ease of use */ if(ret == 0) { ret |= V_REPR; } return ret; }
Expression* Expression_parse(const char** expr) { Expression* ret = NULL; Variable* var; Value* val; const char* equals = strchr(*expr, '='); if(equals == NULL) { /* No assignment, just a plain expression. */ return parseExpr(expr); } /* There is an assignment */ /* First, parse the right side of the assignment */ equals++; val = Value_parse(&equals, 0, 0); if(val->type == VAL_ERR) { /* A parse error occurred */ var = VarErr(Error_copy(val->err)); Value_free(val); return Expression_new(var); } if(val->type == VAL_END) { /* Empty input */ Value_free(val); var = VarErr(earlyEnd()); return Expression_new(var); } /* Now parse the left side */ char* name = nextToken(expr); if(name == NULL) { Value_free(val); var = VarErr(syntaxError("No variable to assign to.")); return Expression_new(var); } trimSpaces(expr); if(**expr == '(') { /* Defining a function */ (*expr)++; /* Array of argument names */ unsigned size = 2; char** args = fmalloc(size * sizeof(*args)); unsigned len = 0; /* Add each argument name to the array */ char* arg = nextToken(expr); if(arg == NULL && **expr != ')') { /* Invalid character */ Value_free(val); free(args); free(name); var = VarErr(badChar(**expr)); return Expression_new(var); } trimSpaces(expr); if(arg == NULL) { /* Empty parameter list means function with no args */ free(args); args = NULL; len = 0; } else { /* Loop through each argument in the list */ while(**expr == ',' || **expr == ')') { args[len++] = arg; if(**expr == ')') break; (*expr)++; /* Expand argument array if it's too small */ if(len >= size) { size *= 2; args = frealloc(args, size * sizeof(*args)); } arg = nextToken(expr); if(arg == NULL) { /* Invalid character */ Value_free(val); free(name); /* Free argument names and return */ unsigned i; for(i = 0; i < len; i++) { free(args[i]); } free(args); var = VarErr(badChar(**expr)); return Expression_new(var); } trimSpaces(expr); } } if(**expr != ')') { /* Invalid character inside argument name list */ Value_free(val); free(name); /* Free argument names and return */ unsigned i; for(i = 0; i < len; i++) { free(args[i]); } free(args); var = VarErr(badChar(**expr)); return Expression_new(var); } /* Skip closing parenthesis */ (*expr)++; trimSpaces(expr); if(**expr != '=') { Value_free(val); free(name); unsigned i; for(i = 0; i < len; i++) { free(args[i]); } free(args); var = VarErr(badChar(**expr)); return Expression_new(var); } /* Construct function and return it */ Function* func = Function_new(len, args, val); var = VarFunc(name, func); free(name); ret = Expression_new(var); } else { /* Defining a variable */ if(**expr != '=') { /* In-place manipulation */ BINTYPE bin = BinOp_nextType(expr, 0, 0); /* Still not an equals sign means invalid character */ if(**expr != '=') { Value_free(val); free(name); var = VarErr(badChar(**expr)); return Expression_new(var); } val = ValExpr(BinOp_new(bin, ValVar(name), val)); } var = VarValue(name, val); free(name); ret = Expression_new(var); } return ret; }