/** * @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 . */ #include "parser.h" #include "builtins.h" #include "ops.h" #include #include #include #include #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; }