From 3b33fa0aeb5262335cd708b167caad8f41b8a5dd Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Thu, 29 Mar 2018 13:36:29 -0400 Subject: [PATCH] documentation, error fixes, organization --- builtins.c | 66 ++++++++++--- builtins.h | 9 ++ error.c | 41 ++++++++ error.h | 34 +++++++ ops.c | 74 ++++++++++---- ops.h | 8 ++ parser.c | 275 ++++++++++++++++------------------------------------- parser.h | 18 ---- string.c | 53 +++++++++++ string.h | 52 ++++++++++ test2 | 17 ++-- test5 | 2 +- variable.c | 95 ++++++++++++++++++ variable.h | 33 ++++++- 14 files changed, 525 insertions(+), 252 deletions(-) create mode 100644 error.c create mode 100644 error.h create mode 100644 string.c create mode 100644 string.h create mode 100644 variable.c diff --git a/builtins.c b/builtins.c index 883dd20..73ce360 100644 --- a/builtins.c +++ b/builtins.c @@ -19,6 +19,19 @@ * along with this program. If not, see . */ +/** + * Built-ins are functions that are meant to be included in every interpreter + * instance. That is, the interpreter would be near worthless as a scripting + * language without these functions. + * + * A built-in function takes an instance, and returns an error code (zero if + * successful). Arguments to the function can be obtained through igetarg(). + * Built-in functions have full access to the instance, its variables and + * stack. To return a variable, push that variable to the stack at the end of + * the function. If stack values are inserted by the function, push a zero so + * error doesn't occur. + */ + #include "builtins.h" #include @@ -28,7 +41,14 @@ #define ELSE_SIG (uint32_t)-3 #define FUNC_SIG (uint32_t)-4 -int bn_set(instance *it); +variable bopen = { + 0, CFUNC, 0, {.p = (uint32_t)bracket_open} +}; + +variable bclose = { + 0, CFUNC, 0, {.p = (uint32_t)bracket_close} +}; + int bn_if(instance *it); int bn_else(instance *it); int bn_end(instance *it); @@ -38,7 +58,6 @@ int bn_solve(instance *it); void iload_builtins(instance *it) { - inew_cfunc(it, "set", bn_set); inew_cfunc(it, "if", bn_if); inew_cfunc(it, "else", bn_else); inew_cfunc(it, "while", bn_while); @@ -46,13 +65,30 @@ void iload_builtins(instance *it) inew_cfunc(it, "solve", bn_solve); } -int bn_set(instance *it) +/** + * Code for an opening bracket ('{', new scope). + */ +int bracket_open(instance *it) +{ + it->indent++; + if (it->sindent & SKIP) { + // make sure this indent is caught by its closing '}'. + ipush(it, SKIP_SIG); + ipush(it, 0); + } + return 0; +} + +/** + * Code for a closing bracket ('}', end of scope) + */ +int bracket_close(instance *it) { - variable *var = igetarg(it, 0); - variable *value = igetarg(it, 1); - var->type = value->type; - var->value.p = value->value.p; - ipush(it, (uint32_t)var); + it->indent--; + // stop skipping if this is the end of the skipped scope + if (it->indent < (it->sindent & ~(SKIP))) + it->sindent = 0; + bn_end(it); return 0; } @@ -82,6 +118,14 @@ int bn_else(instance *it) return 0; } +/** + * bn_end is a special function. The parser is hard-coded to interpret '}' + * characters as calls to this function, which handles closing loops or + * conditionals. + * + * The most recent value on the stack should determine what loop is being + * closed, so that action can be taken accordingly. + */ int bn_end(instance *it) { uint32_t sig = ipop(it); @@ -94,7 +138,7 @@ int bn_end(instance *it) } else if (sig == CALL_SIG) { it->lnidx = ipop(it); it->indent++; - } + } // else, just have *_SIG popped return 0; } @@ -118,9 +162,6 @@ int bn_while(instance *it) int bn_func(instance *it) { variable *f = igetarg(it, 0); - if (f == 0) - return -1; - f->type = FUNC; f->value.p = it->lnidx; it->sindent = SKIP | it->indent; @@ -135,6 +176,7 @@ int bn_solve(instance *it) variable **ops = iparse(it, (const char *)s->value.p); if (ops == 0) { ipush(it, (uint32_t)make_varf(0, 0.0f)); + // return zero, don't let bad solves break the script return 0; } diff --git a/builtins.h b/builtins.h index 6a9b012..5b14101 100644 --- a/builtins.h +++ b/builtins.h @@ -27,6 +27,15 @@ #define SKIP_SIG (uint32_t)-5 #define CALL_SIG (uint32_t)-6 +// open bracket 'operator', for use in a compiled line +extern variable bopen; + +// close bracket 'operator', for use in a compiled line +extern variable bclose; + +int bracket_open(instance *it); +int bracket_close(instance *it); + /** * Loads the built-in functions into the given instance. * @param it the instance to use diff --git a/error.c b/error.c new file mode 100644 index 0000000..5719313 --- /dev/null +++ b/error.c @@ -0,0 +1,41 @@ +/** + * @file error.c + * Provides a simple error-logging mechanism. + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "error.h" + +static int lastError = ENONE; + +static const char *errorTable[] = { + "no error", + "undefined value", + "bad parameter" +}; + +int seterror(int error) +{ + if (error < EMAX) + lastError = error; + return -lastError; +} + +const char *geterror(void) +{ + return errorTable[lastError]; +} diff --git a/error.h b/error.h new file mode 100644 index 0000000..4f0ef6f --- /dev/null +++ b/error.h @@ -0,0 +1,34 @@ +/** + * @file error.h + * Provides a simple error-logging mechanism. + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ERROR_H_ +#define ERROR_H_ + +enum ERRORS { + ENONE = 0, + EUNDEF, + EBADPARAM, + EMAX +}; + +int seterror(int error); +const char *geterror(void); + +#endif // ERROR_H_ diff --git a/ops.c b/ops.c index 1fb878d..e4874e8 100644 --- a/ops.c +++ b/ops.c @@ -18,17 +18,27 @@ * along with this program. If not, see . */ +/** + * Operators are special functions (though different from those that are built- + * in). Operators have no access to the interpreter instance; instead only the + * two 'argument' variables and a variable to contain the result are passed in. + * The operator function returns an integer, zero for success. + * + * Argument 'a' is the variable on the left side of the operator, and 'b' is + * the variable on the right. 'r' is a non-null variable that the result of the + * operation should be placed in. + */ + +#include "error.h" #include "ops.h" +#include "string.h" #include -#include #define OP_DEF(o) int op_##o(variable *r, variable *a, variable *b) #define OP_VAR(o) {0, OPERATOR, 0, {.p = (uint32_t)op_##o}} #define OP_NONE {0, OPERATOR, 0, {.p = 0x0BADCAFE}} -extern char *strclone(const char *s); - OP_DEF(mul); OP_DEF(div); OP_DEF(mod); @@ -47,6 +57,15 @@ OP_DEF(xor); OP_DEF(or); OP_DEF(set); +/** + * Operators are stored here in order of significance, meaning those towards + * the beginning of the array are completed before those after them. This + * priority listing is done in pairs of two, so that mathematical order of + * operations can be respected. For example, the first two operators + * multiplication and division) have the same priority. Should an operator not + * have a 'pair', OP_NONE can be used. Should adjacent operators have the same + * priority, they will be evaluated from left-to-right by the parser. + */ variable opvars[] = { OP_VAR(mul), OP_VAR(div), OP_VAR(mod), OP_NONE, OP_VAR(add), OP_VAR(sub), OP_VAR(shl), OP_VAR(shr), @@ -63,10 +82,25 @@ const char *opnames[] = { "|", "=" }; +variable *igetop(const char *name, int *retlen) +{ + for (uint32_t i = 0; i < OPS_COUNT; i++) { + if (opnames[i] == 0) + continue; + int len = strlen(opnames[i]); + if (opnames[i] != 0 && !strncmp(name, opnames[i], len)) { + if (retlen != 0) + *retlen = len; + return &opvars[i]; + } + } + return 0; +} + OP_DEF(mul) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f * b->value.f; return 0; @@ -74,7 +108,7 @@ OP_DEF(mul) OP_DEF(div) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f / b->value.f; return 0; @@ -82,7 +116,7 @@ OP_DEF(div) OP_DEF(mod) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = (int)a->value.f % (int)b->value.f; return 0; @@ -90,7 +124,7 @@ OP_DEF(mod) OP_DEF(add) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f + b->value.f; return 0; @@ -98,7 +132,7 @@ OP_DEF(add) OP_DEF(sub) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f - b->value.f; return 0; @@ -106,7 +140,7 @@ OP_DEF(sub) OP_DEF(shl) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = (int)a->value.f << (int)b->value.f; return 0; @@ -114,7 +148,7 @@ OP_DEF(shl) OP_DEF(shr) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = (int)a->value.f >> (int)b->value.f; return 0; @@ -122,7 +156,7 @@ OP_DEF(shr) OP_DEF(lte) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f <= b->value.f; return 0; @@ -130,7 +164,7 @@ OP_DEF(lte) OP_DEF(lt) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f < b->value.f; return 0; @@ -138,7 +172,7 @@ OP_DEF(lt) OP_DEF(gte) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f >= b->value.f; return 0; @@ -146,7 +180,7 @@ OP_DEF(gte) OP_DEF(gt) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f > b->value.f; return 0; @@ -159,14 +193,14 @@ OP_DEF(eq) else if (a->type == STRING && b->type == STRING) r->value.f = !strcmp((const char *)a->value.p, (const char *)b->value.p); else - return -1; + return seterror(EBADPARAM); return 0; } OP_DEF(ne) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = a->value.f != b->value.f; return 0; @@ -174,7 +208,7 @@ OP_DEF(ne) OP_DEF(and) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = (int)a->value.f & (int)b->value.f; return 0; @@ -182,7 +216,7 @@ OP_DEF(and) OP_DEF(xor) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = (int)a->value.f ^ (int)b->value.f; return 0; @@ -190,7 +224,7 @@ OP_DEF(xor) OP_DEF(or) { if (a->type != NUMBER || b->type != NUMBER) - return -1; + return seterror(EBADPARAM); r->type = NUMBER; r->value.f = (int)a->value.f | (int)b->value.f; return 0; @@ -210,7 +244,7 @@ OP_DEF(set) r->type = STRING; r->value.p = (uint32_t)strclone((char *)a->value.p); } else { - return -1; + return seterror(EBADPARAM); } return 0; } diff --git a/ops.h b/ops.h index 5941d02..4257f07 100644 --- a/ops.h +++ b/ops.h @@ -42,4 +42,12 @@ extern variable opvars[]; */ extern const char *opnames[]; +/** + * Gets the variable for the given operator. + * @param name the operator (e.g. "+") + * @param retlen if not null, stores the operator string's length here + * @return variable for the operator, zero if not found + */ +variable *igetop(const char *name, int *retlen); + #endif // OPS_H_ diff --git a/parser.c b/parser.c index a1e96cf..14031dc 100644 --- a/parser.c +++ b/parser.c @@ -22,67 +22,26 @@ #include "builtins.h" #include "ops.h" +#include "string.h" +#include "variable.h" #include #include #include -#include + +/** + * Limitations for an instance. TODO make dynamic (no limits). + */ #define MAX_VARS 256 #define MAX_STACK 64 #define MAX_LINES 1000 -int bracket_open(instance *it) -{ - it->indent++; - if (it->sindent & SKIP) { - ipush(it, SKIP_SIG); - ipush(it, 0); - } - return 0; -} -int bracket_close(instance *it) -{ - it->indent--; - if (it->indent < (it->sindent & ~(SKIP))) - it->sindent = 0; - bn_end(it); - return 0; -} -static variable bopen = { - 0, CFUNC, 0, {.p = (uint32_t)bracket_open} -}; -static variable bclose = { - 0, CFUNC, 0, {.p = (uint32_t)bracket_close} -}; - -char *strnclone(const char *s, size_t c) -{ - char *b = strncpy((char *)malloc(c + 1), s, c); - b[c] = '\0'; - return b; -} -char *strclone(const char *s) -{ - return strnclone(s, strlen(s)); -} -char *fixstring(const char *s) -{ - char *n = malloc(strlen(s) + 1 - 2); - int j = 0; - for (int i = 1; s[i] != '\"'; i++, j++) { - if (s[i] == '\\') { - if (s[i + 1] == 'n') - n[j] = '\n'; - i++; - } else { - n[j] = s[i]; - } - } - n[j] = '\0'; - return n; -} - +/** + * Attempts to free memory used by a variable, if the variable is temporary. + * Nothing is done if the variable can't be free'd. + * @param v the variable to free + */ void itryfree(variable *v) { if (v == 0 || v->tmp == 0) @@ -92,6 +51,10 @@ void itryfree(variable *v) free(v); } +// +// instance construction/deconstruction +// + instance *inewinstance(void) { instance *it = (instance *)malloc(sizeof(instance)); @@ -109,26 +72,10 @@ instance *inewinstance(void) return it; } -void idelline(variable **ops) -{ - for (int j = 0; j < 32; j++) { - variable *v = ops[j]; - if (v != 0) { - if (((uint32_t)v & OP_MAGIC) == OP_MAGIC) - continue; - - if (v->type == FUNC || v->type == CFUNC) - j++; // argcount - - if (v->tmp == 1) - itryfree(v); - } - } -} - +void idelline(variable **ops); void idelinstance(instance *it) { - for (uint32_t i = 0; i < MAX_LINES; i++) {// TODO free vars! + for (uint32_t i = 0; i < MAX_LINES; i++) { if (it->lines[i] == 0) continue; @@ -147,6 +94,27 @@ void idelinstance(instance *it) free(it); } +void idelline(variable **ops) +{ + for (int j = 0; j < 32; j++) { + variable *v = ops[j]; + if (v != 0) { + if (((uint32_t)v & OP_MAGIC) == OP_MAGIC) + continue; + + if (v->type == FUNC || v->type == CFUNC) + j++; // argcount + + if (v->tmp == 1) + itryfree(v); + } + } +} + +// +// stack operations +// + void ipush(instance *it, uint32_t v) { it->stack[it->stidx++] = v; @@ -167,7 +135,27 @@ variable *igetarg(instance *it, uint32_t n) return (variable *)it->stack[it->stidx - n - 1]; } -variable *igetvar(instance *it, const char *name); +// +// variable creation +// + +variable *igetvar(instance *it, const char *name) +{ + if (isalpha(name[0])) { + for (uint32_t i = 0; i < MAX_VARS; i++) { + if (it->names[i] == 0) { + it->names[i] = strclone(name); + // default to 0 float + return make_varf(&it->vars[i], 0.0f); + } else if (!strcmp(name, it->names[i])) { + return &it->vars[i]; + } + } + } + + return igetop(name, 0); +} + void inew_cfunc(instance *it, const char *name, func_t func) { variable *v = igetvar(it, name); @@ -189,100 +177,6 @@ void inew_string(instance *it, const char *name, const char *s) v->value.p = (uint32_t)strclone(s); } -variable *varclone(variable *n) -{ - variable *v = (variable *)malloc(sizeof(variable)); - v->tmp = 1; - v->type = n->type; - if (n->type == STRING) - v->value.p = (uint32_t)strclone((char *)n->value.p); - else - v->value.p = n->value.p; - return v; -} - -variable *make_varf(variable *v, float f) -{ - if (v == 0) { - v = (variable *)malloc(sizeof(variable)); - v->tmp = 1; - } - v->type = NUMBER; - v->value.f = f; - return v; -} - -variable *make_vars(variable *v, const char *s) -{ - if (v == 0) { - v = (variable *)malloc(sizeof(variable)); - v->tmp = 1; - } - v->type = STRING; - v->value.p = (uint32_t)strclone(s); - return v; -} - -variable *make_num(const char *text) -{ - int decimal = -1; - char valid = 0; - - int i = 0; - if (text[0] == '-') - i++; - do { - if (text[i] == '.') { - if (decimal >= 0) { - valid = 0; - break; - } - decimal = i; - } else if (isdigit(text[i])) { - valid |= 1; - } else { - break; - } - } while (text[++i] != '\0'); - - if (valid == 0) - return 0; - - char *buf = (char *)malloc(i + 1); - strncpy(buf, text, i); - buf[i] = '\0'; - - variable *v = make_varf(0, strtof(buf, 0)); - free(buf); - return v; -} - -variable *igetop(const char *name) -{ - for (uint32_t i = 0; i < OPS_COUNT; i++) { - if (opnames[i] != 0 && !strcmp(name, opnames[i])) { - return &opvars[i]; - } - } - return 0; -} -variable *igetvar(instance *it, const char *name) -{ - if (isalpha(name[0])) { - for (uint32_t i = 0; i < MAX_VARS; i++) { - if (it->names[i] == 0) { - it->names[i] = strclone(name); - // default to 0 float - return make_varf(&it->vars[i], 0.0f); - } else if (!strcmp(name, it->names[i])) { - return &it->vars[i]; - } - } - } - - return igetop(name); -} - int idoline(instance *it, const char *s) { variable **ops = iparse(it, s); @@ -296,6 +190,8 @@ loop: // itryfree(it->ret); //it->ret = 0; + // clone the 'ops' array carefully, as isolve() frees/deletes used + // variables as it goes copy = (variable **)malloc(32 * sizeof(variable *)); for (int i = 0; i < 32; i++) { variable *v = it->lines[it->lnidx][i]; @@ -318,9 +214,11 @@ loop: } } it->ret = isolve(it, copy, 0); + if (it->ret == 0) { idelline(copy); } else { + // move result global variable "ANS" variable *ret = igetvar(it, "ANS"); ret->type = it->ret->type; ret->value.p = it->ret->value.p; @@ -437,7 +335,7 @@ variable *isolve_(instance *it, variable **ops, uint32_t count) return 0; uint32_t bidx = i + 1; while (ops[bidx] == 0 && ++bidx < count); - if (bidx == count) + if (bidx >= count) return 0; if (!(it->sindent & SKIP)) { @@ -474,13 +372,18 @@ variable **iparse(instance *it, const char *s) int32_t boffset = 1; size_t offset = 0; + // advance to first character + // and insure there's runnable script on the line while (isblank(s[offset])) offset++; if (s[offset] == '#' || s[offset] == '\0' || s[offset] == '\n') goto fail; + // iterate through script to assemble line of 'ops' + // that isolve() can run through ops = (variable **)calloc(32, sizeof(variable *)); while (s[offset] != '\0' && s[offset] != '\n') { + // variable or function if (isalpha(s[offset])) { size_t end = offset + 1; while (isalnum(s[end])) @@ -565,44 +468,32 @@ variable **iparse(instance *it, const char *s) ops[parenidx] = (variable *)(OP_MAGIC | count); offset = i; } else if (!isblank(s[offset])) { - size_t end = offset + 1; - while (!isblank(s[end]) && !isalnum(s[end]) && s[end] != '\0') - end++; - char *word = strnclone(s + offset, end - offset); - - // bracket? - if (!strcmp(word, "{") || !strcmp(word, "}")) { + if (s[offset] == '{' || s[offset] == '}') { for (int32_t i = ooffset - 1; i >= boffset - 1; i--) ops[i + 2] = ops[i]; - if (word[0] == '{') + if (s[offset] == '{') ops[boffset - 1] = &bopen; else ops[boffset - 1] = &bclose; ops[boffset] = (variable *)1; // arg count + 1 boffset += 2; ooffset += 2; + offset++; } else { - variable *v = igetop(word); - if (v == 0) { - free(word); + int len = 0; + variable *v = igetop(s + offset, &len); + if (v == 0) goto fail; - } else { - if (ooffset == 0) { - variable *a; - if (it->ret != 0) - a = it->ret; - else - a = make_varf(0, 0.0f); - ops[ooffset++] = a; - } else if (ops[ooffset - 1]->type == OPERATOR) { - free(word); - goto fail; - } - } + if (ooffset == 0) { + variable *a = (it->ret != 0) ? + a = it->ret : make_varf(0, 0.0f); + ops[ooffset++] = a; + } /*else if (ops[ooffset - 1]->type == OPERATOR) { + goto fail; + }*/ ops[ooffset++] = v; + offset += len; } - free(word); - offset = end; } else { offset++; } diff --git a/parser.h b/parser.h index 0002d06..f1ffe70 100644 --- a/parser.h +++ b/parser.h @@ -101,8 +101,6 @@ void ipush(instance *it, uint32_t v); */ variable *igetarg(instance *it, uint32_t n); - - /** * Parses the given line, returning compiled data to run. * For internal use only. @@ -122,20 +120,4 @@ variable **iparse(instance *it, const char *s); */ variable *isolve(instance *it, variable **ops, uint32_t count); -/** - * Makes a number out of the given variable. - * @param v the variable to use, if zero one is malloc'd - * @param f the number to assign the variable - * @return the new float variable - */ -variable *make_varf(variable *v, float f); - -/** - * Makes a string out of the given variable. - * @param v the variable to use, if zero one is malloc'd - * @param f the string to assign the variable - * @return the new string variable - */ -variable *make_vars(variable *v, const char *s); - #endif // PARSER_H_ diff --git a/string.c b/string.c new file mode 100644 index 0000000..22be853 --- /dev/null +++ b/string.c @@ -0,0 +1,53 @@ +/** + * @file string.c + * Provides string.h from stdlib, plus some extra functions. + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "string.h" + +#include + +char *strnclone(const char *s, size_t c) +{ + char *b = strncpy((char *)malloc(c + 1), s, c); + b[c] = '\0'; + return b; +} + +char *strclone(const char *s) +{ + return strnclone(s, strlen(s)); +} + +char *fixstring(const char *s) +{ + char *n = malloc(strlen(s) + 1 - 2); + int j = 0; + for (int i = 1; s[i] != '\"'; i++, j++) { + if (s[i] == '\\') { + if (s[i + 1] == 'n') + n[j] = '\n'; + i++; + } else { + n[j] = s[i]; + } + } + n[j] = '\0'; + return n; +} + diff --git a/string.h b/string.h new file mode 100644 index 0000000..d516271 --- /dev/null +++ b/string.h @@ -0,0 +1,52 @@ +/** + * @file string.h + * Provides string.h from stdlib, plus some extra functions. + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef STRING_H_ +#define STRING_H_ + +#include + +#ifndef size_t +typedef unsigned int size_t; +#endif + +/** + * Clones a string of a given size into a malloc'd buffer. + * @param s the string to clone + * @param c the number of characters to copy + * @return the malloc'd copy + */ +char *strnclone(const char *s, size_t c); + +/** + * Clones a string into a malloc'd buffer. + * @param s the string to clone + * @return the malloc'd copy + */ +char *strclone(const char *s); + +/** + * 'Fixes' a string, by converting "\n" and others to '\n'. + * @param s the string to fix + * @return the fixed string, in a malloc'd buffer + */ +char *fixstring(const char *s); + +#endif // STRING_H_ diff --git a/test2 b/test2 index f836124..3a03b15 100644 --- a/test2 +++ b/test2 @@ -2,15 +2,16 @@ # variable and function tests # show variable recognition and proper c-function handling -a * 1 -3 + b +print(a * 1) +print(3 + b) -set(a, 5) -a * 1 +print(a = 5) +print(a * 1) -set(c, 4) -a / c +print(c = 4) +print(a / c) -set(b, 2) set(d, 8) +print(b = 2) +print(d = 8) -d + set(e, 4) +print(d + (e = 4)) diff --git a/test5 b/test5 index 440f791..74ce439 100644 --- a/test5 +++ b/test5 @@ -2,7 +2,7 @@ # solver test while (1) { - input = gets() + input = gets if (input == "exit") { exit } diff --git a/variable.c b/variable.c new file mode 100644 index 0000000..f23390b --- /dev/null +++ b/variable.c @@ -0,0 +1,95 @@ +/** + * @file variable.c + * Defines variable data structure, and provides functions for variable + * creation. + * + * Copyright (C) 2018 Clyne Sullivan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "variable.h" +#include "string.h" + +#include +#include + +variable *make_varf(variable *v, float f) +{ + if (v == 0) { + v = (variable *)malloc(sizeof(variable)); + v->tmp = 1; + } + v->type = NUMBER; + v->value.f = f; + return v; +} + +variable *make_vars(variable *v, const char *s) +{ + if (v == 0) { + v = (variable *)malloc(sizeof(variable)); + v->tmp = 1; + } + v->type = STRING; + v->value.p = (uint32_t)strclone(s); + return v; +} + +variable *varclone(variable *n) +{ + variable *v = (variable *)malloc(sizeof(variable)); + v->tmp = 1; + v->type = n->type; + if (n->type == STRING) + v->value.p = (uint32_t)strclone((char *)n->value.p); + else + v->value.p = n->value.p; + return v; +} + +variable *make_num(const char *text) +{ + int decimal = -1; + char valid = 0; + + int i = 0; + if (text[0] == '-') + i++; + do { + if (text[i] == '.') { + if (decimal >= 0) { + valid = 0; + break; + } + decimal = i; + } else if (isdigit(text[i])) { + valid |= 1; + } else { + break; + } + } while (text[++i] != '\0'); + + if (valid == 0) + return 0; + + char *buf = (char *)malloc(i + 1); + strncpy(buf, text, i); + buf[i] = '\0'; + + variable *v = make_varf(0, strtof(buf, 0)); + free(buf); + return v; +} + diff --git a/variable.h b/variable.h index 40b86bd..0c4f584 100644 --- a/variable.h +++ b/variable.h @@ -1,6 +1,7 @@ /** * @file variable.h - * Defines variable data structure + * Defines variable data structure, and provides functions for variable + * creation. * * Copyright (C) 2018 Clyne Sullivan * @@ -47,4 +48,34 @@ enum VARTYPE { CFUNC, /**< C function */ }; +/** + * Makes a number out of the given variable. + * @param v the variable to use, if zero one is malloc'd + * @param f the number to assign the variable + * @return the new float variable + */ +variable *make_varf(variable *v, float f); + +/** + * Makes a string out of the given variable. + * @param v the variable to use, if zero one is malloc'd + * @param f the string to assign the variable + * @return the new string variable + */ +variable *make_vars(variable *v, const char *s); + +/** + * Creates a temporary number variable out of the given text. + * @param text the string to convert to a number + * @return a number variable with the converted value + */ +variable *make_num(const char *text); + +/** + * Clones a variable into a new, malloc'd variable. + * @param n the variable to clone + * @return the cloned, malloc'd variable + */ +variable *varclone(variable *n); + #endif // VARIABLE_H_