From a444bc11bbba6e81b9c757df792cb62e4ac1855e Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Tue, 29 Mar 2022 13:55:51 -0400 Subject: [PATCH] Use dynamically-sized strings for stringObj. Fix webby.pl's `reduce` usage and web.c's struct creation. --- src/env.c | 8 +- src/examples/web/webby.pl | 6 +- src/object.c | 154 ++++++++++++++++++++------------------ src/object.h | 8 +- src/pebblisp.c | 4 +- src/plfunc.c | 14 ++-- src/web.c | 4 +- 7 files changed, 104 insertions(+), 94 deletions(-) diff --git a/src/env.c b/src/env.c index c5b91ac..5e8e6b3 100644 --- a/src/env.c +++ b/src/env.c @@ -42,7 +42,6 @@ Object fetchFromEnvironment(const char* name, struct Environment* env) } printd("Try %d (%s)\n", i, env->strings[i]); - debugObj(&env->objects[i]); if (strcmp(name, env->strings[i]) == 0) { printd("Returning!\n"); return cloneObject(env->objects[i]); } @@ -85,7 +84,7 @@ struct Environment envForLambda(const Object* params, const Object* arg_forms, i for (int i = 0; i < paramCount; i++) { const char* newObjName = itemAt(params, i)->string; // Eval the `march` list - Object newEnvObj = march ? eval(march, outer) : errorObject(NOT_ENOUGH_ARGUMENTS); + Object newEnvObj = march ? eval(march, outer) : errorWithContext(NOT_ENOUGH_ARGUMENTS, newObjName); addToEnv(&env, newObjName, newEnvObj); // Could use eval_forms? cleanObject(&newEnvObj); march = march ? march->forward : NULL; @@ -324,7 +323,6 @@ int runTests(int detailed) for (int hi = 0; hi < currentHelp; hi++) { struct helpText h = helpTexts[hi]; if (h.tests) { - char result[1024]; if (detailed && h.testCount > 0) { printf(" `%s` ", h.symbol); } @@ -333,7 +331,8 @@ int runTests(int detailed) const char* expected = h.tests[ti + 1]; struct Environment env = defaultEnv(); Object o = parseEval(test, &env); - stringObj(result, &o); + size_t length; + char* result = stringObj(&o, &length); cleanObject(&o); if (strcmp(result, expected) != 0) { failureCount++; @@ -346,6 +345,7 @@ int runTests(int detailed) } passCount++; } + free(result); deleteEnv(&env); } if (detailed && h.testCount > 0) { diff --git a/src/examples/web/webby.pl b/src/examples/web/webby.pl index 1981a8e..793fe9c 100755 --- a/src/examples/web/webby.pl +++ b/src/examples/web/webby.pl @@ -6,7 +6,7 @@ (def element (fn (type) (fn (text) (cat "<" type ">" - (if (islist text) (reduce (text "") cat) (reduce ((text) "") cat)) + (if (islist text) (reduce text cat "") (reduce (text) cat "")) "")))) (def html (element "html")) @@ -49,10 +49,8 @@ stuff, when you think about it." (htmlize p2))) )))) -(def revealer (fn (x) (cat "" x))) - (get "/" homepage) -(get "/x" revealer) +(get "/x" (fn (req) (cat "" req))) (def styles (rf "styles.css")) (get "/styles.css" (fn () (styles))) diff --git a/src/object.c b/src/object.c index 2ec6de4..ee5e3b9 100644 --- a/src/object.c +++ b/src/object.c @@ -145,6 +145,29 @@ static const char* errorText[] = {"MISMATCHED_PARENS", "INDEX_PAST_END"}; #endif +size_t inflate(struct string* s, size_t additional) +{ + size_t length = (s->cursor - s->allocPoint) + additional; + + int needsInflation = 0; + while (length > s->capacity - 8) { + s->capacity *= 2; + needsInflation = 1; + } + + if (needsInflation) { + char* oldAlloc = s->allocPoint; + s->allocPoint = malloc(sizeof(char) * s->capacity); + size_t l = sprintf(s->allocPoint, "%s", oldAlloc); + s->cursor = s->allocPoint + l; + free(oldAlloc); + } + + return s->capacity; +} + +int stringNObj(struct string *s, const Object* obj); + /** * Creates a string from a given list Object * Blocks out with parens @@ -152,52 +175,46 @@ static const char* errorText[] = {"MISMATCHED_PARENS", * @param dest The string to copy the list text into * @param obj The list Object to make a string from */ -int stringList(char* dest, const Object* obj) +void stringList(struct string* s, const Object* obj) { - const char* initial = dest; - - dest += sprintf(dest, "("); + s->cursor += sprintf(s->cursor, "("); FOR_POINTER_IN_LIST(obj) { - char tok[256] = ""; - stringObj(tok, POINTER); - dest += sprintf(dest, " %s", tok); + s->cursor += sprintf(s->cursor, " "); + stringNObj(s, POINTER); + inflate(s, 0); } - dest += sprintf(dest, " )"); - return dest - initial; + s->cursor += sprintf(s->cursor, " )"); } /** * Creates a string from a given struct Object * Blocks out with curly braces */ -int stringStruct(char* dest, const Object* obj) +void stringStruct(struct string* s, const Object* obj) { struct StructObject* so = obj->structObject; - const char* initial = dest; - dest += sprintf(dest, "{"); + s->cursor += sprintf(s->cursor, "{"); struct StructDef* def = getStructAt(so->definition); for (int i = 0; i < def->fieldCount; i++) { - dest += sprintf(dest, " %s: ", def->names[i]); + s->cursor += sprintf(s->cursor, " %s: ", def->names[i]); int isString = so->fields[i].type == TYPE_STRING; if (isString) { - dest += sprintf(dest, "\""); + s->cursor += sprintf(s->cursor, "\""); } - char tok[90] = ""; - stringObj(tok, &so->fields[i]); - dest += sprintf(dest, "%s", tok); + stringNObj(s, &so->fields[i]); if (isString) { - dest += sprintf(dest, "\""); + s->cursor += sprintf(s->cursor, "\""); } - dest += sprintf(dest, ","); + s->cursor += sprintf(s->cursor, ","); } - dest--; - dest += sprintf(dest, " }"); - return dest - initial; + s->cursor--; + s->cursor += sprintf(s->cursor, " }"); } + /** * Creates a string from a given Object * Returns NULL if either param is NULL @@ -214,96 +231,90 @@ int stringStruct(char* dest, const Object* obj) * @param dest The string to copy the list text into * @param obj The list Object to make a string from */ -#ifdef STANDALONE -#define stringf(_dest, _len, _format, args...) sprintf(_dest, _format, ## args) -#else -#define stringf(_dest, _len, _format, args...) snprintf(_dest, _len, _format, ## args) -#endif - -int stringNObj(char* dest, const Object* obj, const size_t len) +int stringNObj(struct string* s, const Object* obj) { - const char* initial = dest; - + inflate(s, 16); switch (obj->type) { case TYPE_NUMBER: - dest += stringf(dest, len, "%d", obj->number); + s->cursor += sprintf(s->cursor, "%d", obj->number); break; case TYPE_BOOL: - dest += stringf(dest, len, "%s", obj->number ? "T" : "F"); + s->cursor += sprintf(s->cursor, "%s", obj->number ? "T" : "F"); break; - case TYPE_STRING: - dest += stringf(dest, len, "%s", obj->string); + case TYPE_STRING: { + size_t stringLen = strlen(obj->string); + inflate(s, stringLen); + s->cursor += sprintf(s->cursor, "%s", obj->string); break; + } case TYPE_SYMBOL: - dest += stringf(dest, len, "`%s`", obj->string); + s->cursor += sprintf(s->cursor, "`%s`", obj->string); break; case TYPE_STRUCT: - //snprintf(dest, len, "{%s}", obj->structObject->definition->names[0]); - dest += stringStruct(dest, obj); + stringStruct(s, obj); break; case TYPE_LIST: case TYPE_SLIST: - dest += stringList(dest, obj); + stringList(s, obj); break; case TYPE_ERROR: { int code = getErrorCode(*obj); #ifdef SIMPLE_ERRORS - dest += stringf(dest, len, "E[%d]", (int)code); + s->cursor += sprintf(s->cursor, "E[%d]", (int)code); #else + inflate(s, 128); if (obj->error->context && obj->error->context[0] != '\0') { - dest += stringf(dest, len, "%s: %s", errorText[code], + s->cursor += sprintf(s->cursor, "%s: %s", errorText[code], obj->error->context); } else if (code >= 0 && code <= INDEX_PAST_END) { - dest += stringf(dest, len, "%s", errorText[code]); + s->cursor += sprintf(s->cursor, "%s", errorText[code]); } else { - dest += stringf(dest, len, "BROKEN ERROR CODE: %d", code); + s->cursor += sprintf(s->cursor, "BROKEN ERROR CODE: %d", code); } #endif break; } case TYPE_FUNC: - dest += stringf(dest, len, "F%d", obj->number); + s->cursor += sprintf(s->cursor, "F%d", obj->number); break; case TYPE_LAMBDA: { #ifdef STANDALONE #ifdef DEBUG - dest += stringf(dest, len, "\\x%d", obj->number); + s->cursor += sprintf(s->cursor, len, "\\x%d", obj->number); #endif - dest += stringNObj(dest, &obj->lambda->params, len); - dest += sprintf(dest, " -> "); - dest += stringNObj(dest, &obj->lambda->body, len); - dest += sprintf(dest, ">"); + stringNObj(s, &obj->lambda->params); + s->cursor += sprintf(s->cursor, " -> "); + stringNObj(s, &obj->lambda->body); + s->cursor += sprintf(s->cursor, ">"); #else - dest += stringf(dest, len, "\\x%d", obj->number); + s->cursor += sprintf(s->cursor, len, "\\x%d", obj->number); #endif break; } case TYPE_OTHER: - dest += stringf(dest, len, "%p", obj->other->data); + s->cursor += sprintf(s->cursor, "%p", obj->other->data); break; } if (!isValidType(*obj)) { - dest += stringf(dest, len, "BAD_TYPE(%d) X%d", obj->type, obj->number); + s->cursor += sprintf(s->cursor, "BAD_TYPE(%d) X%d", obj->type, obj->number); } - return dest - initial; + return 0; } -int stringObj(char* dest, const Object* obj) +char* stringObj(const Object* obj, size_t* length) { - return stringNObj(dest, obj, RESULT_LENGTH); -} - -/** - * Prints a given Object only if the DEBUG flag is set - * @param obj The Object to debug - */ -void debugObj(const Object* obj) -{ -#ifdef DEBUG - printObj(obj); -#endif + char* alloc = malloc(8); + struct string s = { + .allocPoint = alloc, + .cursor = alloc, + .capacity = 8, + }; + s.allocPoint[0] = '\0'; + stringNObj(&s, obj); + *length = s.cursor - s.allocPoint; + return s.allocPoint; } #if defined(DEBUG) || defined(STANDALONE) @@ -341,33 +352,28 @@ void printType(const Object* obj) void _printObj(const Object* obj, int newline) { -#ifdef DEBUG - printType(obj); -#endif - if (!obj) { printf(newline ? "\n" : ""); return; } - char temp[1024] = ""; - stringNObj(temp, obj, 1024); + size_t length; + char* temp = stringObj(obj, &length); if (newline) { printf("%s\n", temp); if (obj->type == TYPE_ERROR) { if (obj->error && obj->error->plContext) { printf("%s\n", obj->error->plContext->text); - return; } } } else { printf("%s", temp); } + free(temp); } /** * Prints the given Object and a newline. * Uses stringObj() to create the printed string - * If the DEBUG flag is set, the Object's type will be printed first * @param obj The Object to print */ inline void printObj(const Object* obj) diff --git a/src/object.h b/src/object.h index 20d85b5..47bbebe 100644 --- a/src/object.h +++ b/src/object.h @@ -128,9 +128,13 @@ struct Other { void* data; }; -int stringNObj(char* dest, const Object* obj, size_t len); +struct string { + char* allocPoint; + char* cursor; + size_t capacity; +}; -int stringObj(char* dest, const Object* obj); +char* stringObj(const Object* obj, size_t* length); void printType(const Object* obj); diff --git a/src/pebblisp.c b/src/pebblisp.c index e9954c1..1151205 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -607,8 +607,8 @@ void repl(struct Environment* env) } add_history(buf); Object o = parseEval(buf, env); - char output[1024]; - stringNObj(output, &o, 1024); + size_t length; + char *output = stringObj(&o, &length); printColored(output); printf("\n"); cleanObject(&o); diff --git a/src/plfunc.c b/src/plfunc.c index a92ac81..5e951e0 100644 --- a/src/plfunc.c +++ b/src/plfunc.c @@ -8,7 +8,7 @@ Object typeCheck(const char* funcName, Object* params, int length, { *failed = 1; if ((typeLength - 1) > length) { - return errorObject(NOT_ENOUGH_ARGUMENTS); + return errorWithContext(NOT_ENOUGH_ARGUMENTS, funcName); } for (int i = 0; i < typeLength - 1; i++) { if (typeChecks[i] && !typeChecks[i](params[i])) { // TODO: Use pl func name instead of C function name. @@ -235,16 +235,18 @@ Object _catObjects(Object obj1, Object obj2, struct Environment* env) Object evalObj1 = eval(&obj1, env); Object evalObj2 = eval(&obj2, env); - char str1[CAT_MAX] = ""; - char str2[CAT_MAX] = ""; - int length = stringObj(str1, &evalObj1); - length += stringObj(str2, &evalObj2); + size_t length1; + size_t length2; + char* str1 = stringObj(&evalObj1, &length1); + char* str2 = stringObj(&evalObj2, &length2); cleanObject(&evalObj1); cleanObject(&evalObj2); - Object o = withLen(length, TYPE_STRING); + Object o = withLen(length1 + length2, TYPE_STRING); sprintf(o.string, "%s%s", str1, str2); + free(str1); + free(str2); return o; } diff --git a/src/web.c b/src/web.c index 93644e5..919822f 100644 --- a/src/web.c +++ b/src/web.c @@ -82,8 +82,8 @@ answer_to_connection(void* cls, struct MHD_Connection* connection, Object queryParams = listObject(); MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, add_query_param, &queryParams); - Object res = numberObject(1010);//structObject(requestDefinition); - //res.structObject->fields[0] = queryParams; + Object res = structObject(requestDefinition); + res.structObject->fields[0] = queryParams; Object route = cloneObject(routes[i].routeAction); Object result = listEvalLambda(&route, &res, 2, routes[i].env);