Import existing source tree; original VCS history is no longer available. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1024 lines
21 KiB
C
1024 lines
21 KiB
C
/*(LGPL)
|
|
---------------------------------------------------------------------------
|
|
e_getargs.c - scanf() style "argument fetcher" + expr. evaluator
|
|
---------------------------------------------------------------------------
|
|
* Copyright (C) 2002, 2003, David Olofson
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#include "kobolog.h"
|
|
#include "config.h"
|
|
#include "eel.h"
|
|
#include "e_lexer.h"
|
|
#include "e_util.h"
|
|
#include "e_builtin.h"
|
|
|
|
#define DBG(x)
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Argument list parser
|
|
----------------------------------------------------------*/
|
|
|
|
eel_data_t eel_arg_table[EEL_MAX_ARGS];
|
|
int eel_arg_token_table[EEL_MAX_ARGS];
|
|
|
|
int eel_arg_count = 0;
|
|
eel_data_t *eel_args = eel_arg_table;
|
|
int *eel_arg_tokens = eel_arg_token_table;
|
|
|
|
|
|
int eel_grab_arg(void)
|
|
{
|
|
if(eel_arg_count >= EEL_MAX_ARGS)
|
|
return -1;
|
|
|
|
eel_arg_tokens[eel_arg_count] = eel_current.token;
|
|
|
|
if(eel_current.lval)
|
|
{
|
|
if(EDT_SYMREF == eel_current.lval->type)
|
|
{
|
|
/* Kill "runaway indirection" right at it's root! */
|
|
if(EDT_SYMREF == eel_current.lval->value.sym->data.type)
|
|
eel_d_copy(eel_current.lval,
|
|
&eel_current.lval->value.sym->data);
|
|
}
|
|
|
|
/*
|
|
* Important:
|
|
* This is a *move* operation! This is
|
|
* to avoid copying external string
|
|
* buffers around, while still making
|
|
* sure the new eel_data_t gains true
|
|
* ownership of them.
|
|
*/
|
|
eel_args[eel_arg_count] = *eel_current.lval;
|
|
free(eel_current.lval);
|
|
eel_current.lval = NULL;
|
|
}
|
|
++eel_arg_count;
|
|
return eel_arg_count;
|
|
}
|
|
|
|
|
|
void eel_clear_args(int first)
|
|
{
|
|
int i;
|
|
for(i = first; i < eel_arg_count; ++i)
|
|
eel_d_freestring(eel_args + i);
|
|
eel_arg_count = first;
|
|
}
|
|
|
|
|
|
void eel_remove_arg(int pos)
|
|
{
|
|
int i;
|
|
eel_d_freestring(eel_args + pos);
|
|
for(i = pos + 1; i < eel_arg_count; ++i)
|
|
{
|
|
eel_args[i-1] = eel_args[i];
|
|
eel_arg_tokens[i-1] = eel_arg_tokens[i];
|
|
}
|
|
--eel_arg_count;
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove all arguments in the range ]start, end[.
|
|
* (That is, not including args 'start' or 'end'.)
|
|
*/
|
|
void eel_collapse(int start, int end)
|
|
{
|
|
int i;
|
|
for(i = start + 1; i < end; ++i)
|
|
eel_remove_arg(i);
|
|
}
|
|
|
|
|
|
/*
|
|
* Try to convert 'd' into a value. If it's not
|
|
* possible, nothing will be done.
|
|
*/
|
|
static inline void resolve(eel_data_t *d)
|
|
{
|
|
if(EDT_SYMREF == d->type)
|
|
switch(d->value.sym->type)
|
|
{
|
|
case EST_CONSTANT:
|
|
case EST_VARIABLE:
|
|
case EST_ENUM:
|
|
/* These are OK. */
|
|
*d = d->value.sym->data;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Steenkin' special case for a single unary minus before a term.
|
|
* (Could be recursive, call unary operator callbacks and stuff,
|
|
* but there are too many screaming babies here right now... :-)
|
|
*/
|
|
static inline int neg(int pos)
|
|
{
|
|
eel_data_t *d;
|
|
if((EDT_SYMREF == eel_args[pos].type) &&
|
|
(EST_OPERATOR == eel_args[pos].value.sym->type))
|
|
{
|
|
DBG(log_printf(DLOG, "neg\n");)
|
|
if(eel_op_sub != eel_args[pos].value.sym->data.value.op.cb)
|
|
{
|
|
eel_error("No unary operator '%s'!",
|
|
eel_args[pos].value.sym->name);
|
|
return -1;
|
|
}
|
|
d = &eel_args[pos+1];
|
|
if(!d)
|
|
{
|
|
eel_error("Expected operand for unary minus!");
|
|
return -1;
|
|
}
|
|
resolve(d);
|
|
switch(d->type)
|
|
{
|
|
case EDT_REAL: /* Real */
|
|
d->value.r = -d->value.r;
|
|
break;
|
|
case EDT_INTEGER: /* Integer */
|
|
d->value.i = -d->value.i;
|
|
break;
|
|
case EDT_SYMREF: /* Symbol reference */
|
|
eel_error("Chained unary operators not yet supported!");
|
|
return -1;
|
|
case EDT_SYMTAB: /* Symbol table reference */
|
|
case EDT_STRING: /* String */
|
|
case EDT_CADDR: /* Code address */
|
|
case EDT_SYMNAME: /* Name of a new symbol (String) */
|
|
case EDT_OPERATOR: /* Operator callback */
|
|
case EDT_DIRECTIVE: /* Directive parser callback */
|
|
case EDT_SPECIAL: /* Special parser callback */
|
|
case EDT_ILLEGAL:
|
|
eel_error("Illegal operation!");
|
|
return -1;
|
|
}
|
|
eel_remove_arg(pos);
|
|
}
|
|
else
|
|
resolve(&eel_args[pos]);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Recursively evaluate expression, taking operator
|
|
* precedence in account. The 'left' argument is the
|
|
* index of the left operand, which is also the
|
|
* target for the result, and must be followed by an
|
|
* operator and a valid right term.
|
|
*
|
|
FIXME:
|
|
* Note that a single unary minus is allowed as a
|
|
* part of each term, and will be evaluated here.
|
|
* This feature should be removed as soon as the
|
|
* new operator system is fully implemented. (Unary
|
|
* minus can then be implemented as just another EEL
|
|
* operator.)
|
|
/FIXME
|
|
*
|
|
* Returns the index of the last argument used, or a
|
|
* negative value, if the operation fails.
|
|
*
|
|
FIXME: Handle 0 returns from operators...?
|
|
*/
|
|
static int recursive_eval(int left)
|
|
{
|
|
int right = left + 1;
|
|
if(neg(left) < 0)
|
|
return -1;
|
|
|
|
while(right + 1 < eel_arg_count)
|
|
{
|
|
eel_symbol_t *op, *op2;
|
|
op = eel_args[right].value.sym;
|
|
if(neg(right + 1) < 0)
|
|
return -1;
|
|
|
|
if(right + 2 < eel_arg_count)
|
|
{
|
|
op2 = eel_args[right + 2].value.sym;
|
|
if(op2->data.value.op.priority > op->data.value.op.priority)
|
|
{
|
|
int maxr = recursive_eval(right + 1);
|
|
eel_remove_arg(right); /* Remove operator */
|
|
if(op->data.value.op.cb(2, eel_args + left) < 0)
|
|
return -1;
|
|
|
|
eel_remove_arg(right); /* Remove right term */
|
|
return maxr;
|
|
}
|
|
}
|
|
eel_remove_arg(right); /* Remove operator token */
|
|
if(op->data.value.op.cb(2, eel_args + left) < 0)
|
|
return -1;
|
|
|
|
eel_remove_arg(right); /* Remove right hand term */
|
|
}
|
|
return right;
|
|
}
|
|
|
|
|
|
/*
|
|
* Evaluate expression, starting at position 'start'
|
|
* in the internal argument list. The whole expression
|
|
* will be replaced by a single term, stored in the
|
|
* 'start' position, and all terms after it will be
|
|
* deleted.
|
|
*
|
|
* Returns a token corresponding to the result type,
|
|
* or a negative value in case of an error.
|
|
*/
|
|
static int do_eval(int start)
|
|
{
|
|
if(eel_arg_count - start > 1)
|
|
{
|
|
int right;
|
|
#if 0
|
|
int i;
|
|
for(i = 0; i < start; ++i)
|
|
log_printf(D3LOG, " ");
|
|
log_printf(D3LOG, "do_eval(): ");
|
|
for(i = start; i < eel_arg_count; ++i)
|
|
{
|
|
if(EDT_SYMREF == eel_args[i].type)
|
|
log_printf(D3LOG, "<%s, %d>", eel_symbol_is(eel_args[i].value.sym),
|
|
eel_arg_tokens[i]);
|
|
else
|
|
log_printf(D3LOG, "<%s, %d>", eel_data_is(&eel_args[i]),
|
|
eel_arg_tokens[i]);
|
|
}
|
|
log_printf(D3LOG, "\n");
|
|
#endif
|
|
right = recursive_eval(start);
|
|
if(right < 0)
|
|
return -1;
|
|
|
|
/* Check for bogus "terminating operator" */
|
|
if(right < eel_arg_count)
|
|
{
|
|
eel_error("Expression syntax error!");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Prepare and return result */
|
|
/* eel_clear_args(start + 1);*/
|
|
switch (eel_args[start].type)
|
|
{
|
|
case EDT_REAL:
|
|
eel_arg_tokens[start] = TK_RNUM;
|
|
break;
|
|
case EDT_INTEGER:
|
|
eel_arg_tokens[start] = TK_INUM;
|
|
break;
|
|
case EDT_STRING:
|
|
eel_arg_tokens[start] = TK_STRN;
|
|
break;
|
|
case EDT_SYMNAME:
|
|
eel_arg_tokens[start] = TK_NEWSYM;
|
|
break;
|
|
case EDT_SYMTAB:
|
|
/*
|
|
* Note:
|
|
* If we were to get a namespace reference, it would
|
|
* actually be an EDT_SYMREF referring to the namespace
|
|
* symbol, rather than a raw pointer to namespace
|
|
* symbol table itself.
|
|
*/
|
|
eel_error("Weird... do_eval() saw a symbol table reference.");
|
|
eel_arg_tokens[start] = -1;
|
|
break;
|
|
case EDT_SYMREF:
|
|
case EDT_OPERATOR:
|
|
case EDT_DIRECTIVE:
|
|
case EDT_SPECIAL:
|
|
eel_arg_tokens[start] = TK_SYMREF;
|
|
break;
|
|
default:
|
|
eel_error("Weird things going on in do_eval()!");
|
|
eel_arg_tokens[start] = -1;
|
|
break;
|
|
}
|
|
return eel_arg_tokens[start];
|
|
}
|
|
|
|
|
|
/*
|
|
* Grab, and if required, evaluate the next "token".
|
|
*
|
|
* This function will return a token, and when applicable,
|
|
* the corresponding eel_data_t already last in the argument
|
|
* list.
|
|
*
|
|
* Note that returned arguments may be results of evaluated
|
|
* expressions, rather than constants from the source.
|
|
*
|
|
* Also note that the function will stop at ')' tokens. This
|
|
* functions as a way of supporting the alternative LISP
|
|
* style function calls (required when functions are used
|
|
* inside argument lists and expressions). It's also a
|
|
* feature that's relied upon internally, to recursively
|
|
* resolve expressions containing parentheses.
|
|
*/
|
|
static int eval(int report_eoln)
|
|
{
|
|
int tk = EOF;
|
|
int prev_tk;
|
|
int expect_operator = 0;
|
|
int tokens = 0;
|
|
int start = eel_arg_count;
|
|
int grab_and_return = 0;
|
|
eel_symbol_t *sym;
|
|
/*
|
|
* Get terms and operators, checking that we
|
|
* get term, operator(s), term,... etc. Stop
|
|
* and evaluate if the chain is broken, or
|
|
* any other token is seen.
|
|
*/
|
|
while(1)
|
|
{
|
|
prev_tk = tk;
|
|
tk = eel_lex(report_eoln);
|
|
switch(tk)
|
|
{
|
|
case TK_SYMREF:
|
|
sym = eel_current.lval->value.sym;
|
|
if(expect_operator)
|
|
{
|
|
if(EST_OPERATOR != sym->type)
|
|
{
|
|
eel_unlex();
|
|
return do_eval(start);
|
|
}
|
|
expect_operator = 0;
|
|
}
|
|
else
|
|
if(EST_OPERATOR != sym->type)
|
|
expect_operator = 1;
|
|
break;
|
|
case TK_NEWSYM:
|
|
case TK_RNUM:
|
|
case TK_INUM:
|
|
case TK_STRN:
|
|
if(expect_operator)
|
|
{
|
|
eel_unlex();
|
|
return do_eval(start);
|
|
}
|
|
expect_operator = !expect_operator;
|
|
break;
|
|
case '(':
|
|
if(eval(report_eoln) < 0)
|
|
return -1;
|
|
tk = eel_lex(report_eoln);
|
|
if(')' != tk)
|
|
{
|
|
eel_error("Unmatched '(' in expression!");
|
|
return -1;
|
|
}
|
|
continue;
|
|
case ')':
|
|
if(0 == tokens)
|
|
/* Not part of the expression. */
|
|
return ')';
|
|
else
|
|
{
|
|
eel_unlex();
|
|
return do_eval(start);
|
|
}
|
|
break;
|
|
default:
|
|
if(0 == tokens)
|
|
{
|
|
return tk;
|
|
}
|
|
else
|
|
{
|
|
eel_unlex();
|
|
return do_eval(start);
|
|
}
|
|
}
|
|
if(eel_grab_arg() < 0)
|
|
{
|
|
eel_error("Argument overflow!");
|
|
return -1;
|
|
}
|
|
if(grab_and_return)
|
|
return tk;
|
|
++tokens;
|
|
}
|
|
}
|
|
|
|
|
|
int eel_parse_args(const char *separators, char terminator)
|
|
{
|
|
int sep_required = (strchr(separators, ' ') == 0);
|
|
int expect_separator = 0;
|
|
int tk;
|
|
eel_current.arg = 1;
|
|
while( (tk = eval('\n' == terminator)) )
|
|
{
|
|
if(tk == terminator)
|
|
return 0;
|
|
switch(tk)
|
|
{
|
|
case TK_SYMREF:
|
|
/* No operators here, please! */
|
|
if(EST_OPERATOR == eel_args[eel_arg_count-1].
|
|
value.sym->type)
|
|
{
|
|
eel_error("Incorrectly used operator!");
|
|
eel_current.arg = 0;
|
|
return -1;
|
|
}
|
|
case TK_NEWSYM:
|
|
case TK_RNUM:
|
|
case TK_INUM:
|
|
case TK_STRN:
|
|
if(expect_separator)
|
|
{
|
|
eel_error("Argument separator expected!");
|
|
eel_current.arg = 0;
|
|
return -1;
|
|
}
|
|
expect_separator = sep_required;
|
|
break;
|
|
default:
|
|
if(tk < 256)
|
|
if(strchr(separators, tk))
|
|
{
|
|
expect_separator = 0;
|
|
++eel_current.arg;
|
|
break;
|
|
}
|
|
/* eel_error("Unexpected token!");*/
|
|
eel_current.arg = 0;
|
|
return -1;
|
|
}
|
|
}
|
|
eel_error("Unexpected end of file!");
|
|
eel_current.arg = 0;
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Argument list fetcher
|
|
----------------------------------------------------------*/
|
|
|
|
/*
|
|
* Extract a "value" out of an argument.
|
|
* Only types marked in 'types' are accepted.
|
|
*
|
|
* If symrefs are accepted, the 'stypes' mask
|
|
* determines which symbol types are accepted.
|
|
*
|
|
* Returns NULL upon failure, or the address of
|
|
* a eel_data_t struct.
|
|
*/
|
|
static eel_data_t *get_value(eel_data_t *arg, int types, int stypes)
|
|
{
|
|
eel_data_t *dat;
|
|
|
|
dat = arg;
|
|
|
|
/* Find actual source data */
|
|
#if 0
|
|
while(EDT_SYMREF == dat->type)
|
|
#else
|
|
if(EDT_SYMREF == dat->type)
|
|
#endif
|
|
{
|
|
switch(dat->value.sym->type)
|
|
{
|
|
case EST_ENUM:
|
|
/* Keep symref if enums are desired! */
|
|
if(types & EDTM_SYMREF)
|
|
break;
|
|
case EST_CONSTANT:
|
|
case EST_VARIABLE:
|
|
dat = &dat->value.sym->data;
|
|
break;
|
|
default:
|
|
eel_error("Invalid argument!");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if((EDT_SYMREF == dat->type) && (types & EDTM_SYMREF))
|
|
{
|
|
/* Check symbol type */
|
|
if(!(stypes & (1<<dat->value.sym->type)))
|
|
{
|
|
eel_error("Wrong symbol type! (%s)",
|
|
eel_data_is(dat));
|
|
return NULL;
|
|
}
|
|
return arg;
|
|
}
|
|
else
|
|
{
|
|
/* Check data type */
|
|
if(!(types & (1<<dat->type)))
|
|
{
|
|
eel_error("Wrong data type! (%s)",
|
|
eel_data_is(dat));
|
|
return NULL;
|
|
}
|
|
return dat;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Find the symbol table entry for the variable
|
|
* referenced by 'arg'. If 'arg' is not a symbol
|
|
* reference, or if the referenced symbol is not
|
|
* a variable, NULL is returned, otherwise the
|
|
* symbol is returned.
|
|
*/
|
|
static eel_symbol_t *get_variable(eel_data_t *arg)
|
|
{
|
|
eel_symbol_t *sym;
|
|
|
|
if(EDT_SYMREF != arg->type)
|
|
return NULL;
|
|
|
|
sym = arg->value.sym;
|
|
|
|
if(sym->type != EST_VARIABLE)
|
|
{
|
|
eel_error("Expected a variable, not %s!", eel_symbol_is(sym));
|
|
return NULL;
|
|
}
|
|
return sym;
|
|
}
|
|
|
|
|
|
static int eel_get_argsv(int argc, struct eel_data_t *argv,
|
|
const char *fmt, va_list args)
|
|
{
|
|
int parsing = 1;
|
|
int optional = 0;
|
|
int tuple = 0;
|
|
int using_tuple = 0;
|
|
const char *tuple_start = NULL;
|
|
int got = 0;
|
|
int flags;
|
|
eel_data_t *dat;
|
|
eel_symbol_t *sym;
|
|
va_list args_tuple_start;
|
|
|
|
int *i;
|
|
double *r;
|
|
char **s;
|
|
eel_data_t **datp;
|
|
eel_symbol_t **symp;
|
|
|
|
eel_current.arg = 1;
|
|
|
|
va_copy(args_tuple_start, args);
|
|
|
|
while(parsing > 0)
|
|
{
|
|
eel_data_t *a;
|
|
|
|
/* Handle "harmless" control characters */
|
|
switch(*fmt)
|
|
{
|
|
case 0:
|
|
/* case ')': For LISP style function calls */
|
|
if(tuple_start)
|
|
{
|
|
/*
|
|
* We should never get here if
|
|
* we have an argument tuple!
|
|
*/
|
|
log_printf(ELOG,"eel eel_get_args(): Error in"
|
|
" format string; "
|
|
"unterminated argument"
|
|
"tuple!\n");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
if(!optional && (got < argc))
|
|
{
|
|
eel_error("%d arguments required; got %d!",
|
|
got, argc);
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
parsing = 0;
|
|
continue;
|
|
case '<':
|
|
/* Argument tuple start */
|
|
++fmt;
|
|
va_end(args_tuple_start);
|
|
if(using_tuple)
|
|
{
|
|
log_printf(ELOG,"eel eel_get_args(): Error in"
|
|
" format string; "
|
|
"'<' inside tuple!\n");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
using_tuple = 1;
|
|
tuple_start = fmt;
|
|
va_copy(args_tuple_start, args);
|
|
continue;
|
|
case '>':
|
|
/* Argument tuple end */
|
|
if(!tuple_start)
|
|
{
|
|
log_printf(ELOG,"eel eel_get_args(): Error in"
|
|
" format string; "
|
|
"'>' without '<'!\n");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
fmt = tuple_start;
|
|
va_end(args);
|
|
va_copy(args, args_tuple_start);
|
|
++tuple;
|
|
continue;
|
|
case '[':
|
|
/* Optional arguments */
|
|
optional = 1;
|
|
case ']':
|
|
++fmt;
|
|
continue;
|
|
case '*':
|
|
/* Throw away this argument */
|
|
++fmt;
|
|
++eel_current.arg;
|
|
continue;
|
|
case ',':
|
|
++fmt;
|
|
continue;
|
|
default:
|
|
if(got < argc)
|
|
a = argv + got;
|
|
else
|
|
{
|
|
parsing = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Handle actual data extraction tokens */
|
|
switch(*fmt)
|
|
{
|
|
case '?':
|
|
datp = va_arg(args, eel_data_t **);
|
|
datp[tuple] = a;
|
|
break;
|
|
case 'E':
|
|
symp = va_arg(args, eel_symbol_t **);
|
|
if(EDT_SYMREF != a->type)
|
|
{
|
|
eel_error("Not an enum constant!\n");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
symp[tuple] = a->value.sym;
|
|
|
|
/* Handle variables containing symrefs! */
|
|
if( (symp[tuple]->type == EST_VARIABLE) &&
|
|
(symp[tuple]->data.type == EDT_SYMREF) )
|
|
symp[tuple] = symp[tuple]->data.value.sym;
|
|
|
|
if(symp[tuple]->type != EST_ENUM)
|
|
{
|
|
eel_error("%s, not an enum constant!",
|
|
eel_symbol_is(symp[tuple]));
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
break;
|
|
case 'V':
|
|
datp = va_arg(args, eel_data_t **);
|
|
if('(' == *(fmt+1))
|
|
{
|
|
fmt += 2;
|
|
flags = 0;
|
|
while(*fmt != ')')
|
|
{
|
|
switch(*fmt)
|
|
{
|
|
case 0:
|
|
log_printf(ELOG,"Unexpected end"
|
|
" of control"
|
|
"string!\n");
|
|
parsing = -1;
|
|
break;
|
|
case 'i':
|
|
flags |= EDTM_INTEGER;
|
|
break;
|
|
case 'r':
|
|
flags |= EDTM_REAL;
|
|
break;
|
|
case 's':
|
|
flags |= EDTM_STRING;
|
|
break;
|
|
case 'c':
|
|
flags |= EDTM_CADDR;
|
|
break;
|
|
case 'e':
|
|
flags |= EDTM_SYMREF;
|
|
break;
|
|
default:
|
|
log_printf(ELOG,"Unknown type"
|
|
" code '%c'!",
|
|
*fmt);
|
|
parsing = -1;
|
|
break;
|
|
}
|
|
if(parsing < 0)
|
|
break;
|
|
++fmt;
|
|
}
|
|
if(parsing < 0)
|
|
continue;
|
|
}
|
|
else
|
|
flags = EDTM_REAL | EDTM_INTEGER |
|
|
EDTM_STRING | EDTM_CADDR;
|
|
datp[tuple] = get_value(a, flags, ESTM_ENUM);
|
|
if(!datp[tuple])
|
|
{
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
break;
|
|
case 'e':
|
|
i = va_arg(args, int *);
|
|
if(a->type != EDT_SYMREF)
|
|
{
|
|
eel_error("Enum argument must be a symref, "
|
|
"not %s!", eel_data_is(a));
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
sym = a->value.sym;
|
|
if(sym->type == EST_VARIABLE)
|
|
{
|
|
/* Follow reference */
|
|
eel_data_t *sd = &sym->data;
|
|
if(sd->type == EDT_SYMREF)
|
|
if(sd->value.sym->type == EST_ENUM)
|
|
sym = sd->value.sym;
|
|
}
|
|
if(sym->type != EST_ENUM)
|
|
{
|
|
eel_error("Expected enumerated type, not %s!",
|
|
eel_symbol_is(sym));
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
if(sym->token != *i)
|
|
{
|
|
/* Oops, inter-enum namespace conflict!
|
|
* See if we can find the same identifier
|
|
* in the *right* class...
|
|
*/
|
|
sym = eel_s_find_n_tk(sym, sym->name, *i);
|
|
if(!sym)
|
|
{
|
|
eel_error("Wrong enumeration class!");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
}
|
|
i[tuple] = sym->data.value.i;
|
|
break;
|
|
case 'f':
|
|
symp = va_arg(args, eel_symbol_t **);
|
|
if(EDT_SYMREF != a->type)
|
|
{
|
|
eel_error("Not a function!\n");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
symp[tuple] = a->value.sym;
|
|
|
|
/* Handle variables containing symrefs! */
|
|
if( (symp[tuple]->type == EST_VARIABLE) &&
|
|
(symp[tuple]->data.type == EDT_SYMREF) )
|
|
symp[tuple] = symp[tuple]->data.value.sym;
|
|
|
|
if(symp[tuple]->type != EST_FUNCTION)
|
|
{
|
|
eel_error("%s, not a function!",
|
|
eel_symbol_is(symp[tuple]));
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
break;
|
|
case 'i':
|
|
i = va_arg(args, int *);
|
|
dat = get_value(a, EDTM_INTEGER | EDTM_REAL | EDTM_STRING,
|
|
0);
|
|
if(!dat)
|
|
{
|
|
eel_error("Failed to cast argument to integer.");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
switch(dat->type)
|
|
{
|
|
case EDT_REAL:
|
|
i[tuple] = (int)dat->value.r;
|
|
break;
|
|
case EDT_INTEGER:
|
|
i[tuple] = dat->value.i;
|
|
break;
|
|
case EDT_STRING:
|
|
i[tuple] = (int)atof(dat->value.s);
|
|
break;
|
|
default:
|
|
eel_error("INTERNAL: Got type I didn't ask for.");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
break;
|
|
case 'n':
|
|
symp = va_arg(args, eel_symbol_t **);
|
|
symp[tuple] = get_variable(a);
|
|
if(!symp[tuple])
|
|
{
|
|
if(a->type == EDT_SYMNAME)
|
|
symp[tuple] = eel_set_integer(
|
|
a->value.s, 0);
|
|
if(!symp[tuple])
|
|
{
|
|
eel_error("Failed to create new symbol! '%s'",
|
|
a->value.s);
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
case 'r':
|
|
r = va_arg(args, double *);
|
|
dat = get_value(a, EDTM_INTEGER | EDTM_REAL | EDTM_STRING,
|
|
0);
|
|
if(!dat)
|
|
{
|
|
eel_error("Failed to cast argument to real.");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
switch(dat->type)
|
|
{
|
|
case EDT_REAL:
|
|
r[tuple] = dat->value.r;
|
|
break;
|
|
case EDT_INTEGER:
|
|
r[tuple] = (double)dat->value.i;
|
|
break;
|
|
case EDT_STRING:
|
|
r[tuple] = atof(dat->value.s);
|
|
break;
|
|
default:
|
|
eel_error("INTERNAL: Got type I didn't ask for.");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
break;
|
|
case 's':
|
|
s = va_arg(args, char **);
|
|
dat = get_value(a, EDTM_STRING, 0);
|
|
if(!dat)
|
|
{
|
|
eel_error("Failed to cast argument to string.");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
switch(dat->type)
|
|
{
|
|
#if 0
|
|
TODO: (String memory management problem)
|
|
case EDT_REAL:
|
|
break;
|
|
case EDT_INTEGER:
|
|
break;
|
|
#endif
|
|
case EDT_STRING:
|
|
s[tuple] = dat->value.s;
|
|
break;
|
|
default:
|
|
eel_error("INTERNAL: Got type I didn't ask for.");
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
break;
|
|
case 'v':
|
|
symp = va_arg(args, eel_symbol_t **);
|
|
symp[tuple] = get_variable(a);
|
|
if(!symp[tuple])
|
|
{
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
eel_error("eel_get_args(): Error in format string; "
|
|
"unknown control char '%c'.\n", *fmt);
|
|
parsing = -1;
|
|
continue;
|
|
}
|
|
++fmt;
|
|
++got;
|
|
++eel_current.arg; /* (For error messages) */
|
|
}
|
|
va_end(args_tuple_start);
|
|
|
|
if(parsing < 0)
|
|
return -1; /* Error exit! */
|
|
|
|
eel_current.arg = 0;
|
|
|
|
if(!optional)
|
|
{
|
|
/*
|
|
* We *should* be at the end of the format string
|
|
* now. Let's check...
|
|
*/
|
|
while(*fmt)
|
|
{
|
|
switch(*fmt)
|
|
{
|
|
case '>':
|
|
case ']':
|
|
break;
|
|
default:
|
|
eel_error("Insufficient number of arguments!");
|
|
return -1;
|
|
}
|
|
++fmt;
|
|
}
|
|
}
|
|
|
|
if(argc > got)
|
|
{
|
|
eel_error("Too many arguments!");
|
|
eel_current.arg = 0;
|
|
return -1;
|
|
}
|
|
|
|
return got;
|
|
}
|
|
|
|
|
|
int eel_get_args_from(int argc, struct eel_data_t *argv, const char *fmt, ...)
|
|
{
|
|
int ret;
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
ret = eel_get_argsv(argc, argv, fmt, args);
|
|
va_end(args);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int eel_get_args(const char *fmt, ...)
|
|
{
|
|
int ret;
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
ret = eel_get_argsv(eel_arg_count, eel_args, fmt, args);
|
|
va_end(args);
|
|
return ret;
|
|
}
|
|
|