interpreter overhaul, should be better
parent
28447df15a
commit
140a0bbecc
@ -1,23 +1,13 @@
|
|||||||
#CC = gcc -m32
|
CFLAGS = -ggdb
|
||||||
#AR = ar
|
CFILES = $(wildcard *.c)
|
||||||
CC = arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16
|
|
||||||
AR = arm-none-eabi-ar
|
all:
|
||||||
|
@echo $(CFILES)
|
||||||
CFLAGS = -Wall -Wextra -Werror -pedantic \
|
@gcc -m32 $(CFLAGS) $(CFILES) -o shell
|
||||||
-Wno-discarded-qualifiers \
|
|
||||||
-I. -fsigned-char -fno-builtin -ggdb
|
arm:
|
||||||
|
@mv shell.c shell.c.bak
|
||||||
FILES = $(wildcard *.c)
|
@arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(CFLAGS) -c *.c
|
||||||
OUTFILES = $(patsubst %.c, %.o, $(FILES))
|
@arm-none-eabi-ar r libinterp.a *.o
|
||||||
|
@mv shell.c.bak shell.c
|
||||||
all: $(OUTFILES)
|
@rm *.o
|
||||||
@#$(CC) $(CFLAGS) *.o -o shell
|
|
||||||
@$(AR) r libinterp.a *.o
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@echo " CLEAN"
|
|
||||||
@rm -f *.o shell libinterp.a
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
@echo " CC " $<
|
|
||||||
@$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
# interpreter
|
# interpreter
|
||||||
This project aims to provide a very minimal scripting language for embedded systems. Many other languages already exist, such as Lua, Tcl, or BASIC; however, most implementations require certain system calls like a read() and write(), as they expect a filesystem. This interpreter aims to be as independent and portable as possible: parsing script from strings one at a time, having minimal built-in functions (so the user can define their own prints and such), and only requiring a few standard library functions.
|
This project aims to provide a very minimal scripting language for embedded systems. Many other languages already exist, such as Lua, Tcl, or BASIC; however, most implementations require certain system calls like read() and write(), as they expect a filesystem. This interpreter wants to be as system-independent and portable as possible: parsing script from strings one at a time, having minimal built-in functions (so the user can define their own IO calls and such), and only requiring a few standard library functions.
|
||||||
|
|
||||||
To use this program with your own device, you need some malloc/free implementation, and string functions like those in string.h, atoi, and snprintf. Some of these functions may become coded in so that a standard library isn't required.
|
To use this program with your own device, you'll need some malloc/free implementation, and a freestanding standard library. Newlib works well for this; however, functions like atoi() and snprintf() will probably need to be rewritten (if you don't have an \_sbrk defined).
|
||||||
|
|
||||||
Only a few commands are built in to the interpreter:
|
Interpreter features:
|
||||||
* set - set variables
|
* Variable/function definition - in C and in script
|
||||||
* func/end - define functions
|
* if/else and while loops
|
||||||
* if/end - if conditional
|
* a solve function to parse strings at runtime
|
||||||
* do/while
|
|
||||||
* ret - return value from function
|
|
||||||
|
|
||||||
Other features:
|
Inconvenient features:
|
||||||
* function/variable defining in c
|
|
||||||
* expression solving
|
|
||||||
* no local variables
|
* no local variables
|
||||||
* whitespace hopefully ignored
|
* whitespace sometimes ignored
|
||||||
|
|
||||||
Soon:
|
Some TODO items:
|
||||||
* error messages
|
* fix all memory leaks
|
||||||
|
* add better error messages
|
||||||
* arrays?
|
* arrays?
|
||||||
* maybe for loops
|
* for loops
|
||||||
|
|
||||||
This project is still in heavy development, so don't expect much. To include it in your own project, just link in parser.o and use the header files.
|
|
||||||
|
This project can be made for the host system (```make```) or an ARM system (```make arm```).
|
||||||
|
This project is still in heavy development, so don't expect much. To include it in your own project, just link in libinterp.a (for ARM) and use the header files.
|
||||||
|
@ -1,179 +1,124 @@
|
|||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include "stack.h"
|
|
||||||
#include "shelpers.h"
|
|
||||||
|
|
||||||
#include <memory.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
int ifunc_set(interpreter *it);
|
|
||||||
int ifunc_label(interpreter *it);
|
|
||||||
int ifunc_end(interpreter *it);
|
|
||||||
int ifunc_if(interpreter *it);
|
|
||||||
int ifunc_do(interpreter *it);
|
|
||||||
int ifunc_while(interpreter *it);
|
|
||||||
int ifunc_ret(interpreter *it);
|
|
||||||
int ifunc_else(interpreter *it);
|
|
||||||
int ifunc_solve(interpreter *it);
|
|
||||||
|
|
||||||
const func_t indent_up[IUP_COUNT] = {
|
|
||||||
ifunc_if, ifunc_do, ifunc_label
|
|
||||||
};
|
|
||||||
|
|
||||||
const func_t indent_down[IDOWN_COUNT] = {
|
|
||||||
ifunc_else, ifunc_end, ifunc_while,
|
|
||||||
};
|
|
||||||
|
|
||||||
void iload_core(interpreter *interp)
|
|
||||||
{
|
|
||||||
inew_cfunc(interp, "set", ifunc_set);
|
|
||||||
inew_cfunc(interp, "func", ifunc_label);
|
|
||||||
inew_cfunc(interp, "end", ifunc_end);
|
|
||||||
inew_cfunc(interp, "if", ifunc_if);
|
|
||||||
inew_cfunc(interp, "do", ifunc_do);
|
|
||||||
inew_cfunc(interp, "while", ifunc_while);
|
|
||||||
inew_cfunc(interp, "ret", ifunc_ret);
|
|
||||||
inew_cfunc(interp, "else", ifunc_else);
|
|
||||||
inew_cfunc(interp, "solve", ifunc_solve);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ifunc_solve(interpreter *it)
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define IF_SIG (uint32_t)-1
|
||||||
|
#define WHILE_SIG (uint32_t)-2
|
||||||
|
#define ELSE_SIG (uint32_t)-3
|
||||||
|
#define FUNC_SIG (uint32_t)-4
|
||||||
|
|
||||||
|
int bn_set(instance *it);
|
||||||
|
int bn_if(instance *it);
|
||||||
|
int bn_else(instance *it);
|
||||||
|
int bn_end(instance *it);
|
||||||
|
int bn_while(instance *it);
|
||||||
|
int bn_func(instance *it);
|
||||||
|
int bn_solve(instance *it);
|
||||||
|
|
||||||
|
void iload_builtins(instance *it)
|
||||||
{
|
{
|
||||||
const char *expr = igetarg_string(it, 0);
|
inew_cfunc(it, "set", bn_set);
|
||||||
int len = strlen(expr);
|
inew_cfunc(it, "if", bn_if);
|
||||||
char *buf = (char *)malloc(len + 2);
|
inew_cfunc(it, "else", bn_else);
|
||||||
strcpy(buf, expr);
|
inew_cfunc(it, "while", bn_while);
|
||||||
buf[len] = ')';
|
inew_cfunc(it, "func", bn_func);
|
||||||
buf[len + 1] = '\0';
|
inew_cfunc(it, "solve", bn_solve);
|
||||||
variable *r = idoexpr(it, buf);
|
|
||||||
free(buf);
|
|
||||||
if (r == 0)
|
|
||||||
r = make_varn(0, 0.0f);
|
|
||||||
iret(it, r);
|
|
||||||
free(r);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ifunc_set(interpreter *it)
|
int bn_set(instance *it)
|
||||||
{
|
{
|
||||||
variable *n = igetarg(it, 0);
|
variable *var = igetarg(it, 0);
|
||||||
variable *v = igetarg(it, 1);
|
variable *value = igetarg(it, 1);
|
||||||
|
var->type = value->type;
|
||||||
if (n == 0)
|
var->value.p = value->value.p;
|
||||||
return -1;
|
ipush(it, (uint32_t)var);
|
||||||
|
|
||||||
if (n->valtype == STRING)
|
|
||||||
free((void *)n->value.p);
|
|
||||||
n->valtype = v->valtype;
|
|
||||||
n->value.p = v->value.p;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ifunc_label(interpreter *it)
|
int bn_if(instance *it)
|
||||||
{
|
{
|
||||||
variable *n = igetarg(it, 0);
|
variable *cond = (variable *)ipop(it);
|
||||||
|
uint32_t result = cond->value.p;
|
||||||
|
|
||||||
if (n == 0)
|
ipush(it, result);
|
||||||
return -1;
|
ipush(it, IF_SIG);
|
||||||
|
if (result == 0)
|
||||||
|
it->sindent = SKIP | it->indent;
|
||||||
|
ipush(it, 0);
|
||||||
|
ipush(it, 0); // need to return because stack modify
|
||||||
|
|
||||||
n->valtype = FUNC;
|
|
||||||
n->value.p = it->lnidx;
|
|
||||||
iskip(it);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ifunc_if(interpreter *it)
|
static uint32_t if_cond = 0;
|
||||||
|
int bn_else(instance *it)
|
||||||
{
|
{
|
||||||
int v = igetarg(it, 0)->value.p;
|
uint32_t cond = if_cond;
|
||||||
if (v == 0)
|
if (cond != 0)
|
||||||
iskip(it);
|
it->sindent = SKIP | it->indent;
|
||||||
void *arg = ipop(it);
|
ipush(it, ELSE_SIG);
|
||||||
ipush(it, (void *)v);
|
ipush(it, 0); // for ret
|
||||||
ipush(it, (void *)-1);
|
|
||||||
ipush(it, arg);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ifunc_end(interpreter *it)
|
int bn_end(instance *it)
|
||||||
{
|
{
|
||||||
if (it->stidx == 0)
|
uint32_t sig = ipop(it);
|
||||||
return 0;
|
if (sig == IF_SIG) {
|
||||||
|
if_cond = ipop(it);
|
||||||
uint32_t lnidx = (uint32_t)ipop(it) + 1;
|
} else if (sig == WHILE_SIG) {
|
||||||
if (lnidx == 0) { // from an if, have conditional
|
uint32_t lnidx = ipop(it);
|
||||||
ipop(it); // whatever
|
if (lnidx != (int32_t)-1)
|
||||||
} else {
|
it->lnidx = lnidx - 1;
|
||||||
if (lnidx == (uint32_t)-1) {
|
} else if (sig == CALL_SIG) {
|
||||||
// script-func call
|
it->lnidx = ipop(it);
|
||||||
lnidx = (uint32_t)ipop(it);
|
it->indent++;
|
||||||
it->indent = (uint32_t)ipop(it);
|
|
||||||
}
|
|
||||||
it->lnidx = lnidx;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ifunc_else(interpreter *it)
|
int bn_while(instance *it)
|
||||||
{
|
{
|
||||||
if (it->stidx == 0)
|
variable *cond = (variable *)ipop(it);
|
||||||
return 0;
|
uint32_t result = cond->value.p;
|
||||||
|
|
||||||
ipop(it); // the -1
|
if (result == 0) {
|
||||||
int cond = (int)ipop(it);
|
it->sindent = SKIP | it->indent;
|
||||||
it->indent++;
|
ipush(it, (uint32_t)-1);
|
||||||
if (cond != 0)
|
} else {
|
||||||
iskip(it);
|
ipush(it, it->lnidx);
|
||||||
// otherwise it's whatever?
|
}
|
||||||
|
ipush(it, WHILE_SIG);
|
||||||
ipush(it, 0);
|
ipush(it, 0);
|
||||||
ipush(it, (void *)-1);
|
ipush(it, 0); // need to ret
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ifunc_do(interpreter *it)
|
int bn_func(instance *it)
|
||||||
{
|
{
|
||||||
ipush(it, (void *)it->lnidx);
|
variable *f = igetarg(it, 0);
|
||||||
return 0;
|
if (f == 0)
|
||||||
}
|
return -1;
|
||||||
|
|
||||||
int ifunc_while(interpreter *it)
|
f->type = FUNC;
|
||||||
{
|
f->value.p = it->lnidx;
|
||||||
int c = igetarg(it, 0)->value.p;
|
it->sindent = SKIP | it->indent;
|
||||||
ipop(it);
|
ipush(it, FUNC_SIG);
|
||||||
int nidx = (int)ipop(it);
|
ipush(it, 0); // for ret
|
||||||
if (c != 0) {
|
|
||||||
//ipush(it, (void *)nidx);
|
|
||||||
it->lnidx = nidx - 1;
|
|
||||||
}
|
|
||||||
ipush(it, 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void iret(interpreter *it, variable *v)
|
int bn_solve(instance *it)
|
||||||
{
|
{
|
||||||
switch (v->valtype) {
|
variable *s = igetarg(it, 0);
|
||||||
case NUMBER:
|
variable **ops = iparse(it, (const char *)s->value.p);
|
||||||
inew_number(it, "RET", v->value.f);
|
if (ops == 0)
|
||||||
break;
|
return -1;
|
||||||
case STRING:
|
|
||||||
inew_string(it, "RET", (char *)v->value.p);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (it->ret != 0) {
|
|
||||||
if (it->ret->valtype == STRING && it->ret->value.p != 0)
|
|
||||||
free((void *)it->ret->value.p);
|
|
||||||
it->ret->valtype = v->valtype;
|
|
||||||
it->ret->value.p = v->value.p;
|
|
||||||
it->ret = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ifunc_ret(interpreter *it)
|
variable *a = isolve(it, ops, 0);
|
||||||
{
|
free(ops);
|
||||||
variable *v = igetarg(it, 0);
|
|
||||||
iret(it, v);
|
ipush(it, (uint32_t)a);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#ifndef MEMORY_H_
|
|
||||||
#define MEMORY_H_
|
|
||||||
|
|
||||||
void *malloc(unsigned int);
|
|
||||||
void *calloc(unsigned int, unsigned int);
|
|
||||||
void free(void *);
|
|
||||||
|
|
||||||
#endif // MEMORY_H_
|
|
Binary file not shown.
@ -1,117 +1,197 @@
|
|||||||
#include "ops.h"
|
#include "ops.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void iop_add(variable *, variable *, variable *);
|
#define OP_DEF(o) int op_##o(variable *r, variable *a, variable *b)
|
||||||
void iop_sub(variable *, variable *, variable *);
|
#define OP_VAR(o) {0, OPERATOR, 0, {.p = (uint32_t)op_##o}}
|
||||||
void iop_mult(variable *, variable *, variable *);
|
#define OP_NONE {0, OPERATOR, 0, {.p = 0x0BADCAFE}}
|
||||||
void iop_div(variable *, variable *, variable *);
|
|
||||||
void iop_and(variable *, variable *, variable *);
|
extern char *strclone(const char *s);
|
||||||
void iop_or(variable *, variable *, variable *);
|
|
||||||
void iop_xor(variable *, variable *, variable *);
|
OP_DEF(mul);
|
||||||
void iop_shr(variable *, variable *, variable *);
|
OP_DEF(div);
|
||||||
void iop_shl(variable *, variable *, variable *);
|
OP_DEF(mod);
|
||||||
void iop_eq(variable *, variable *, variable *);
|
OP_DEF(add);
|
||||||
void iop_lt(variable *, variable *, variable *);
|
OP_DEF(sub);
|
||||||
void iop_gt(variable *, variable *, variable *);
|
OP_DEF(shl);
|
||||||
void iop_lte(variable *, variable *, variable *);
|
OP_DEF(shr);
|
||||||
void iop_gte(variable *, variable *, variable *);
|
OP_DEF(lte);
|
||||||
void iop_ne(variable *, variable *, variable *);
|
OP_DEF(lt);
|
||||||
void iop_mod(variable *, variable *, variable *);
|
OP_DEF(gte);
|
||||||
|
OP_DEF(gt);
|
||||||
char *iops[IOPS_COUNT] = {
|
OP_DEF(eq);
|
||||||
"*", "/", "%", "+", "-", "<<", ">>", "<=",
|
OP_DEF(ne);
|
||||||
"<", ">=", ">", "==", "!=", "&", "^", "|"
|
OP_DEF(and);
|
||||||
|
OP_DEF(xor);
|
||||||
|
OP_DEF(or);
|
||||||
|
OP_DEF(set);
|
||||||
|
|
||||||
|
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),
|
||||||
|
OP_VAR(lte), OP_VAR(lt), OP_VAR(gte), OP_VAR(gt),
|
||||||
|
OP_VAR(eq), OP_VAR(ne), OP_VAR(and), OP_VAR(xor),
|
||||||
|
OP_VAR(or), OP_VAR(set)
|
||||||
};
|
};
|
||||||
|
|
||||||
operation_t iopfuncs[IOPS_COUNT] = {
|
const char *opnames[] = {
|
||||||
iop_mult, iop_div, iop_mod, iop_add, iop_sub,
|
"*", "/", "%", 0,
|
||||||
iop_shl, iop_shr, iop_lte, iop_lt, iop_gte,
|
"+", "-", "<<", ">>",
|
||||||
iop_gt, iop_eq, iop_ne, iop_and, iop_xor,
|
"<=", "<", ">=", ">",
|
||||||
iop_or
|
"==", "!=", "&", "^",
|
||||||
|
"|", "="
|
||||||
};
|
};
|
||||||
|
|
||||||
|
OP_DEF(mul)
|
||||||
void iop_add(variable *r, variable *a, variable *b)
|
|
||||||
{
|
|
||||||
r->value.f = a->value.f + b->value.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void iop_sub(variable *r, variable *a, variable *b)
|
|
||||||
{
|
|
||||||
r->value.f = a->value.f - b->value.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void iop_mult(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
r->value.f = a->value.f * b->value.f;
|
r->value.f = a->value.f * b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(div)
|
||||||
void iop_div(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
r->value.f = a->value.f / b->value.f;
|
r->value.f = a->value.f / b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(mod)
|
||||||
void iop_and(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = (float)((int)a->value.f & (int)b->value.f);
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = (int)a->value.f % (int)b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(add)
|
||||||
void iop_or(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = (float)((int)a->value.f | (int)b->value.f);
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = a->value.f + b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(sub)
|
||||||
void iop_xor(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = (float)((int)a->value.f ^ (int)b->value.f);
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = a->value.f - b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(shl)
|
||||||
void iop_shr(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = (float)((int)a->value.f >> (int)b->value.f);
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = (int)a->value.f << (int)b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(shr)
|
||||||
void iop_shl(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = (float)((int)a->value.f << (int)b->value.f);
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = (int)a->value.f >> (int)b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(lte)
|
||||||
void iop_eq(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
if (a->valtype == STRING && b->valtype == STRING)
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
r->value.f = (float)!strcmp((char *)a->value.p, (char *)b->value.p);
|
return -1;
|
||||||
else
|
r->type = NUMBER;
|
||||||
r->value.f = a->value.f == b->value.f;
|
r->value.f = a->value.f <= b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(lt)
|
||||||
void iop_lt(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
r->value.f = a->value.f < b->value.f;
|
r->value.f = a->value.f < b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(gte)
|
||||||
void iop_gt(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = a->value.f > b->value.f;
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = a->value.f >= b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(gt)
|
||||||
void iop_lte(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = a->value.f <= b->value.f;
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = a->value.f > b->value.f;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
OP_DEF(eq)
|
||||||
void iop_gte(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = a->value.f >= b->value.f;
|
r->type = NUMBER;
|
||||||
}
|
if (a->type == NUMBER && b->type == NUMBER)
|
||||||
|
r->value.f = a->value.f == b->value.f;
|
||||||
|
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;
|
||||||
|
|
||||||
void iop_ne(variable *r, variable *a, variable *b)
|
return 0;
|
||||||
{
|
|
||||||
r->value.f = a->value.f != b->value.f;
|
|
||||||
}
|
}
|
||||||
|
OP_DEF(ne)
|
||||||
void iop_mod(variable *r, variable *a, variable *b)
|
|
||||||
{
|
{
|
||||||
r->value.f = (float)((int)a->value.f % (int)b->value.f);
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = a->value.f != b->value.f;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
OP_DEF(and)
|
||||||
|
{
|
||||||
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = (int)a->value.f & (int)b->value.f;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
OP_DEF(xor)
|
||||||
|
{
|
||||||
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = (int)a->value.f ^ (int)b->value.f;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
OP_DEF(or)
|
||||||
|
{
|
||||||
|
if (a->type != NUMBER || b->type != NUMBER)
|
||||||
|
return -1;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = (int)a->value.f | (int)b->value.f;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
OP_DEF(set)
|
||||||
|
{
|
||||||
|
if (b->type == NUMBER) {
|
||||||
|
a->type = NUMBER;
|
||||||
|
a->value.f = b->value.f;
|
||||||
|
r->type = NUMBER;
|
||||||
|
r->value.f = a->value.f;
|
||||||
|
} else if (b->type == STRING) {
|
||||||
|
a->type = STRING;
|
||||||
|
if (a->value.p != 0)
|
||||||
|
free((void *)a->value.p);
|
||||||
|
a->value.p = (uint32_t)strclone((char *)b->value.p);
|
||||||
|
r->type = STRING;
|
||||||
|
r->value.p = (uint32_t)strclone((char *)a->value.p);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
#ifndef OPS_H_
|
#ifndef OPS_H_
|
||||||
#define OPS_H_
|
#define OPS_H_
|
||||||
|
|
||||||
#include "parser.h"
|
#include "variable.h"
|
||||||
|
|
||||||
#define IOPS_COUNT 16
|
#define OPS_COUNT 18
|
||||||
|
#define OP_MAGIC 0xCAFE3900
|
||||||
|
|
||||||
typedef void (*operation_t)(variable *, variable *, variable *);
|
typedef int (*opfunc_t)(variable *, variable *, variable *);
|
||||||
|
|
||||||
extern char *iops[IOPS_COUNT];
|
extern variable opvars[];
|
||||||
extern operation_t iopfuncs[IOPS_COUNT];
|
extern const char *opnames[];
|
||||||
|
|
||||||
#endif // OPS_H_
|
#endif // OPS_H_
|
||||||
|
@ -1,36 +1,40 @@
|
|||||||
#ifndef PARSER_H_
|
#ifndef PARSER_H_
|
||||||
#define PARSER_H_
|
#define PARSER_H_
|
||||||
|
|
||||||
#include <variable.h>
|
#include "variable.h"
|
||||||
|
|
||||||
typedef variable *stack_t;
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
variable *vars;
|
variable *vars;
|
||||||
char **vnames;
|
char **names;
|
||||||
stack_t *stack;
|
uint32_t *stack;
|
||||||
uint32_t stidx;
|
uint32_t stidx;
|
||||||
variable ***lines;
|
char **lines;
|
||||||
uint32_t lnidx;
|
uint32_t lnidx;
|
||||||
int8_t indent;
|
|
||||||
uint8_t sindent;
|
|
||||||
variable *ret;
|
variable *ret;
|
||||||
} interpreter;
|
uint8_t indent;
|
||||||
|
uint8_t sindent;
|
||||||
|
} instance;
|
||||||
|
|
||||||
#define SKIP (1 << 7)
|
#define SKIP (1 << 7)
|
||||||
|
|
||||||
typedef int (*func_t)(interpreter *);
|
typedef int (*func_t)(instance *);
|
||||||
|
|
||||||
|
instance *inewinstance(void);
|
||||||
|
void idelinstance(instance *it);
|
||||||
|
|
||||||
void iinit(interpreter *);
|
int idoline(instance *it, const char *s);
|
||||||
void iend(interpreter *);
|
variable **iparse(instance *it, const char *s);
|
||||||
|
variable *isolve(instance *it, variable **ops, uint32_t count);
|
||||||
|
|
||||||
void iskip(interpreter *);
|
void inew_cfunc(instance *it, const char *name, func_t func);
|
||||||
|
|
||||||
variable *inew_string(interpreter *, const char *, const char *);
|
variable *make_varf(variable *v, float f);
|
||||||
variable *inew_number(interpreter *, const char *, float);
|
variable *make_vars(variable *v, const char *s);
|
||||||
variable *inew_cfunc(interpreter *, const char *, func_t);
|
|
||||||
|
|
||||||
int idoline(interpreter *, const char *);
|
uint32_t ipop(instance *it);
|
||||||
variable *idoexpr(interpreter *, const char *);
|
void ipush(instance *it, uint32_t v);
|
||||||
|
variable *igetarg(instance *it, uint32_t n);
|
||||||
|
|
||||||
#endif // PARSER_H_
|
#endif // PARSER_H_
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
set x 42
|
|
||||||
set eq ".a/a"
|
|
||||||
|
|
||||||
solve eq > ans
|
|
||||||
|
|
||||||
print ans
|
|
||||||
print "\n"
|
|
@ -1,64 +0,0 @@
|
|||||||
#include "shelpers.h"
|
|
||||||
|
|
||||||
#include <memory.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
char *strclone(const char *s)
|
|
||||||
{
|
|
||||||
char *clone = (char *)malloc(strlen(s) + 1);
|
|
||||||
strcpy(clone, s);
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *strnclone(const char *s, uint32_t n)
|
|
||||||
{
|
|
||||||
char *clone = (char *)malloc(n + 1);
|
|
||||||
strncpy(clone, s, n);
|
|
||||||
clone[n] = '\0';
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t eol(int c)
|
|
||||||
{
|
|
||||||
return c == '\n' || c == '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t eot(int c)
|
|
||||||
{
|
|
||||||
return eol(c) || c == ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t eoe(int c)
|
|
||||||
{
|
|
||||||
return eol(c) || c == ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t findend(const char *s, char o, char c)
|
|
||||||
{
|
|
||||||
uint8_t indent = 0;
|
|
||||||
uint32_t i;
|
|
||||||
for (i = 1; !eol(s[i]); i++) {
|
|
||||||
if (s[i] == o) {
|
|
||||||
indent++;
|
|
||||||
} else if (s[i] == c) {
|
|
||||||
if (indent == 0)
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
indent--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void skipblank(const char *s, uint8_t (*cmp)(int), uint32_t *offset)
|
|
||||||
{
|
|
||||||
uint32_t i = *offset;
|
|
||||||
while (!cmp(s[i])) {
|
|
||||||
if (s[i] != ' ' && s[i] != '\t')
|
|
||||||
break;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
*offset = i;
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
|||||||
#ifndef SHELPERS_H_
|
|
||||||
#define SHELPERS_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones the given string, malloc'ing a new one.
|
|
||||||
* @param s the string to clone
|
|
||||||
* @return the malloc'd copy
|
|
||||||
*/
|
|
||||||
char *strclone(const char *s);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones the given string until the given count.
|
|
||||||
* @param s the string to clone
|
|
||||||
* @param n the number of characters to clone
|
|
||||||
* @return the malloc'd copy
|
|
||||||
*/
|
|
||||||
char *strnclone(const char *s, uint32_t n);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns non-zero if the character is considered an end-of-line.
|
|
||||||
* @param c a character
|
|
||||||
* @return non-zero if eol, zero if not
|
|
||||||
*/
|
|
||||||
uint8_t eol(int c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns non-zero if the character is considered an end-of-token.
|
|
||||||
* @param c a character
|
|
||||||
* @return non-zero if eot, zero if not
|
|
||||||
*/
|
|
||||||
uint8_t eot(int c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns non-zero if the character is considered an end-of-expression.
|
|
||||||
* @param c a character
|
|
||||||
* @return non-zero if eoe, zero if not
|
|
||||||
*/
|
|
||||||
uint8_t eoe(int c);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the matching end character in a string, e.g. matching parens.
|
|
||||||
* @param s the string to search
|
|
||||||
* @param o the starting, opening character (e.g. '(')
|
|
||||||
* @param c the end, closing character (e.g. ')')
|
|
||||||
* @return offset of the end character in the string
|
|
||||||
*/
|
|
||||||
uint32_t findend(const char *s, char o, char c);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments offset until the character in the string is not blank or fails
|
|
||||||
* the given comparison.
|
|
||||||
* @param s the string to use
|
|
||||||
* @param cmp a comparing function, stops search if returns true
|
|
||||||
* @param offset the variable to increment while searching
|
|
||||||
*/
|
|
||||||
void skipblank(const char *s, uint8_t (*cmp)(int), uint32_t *offset);
|
|
||||||
|
|
||||||
#endif // SHELPERS_H_
|
|
@ -1,38 +0,0 @@
|
|||||||
#include "stack.h"
|
|
||||||
|
|
||||||
void ipush(interpreter *it, void *v)
|
|
||||||
{
|
|
||||||
it->stack[it->stidx++] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *ipop(interpreter *it)
|
|
||||||
{
|
|
||||||
return it->stack[--it->stidx];
|
|
||||||
}
|
|
||||||
|
|
||||||
void ipopm(interpreter *it, uint32_t count)
|
|
||||||
{
|
|
||||||
it->stidx -= count;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable *igetarg(interpreter *interp, uint32_t index)
|
|
||||||
{
|
|
||||||
return interp->stack[interp->stidx - index - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *igetarg_string(interpreter *interp, uint32_t index)
|
|
||||||
{
|
|
||||||
if (index >= interp->stidx)
|
|
||||||
return 0;
|
|
||||||
variable *v = igetarg(interp, index);
|
|
||||||
return (const char *)v->value.p;
|
|
||||||
}
|
|
||||||
|
|
||||||
float igetarg_number(interpreter *interp, uint32_t index)
|
|
||||||
{
|
|
||||||
if (index >= interp->stidx)
|
|
||||||
return 0;
|
|
||||||
variable *v = igetarg(interp, index);
|
|
||||||
return v->value.f;
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
#ifndef STACK_H_
|
|
||||||
#define STACK_H_
|
|
||||||
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
void ipush(interpreter *it, void *v);
|
|
||||||
void *ipop(interpreter *it);
|
|
||||||
void ipopm(interpreter *it, uint32_t count);
|
|
||||||
|
|
||||||
variable *igetarg(interpreter *interp, uint32_t index);
|
|
||||||
const char *igetarg_string(interpreter *interp, uint32_t index);
|
|
||||||
float igetarg_number(interpreter *interp, uint32_t index);
|
|
||||||
|
|
||||||
#define igetarg_integer(i, x) (int)igetarg_number(i, x)
|
|
||||||
|
|
||||||
#endif // STACK_H_
|
|
@ -1,9 +0,0 @@
|
|||||||
#ifndef STDLIB_H_
|
|
||||||
#define STDLIB_H_
|
|
||||||
|
|
||||||
char *snprintf(char *buf, unsigned int max, const char *format, ...);
|
|
||||||
float strtof(const char *s, char **endptr);
|
|
||||||
|
|
||||||
int atoi(const char *);
|
|
||||||
|
|
||||||
#endif // STDLIB_H_
|
|
@ -0,0 +1,13 @@
|
|||||||
|
# test1
|
||||||
|
# arithmetic tests
|
||||||
|
# looking for proper basic function, respect for order of ops,
|
||||||
|
# and respect for parentheses
|
||||||
|
|
||||||
|
2 + 5
|
||||||
|
14 - 9
|
||||||
|
3 * 8 + 3
|
||||||
|
9 - 3 / 2
|
||||||
|
3 * (8 + 3)
|
||||||
|
(9 - 3) / 2
|
||||||
|
(4 + 5) * ((9 - 1) + 3)
|
||||||
|
5 - 3 + 4
|
@ -0,0 +1,16 @@
|
|||||||
|
# test2
|
||||||
|
# variable and function tests
|
||||||
|
# show variable recognition and proper c-function handling
|
||||||
|
|
||||||
|
a * 1
|
||||||
|
3 + b
|
||||||
|
|
||||||
|
set(a, 5)
|
||||||
|
a * 1
|
||||||
|
|
||||||
|
set(c, 4)
|
||||||
|
a / c
|
||||||
|
|
||||||
|
set(b, 2) set(d, 8)
|
||||||
|
|
||||||
|
d + set(e, 4)
|
@ -0,0 +1,25 @@
|
|||||||
|
# test3
|
||||||
|
# verify builtin functions, conditionals and such
|
||||||
|
|
||||||
|
a = 5
|
||||||
|
|
||||||
|
func(checka) {
|
||||||
|
if (a == 5) {
|
||||||
|
print("a == 5")
|
||||||
|
} else {
|
||||||
|
print("a != 5")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checka
|
||||||
|
|
||||||
|
print("Increment a...")
|
||||||
|
a = a + 1
|
||||||
|
checka
|
||||||
|
|
||||||
|
d = 0
|
||||||
|
while (d < 10) {
|
||||||
|
print(d)
|
||||||
|
d = d + 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
# test4
|
||||||
|
# find memory leaks
|
||||||
|
|
||||||
|
x = 4
|
||||||
|
y = solve("x-2")
|
||||||
|
print(y)
|
@ -1,135 +0,0 @@
|
|||||||
#include "variable.h"
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <memory.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <shelpers.h>
|
|
||||||
|
|
||||||
extern int atoi(const char *);
|
|
||||||
|
|
||||||
char *fixstring(char *s)
|
|
||||||
{
|
|
||||||
char *n = malloc(strlen(s) + 1);
|
|
||||||
int j = 0;
|
|
||||||
for (int i = 0; s[i] != '\0'; i++, j++) {
|
|
||||||
if (s[i] == '\\') {
|
|
||||||
if (s[i + 1] == 'n')
|
|
||||||
n[j] = '\n';
|
|
||||||
i++;
|
|
||||||
} else {
|
|
||||||
n[j] = s[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n[j] = '\0';
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable *make_varn(variable *v, float value)
|
|
||||||
{
|
|
||||||
if (v == 0)
|
|
||||||
v = (variable *)malloc(sizeof(variable));
|
|
||||||
v->used = 0;
|
|
||||||
v->fromc = 0;
|
|
||||||
v->valtype = NUMBER;
|
|
||||||
v->value.f = value;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable *make_vars(variable *v, const char *value)
|
|
||||||
{
|
|
||||||
if (v == 0)
|
|
||||||
v = (variable *)malloc(sizeof(variable));
|
|
||||||
v->used = 0;
|
|
||||||
v->fromc = 0;
|
|
||||||
v->valtype = STRING;
|
|
||||||
v->value.p = (value != 0) ? (uint32_t)fixstring(value) : 0;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable *make_varf(variable *v, uint8_t fromc, uint32_t func)
|
|
||||||
{
|
|
||||||
if (v == 0)
|
|
||||||
v = (variable *)malloc(sizeof(variable));
|
|
||||||
v->used = 0;
|
|
||||||
v->fromc = fromc;
|
|
||||||
v->valtype = FUNC;
|
|
||||||
v->value.p = func;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable *make_vare(variable *v, const char *expr)
|
|
||||||
{
|
|
||||||
if (v == 0)
|
|
||||||
v = (variable *)malloc(sizeof(variable));
|
|
||||||
v->used = 0;
|
|
||||||
v->fromc = 0;
|
|
||||||
v->valtype = EXPR;
|
|
||||||
v->value.p = (uint32_t)strclone(expr);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
int try_variable(char **name, const char *text)
|
|
||||||
{
|
|
||||||
if (name == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
int neg = 1;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (text[0] == '-') {
|
|
||||||
neg = -1;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (!isalpha(text[i]))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (i++; isalnum(text[i]); i++);
|
|
||||||
|
|
||||||
int o = (neg < 0);
|
|
||||||
if (neg < 0)
|
|
||||||
i--;
|
|
||||||
*name = (char *)malloc(i + 1);
|
|
||||||
strncpy(*name, text + o, i);
|
|
||||||
(*name)[i] = '\0';
|
|
||||||
return (neg > 0) ? i : -(i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int try_number(variable *v, const char *text)
|
|
||||||
{
|
|
||||||
if (v == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
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';
|
|
||||||
|
|
||||||
make_varn(v, strtof(buf, 0));
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
return i;
|
|
||||||
}
|
|
Loading…
Reference in New Issue