winamp/Src/ns-eel2/nseel-eval.c
2024-09-24 14:54:57 +02:00

448 lines
11 KiB
C

/*
Expression Evaluator Library (NS-EEL) v2
Copyright (C) 2004-2013 Cockos Incorporated
Copyright (C) 1999-2003 Nullsoft, Inc.
nseel-eval.c
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include <string.h>
#include <ctype.h>
#include "ns-eel-int.h"
#include "wdlcstring.h"
static const char *nseel_skip_space_and_comments(const char *p, const char *endptr)
{
for (;;)
{
while (p < endptr && isspace(p[0])) p++;
if (p >= endptr-1 || *p != '/') return p;
if (p[1]=='/')
{
while (p < endptr && *p != '\r' && *p != '\n') p++;
}
else if (p[1] == '*')
{
p+=2;
while (p < endptr-1 && (p[0] != '*' || p[1] != '/')) p++;
p+=2;
if (p>=endptr) return endptr;
}
else return p;
}
}
// removes any escaped characters, also will convert pairs delim_char into single delim_chars
int nseel_filter_escaped_string(char *outbuf, int outbuf_sz, const char *rdptr, size_t rdptr_size, char delim_char)
{
int outpos = 0;
const char *rdptr_end = rdptr + rdptr_size;
while (rdptr < rdptr_end && outpos < outbuf_sz-1)
{
char thisc=*rdptr;
if (thisc == '\\' && rdptr < rdptr_end-1)
{
const char nc = rdptr[1];
if (nc == 'r' || nc == 'R') { thisc = '\r'; }
else if (nc == 'n' || nc == 'N') { thisc = '\n'; }
else if (nc == 't' || nc == 'T') { thisc = '\t'; }
else if (nc == 'b' || nc == 'B') { thisc = '\b'; }
else if ((nc >= '0' && nc <= '9') || nc == 'x' || nc == 'X')
{
unsigned char c=0;
char base_shift = 3;
char num_top = '7';
rdptr++; // skip backslash
if (nc > '9') // implies xX
{
base_shift = 4;
num_top = '9';
rdptr ++; // skip x
}
while (rdptr < rdptr_end)
{
char tc=*rdptr;
if (tc >= '0' && tc <= num_top)
{
c = (c<<base_shift) + tc - '0';
}
else if (base_shift==4)
{
if (tc >= 'a' && tc <= 'f')
{
c = (c<<base_shift) + (tc - 'a' + 10);
}
else if (tc >= 'A' && tc <= 'F')
{
c = (c<<base_shift) + (tc - 'A' + 10);
}
else break;
}
else break;
rdptr++;
}
outbuf[outpos++] = (char)c;
continue;
}
else // \c where c is an unknown character drops the backslash -- works for \, ', ", etc
{
thisc = nc;
}
rdptr+=2;
}
else
{
if (thisc == delim_char) break;
rdptr++;
}
outbuf[outpos++] = thisc;
}
outbuf[outpos]=0;
return outpos;
}
int nseel_stringsegments_tobuf(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list) // call with NULL to calculate size, or non-null to generate to buffer (returning size used)
{
int pos=0;
while (list)
{
if (!bufOut)
{
pos += list->str_len;
}
else if (list->str_len > 1)
{
if (pos >= bufout_sz) break;
pos += nseel_filter_escaped_string(bufOut + pos, bufout_sz-pos, list->str_start+1, list->str_len-1, list->str_start[0]);
}
list = list->_next;
}
return pos;
}
// state can be NULL, it will be set if finished with unterminated thing: 1 for multiline comment, ' or " for string
const char *nseel_simple_tokenizer(const char **ptr, const char *endptr, int *lenOut, int *state)
{
const char *p = *ptr;
const char *rv = p;
if (state) // if state set, returns comments as tokens
{
if (*state == 1) goto in_comment;
#ifndef NSEEL_EEL1_COMPAT_MODE
if (*state == '\'' || *state == '\"')
{
delim = (char)*state;
goto in_string;
}
#endif
// skip any whitespace
while (p < endptr && isspace(p[0])) p++;
}
else
{
// state not passed, skip comments (do not return them as tokens)
p = nseel_skip_space_and_comments(p,endptr);
}
if (p >= endptr)
{
*ptr = endptr;
*lenOut = 0;
return NULL;
}
rv=p;
if (*p == '$' && p+3 < endptr && p[1] == '\'' && p[3] == '\'')
{
p+=4;
}
else if (state && *p == '/' && p < endptr-1 && (p[1] == '/' || p[1] == '*'))
{
if (p[1] == '/')
{
while (p < endptr && *p != '\r' && *p != '\n') p++; // advance to end of line
}
else
{
if (state) *state=1;
p+=2;
in_comment:
while (p < endptr)
{
const char c = *p++;
if (c == '*' && p < endptr && *p == '/')
{
p++;
if (state) *state=0;
break;
}
}
}
}
else if (isalnum(*p) || *p == '_' || *p == '#' || *p == '$')
{
if (*p == '$' && p < endptr-1 && p[1] == '~') p++;
p++;
while (p < endptr && (isalnum(*p) || *p == '_' || *p == '.')) p++;
}
#ifndef NSEEL_EEL1_COMPAT_MODE
else if (*p == '\'' || *p == '\"')
{
delim = *p++;
if (state) *state=delim;
in_string:
while (p < endptr)
{
const char c = *p++;
if (p < endptr && c == '\\') p++; // skip escaped characters
else if (c == delim)
{
if (state) *state=0;
break;
}
}
}
#endif
else
{
p++;
}
*ptr = p;
*lenOut = (int) (p - rv);
return p>rv ? rv : NULL;
}
#ifdef NSEEL_SUPER_MINIMAL_LEXER
int nseellex(opcodeRec **output, YYLTYPE * yylloc_param, compileContext *scctx)
{
int rv=0,toklen=0;
const char *rdptr = scctx->rdbuf;
const char *endptr = scctx->rdbuf_end;
const char *tok = nseel_simple_tokenizer(&rdptr,endptr,&toklen,NULL);
*output = 0;
if (tok)
{
rv = tok[0];
if (rv == '$')
{
if (rdptr != tok+1)
{
*output = nseel_translate(scctx,tok,rdptr-tok);
if (*output) rv=VALUE;
}
}
#ifndef NSEEL_EEL1_COMPAT_MODE
else if (rv == '#' && scctx->onNamedString)
{
*output = nseel_translate(scctx,tok,rdptr-tok);
if (*output) rv=STRING_IDENTIFIER;
}
else if (rv == '\'')
{
if (toklen > 1 && tok[toklen-1] == '\'')
{
*output = nseel_translate(scctx, tok, toklen);
if (*output) rv = VALUE;
}
else scctx->gotEndOfInput|=8;
}
else if (rv == '\"' && scctx->onString)
{
if (toklen > 1 && tok[toklen-1] == '\"')
{
*output = (opcodeRec *)nseel_createStringSegmentRec(scctx,tok,toklen);
if (*output) rv = STRING_LITERAL;
}
else scctx->gotEndOfInput|=16;
}
#endif
else if (isalpha(rv) || rv == '_')
{
// toklen already valid
char buf[NSEEL_MAX_VARIABLE_NAMELEN*2];
if (toklen > sizeof(buf) - 1) toklen=sizeof(buf) - 1;
memcpy(buf,tok,toklen);
buf[toklen]=0;
*output = nseel_createCompiledValuePtr(scctx, NULL, buf);
if (*output) rv = IDENTIFIER;
}
else if ((rv >= '0' && rv <= '9') || (rv == '.' && (rdptr < endptr && rdptr[0] >= '0' && rdptr[0] <= '9')))
{
if (rv == '0' && rdptr < endptr && (rdptr[0] == 'x' || rdptr[0] == 'X'))
{
rdptr++;
while (rdptr < endptr && (rv=rdptr[0]) && ((rv>='0' && rv<='9') || (rv>='a' && rv<='f') || (rv>='A' && rv<='F'))) rdptr++;
}
else
{
int pcnt=rv == '.';
while (rdptr < endptr && (rv=rdptr[0]) && ((rv>='0' && rv<='9') || (rv == '.' && !pcnt++))) rdptr++;
}
*output = nseel_translate(scctx,tok,rdptr-tok);
if (*output) rv=VALUE;
}
else if (rv == '<')
{
const char nc=*rdptr;
if (nc == '<')
{
rdptr++;
rv=TOKEN_SHL;
}
else if (nc == '=')
{
rdptr++;
rv=TOKEN_LTE;
}
}
else if (rv == '>')
{
const char nc=*rdptr;
if (nc == '>')
{
rdptr++;
rv=TOKEN_SHR;
}
else if (nc == '=')
{
rdptr++;
rv=TOKEN_GTE;
}
}
else if (rv == '&' && *rdptr == '&')
{
rdptr++;
rv = TOKEN_LOGICAL_AND;
}
else if (rv == '|' && *rdptr == '|')
{
rdptr++;
rv = TOKEN_LOGICAL_OR;
}
else if (*rdptr == '=')
{
switch (rv)
{
case '+': rv=TOKEN_ADD_OP; rdptr++; break;
case '-': rv=TOKEN_SUB_OP; rdptr++; break;
case '%': rv=TOKEN_MOD_OP; rdptr++; break;
case '|': rv=TOKEN_OR_OP; rdptr++; break;
case '&': rv=TOKEN_AND_OP; rdptr++; break;
case '~': rv=TOKEN_XOR_OP; rdptr++; break;
case '/': rv=TOKEN_DIV_OP; rdptr++; break;
case '*': rv=TOKEN_MUL_OP; rdptr++; break;
case '^': rv=TOKEN_POW_OP; rdptr++; break;
case '!':
rdptr++;
if (rdptr < endptr && *rdptr == '=')
{
rdptr++;
rv=TOKEN_NE_EXACT;
}
else
rv=TOKEN_NE;
break;
case '=':
rdptr++;
if (rdptr < endptr && *rdptr == '=')
{
rdptr++;
rv=TOKEN_EQ_EXACT;
}
else
rv=TOKEN_EQ;
break;
}
}
}
scctx->rdbuf = rdptr;
yylloc_param->first_column = (int)(tok - scctx->rdbuf_start);
return rv;
}
void nseelerror(YYLTYPE *pos,compileContext *ctx, const char *str)
{
ctx->errVar=pos->first_column>0?pos->first_column:(int)(ctx->rdbuf_end - ctx->rdbuf_start);
}
#else
int nseel_gets(compileContext *ctx, char *buf, size_t sz)
{
int n=0;
const char *endptr = ctx->rdbuf_end;
const char *rdptr = ctx->rdbuf;
if (!rdptr) return 0;
while (n < sz && rdptr < endptr) buf[n++] = *rdptr++;
ctx->rdbuf=rdptr;
return n;
}
//#define EEL_TRACE_LEX
#ifdef EEL_TRACE_LEX
#define nseellex nseellex2
#endif
#include "lex.nseel.c"
#ifdef EEL_TRACE_LEX
#undef nseellex
int nseellex(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
{
int a=nseellex2(yylval_param,yylloc_param,yyscanner);
char buf[512];
sprintf(buf,"tok: %c (%d)\n",a,a);
OutputDebugString(buf);
return a;
}
#endif//EEL_TRACE_LEX
void nseelerror(YYLTYPE *pos,compileContext *ctx, const char *str)
{
ctx->errVar=pos->first_column>0?pos->first_column:(int)(ctx->rdbuf_end - ctx->rdbuf_start);
}
#endif // !NSEEL_SUPER_MINIMAL_LEXER