]> code.bitgloo.com Git - clyne/interpreter.git/commitdiff
documentation, error fixes, organization
authorClyne Sullivan <tullivan99@gmail.com>
Thu, 29 Mar 2018 17:36:29 +0000 (13:36 -0400)
committerClyne Sullivan <tullivan99@gmail.com>
Thu, 29 Mar 2018 17:36:29 +0000 (13:36 -0400)
14 files changed:
builtins.c
builtins.h
error.c [new file with mode: 0644]
error.h [new file with mode: 0644]
ops.c
ops.h
parser.c
parser.h
string.c [new file with mode: 0644]
string.h [new file with mode: 0644]
test2
test5
variable.c [new file with mode: 0644]
variable.h

index 883dd207696afc9e8e94a507de2c75ffccbf5d4a..73ce360694c1f9825f450bb2ec99b4a37592cb00 100644 (file)
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+/**
+ * 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 <stdlib.h>
 #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;
        }
 
index 6a9b0121c57f4e75c3b24d5196ff62ff27500fc8..5b14101ca01054432c82e3dcd36bc6d09d8b6468 100644 (file)
 #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 (file)
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 <https://www.gnu.org/licenses/>.
+ */
+
+#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 (file)
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 <https://www.gnu.org/licenses/>.
+ */
+
+#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 1fb878d6e2ac28197f6e21aabd4847eca58e75f6..e4874e81a31410ef694624e2fb7ef20512b5cdcf 100644 (file)
--- a/ops.c
+++ b/ops.c
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+/**
+ * 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 <stdlib.h>
-#include <string.h>
 
 #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 5941d02943e88eda7f44a231beb7f9e68055be78..4257f07eb2ad2debad92fec1b7f6c4ac045ccc5f 100644 (file)
--- 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_
index a1e96cf74878441e20191d70331d7deab4ae2812..14031dc90ba251cbcc739dd0a1a178e8511d6328 100644 (file)
--- a/parser.c
+++ b/parser.c
 
 #include "builtins.h"
 #include "ops.h"
+#include "string.h"
+#include "variable.h"
 
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
+
+/**
+ * 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++;
                }
index 0002d065f748ccc25644f9e681063f77081f8177..f1ffe70ed5fd980be6d7c66c816150bb9e4653e1 100644 (file)
--- 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 (file)
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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "string.h"
+
+#include <stdlib.h>
+
+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 (file)
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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STRING_H_
+#define STRING_H_
+
+#include <string.h>
+
+#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 f836124eda3cefb63949e66cb21e5ff49f3f5b13..3a03b157d7898484e10be63ffec86af58ee30f1a 100644 (file)
--- 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 440f7912e8cad109822f06ca9527bb71bfee93f5..74ce439a2bcadd62efc83cfaf621cf6410a8cfb5 100644 (file)
--- 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 (file)
index 0000000..f23390b
--- /dev/null
@@ -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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "variable.h"
+#include "string.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+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;
+}
+
index 40b86bd3240506faf2748085a156e5673049d19f..0c4f584788511df8fc672512cf0789784217d19f 100644 (file)
@@ -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_