459 lines
11 KiB
C
459 lines
11 KiB
C
/* #includes */ /*{{{C}}}*//*{{{*/
|
|
#ifndef NO_POSIX_SOURCE
|
|
#undef _POSIX_SOURCE
|
|
#define _POSIX_SOURCE 1
|
|
#undef _POSIX_C_SOURCE
|
|
#define _POSIX_C_SOURCE 2
|
|
#endif
|
|
|
|
#ifdef DMALLOC
|
|
#include "dmalloc.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
extern char *strdup(const char* s);
|
|
extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */
|
|
extern long double strtold(const char *nptr, char **endptr); /* SunOS 4 hack */
|
|
#include <string.h>
|
|
|
|
|
|
#include "default.h"
|
|
#include "func.h"
|
|
#include "main.h"
|
|
#include "misc.h"
|
|
#include "utf8.h"
|
|
#include "scanner.h"
|
|
/*}}}*/
|
|
|
|
const char *Type_Name[] =
|
|
{ [EMPTY] = "EMPTY", [STRING] = "STRING", [FLOAT] = "FLOAT", [INT] = "INT",
|
|
[OPERATOR] = "OPERATOR", [LIDENT] = "LABEL", [FIDENT] = "FUNCTION",
|
|
[LOCATION] = "LOCATION", [FUNCALL] = "FUNCTION-CALL", [EEK] = "ERROR"
|
|
};
|
|
|
|
const char *Op_Name[] =
|
|
{ [PLUS] = "+", [MINUS] = "-", [MUL] = "*", [DIV] = "/",
|
|
[OP] = "(", [CP] = ")", [COMMA] = ",",
|
|
[LT] = "<", [LE] = "<=", [GE] = ">=", [GT] = ">",
|
|
[ISEQUAL] = "==", [ABOUTEQ] = "~=", [NE] = "!=",
|
|
[POW] = "^", [MOD] = "%"
|
|
};
|
|
|
|
/* loc_in_box -- returns true if test is in the box determined by b and c */
|
|
bool loc_in_box(const Location test,
|
|
const Location b, const Location c)
|
|
{
|
|
for (Dimensions dim = X; dim < HYPER; ++dim)
|
|
{
|
|
if (test[dim] < b[dim] && test[dim] < c[dim]) return false;
|
|
if (test[dim] > b[dim] && test[dim] > c[dim]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* cleartoken - Initialize all of the memory of a token */ /*{{{*/
|
|
void cleartoken(Token* tok)
|
|
{
|
|
tok->type = EMPTY;
|
|
tok->u.location[0] = 0;
|
|
tok->u.location[1] = 0;
|
|
tok->u.location[2] = 0;
|
|
}
|
|
|
|
/* duperror - Sets tok to an error and strdups the message into place */
|
|
Token duperror(Token* tok, const char* erro)
|
|
{
|
|
tok->type = EEK;
|
|
tok->u.err = strdup(erro);
|
|
return *tok;
|
|
}
|
|
|
|
/* charstring -- match quoted string and return token */ /*{{{*/
|
|
static Token *charstring(const char **s)
|
|
{
|
|
const char *r;
|
|
|
|
r=*s;
|
|
if (**s=='"')
|
|
{
|
|
++(*s);
|
|
while (**s!='\0' && **s!='"') if (**s=='\\' && *((*s)+1)!='\0') (*s)+=2; else ++(*s);
|
|
if (**s=='\0') { *s=r; return 0; }
|
|
else
|
|
{
|
|
Token *n;
|
|
char *t;
|
|
|
|
++(*s);
|
|
n=malloc(sizeof(Token));
|
|
n->type=STRING;
|
|
t=n->u.string=malloc((size_t)(*s-r));
|
|
/* Clean string of quotes. This may waste a few bytes, so? */
|
|
++r;
|
|
while (r<(*s-1)) if (*r=='\\') { *t++=*(r+1); r+=2; } else *t++=*r++;
|
|
*t='\0';
|
|
return n;
|
|
}
|
|
}
|
|
else return (Token*)0;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* integer -- match an unsigned integer and return token */ /*{{{*/
|
|
static Token *integer(const char **s)
|
|
{
|
|
const char *r;
|
|
long i;
|
|
|
|
r=*s;
|
|
i=posnumber(r,s);
|
|
if (*s!=r && **s!='.' && **s!='e')
|
|
{
|
|
Token *n;
|
|
|
|
n=malloc(sizeof(Token));
|
|
n->type=INT;
|
|
n->u.integer=i;
|
|
return n;
|
|
}
|
|
else { *s=r; return (Token*)0; }
|
|
}
|
|
/*}}}*/
|
|
|
|
/* flt -- match a floating point number */ /*{{{*/
|
|
static Token *flt(const char **s)
|
|
{
|
|
/* variables */ /*{{{*/
|
|
const char *t;
|
|
char *end;
|
|
Token *n;
|
|
FltT x;
|
|
/*}}}*/
|
|
|
|
t=*s;
|
|
x = STRTOFLT(t,&end);
|
|
*s = end;
|
|
if (t!=*s && dblfinite(x)==(const char*)0)
|
|
{
|
|
n=malloc(sizeof(Token));
|
|
n->type=FLOAT;
|
|
n->u.flt=x;
|
|
return n;
|
|
}
|
|
else
|
|
{
|
|
*s=t;
|
|
return (Token*)0;
|
|
}
|
|
}
|
|
/*}}}*/
|
|
|
|
/* op -- match an op and return token */ /*{{{*/
|
|
static Token *op(const char **s)
|
|
{
|
|
Token *n;
|
|
Operator op;
|
|
|
|
switch (**s)
|
|
{
|
|
case '+': op=PLUS; break;
|
|
case '-': op=MINUS; break;
|
|
case '*': op=MUL; break;
|
|
case '/': op=DIV; break;
|
|
case '%': op=MOD; break;
|
|
case '(': op=OP; break;
|
|
case ')': op=CP; break;
|
|
case ',': op=COMMA; break;
|
|
case '^': op=POW; break;
|
|
case '<': if (*(*s+1)=='=') { ++(*s); op=LE; } else op=LT; break;
|
|
case '>': if (*(*s+1)=='=') { ++(*s); op=GE; } else op=GT; break;
|
|
case '=': if (*(*s+1)=='=') { ++(*s); op=ISEQUAL; } else return (Token*)0; break;
|
|
case '~': if (*(*s+1)=='=') { ++(*s); op=ABOUTEQ; } else return (Token*)0; break;
|
|
case '!': if (*(*s+1)=='=') { ++(*s); op=NE; } else return (Token*)0; break;
|
|
default: return (Token*)0;
|
|
}
|
|
n=malloc(sizeof(Token));
|
|
n->type=OPERATOR;
|
|
n->u.op=op;
|
|
++(*s);
|
|
return n;
|
|
}
|
|
/*}}}*/
|
|
/* ident -- match an identifier and return token */ /*{{{*/
|
|
static Token *ident(const char **s)
|
|
{
|
|
const char *begin;
|
|
Token *result;
|
|
|
|
if (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$')
|
|
{
|
|
int fident;
|
|
|
|
begin=*s; ++(*s);
|
|
while (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$' || isdigit((int)**s)) ++(*s);
|
|
result=malloc(sizeof(Token));
|
|
if ((fident = identcode(begin,(size_t)(*s-begin))) == NOT_A_FUNCTION)
|
|
{
|
|
result->type=LIDENT;
|
|
result->u.lident=malloc((size_t)(*s-begin+1));
|
|
(void)strncpy(result->u.lident,begin,(size_t)(*s-begin));
|
|
result->u.lident[*s-begin]='\0';
|
|
}
|
|
else
|
|
{
|
|
result->type=FIDENT;
|
|
result->u.fident=fident;
|
|
}
|
|
return result;
|
|
}
|
|
return (Token*)0;
|
|
}
|
|
/*}}}*/
|
|
|
|
/* scan -- scan string into tokens */ /*{{{*/
|
|
Token **scan(const char **s)
|
|
{
|
|
/* variables */ /*{{{*/
|
|
Token **na,*n;
|
|
const char *r;
|
|
/*}}}*/
|
|
|
|
/* compute number of tokens */ /*{{{*/
|
|
r = *s;
|
|
while (*r == ' ') ++r;
|
|
int i = 0;
|
|
for (; *r != '\0'; ++i)
|
|
{
|
|
const char *or;
|
|
|
|
or = r;
|
|
while (*r == ' ') ++r;
|
|
n = charstring(&r);
|
|
if (n == NULLTOKEN) n = op(&r);
|
|
if (n == NULLTOKEN) n = integer(&r);
|
|
if (n == NULLTOKEN) n = flt(&r);
|
|
if (n == NULLTOKEN) n = ident(&r);
|
|
if (or == r) { *s = r; return EMPTY_TVEC; }
|
|
}
|
|
/*}}}*/
|
|
/* allocate token space */ /*{{{*/
|
|
na = malloc(sizeof(Token*)*(i+1));
|
|
/*}}}*/
|
|
/* store tokens */ /*{{{*/
|
|
r = *s;
|
|
while (*r==' ') ++r;
|
|
for (int j = 0; j < i; ++j)
|
|
{
|
|
while (*r == ' ') ++r;
|
|
n = charstring(&r);
|
|
if (n == NULLTOKEN) n = op(&r);
|
|
if (n == NULLTOKEN) n = integer(&r);
|
|
if (n == NULLTOKEN) n = flt(&r);
|
|
if (n == NULLTOKEN) n = ident(&r);
|
|
na[j] = n;
|
|
}
|
|
na[i] = NULLTOKEN;
|
|
/*}}}*/
|
|
return na;
|
|
}
|
|
/*}}}*/
|
|
|
|
static int print_fident(char* dest, size_t space, int id)
|
|
{
|
|
size_t identlen = strlen(tfunc[id].name);
|
|
if ((identlen+1) < space) strcpy(dest, tfunc[id].name);
|
|
else {
|
|
(void)strncpy(dest, tfunc[id].name, space);
|
|
dest[space-1] = '\0';
|
|
}
|
|
return identlen;
|
|
}
|
|
|
|
/* printtok -- print a single token, passed by address, although not changed */ /*{{{*/
|
|
size_t printtok(char* dest, size_t size, size_t field_width,
|
|
bool quote_strings, bool use_scientific,
|
|
int precision, bool verbose_error, const Token *tok)
|
|
{
|
|
if (debug_level > 2) {
|
|
printf("..Entering printtok; bufsize %d, field_width %d, qs %d, us %d, prec %d, verr %d\n", size, field_width, quote_strings, use_scientific, precision, verbose_error);
|
|
}
|
|
if (size > 0 ) *dest = '\0';
|
|
if (tok == NULLTOKEN || tok->type == EMPTY) return 0;
|
|
size_t cur = 0;
|
|
switch (tok->type)
|
|
{
|
|
/* STRING */ /*{{{*/
|
|
case STRING:
|
|
{
|
|
char *str = tok->u.string;
|
|
|
|
if (quote_strings && cur<size) dest[cur++] = '"';
|
|
for (;cur<size && *str != '\0'; ++str)
|
|
{
|
|
if (quote_strings && (*str == '"' || *str=='\\')) dest[cur++] = '\\';
|
|
if (cur<size) dest[cur++]=*str;
|
|
}
|
|
if (quote_strings && cur<size) dest[cur++] = '"';
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* INT */ /*{{{*/
|
|
case INT:
|
|
{
|
|
char buf[64];
|
|
size_t buflen;
|
|
|
|
buflen = sprintf(buf, INT_T_FMT, tok->u.integer);
|
|
assert(buflen < sizeof(buf));
|
|
(void)strncpy(dest+cur,buf,size-cur-1);
|
|
cur+=buflen;
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* FLOAT */ /*{{{*/
|
|
case FLOAT:
|
|
{
|
|
/* variables */ /*{{{*/
|
|
char buf[1024],*p;
|
|
size_t len;
|
|
/*}}}*/
|
|
|
|
bool use_sci = use_scientific
|
|
|| (int)ABSFLT(LOG10FLT(tok->u.flt)) > FLT_T_DIG;
|
|
|
|
len = sprintf(buf, use_sci ? FLT_T_SCI_FMT : FLT_T_STD_FMT,
|
|
precision == -1 ? FLT_T_DIG-2 : precision,
|
|
tok->u.flt);
|
|
assert(len<sizeof(buf));
|
|
if (!use_sci)
|
|
{
|
|
p=&buf[len-1];
|
|
while (p>buf && *p == '0' && *(p-1) != '.') { *p = '\0'; --p; --len; }
|
|
}
|
|
p = buf+len;
|
|
while (*--p == ' ') { *p = '\0'; --len; }
|
|
(void)strncpy(dest+cur,buf,size-cur-1);
|
|
cur += len;
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* OPERATOR */ /*{{{*/
|
|
case OPERATOR:
|
|
{
|
|
static const char *ops[]={ "+", "-", "*", "/", "(", ")", ",", "<", "<=", ">=", ">", "==", "~=", "!=", "^", "%" };
|
|
|
|
if ((size-cur)>1)
|
|
{
|
|
dest[cur++]=*ops[tok->u.op];
|
|
if (*(ops[tok->u.op]+1) && size>cur) dest[cur++]=*(ops[tok->u.op]+1);
|
|
}
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* LIDENT */ /*{{{*/
|
|
case LIDENT:
|
|
{
|
|
size_t identlen;
|
|
|
|
identlen=strlen(tok->u.lident);
|
|
if ((cur+identlen+1)<=size) strcpy(dest+cur,tok->u.lident);
|
|
else (void)strncpy(dest+cur,tok->u.lident,size-cur-1);
|
|
cur+=identlen;
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* FIDENT */ /*{{{*/
|
|
case FIDENT:
|
|
if (debug_level > 2) {
|
|
printf("...Found function [%s].\n", tfunc[tok->u.fident].name);
|
|
}
|
|
cur += print_fident(dest+cur, size-cur-1, tok->u.fident);
|
|
break;
|
|
/*}}}*/
|
|
/* LOCATION */ /*{{{*/
|
|
case LOCATION:
|
|
{
|
|
char buf[60];
|
|
|
|
sprintf(buf,"&(%d,%d,%d)",tok->u.location[0],tok->u.location[1],tok->u.location[2]);
|
|
(void)strncpy(dest+cur,buf,size-cur-1);
|
|
cur+=strlen(buf);
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* FUNCALL */ /*{{{*/
|
|
case FUNCALL:
|
|
{
|
|
cur += print_fident(dest+cur, size-cur-1, tok->u.funcall.fident);
|
|
if (tok->u.funcall.argc >= 0 && cur + 2 < size) /* -1 args is bare func */
|
|
{
|
|
dest[cur++] = '\(';
|
|
for (size_t ai = 0; ai < tok->u.funcall.argc && cur < size-1; ++ai)
|
|
{
|
|
if (ai > 0 && cur < size) dest[cur++] = ',';
|
|
cur += printtok(dest+cur, size-cur-1, field_width-cur, quote_strings,
|
|
use_scientific, precision, verbose_error,
|
|
tok->u.funcall.argv + ai);
|
|
}
|
|
if (cur < size) dest[cur++] = ')';
|
|
}
|
|
break;
|
|
}
|
|
/* EEK */ /*{{{*/
|
|
case EEK:
|
|
{
|
|
size_t errlen;
|
|
|
|
(void)strncpy(dest+cur,_("ERROR"),size-cur-1);
|
|
cur += strlen(_("ERROR"));
|
|
if (verbose_error)
|
|
{
|
|
(void)strncpy(dest+cur, ": ", size-cur-1);
|
|
cur += 2;
|
|
errlen = strlen(tok->u.err);
|
|
if ((cur+errlen+1) <= size) strcpy(dest+cur, tok->u.err);
|
|
else (void)strncpy(dest+cur, tok->u.err, size-cur-1);
|
|
cur += errlen;
|
|
}
|
|
break;
|
|
}
|
|
/*}}}*/
|
|
/* default */ /*{{{*/
|
|
default: assert(0);
|
|
/*}}}*/
|
|
}
|
|
if (cur<size) dest[cur] = 0;
|
|
else
|
|
{
|
|
dest[size-1] = 0;
|
|
cur = size;
|
|
}
|
|
if (field_width && mbslen(dest) > field_width) {
|
|
for (cur = 0; cur < field_width; ++cur) dest[cur] = '#';
|
|
dest[cur] = 0;
|
|
}
|
|
return cur;
|
|
}
|
|
/*}}}*/
|
|
/* print -- print token sequence */ /*{{{*/
|
|
void print(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Token **n)
|
|
{
|
|
size_t cur;
|
|
|
|
cur=0;
|
|
if (n != EMPTY_TVEC) for (; cur<size-1 && (*n) != NULLTOKEN; ++n)
|
|
cur += printtok(s+cur, size-cur, 0, quote, scientific, precision, 0, *n);
|
|
if (cur<size) s[cur] = 0;
|
|
else s[size-1] = 0;
|
|
if (chars && mbslen(s) > chars) {
|
|
for (cur=0; cur < chars; ++cur) s[cur] = '#';
|
|
s[cur] = 0;
|
|
}
|
|
}
|
|
/*}}}*/
|