You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

610 lines
13 KiB
C

/**
* @file parser.c
* Main library component; parses, manages and runs script
*
* 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 "parser.h"
#include "builtins.h"
#include "ops.h"
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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;
}
void itryfree(variable *v)
{
if (v == 0 || v->tmp == 0)
return;
if (v->type == STRING)
free((void *)v->value.p);
free(v);
}
instance *inewinstance(void)
{
instance *it = (instance *)malloc(sizeof(instance));
it->vars = (variable *)calloc(MAX_VARS, sizeof(variable));
it->names = (char **)calloc(MAX_VARS, sizeof(char *));
it->stack = (uint32_t *)malloc(MAX_STACK * sizeof(uint32_t));
it->stidx = 0;
it->lines = (variable ***)calloc(MAX_LINES, sizeof(variable **));
it->lnidx = 0;
it->ret = 0;
it->indent = 0;
it->sindent = 0;
iload_builtins(it);
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 idelinstance(instance *it)
{
for (uint32_t i = 0; i < MAX_LINES; i++) {// TODO free vars!
if (it->lines[i] == 0)
continue;
idelline(it->lines[i]);
free(it->lines[i]);
}
free(it->lines);
free(it->vars);
for (uint32_t i = 0; i < MAX_VARS; i++)
free(it->names[i]);
free(it->names);
free(it->stack);
itryfree(it->ret);
free(it);
}
void ipush(instance *it, uint32_t v)
{
it->stack[it->stidx++] = v;
}
uint32_t ipop(instance *it)
{
return it->stack[--it->stidx];
}
void ipopm(instance *it, uint32_t count)
{
it->stidx -= count;
}
variable *igetarg(instance *it, uint32_t n)
{
return (variable *)it->stack[it->stidx - n - 1];
}
variable *igetvar(instance *it, const char *name);
void inew_cfunc(instance *it, const char *name, func_t func)
{
variable *v = igetvar(it, name);
v->type = CFUNC;
v->value.p = (uint32_t)func;
}
void inew_number(instance *it, const char *name, float f)
{
variable *v = igetvar(it, name);
v->type = NUMBER;
v->value.f = f;
}
void inew_string(instance *it, const char *name, const char *s)
{
variable *v = igetvar(it, name);
v->type = STRING;
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);
if (ops == 0)
return 0;
it->lines[it->lnidx] = ops;
loop:
if (it->ret != 0)
itryfree(it->ret);
it->ret = 0;
variable **copy = (variable **)malloc(32 * sizeof(variable *));
for (int i = 0; i < 32; i++) {
variable *v = it->lines[it->lnidx][i];
if (v != 0) {
if (((uint32_t)v & OP_MAGIC) == OP_MAGIC) {
copy[i] = v;
continue;
}
if (v->tmp == 1)
copy[i] = varclone(v);
else
copy[i] = v;
if (v->type == FUNC || v->type == CFUNC) {
i++;
copy[i] = it->lines[it->lnidx][i]; // argcount
}
} else {
copy[i] = 0;
}
}
it->ret = isolve(it, copy, 0);
if (it->ret == 0)
idelline(copy);
free(copy);
it->lnidx++;
if (it->lines[it->lnidx] != 0)
goto loop;
return 0;
}
variable *isolve_(instance *it, variable **ops, uint32_t count);
variable *isolve(instance *it, variable **ops, uint32_t count)
{
if (count == 0)
for (count = 0; ops[count] != 0; count++);
for (uint32_t i = 0; i < count; i++) {
if (((uint32_t)ops[i] & OP_MAGIC) == OP_MAGIC) {
uint32_t count_ = (uint32_t)ops[i] & 0xFF;
ops[i] = isolve(it, ops + i + 1, count_);
for (uint32_t j = 1; j <= count_; j++)
ops[i + j] = 0;
}
}
return isolve_(it, ops, count);
}
variable *isolve_(instance *it, variable **ops, uint32_t count)
{
// first, look for functions
for (uint32_t i = 0; i < count; i++) {
if (ops[i] == 0)
continue;
if (ops[i]->type == CFUNC || ops[i]->type == FUNC) {
uint32_t nargs = (uint32_t)ops[i + 1] - 1;
uint32_t start = i;
i++;
if (nargs > 0)
i++;
int32_t j;
for (j = nargs; j > 0 && i < count; i++) {
if (ops[i] != 0) {
if (ops[start]->type == CFUNC) {
it->stack[it->stidx + j - 1] = (uint32_t)ops[i];
} else {
char namebuf[6];
snprintf(namebuf, 6, "arg%u",
(uint16_t)(nargs - j));
if (ops[i]->type == NUMBER)
inew_number(it, namebuf, ops[i]->value.f);
else
inew_string(it, namebuf,
(const char *)ops[i]->value.p);
}
j--;
}
}
if (j != 0)
return 0;
if (ops[start]->type == CFUNC) {
func_t func = (func_t)ops[start]->value.p;
it->stidx += nargs;
uint32_t sidx = it->stidx;
int ret = 0;
if (!(it->sindent & SKIP) || (func == bracket_open ||
func == bracket_close))
ret = func(it);
if (ret != 0)
return 0;
if (it->stidx > sidx)
ops[start] = (variable *)ipop(it);
else
ops[start] = 0;
it->stidx -= nargs;
} else {
ipush(it, it->lnidx);
ipush(it, CALL_SIG);
it->lnidx = ops[start]->value.p;
}
ops[start + 1] = 0;
for (uint32_t j = start + 2; j < i; j++) {
itryfree(ops[j]);
ops[j] = 0;
}
}
}
// next, operators
for (uint32_t j = 0; j < OPS_COUNT; j += 2) {
for (uint32_t i = 0; i < count; i++) {
if (ops[i] == 0)
continue;
if (ops[i]->type == OPERATOR) {
if (ops[i]->value.p != (uint32_t)opvars[j].value.p) {
if (ops[i]->value.p != (uint32_t)opvars[j + 1].value.p)
continue;
}
opfunc_t func = (opfunc_t)ops[i]->value.p;
uint32_t aidx = i - 1;
while (ops[aidx] == 0 && aidx != 0)
aidx--;
if (ops[aidx] == 0)
return 0;
uint32_t bidx = i + 1;
while (ops[bidx] == 0 && ++bidx < count);
if (bidx == count)
return 0;
if (!(it->sindent & SKIP)) {
variable *v = !ops[aidx]->tmp ? varclone(ops[aidx]) : ops[aidx];
if (func(v, ops[aidx], ops[bidx]) != 0)
return 0;
ops[aidx] = v;
} else {
itryfree(ops[aidx]);
ops[aidx] = 0;
}
itryfree(ops[bidx]);
ops[bidx] = 0;
ops[i] = 0;
}
}
}
// implicit multiply
/*if (ops[0] != 0 && ops[0]->type == NUMBER) {
for (uint32_t i = 1; i < count; i++) {
if (ops[i] != 0 && ops[i]->type == NUMBER)
ops[0]->value.f *= ops[i]->value.f;
}
}*/
return ops[0];
}
variable **iparse(instance *it, const char *s)
{
variable **ops = 0;
uint32_t ooffset = 0;
int32_t boffset = 1;
size_t offset = 0;
while (isblank(s[offset]))
offset++;
if (s[offset] == '#' || s[offset] == '\0' || s[offset] == '\n')
goto fail;
ops = (variable **)calloc(32, sizeof(variable *));
while (s[offset] != '\0' && s[offset] != '\n') {
if (isalpha(s[offset])) {
size_t end = offset + 1;
while (isalnum(s[end]))
end++;
char *name = strnclone(s + offset, end - offset);
ops[ooffset++] = igetvar(it, name);
free(name);
while (isblank(s[end]))
end++;
if (s[end] == '(') {
uint32_t argidx = ooffset;
uint32_t argcount = 1;
ooffset++;
end++;
uint32_t last = end;
for (int c = 0; c >= 0; end++) {
if (s[end] == '(')
c++;
if (c == 0 && last != end && (s[end] == ',' || s[end] == ')')) {
argcount++;
char *arg = strnclone(s + last, end - last);
uint32_t parenidx = ooffset;
ooffset++;
variable **moreops = iparse(it, arg);
uint32_t count = 0;
if (moreops != 0) {
for (uint32_t i = 0; moreops[i] != 0; count++, i++)
ops[ooffset++] = moreops[i];
free(moreops);
}
free(arg);
ops[parenidx] = (variable *)(OP_MAGIC | count);
last = end + 1;
}
if (s[end] == ')')
c--;
}
if (s[end] != '\0')
end++;
ops[argidx] = (variable *)argcount;
} else if (ops[ooffset - 1]->type == FUNC || ops[ooffset - 1]->type == CFUNC) {
ops[ooffset++] = (variable *)1;
}
offset = end;
} else if (isdigit(s[offset])) {// || (s[offset] == '-' && isdigit(s[offset + 1]))) {
size_t end = offset + 1;
while (isdigit(s[end]) || s[end] == '.')
end++;
char *word = strnclone(s + offset, end - offset);
ops[ooffset++] = make_num(word);
free(word);
offset = end;
} else if (s[offset] == '\"') {
size_t end = offset + 1;
while (s[end] != '\"')// && s[end - 1] == '\\')
end++;
end++;
char *word = strnclone(s + offset, end - offset);
char *fword = fixstring(word);
ops[ooffset++] = make_vars(0, fword);
free(word);
free(fword);
offset = end;
} else if (s[offset] == '(') {
size_t i = offset + 1;
for (int c = 0; s[i] != ')' || --c >= 0; i++) {
if (s[i] == '(')
c++;
}
i++;
char *word = strnclone(s + offset + 1, i - offset - 2);
uint32_t parenidx = ooffset;
ooffset++;
variable **moreops = iparse(it, word);
uint32_t count = 0;
if (moreops != 0) {
for (uint32_t i = 0; moreops[i] != 0; count++, i++)
ops[ooffset++] = moreops[i];
free(moreops);
}
free(word);
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, "}")) {
for (int32_t i = ooffset - 1; i >= boffset - 1; i--)
ops[i + 2] = ops[i];
if (word[0] == '{')
ops[boffset - 1] = &bopen;
else
ops[boffset - 1] = &bclose;
ops[boffset] = (variable *)1; // arg count + 1
boffset += 2;
ooffset += 2;
} else {
variable *v = igetop(word);
if (v == 0) {
free(word);
goto fail;
} else {
if (ooffset == 0) {
ops[ooffset++] = make_varf(0, 0.0f);
} else if (ops[ooffset - 1]->type == OPERATOR) {
free(word);
goto fail;
}
}
ops[ooffset++] = v;
}
free(word);
offset = end;
} else {
offset++;
}
}
// mark end
ops[ooffset] = 0;
return ops;
fail:
if (ops != 0) {
idelline(ops);
free(ops);
}
return 0;
}