documentation, error fixes, organization

master
Clyne Sullivan 7 years ago
parent 7faeef296d
commit 3b33fa0aeb

@ -19,6 +19,19 @@
* 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>
@ -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;
}

@ -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

@ -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];
}

@ -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_

74
ops.c

@ -18,17 +18,27 @@
* 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;
}

@ -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_

@ -22,67 +22,26 @@
#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++;
}

@ -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_

@ -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;
}

@ -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_

17
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))

@ -2,7 +2,7 @@
# solver test
while (1) {
input = gets()
input = gets
if (input == "exit") {
exit
}

@ -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;
}

@ -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_

Loading…
Cancel
Save