1178 lines
42 KiB
C
1178 lines
42 KiB
C
/*
|
|
* no-nonsense TRAC programming language implementation (T64 standard)
|
|
*
|
|
* Build with: cc [-static] -std=c99 -Os -s nntrac.c -o nntrac
|
|
*
|
|
* see README.md for details
|
|
*
|
|
* Created by Luxferre in 2023, released into public domain
|
|
*/
|
|
|
|
#define POSIX_SOURCE
|
|
#define POSIX_C_SOURCE 1
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#define uint unsigned int
|
|
#define uchar unsigned char
|
|
#ifndef NNT_SHARP
|
|
#define NNT_SHARP '#' /* can be redefined to : or other unused character */
|
|
#endif
|
|
#ifndef NNT_SYMNAMELEN
|
|
#define NNT_SYMNAMELEN 32 /* max symbol name length, including null */
|
|
#endif
|
|
|
|
#ifndef strrev
|
|
char *strrev(char *str) {
|
|
char *p1, *p2;
|
|
if(!str || !*str) return str;
|
|
for(p1=str,p2=str+strlen(str)-1;p2>p1;++p1,--p2) {
|
|
*p1 ^= *p2; *p2 ^= *p1; *p1 ^= *p2;
|
|
}
|
|
return str;
|
|
}
|
|
#endif
|
|
|
|
enum NNT_MARKERS { /* various markers: 248 to 255 never occur in UTF-8 */
|
|
NNT_AFST=-8, /* active function start */
|
|
NNT_NFST, /* neutral function start */
|
|
NNT_EOF, /* end of function */
|
|
NNT_ADEL, /* argument delimiter */
|
|
NNT_SEGGAP /* segment gap character */
|
|
};
|
|
enum NNT_MODES {NNT_NORMAL=1, NNT_LEGACY, NNT_SECURE}; /* operation modes */
|
|
static const char NNT_ADEL_S[2] = {NNT_ADEL, 0};
|
|
static uchar nnt_meta = '\''; /* apostrophe by default */
|
|
|
|
/* combined active + neutral string buffer and evaluation result buffer */
|
|
static char *nnt_prog, *nnt_res, nnt_mode = NNT_NORMAL, nnt_mlock = 0;
|
|
/* scanning pointers and buffer lengths */
|
|
static int nnt_ascan, nnt_alen, nnt_flen, nnt_reslen, nnt_trace = 0;
|
|
|
|
/* string to number */
|
|
long long snum(char *str) { /* strtoll wrapper */
|
|
char *endptr;
|
|
return strtoll(str, &endptr, 0);
|
|
}
|
|
|
|
/* number to string */
|
|
char *n2s(long long num, char *s, int *slen) {
|
|
*slen = snprintf(NULL, 0, "%lld", num) + 1;
|
|
s = realloc(s, *slen);
|
|
memset(s, 0, *slen); /* fill it with zeroes*/
|
|
snprintf(s, *slen, "%lld", num);
|
|
s = realloc(s, (*slen) = strlen(s));
|
|
return s;
|
|
}
|
|
|
|
/* primitive function definition */
|
|
|
|
typedef struct nnt_primitive_t {
|
|
char name[NNT_SYMNAMELEN]; /* primitive function name */
|
|
/* registered function pointer */
|
|
char* (*handler)(char *arglist, char *res, int *reslen);
|
|
} nnt_primitive;
|
|
|
|
static nnt_primitive *nnt_primitives; /* primitives table */
|
|
static uint nnt_primitive_len = 0; /* primitives table size */
|
|
|
|
/* find a primitive function index by name, -1 if not found */
|
|
int nnt_findprimitive(const char *name) {
|
|
int i;
|
|
for(i=0;i<nnt_primitive_len;i++)
|
|
if(strncmp(nnt_primitives[i].name, name, NNT_SYMNAMELEN) == 0)
|
|
return i; /* found */
|
|
return -1; /* nothing found */
|
|
}
|
|
|
|
/* register a primitive function, overwrite if already exists */
|
|
void nnt_regprimitive(const char *name, void *fptr) {
|
|
int pindex = nnt_findprimitive(name);
|
|
nnt_primitive newprim; /* create a new primitive template */
|
|
strncpy(newprim.name, name, NNT_SYMNAMELEN); /* fill the name */
|
|
newprim.handler = fptr; /* fill the handler pointer */
|
|
if(pindex == -1) { /* create a new table entry */
|
|
nnt_primitive_len++;
|
|
nnt_primitives = realloc(nnt_primitives,
|
|
nnt_primitive_len * sizeof(nnt_primitive));
|
|
nnt_primitives[nnt_primitive_len - 1] = newprim;
|
|
}
|
|
else nnt_primitives[pindex] = newprim; /* replace the entry */
|
|
}
|
|
|
|
/* forms definition */
|
|
|
|
typedef struct nnt_form_t {
|
|
char name[NNT_SYMNAMELEN]; /* form (var/func) name */
|
|
char *value; /* (reallocatable) value pointer (null-terminated) */
|
|
uint len; /* form value length */
|
|
int fptr; /* form character pointer */
|
|
} nnt_form;
|
|
|
|
static nnt_form *nnt_forms; /* forms table */
|
|
static uint nnt_forms_len = 0; /* forms table size */
|
|
|
|
/* find a form index by name, -1 if not found */
|
|
int nnt_findform(char *name) {
|
|
int i;
|
|
for(i=0;i<nnt_forms_len;i++)
|
|
if(strncmp(nnt_forms[i].name, name, NNT_SYMNAMELEN) == 0)
|
|
return i; /* found */
|
|
return -1; /* nothing found */
|
|
}
|
|
|
|
/* find a free form among previously deleted entries */
|
|
int nnt_findfreeform() {
|
|
int i;
|
|
for(i=0;i<nnt_forms_len;i++)
|
|
if(nnt_forms[i].name[0] == NNT_EOF) return i; /* found */
|
|
return -1; /* nothing found */
|
|
}
|
|
|
|
/* assign a value to a form, creating it if doesn't exist */
|
|
void nnt_assignform(char *name, char *value, uint len, int ptr) {
|
|
int findex = nnt_findform(name);
|
|
if(findex < 0) findex = nnt_findfreeform(); /* search among deleted */
|
|
if(findex > -1) { /* existing form: just update the entry */
|
|
strncpy(nnt_forms[findex].name, name, NNT_SYMNAMELEN);
|
|
nnt_forms[findex].value = realloc(nnt_forms[findex].value, len);
|
|
memcpy(nnt_forms[findex].value, value, len);
|
|
nnt_forms[findex].len = len; /* also update the length */
|
|
nnt_forms[findex].fptr = ptr; /* reset the pointer */
|
|
} else { /* create a new form and update the table */
|
|
nnt_form newform;
|
|
strncpy(newform.name, name, NNT_SYMNAMELEN); /* fill the name */
|
|
newform.value = calloc(len, 1); /* allocate the space */
|
|
memcpy(newform.value, value, len); /* populate the value */
|
|
newform.len = len; /* populate the length */
|
|
newform.fptr = ptr; /* init the pointer */
|
|
nnt_forms_len++;
|
|
nnt_forms = realloc(nnt_forms, nnt_forms_len * sizeof(nnt_form));
|
|
nnt_forms[nnt_forms_len - 1] = newform;
|
|
}
|
|
}
|
|
|
|
/* delete a form by its name */
|
|
/* doesn't fully delete the structure, just marks it as free to use */
|
|
void nnt_delform(char *name) {
|
|
int findex = nnt_findform(name);
|
|
if(findex > -1) {
|
|
nnt_forms[findex].value = realloc(nnt_forms[findex].value, 0);
|
|
nnt_forms[findex].len = 0;
|
|
nnt_forms[findex].fptr = 0;
|
|
memset(nnt_forms[findex].name, 0, NNT_SYMNAMELEN);
|
|
/* null name is a valid form name in TRAC, so we use a marker char */
|
|
nnt_forms[findex].name[0] = NNT_EOF;
|
|
}
|
|
}
|
|
|
|
/* form serialization/deserialization using netstrings */
|
|
|
|
/* serialize the form and populate the buffer and length */
|
|
char* nnt_serializeform(nnt_form *form, char *buf, int *len) {
|
|
*len = NNT_SYMNAMELEN + form->len + sizeof(int);
|
|
int lbl = snprintf(NULL, 0, "%d:", *len); /* length buffer length */
|
|
char *lenbuf = calloc(lbl + 1, 1);
|
|
snprintf(lenbuf, lbl+1, "%d:", *len); /* fill in length buffer */
|
|
*len += lbl;
|
|
buf = realloc(buf, *len);
|
|
memset(buf, 0, *len); /* zero out the buffer */
|
|
memcpy(buf, lenbuf, lbl); /* fill the length buffer */
|
|
memcpy(buf + lbl, form->name, NNT_SYMNAMELEN); /* fill name */
|
|
memcpy(buf + lbl + NNT_SYMNAMELEN, &(form->fptr), sizeof(int)); /* fill fptr */
|
|
/* fill value */
|
|
memcpy(buf + lbl + NNT_SYMNAMELEN + sizeof(int), form->value, form->len);
|
|
buf[(*len) - 1] = ','; /* trailing comma */
|
|
free(lenbuf); return buf;
|
|
}
|
|
|
|
/* deserialize the form given the netstring buffer
|
|
(push it into the form storage using nnt_assignform) */
|
|
int nnt_deserializeform(char *buf) {
|
|
char lenbuf[11] = {0};
|
|
int i, rdlen, fullen, rdptr, fid;
|
|
for(i=0;buf[i]!=':' && i<11;i++) lenbuf[i] = buf[i];
|
|
rdlen = snum(lenbuf); /* practical read length */
|
|
fullen = rdlen + strlen(lenbuf);
|
|
if(buf[fullen] != ',') return -1; /* invalid netstring detected */
|
|
memcpy(&rdptr, &buf[i+1+NNT_SYMNAMELEN], sizeof(int)); /* fetch pointer */
|
|
/* name is at i + 1, value is NNT_SYMNAMELEN + sizeof(int) away */
|
|
nnt_assignform(&buf[i+1], &buf[i+1+NNT_SYMNAMELEN+sizeof(int)],
|
|
rdlen-NNT_SYMNAMELEN-sizeof(int)-1, rdptr);
|
|
/* return the total length of processed data */
|
|
return fullen;
|
|
}
|
|
|
|
/* parser logic */
|
|
|
|
/* find the matching character */
|
|
int nnt_findmatch(char *str, uint len, int curpos,
|
|
uchar c1, uchar c2, int dir) {
|
|
int balance = 0, xpos = curpos;
|
|
if(xpos >= len) return -1;
|
|
for(;xpos >=0 && xpos < len ;xpos += dir) {
|
|
if(str[xpos] == c1) balance++;
|
|
if(str[xpos] == c2) balance--;
|
|
if(!balance) break;
|
|
}
|
|
return (xpos >= 0 && xpos < len) ? xpos : -1;
|
|
}
|
|
|
|
/* delete len chars from buf at pos, return new buf length */
|
|
int nnt_delchars(char *buf, int buflen, int pos, int len) {
|
|
if(len < 1) len = 1; /* safeguard */
|
|
if(pos < 0) pos = 0; /* safeguard */
|
|
memmove(&buf[pos], &buf[pos+len], buflen - pos - len);
|
|
return buflen - len;
|
|
}
|
|
|
|
/* delete a single character under the pos in the active buffer */
|
|
int nnt_delchar(int pos) {
|
|
nnt_flen = nnt_delchars(nnt_prog, nnt_flen, pos, 1);
|
|
nnt_prog[nnt_flen] = 0;
|
|
nnt_alen = nnt_flen - nnt_ascan; /* update active length */
|
|
return nnt_flen;
|
|
}
|
|
|
|
/* insert len chars into buffer dest of length dlen from buffer src at pos */
|
|
/* it reallocs dest, updates dlen and returns the resulting dest pointer */
|
|
char* nnt_inschars(char *dest, int *dlen, char *src, int len, int pos) {
|
|
*dlen += len; /* increase the length */
|
|
dest = realloc(dest, *dlen); /* reallocate the buffer */
|
|
memmove(&dest[pos+len], &dest[pos], (*dlen)-pos-len); /* free up the space */
|
|
memmove(&dest[pos], src, len); /* copy the source */
|
|
return dest;
|
|
}
|
|
|
|
/* replace all occurrences of from with to in str (all null-terminated) */
|
|
/* returns the new str pointer (str must be dynamically allocated before) */
|
|
char *nnt_replace(char *str, char *from, char *to) {
|
|
int len = strlen(str), fromlen = strlen(from), tolen = strlen(to), i;
|
|
char *ptr = str;
|
|
while((ptr = strstr(ptr, from)) != NULL) { /* find the occurrence */
|
|
len = nnt_delchars(str, len, (int) (ptr - str), fromlen); /* delete */
|
|
str = nnt_inschars(str, &len, to, tolen, (int) (ptr - str)); /* add */
|
|
ptr += tolen; /* move ptr */
|
|
}
|
|
str[len] = 0; /* ensure null termination in the result */
|
|
return str;
|
|
}
|
|
|
|
/* cl primitive: call string (form) */
|
|
char* prim_cl(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *name, *seg, snum = 0;
|
|
char idstr[3] = {NNT_SEGGAP, 0, 0}; /* update idstr[1] then */
|
|
int fid = -1;
|
|
/* skip the first one */
|
|
name = strtok(NULL, NNT_ADEL_S); /* get the name */
|
|
if(name != NULL && (fid = nnt_findform(name)) > -1) {
|
|
/* populate the result */
|
|
*reslen = nnt_forms[fid].len;
|
|
res = realloc(res, *reslen);
|
|
memcpy(res, nnt_forms[fid].value, *reslen);
|
|
do { /* receive the segment ids */
|
|
seg = strtok(NULL, NNT_ADEL_S);
|
|
if(seg != NULL) {
|
|
snum++; /* we start from 1 to avoid null bytes */
|
|
idstr[1] = snum; /* update the segmentid byte */
|
|
res = nnt_replace(res, idstr, seg);
|
|
*reslen = strlen(res) + 1;
|
|
}
|
|
} while(seg != NULL);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* evaluate a TRAC function based on the neutral string
|
|
storing the results into nnt_res and nnt_reslen */
|
|
void nnt_evalfunc(int startptr) {
|
|
char *arglist, *primname, *arg;
|
|
int i, l=0, primindex;
|
|
for(i=startptr;nnt_prog[i]!=NNT_EOF;i++,l++);
|
|
arglist = calloc(l, 1);
|
|
for(i=0;i<l;i++) arglist[i] = nnt_prog[startptr + i];
|
|
/* now we have NNT_ADEL-delimited argument list to pass */
|
|
nnt_reslen = 0; /* null value is by default */
|
|
primname = strtok(arglist, NNT_ADEL_S); /* get primitive name */
|
|
primindex = nnt_findprimitive(primname);
|
|
if(primindex > -1) { /* primitive found */
|
|
if(nnt_trace) { /* trace mode */
|
|
fprintf(stderr, "[nntrace] (%s", primname);
|
|
while((arg = strtok(NULL, NNT_ADEL_S)) != NULL)
|
|
fprintf(stderr, ",%s", arg);
|
|
fprintf(stderr, ")");
|
|
}
|
|
for(i=0;i<l;i++) arglist[i] = nnt_prog[startptr + i]; /* restore */
|
|
nnt_res = (*(nnt_primitives[primindex].handler))(arglist, nnt_res, &nnt_reslen);
|
|
}
|
|
/* now we have some core-level primitives */
|
|
else if(strncmp(primname, "hl", 3) == 0) nnt_reslen = -1; /* halt */
|
|
else if(strncmp(primname, "tn", 3) == 0) nnt_trace = 1; /* trace on */
|
|
else if(strncmp(primname, "tf", 3) == 0) nnt_trace = 0; /* trace off */
|
|
else { /* not found, run the default call logic */
|
|
if(nnt_trace) { /* trace mode */
|
|
fprintf(stderr, "[nntrace] (cl,%s",primname);
|
|
while((arg = strtok(NULL, NNT_ADEL_S)) != NULL)
|
|
fprintf(stderr, ",%s", arg);
|
|
fprintf(stderr, ")");
|
|
}
|
|
/* expand the arglist */
|
|
arglist = realloc(arglist, l + 2);
|
|
/* insert a dummy char and the argument delimiter at the start */
|
|
arglist[0] = '0'; arglist[1] = NNT_ADEL;
|
|
for(i=0;i<l;i++) arglist[i+2] = nnt_prog[startptr + i]; /* restore */
|
|
nnt_res = prim_cl(arglist, nnt_res, &nnt_reslen);
|
|
}
|
|
if(nnt_trace) { /* trace mode */
|
|
if(nnt_reslen)
|
|
fprintf(stderr, " => %*.*s\r\n", nnt_reslen, nnt_reslen, nnt_res);
|
|
else fprintf(stderr, " => null\r\n");
|
|
}
|
|
if(arglist) free(arglist);
|
|
}
|
|
|
|
/* main TRAC processing algorithm */
|
|
void nnt_proc(char *prog, uint len) {
|
|
nnt_res = malloc(nnt_reslen = 0); /* init the result buffer */
|
|
nnt_prog = calloc(len+1, 1); /* init the neutral+active string buffer */
|
|
/* load the entire program into the active string */
|
|
memcpy(nnt_prog, prog, len);
|
|
uchar cc; /* currently processed character */
|
|
int bp, rl, j; /* position buffers for various needs */
|
|
nnt_ascan = 0; /* init the pointer and NS length */
|
|
nnt_alen = nnt_flen = len; /* init active length with the full length */
|
|
while(nnt_alen >= 0) { /* processing loop start */
|
|
switch((cc = nnt_prog[nnt_ascan])) { /* rule 1 */
|
|
case '(': /* rule 2 */
|
|
bp = nnt_findmatch(nnt_prog, nnt_flen, nnt_ascan, '(', ')', 1);
|
|
if(bp > -1) { /* copy the entire region into the neutral string */
|
|
rl = bp - nnt_ascan; /* nested region length */
|
|
nnt_delchar(nnt_ascan); /* delete this paren */
|
|
nnt_ascan += rl - 1; /* increment active pointer */
|
|
nnt_delchar(nnt_ascan); /* delete matching paren */
|
|
}
|
|
break;
|
|
case '\r': case '\n': case '\t': /* rule 3 */
|
|
nnt_delchar(nnt_ascan);
|
|
break;
|
|
case ',': /* rule 4 */
|
|
nnt_prog[nnt_ascan++] = NNT_ADEL; /* argument delimiter */
|
|
break;
|
|
case NNT_SHARP: /* rules 5 to 7 */
|
|
switch(nnt_prog[nnt_ascan+1]) {
|
|
case '(': /* rule 5 - active function start */
|
|
nnt_delchar(nnt_ascan); /* remove # character */
|
|
nnt_prog[nnt_ascan++] = NNT_AFST;
|
|
break;
|
|
case NNT_SHARP: /* rule 6 */
|
|
if(nnt_prog[nnt_ascan+2] == '(') { /* neutral function start */
|
|
nnt_delchar(nnt_ascan); /* remove # character */
|
|
nnt_delchar(nnt_ascan); /* remove another # character */
|
|
nnt_prog[nnt_ascan++] = NNT_NFST;
|
|
} else nnt_ascan++; /* rule 7 */
|
|
break;
|
|
default: nnt_ascan++; /* rule 7 */
|
|
}
|
|
break;
|
|
case ')': /* rule 8 */
|
|
nnt_prog[nnt_ascan] = NNT_EOF;
|
|
for(bp = nnt_ascan-1;bp>0;bp--) /* find function start */
|
|
if(nnt_prog[bp] == NNT_AFST || nnt_prog[bp] == NNT_NFST) break;
|
|
/* evaluate the function: the result is stored into nnt_res,
|
|
its length into nnt_reslen */
|
|
nnt_evalfunc(bp + 1); /* skip function start marker */
|
|
if(nnt_reslen > 0) {
|
|
nnt_prog = nnt_inschars(nnt_prog, &nnt_flen,
|
|
nnt_res, nnt_reslen, nnt_ascan + 1);
|
|
if(nnt_prog[bp] == NNT_AFST) { /* rule 11 */
|
|
nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1);
|
|
nnt_ascan = bp;
|
|
} else if(nnt_prog[bp] == NNT_NFST) { /* rule 12 */
|
|
nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1);
|
|
nnt_ascan = bp + nnt_reslen;
|
|
}
|
|
} else if(nnt_reslen == 0) { /* rule 10 */
|
|
nnt_flen = nnt_delchars(nnt_prog, nnt_flen, bp, nnt_ascan - bp + 1);
|
|
nnt_ascan = bp;
|
|
} else nnt_ascan = nnt_flen + 1; /* negative reslen causes the halt */
|
|
break;
|
|
default: nnt_ascan++; /* rule 9 */
|
|
}
|
|
nnt_alen = nnt_flen - nnt_ascan; /* update the length on every step */
|
|
nnt_prog[nnt_flen] = 0;
|
|
}
|
|
/* free the buffers */
|
|
free(nnt_prog);
|
|
free(nnt_res);
|
|
}
|
|
|
|
/* all other nntrac primitives */
|
|
|
|
/* ps primitive: print string */
|
|
char* prim_ps(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S);
|
|
/* skip the first one (the ps string) */
|
|
do {
|
|
arg = strtok(NULL, NNT_ADEL_S);
|
|
if(arg != NULL) printf("%s", arg);
|
|
} while(arg != NULL);
|
|
return res;
|
|
}
|
|
|
|
/* rc primitive: read char */
|
|
char* prim_rc(char *arglist, char *res, int *reslen) {
|
|
res = realloc(res, 1);
|
|
res[0] = getchar();
|
|
*reslen = 1;
|
|
return res;
|
|
}
|
|
|
|
/* rs primitive: read string */
|
|
char* prim_rs(char *arglist, char *res, int *reslen) {
|
|
int c, i = 0;
|
|
res = realloc(res, (*reslen) = 0);
|
|
while((c = getchar()) > 0 && c != nnt_meta) {
|
|
(*reslen)++;
|
|
res = realloc(res, (*reslen) + 1);
|
|
res[(*reslen) - 1] = c&255;
|
|
res[*reslen] = 0; /* prefill with null terminator */
|
|
}
|
|
return res = realloc(res, (*reslen) = strlen(res));
|
|
}
|
|
|
|
/* cm primitive: change meta */
|
|
char* prim_cm(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S);
|
|
arg = strtok(NULL, NNT_ADEL_S);
|
|
if(arg != NULL) nnt_meta = arg[0];
|
|
return res;
|
|
}
|
|
|
|
/* ds primitive: define string (form) */
|
|
char* prim_ds(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *name, *value;
|
|
name = strtok(NULL, NNT_ADEL_S); /* get the name */
|
|
if(name != NULL) {
|
|
value = strtok(NULL, NNT_ADEL_S); /* get the value */
|
|
if(value != NULL) nnt_assignform(name, value, strlen(value), 0);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* ss primitive: segment string (form) */
|
|
char* prim_ss(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *name, *seg, sid = 0;
|
|
char idstr[3] = {NNT_SEGGAP, 0, 0}; /* update idstr[1] then */
|
|
int fid = -1;
|
|
/* skip the first one (the ps string) */
|
|
name = strtok(NULL, NNT_ADEL_S); /* get the name */
|
|
if(name != NULL && (fid = nnt_findform(name)) > -1) {
|
|
do { /* receive the segment ids */
|
|
seg = strtok(NULL, NNT_ADEL_S);
|
|
if(seg != NULL) {
|
|
sid++; /* we start from 1 to avoid null bytes */
|
|
idstr[1] = sid; /* update the segmentid byte */
|
|
nnt_forms[fid].value = nnt_replace(nnt_forms[fid].value, seg, idstr);
|
|
nnt_forms[fid].len = strlen(nnt_forms[fid].value) + 1;
|
|
}
|
|
} while(seg != NULL);
|
|
}
|
|
nnt_forms[fid].fptr = 0;
|
|
return res;
|
|
}
|
|
|
|
/* cr primitive: call restore */
|
|
char* prim_cr(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *name;
|
|
int fid = -1;
|
|
name = strtok(NULL, NNT_ADEL_S);
|
|
if(name != NULL && (fid = nnt_findform(name)) > -1)
|
|
nnt_forms[fid].fptr = 0;
|
|
return res;
|
|
}
|
|
|
|
/* move form pointer helper */
|
|
void nnt_moveptr(nnt_form *form, int offs) {
|
|
int i = 0; if(offs < 1) offs = 1;
|
|
for(i=0;i<offs;i++) {
|
|
form->fptr++;
|
|
while(form->value[form->fptr] == NNT_SEGGAP && form->fptr < form->len)
|
|
form->fptr += 2; /* skip the gap and the segment id */
|
|
if(form->fptr > form->len) {
|
|
form->fptr = form->len;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* partial character fetch helper (returns -1 if unavailable) */
|
|
char nnt_fetchformchar(nnt_form *form, char dir) {
|
|
if(dir >= 0) { /* move forward */
|
|
while(form->value[form->fptr] == NNT_SEGGAP) {
|
|
form->fptr += 2;
|
|
if(form->fptr > form->len) { form->fptr = form->len; return -1; }
|
|
}
|
|
} else { /* move back */
|
|
while(form->fptr >= 0 && form->value[form->fptr] == NNT_SEGGAP) {
|
|
form->fptr--;
|
|
if(form->fptr > 0 && form->value[form->fptr - 1] == NNT_SEGGAP)
|
|
form->fptr--; /* skip the segment id byte */
|
|
if(form->fptr < 0) { form->fptr = 0; return -1; }
|
|
};
|
|
}
|
|
if(form->fptr >= 0 && form->fptr < form->len)
|
|
return form->value[form->fptr];
|
|
else return -1;
|
|
}
|
|
|
|
/* cs primitive: call segment */
|
|
char* prim_cs(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *name, *def;
|
|
int fid = -1;
|
|
name = strtok(NULL, NNT_ADEL_S);
|
|
def = strtok(NULL, NNT_ADEL_S);
|
|
if(name != NULL && (fid = nnt_findform(name)) > -1) {
|
|
int sl; /* segment length */
|
|
for(sl=0;sl<nnt_forms[fid].len;sl++)
|
|
if(nnt_forms[fid].value[nnt_forms[fid].fptr+sl] == NNT_SEGGAP) break;
|
|
if(nnt_forms[fid].fptr + sl > nnt_forms[fid].len)
|
|
sl = nnt_forms[fid].len - nnt_forms[fid].fptr;
|
|
if(sl > 0) {
|
|
res = realloc(res, (*reslen) = sl);
|
|
memcpy(res, &nnt_forms[fid].value[nnt_forms[fid].fptr], sl);
|
|
nnt_moveptr(&nnt_forms[fid], sl); /* move the pointer */
|
|
} else if(def != NULL) { /* set the default value */
|
|
res = realloc(res, (*reslen) = strlen(def));
|
|
memcpy(res, def, *reslen);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* cc primitive: call character */
|
|
char* prim_cc(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *name, *def;
|
|
int fid = -1;
|
|
name = strtok(NULL, NNT_ADEL_S);
|
|
def = strtok(NULL, NNT_ADEL_S);
|
|
if(name != NULL && (fid = nnt_findform(name)) > -1) {
|
|
res = realloc(res, (*reslen) = 1);
|
|
res[0] = nnt_fetchformchar(&nnt_forms[fid], 1);
|
|
if(res[0] == -1) { /* set the default value */
|
|
if(def != NULL) {
|
|
res = realloc(res, (*reslen) = strlen(def));
|
|
memcpy(res, def, *reslen);
|
|
} else *reslen = 0;
|
|
}
|
|
nnt_moveptr(&nnt_forms[fid], 1); /* move the pointer */
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* cn primitive: call N characters */
|
|
char* prim_cn(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *name, *offs, *def, c;
|
|
int fid = -1, i, roff, ccount = 0;
|
|
name = strtok(NULL, NNT_ADEL_S);
|
|
offs = strtok(NULL, NNT_ADEL_S);
|
|
def = strtok(NULL, NNT_ADEL_S);
|
|
if(name != NULL && (fid = nnt_findform(name)) > -1) {
|
|
roff = snum(offs); /* real offset */
|
|
if(roff > 0) { /* positive offset */
|
|
res = realloc(res, (*reslen) = roff + 1);
|
|
memset(res, 0, *reslen);
|
|
for(i=0;i<roff;i++) {
|
|
c = nnt_fetchformchar(&nnt_forms[fid], 1);
|
|
if(c < 0) break; else res[i] = c;
|
|
nnt_forms[fid].fptr++; ccount++;
|
|
}
|
|
if(nnt_forms[fid].fptr > nnt_forms[fid].len)
|
|
nnt_forms[fid].fptr = nnt_forms[fid].len;
|
|
*reslen = strlen(res); /* update the length */
|
|
} else if(roff < 0) { /* negative offset */
|
|
roff = -roff; /* get absolute value */
|
|
res = realloc(res, (*reslen) = roff + 1);
|
|
memset(res, 0, *reslen);
|
|
nnt_forms[fid].fptr--; /* start counting from the previous char */
|
|
for(i=0;i<roff;i++) {
|
|
c = nnt_fetchformchar(&nnt_forms[fid], -1);
|
|
if(c < 0) break; else res[i] = c;
|
|
nnt_forms[fid].fptr--; ccount++;
|
|
}
|
|
if(nnt_forms[fid].fptr < 0) nnt_forms[fid].fptr = 0;
|
|
*reslen = strlen(res); /* update the length */
|
|
res = strrev(res); /* reverse the order */
|
|
}
|
|
if(ccount && *reslen) /* finalize the allocation */
|
|
res = realloc(res, *reslen);
|
|
else if(def != NULL) { /* return the default value */
|
|
res = realloc(res, (*reslen) = strlen(def));
|
|
memcpy(res, def, *reslen);
|
|
} else *reslen = 0;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* in primitive: initial */
|
|
char* prim_in(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *name, *subs, *def;
|
|
name = strtok(NULL, NNT_ADEL_S);
|
|
subs = strtok(NULL, NNT_ADEL_S);
|
|
def = strtok(NULL, NNT_ADEL_S);
|
|
int fid, i, slen;
|
|
if(name != NULL && subs != NULL && (fid = nnt_findform(name)) > -1) {
|
|
char *ntv = calloc(nnt_forms[fid].len + 1, 1); /* null-terminated value */
|
|
memcpy(ntv, nnt_forms[fid].value, nnt_forms[fid].len);
|
|
char *found = strstr(ntv, subs);
|
|
if(found != NULL && nnt_forms[fid].fptr < nnt_forms[fid].len) {
|
|
slen = ((long) (found - ntv)) - nnt_forms[fid].fptr;
|
|
if(slen > 0) {
|
|
res = realloc(res, slen + 1);
|
|
memset(res, 0, slen + 1); /* null-terminate */
|
|
for(i=0;i<slen;i++) { /* copy ignoring the seggaps */
|
|
res[i] = nnt_fetchformchar(&nnt_forms[fid], 1);
|
|
nnt_moveptr(&nnt_forms[fid], 1);
|
|
}
|
|
*reslen = strlen(res); /* update the length */
|
|
res = realloc(res, *reslen); /* finalize */
|
|
nnt_moveptr(&nnt_forms[fid], strlen(subs)); /* move after the fptr */
|
|
} else found = NULL;
|
|
} else found = NULL;
|
|
if(found == NULL && def != NULL) { /* return the default value */
|
|
res = realloc(res, (*reslen) = strlen(def));
|
|
memcpy(res, def, *reslen);
|
|
}
|
|
free(ntv);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* dd primitive: delete definition(s) */
|
|
char* prim_dd(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S);
|
|
do {
|
|
arg = strtok(NULL, NNT_ADEL_S);
|
|
if(arg != NULL) nnt_delform(arg);
|
|
} while(arg != NULL);
|
|
return res;
|
|
}
|
|
|
|
/* da primitive: delete all forms */
|
|
char* prim_da(char *arglist, char *res, int *reslen) {
|
|
int i = 0;
|
|
for(i=0;i<nnt_forms_len;i++) free(nnt_forms[i].value);
|
|
nnt_forms_len = 0;
|
|
nnt_forms = realloc(nnt_forms, 0);
|
|
return res;
|
|
}
|
|
|
|
/* eq primitive: string equality */
|
|
char* prim_eq(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *x1, *x2, *x3, *x4;
|
|
x1 = strtok(NULL, NNT_ADEL_S); x2 = strtok(NULL, NNT_ADEL_S);
|
|
x3 = strtok(NULL, NNT_ADEL_S); x4 = strtok(NULL, NNT_ADEL_S);
|
|
if(x1 && x2 && x3 && x4) {
|
|
arg = strcmp(x1, x2) ? x4 : x3;
|
|
*reslen = strlen(arg);
|
|
res = realloc(res, *reslen);
|
|
memcpy(res, arg, *reslen);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* gr primitive: number inequality */
|
|
char* prim_gr(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *d1, *d2, *x1, *x2;
|
|
d1 = strtok(NULL, NNT_ADEL_S); d2 = strtok(NULL, NNT_ADEL_S);
|
|
x1 = strtok(NULL, NNT_ADEL_S); x2 = strtok(NULL, NNT_ADEL_S);
|
|
if(x1 && x2 && d1 && d2) {
|
|
arg = (snum(d1) > snum(d2)) ? x1 : x2;
|
|
*reslen = strlen(arg);
|
|
res = realloc(res, *reslen);
|
|
memcpy(res, arg, *reslen);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* theoretically we can safely operate from -2**62 to 2**62 */
|
|
#define ARITH_LIMIT ((long long) (((unsigned long long) - 1LL) >> 2))
|
|
#define BITWISE_BITS 32
|
|
#define BITWISE_LIMIT (unsigned long long) ((1LL<<BITWISE_BITS) - 1LL)
|
|
|
|
/* reusable arithmetic ops helper */
|
|
char* handle_dec_result(long long nres, char *ovr, char *res, int *reslen) {
|
|
if(nres <= ARITH_LIMIT && nres > -ARITH_LIMIT) { /* valid result */
|
|
res = n2s(nres, res, reslen);
|
|
} else if(ovr) { /* return the overflow/underflow value */
|
|
*reslen = strlen(ovr);
|
|
res = realloc(res, *reslen); /* resize to actual length */
|
|
memcpy(res, ovr, *reslen); /* fill the overflow value */
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* ad primitive: addition (overflow arg is optional) */
|
|
char* prim_ad(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
ovr = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) res = handle_dec_result(snum(as) + snum(bs), ovr, res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* su primitive: subtraction (overflow arg is optional) */
|
|
char* prim_su(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
ovr = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) res = handle_dec_result(snum(as) - snum(bs), ovr, res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* ml primitive: multiplication (overflow arg is optional) */
|
|
char* prim_ml(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
ovr = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) res = handle_dec_result(snum(as) * snum(bs), ovr, res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* dv primitive: division (overflow arg is optional) */
|
|
char* prim_dv(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs, *ovr;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
ovr = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) {
|
|
long long b = snum(bs);
|
|
if(b) res = handle_dec_result(snum(as) / b, ovr, res, reslen);
|
|
else if(ovr) { /* division by zero, return ovr */
|
|
*reslen = strlen(ovr);
|
|
res = realloc(res, *reslen); /* resize to actual length */
|
|
memcpy(res, ovr, *reslen); /* fill the overflow value */
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* bu primitive: bitwise union (OR) */
|
|
char* prim_bu(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) res = handle_dec_result((snum(as) | snum(bs)) & BITWISE_LIMIT,
|
|
NULL, res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* bi primitive: bitwise intersect (AND) */
|
|
char* prim_bi(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) res = handle_dec_result((snum(as) & snum(bs)) & BITWISE_LIMIT,
|
|
NULL, res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* [new] bx primitive: bitwise exclusive or (XOR) */
|
|
char* prim_bx(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) res = handle_dec_result((snum(as) ^ snum(bs)) & BITWISE_LIMIT,
|
|
NULL, res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* bc primitive: bitwise complement (NOT) */
|
|
char* prim_bc(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as = strtok(NULL, NNT_ADEL_S);
|
|
if(as) res = handle_dec_result((~snum(as)) & BITWISE_LIMIT,
|
|
NULL, res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* br primitive: bitwise rotation */
|
|
char* prim_br(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) {
|
|
long long a = snum(as), b = snum(bs), nres;
|
|
/* we rotate b according to the value of a */
|
|
b &= BITWISE_LIMIT; /* bit-clip the value */
|
|
if(a > 0) { /* rotate left */
|
|
if(a > BITWISE_BITS) a %= BITWISE_BITS;
|
|
nres = (b << a) | (b >> (BITWISE_BITS - a));
|
|
} else if(a < 0) { /* rotate right */
|
|
a = -a;
|
|
if(a > BITWISE_BITS) a %= BITWISE_BITS;
|
|
nres = (b << (BITWISE_BITS - a)) | (b >> a);
|
|
}
|
|
else nres = b; /* no rotation */
|
|
res = handle_dec_result(nres & BITWISE_LIMIT, NULL, res, reslen);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* bs primitive: bitwise shift */
|
|
char* prim_bs(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *as, *bs;
|
|
as = strtok(NULL, NNT_ADEL_S); bs = strtok(NULL, NNT_ADEL_S);
|
|
if(as && bs) {
|
|
long long a = snum(as), b = snum(bs), nres;
|
|
/* we shift b according to the value of a */
|
|
if(a > 0) nres = b << a; /* shift left */
|
|
else if(a < 0) nres = b >> (-a); /* shift right */
|
|
else nres = b; /* no shift */
|
|
res = handle_dec_result(nres & BITWISE_LIMIT, NULL, res, reslen);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* ln primitive: list names */
|
|
char* prim_ln(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *pref = strtok(NULL, NNT_ADEL_S);
|
|
int i;
|
|
for(i=0;i<nnt_forms_len;i++) {
|
|
if(nnt_forms[i].name[0] != NNT_EOF) {
|
|
if(pref) printf("%s", pref);
|
|
printf("%s", nnt_forms[i].name);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* pf primitive: print form */
|
|
char* prim_pf(char *arglist, char *res, int *reslen) {
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S);
|
|
if(fname != NULL) {
|
|
int i = nnt_findform(fname), j;
|
|
if(i > -1) { /* form found */
|
|
for(j=0;j<=nnt_forms[i].len;j++) {
|
|
if(j == nnt_forms[i].fptr) printf("<^>");
|
|
if(nnt_forms[i].value[j] == NNT_SEGGAP)
|
|
printf("<%u>", (unsigned int) nnt_forms[i].value[++j]);
|
|
else printf("%c",nnt_forms[i].value[j]);
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* filesystem primitives */
|
|
|
|
/* sb primitive: store block */
|
|
char *prim_sb(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname;
|
|
fname = strtok(NULL, NNT_ADEL_S);
|
|
if(fname != NULL) { /* output file name */
|
|
FILE *hnd = fopen(fname, "wb"); /* open the output file */
|
|
if(hnd != NULL) {
|
|
char *vbuf = malloc(0); /* value buffer */
|
|
int findex, vlen = 0; /* value len */
|
|
do {
|
|
fmname = strtok(NULL, NNT_ADEL_S); /* form name */
|
|
if(fmname != NULL) {
|
|
findex = nnt_findform(fmname);
|
|
if(findex > -1) {
|
|
vbuf = nnt_serializeform(&nnt_forms[findex], vbuf, &vlen);
|
|
fwrite(vbuf, vlen, 1, hnd); /* write the value out */
|
|
nnt_delform(fmname); /* delete after exporting as per the spec */
|
|
}
|
|
}
|
|
} while(fmname != NULL);
|
|
fclose(hnd); /* close the output file */
|
|
free(vbuf); /* free value buffer */
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* full file read helper */
|
|
char* nnt_readfile(FILE *hnd, char *fbuf, int *buflen) {
|
|
fseek(hnd, 0, SEEK_END);
|
|
*buflen = ftell(hnd);
|
|
fseek(hnd, 0, SEEK_SET);
|
|
fbuf = realloc(fbuf, *buflen); /* allocate the full buffer */
|
|
if(fbuf == NULL) return NULL; /* if allocation failed */
|
|
fread(fbuf, 1, *buflen, hnd); /* read the entire file */
|
|
return fbuf;
|
|
}
|
|
|
|
/* fb primitive: fetch block */
|
|
char *prim_fb(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S);
|
|
if(fname != NULL) { /* input file name */
|
|
FILE *hnd = fopen(fname, "rb"); /* open the input file */
|
|
if(hnd != NULL) {
|
|
char *fbuf = malloc(0);
|
|
int flen;
|
|
fbuf = nnt_readfile(hnd, fbuf, &flen);
|
|
fclose(hnd);
|
|
int i = 0, dlen; /* running index and length */
|
|
while(i < flen) { /* form queue to deserialize */
|
|
dlen = nnt_deserializeform(&fbuf[i]);
|
|
if(dlen < 0) break;
|
|
i += dlen + 1;
|
|
}
|
|
free(fbuf); /* free file buffer */
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* [new] sf primitive: store raw file */
|
|
char *prim_sf(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname;
|
|
fname = strtok(NULL, NNT_ADEL_S); /* file name */
|
|
fmname = strtok(NULL, NNT_ADEL_S); /* form name */
|
|
if(fname != NULL && fmname != NULL) {
|
|
int fid = nnt_findform(fmname);
|
|
if(fid > -1) {
|
|
FILE *hnd = fopen(fname, "wb"); /* open the output file */
|
|
if(hnd != NULL) { /* write the value out */
|
|
fwrite(nnt_forms[fid].value, nnt_forms[fid].len, 1, hnd);
|
|
fclose(hnd);
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* [new] ff primitive: fetch raw file */
|
|
char *prim_ff(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *fname, *fmname;
|
|
fname = strtok(NULL, NNT_ADEL_S); /* file name */
|
|
fmname = strtok(NULL, NNT_ADEL_S); /* form name */
|
|
if(fname != NULL && fmname != NULL) {
|
|
FILE *hnd = fopen(fname, "rb"); /* open the input file */
|
|
if(hnd != NULL) {
|
|
char *fbuf = malloc(0);
|
|
int flen;
|
|
fbuf = nnt_readfile(hnd, fbuf, &flen);
|
|
fclose(hnd);
|
|
nnt_assignform(fmname, fbuf, flen, 0); /* store it into the form */
|
|
free(fbuf); /* and free the resources */
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* eb primitive: erase block or raw file */
|
|
char *prim_eb(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *fname = strtok(NULL, NNT_ADEL_S);
|
|
if(fname != NULL) remove(fname); /* delete the input file name */
|
|
return res;
|
|
}
|
|
|
|
/* mode primitive: set operation mode */
|
|
char *prim_mo(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mlock == 1) return res; /* mode change is already locked */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *modechar, *mlk;
|
|
modechar = strtok(NULL, NNT_ADEL_S);
|
|
if(modechar != NULL) {
|
|
switch(modechar[0]) {
|
|
case 'S': nnt_mode = NNT_SECURE; break;
|
|
case 'L': nnt_mode = NNT_LEGACY; break;
|
|
case 'E': nnt_mode = NNT_NORMAL; break;
|
|
}
|
|
mlk = strtok(NULL, NNT_ADEL_S);
|
|
if(mlk != NULL && mlk[0] == 'L') nnt_mlock = 1; /* set mode lock */
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* [new] ac primitive: ASCII code */
|
|
char *prim_ac(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *cvs = strtok(NULL, NNT_ADEL_S);
|
|
if(cvs != NULL) res = n2s((uchar) cvs[0], res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* [new] av primitive: ASCII value */
|
|
char *prim_av(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *cns = strtok(NULL, NNT_ADEL_S);
|
|
long long rcode = 0;
|
|
if(cns != NULL) rcode = snum(cns);
|
|
res = realloc(res, (*reslen) = 1);
|
|
res[0] = rcode&255;
|
|
return res;
|
|
}
|
|
|
|
/* [new] fn primitive: format number */
|
|
char *prim_fn(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *fmt, *cns;
|
|
fmt = strtok(NULL, NNT_ADEL_S);
|
|
cns = strtok(NULL, NNT_ADEL_S);
|
|
long long num = 0;
|
|
if(fmt != NULL && cns != NULL) {
|
|
num = snum(cns);
|
|
*reslen = snprintf(NULL, 0, fmt, num); /* estimate the size */
|
|
res = realloc(res, 1 + (*reslen));
|
|
snprintf(res, 1 + (*reslen), fmt, num); /* actually format the number */
|
|
res = realloc(res, (*reslen) = strlen(res)); /* final reallocation */
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifndef NNT_NO_EXTSHELL
|
|
/* [new] os primitive: run external OS command */
|
|
char *prim_os(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_SECURE) return res; /* prevent running in secure mode */
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *cmd = strtok(NULL, NNT_ADEL_S);
|
|
int rescode;
|
|
if(cmd != NULL) {
|
|
rescode = system(cmd); /* run the command and get the status code */
|
|
res = n2s(rescode, res, reslen);
|
|
}
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
/* [new] tm primitive: local/UTC/Epoch time */
|
|
char *prim_tm(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *fmt = strtok(NULL, NNT_ADEL_S),
|
|
*opt = strtok(NULL, NNT_ADEL_S), utc = !!(opt!=NULL && opt[0]=='U');
|
|
if(fmt != NULL) { /* format string exists */
|
|
time_t curtime = time(NULL);
|
|
if(fmt[0] == 'E' && fmt[1] == 0) res = n2s(curtime, res, reslen);
|
|
else {
|
|
struct tm *tminfo = utc ? gmtime(&curtime) : localtime(&curtime);
|
|
res = realloc(res, (*reslen) = 512);
|
|
memset(res, 0, *reslen);
|
|
*reslen = strftime(res, *reslen, fmt, tminfo); /* populate the time */
|
|
res = realloc(res, *reslen); /* shrink the result */
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static unsigned long long nnt_rngs; /* PRNG state*/
|
|
unsigned long long xorshift64s() {
|
|
nnt_rngs^=(nnt_rngs>>12);nnt_rngs^=(nnt_rngs<<25);nnt_rngs^=(nnt_rngs>>27);
|
|
unsigned long long inter = (nnt_rngs * 0x2545F4914F6CDD1DULL) >> 32;
|
|
nnt_rngs^=(nnt_rngs>>12);nnt_rngs^=(nnt_rngs<<25);nnt_rngs^=(nnt_rngs>>27);
|
|
return (inter << 32) | ((nnt_rngs * 0x2545F4914F6CDD1DULL) & 0xFFFFFFFF);
|
|
}
|
|
|
|
/* [new] rn primitive: (pseudo)random number */
|
|
char *prim_rn(char *arglist, char *res, int *reslen) {
|
|
if(nnt_mode == NNT_LEGACY) return res; /* prevent running in legacy mode */
|
|
char *arg = strtok(arglist, NNT_ADEL_S), *froms = strtok(NULL, NNT_ADEL_S),
|
|
*tos = strtok(NULL, NNT_ADEL_S);
|
|
long long a = (froms == NULL) ? 0 : snum(froms),
|
|
b = (tos == NULL) ? ARITH_LIMIT : snum(tos);
|
|
res = n2s(a == b ? a : (a + xorshift64s() % (b - a)), res, reslen);
|
|
return res;
|
|
}
|
|
|
|
/* init the resources and built-in primitives */
|
|
void nnt_init() {
|
|
nnt_forms = malloc(0); /* init the forms table */
|
|
nnt_primitives = malloc(0); /* init the primitives table */
|
|
nnt_rngs = time(NULL); xorshift64s(); /* init the PRNG */
|
|
nnt_regprimitive("ps", &prim_ps);
|
|
nnt_regprimitive("rc", &prim_rc);
|
|
nnt_regprimitive("rs", &prim_rs);
|
|
nnt_regprimitive("cm", &prim_cm);
|
|
nnt_regprimitive("ds", &prim_ds);
|
|
nnt_regprimitive("ss", &prim_ss);
|
|
nnt_regprimitive("cl", &prim_cl);
|
|
nnt_regprimitive("cr", &prim_cr);
|
|
nnt_regprimitive("cc", &prim_cc);
|
|
nnt_regprimitive("cs", &prim_cs);
|
|
nnt_regprimitive("cn", &prim_cn);
|
|
nnt_regprimitive("in", &prim_in);
|
|
nnt_regprimitive("dd", &prim_dd);
|
|
nnt_regprimitive("da", &prim_da);
|
|
nnt_regprimitive("eq", &prim_eq);
|
|
nnt_regprimitive("gr", &prim_gr);
|
|
nnt_regprimitive("ad", &prim_ad);
|
|
nnt_regprimitive("su", &prim_su);
|
|
nnt_regprimitive("ml", &prim_ml);
|
|
nnt_regprimitive("dv", &prim_dv);
|
|
nnt_regprimitive("bu", &prim_bu);
|
|
nnt_regprimitive("bi", &prim_bi);
|
|
nnt_regprimitive("bx", &prim_bx);
|
|
nnt_regprimitive("bc", &prim_bc);
|
|
nnt_regprimitive("br", &prim_br);
|
|
nnt_regprimitive("bs", &prim_bs);
|
|
nnt_regprimitive("ln", &prim_ln);
|
|
nnt_regprimitive("pf", &prim_pf);
|
|
nnt_regprimitive("sb", &prim_sb);
|
|
nnt_regprimitive("fb", &prim_fb);
|
|
nnt_regprimitive("eb", &prim_eb);
|
|
nnt_regprimitive("mo", &prim_mo);
|
|
nnt_regprimitive("ac", &prim_ac);
|
|
nnt_regprimitive("av", &prim_av);
|
|
nnt_regprimitive("fn", &prim_fn);
|
|
nnt_regprimitive("ff", &prim_ff);
|
|
nnt_regprimitive("sf", &prim_sf);
|
|
#ifndef NNT_NO_EXTSHELL
|
|
nnt_regprimitive("os", &prim_os);
|
|
#endif
|
|
nnt_regprimitive("tm", &prim_tm);
|
|
nnt_regprimitive("rn", &prim_rn);
|
|
}
|
|
|
|
/* free the interpreter resources */
|
|
void nnt_finish() {
|
|
prim_da(NULL, nnt_res, &nnt_reslen); /* free internal form resources */
|
|
if(nnt_primitives) free(nnt_primitives);
|
|
if(nnt_forms) free(nnt_forms);
|
|
}
|
|
|
|
/* non-embed entry point */
|
|
#ifndef NNT_EMBED
|
|
int main(int argc, char *argv[]) {
|
|
char *fname = "-", *prog, *buf; /* stdin by default */
|
|
int proglen, i, nlen;
|
|
if(argc > 1) fname = argv[1];
|
|
FILE* fd = stdin;
|
|
if(!(fname[0] == '-' && fname[1] == 0)) {
|
|
fd = fopen(fname, "rb");
|
|
if(fd == NULL) {perror("Error");return 1;}
|
|
}
|
|
if(fd == stdin) { /* interactive session */
|
|
proglen = strlen(prog = "#(ps,#(rs))");
|
|
fprintf(stderr, "%s\n", "nntrac by Luxferre, 2023, public domain");
|
|
} else prog = nnt_readfile(fd, malloc(0), &proglen);
|
|
nnt_init(); /* init processing resources */
|
|
if(argc > 1) { /* populate nnt-argc and nnt-argv forms */
|
|
nlen = snprintf(NULL, 0, "%d", argc - 1);
|
|
buf = calloc(nlen+1, 1);
|
|
snprintf(buf, nlen+1, "%d", argc - 1);
|
|
nnt_assignform("nnt-argc", buf, nlen, 0);
|
|
buf = realloc(buf, 1); /* reuse the buffer for nnt-argv */
|
|
buf[0] = 0; /* ensure it's an empty string */
|
|
char segsep[3] = {NNT_SEGGAP, 1, 0};
|
|
for(i=1,nlen=0;i<argc;i++) {
|
|
nlen += strlen(argv[i]) + 2;
|
|
buf = realloc(buf, nlen);
|
|
strcat(buf, argv[i]); /* append the parameter */
|
|
strcat(buf, segsep); /* and the segment separator */
|
|
}
|
|
nnt_assignform("nnt-argv", buf, nlen, 0);
|
|
free(buf);
|
|
}
|
|
nnt_proc(prog, proglen); /* start main processor */
|
|
nnt_finish(); /* finalize processing resources */
|
|
fclose(fd);
|
|
puts(""); /* just print a newline before exiting */
|
|
if(fd != stdin) free(prog); /* free the input buffer */
|
|
return 0;
|
|
}
|
|
#endif |