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_