ngspice/src/frontend/numparam/xpressn.c

1715 lines
44 KiB
C
Raw Normal View History

/* xpressn.c Copyright (C) 2002 Georg Post
2009-11-15 22:22:04 +01:00
This file is part of Numparam, see: readme.txt
Free software under the terms of the GNU Lesser General Public License
*/
#include "ngspice/ngspice.h"
#include "general.h"
#include "numparam.h"
#include "ngspice/cpdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/dvec.h"
#include "../frontend/variable.h"
#include "ngspice/compatmode.h"
2017-11-18 18:37:05 +01:00
#include "ngspice/stringskip.h"
2009-08-08 22:12:46 +02:00
2008-11-29 21:21:56 +01:00
/* random numbers in /maths/misc/randnumb.c */
#include "ngspice/randnumb.h"
/************ keywords ************/
extern long dynsubst; /* see inpcom.c */
2008-11-26 21:31:50 +01:00
#define ACT_CHARACTS 25 /* actual string length to be inserted and replaced */
2010-01-29 23:33:59 +01:00
#define S_init 0
#define S_atom 1
#define S_binop 2
#define S_unop 3
#define S_stop 4
static double
ternary_fcn(double conditional, double if_value, double else_value)
{
if (conditional != 0.0)
return if_value;
else
return else_value;
}
2008-11-26 21:31:50 +01:00
static double
agauss(double nominal_val, double abs_variation, double sigma)
{
double stdvar;
stdvar = abs_variation / sigma;
return (nominal_val + stdvar * gauss0());
}
static double
gauss(double nominal_val, double rel_variation, double sigma)
{
double stdvar;
stdvar = nominal_val * rel_variation / sigma;
return (nominal_val + stdvar * gauss0());
}
static double
unif(double nominal_val, double rel_variation)
{
return (nominal_val + nominal_val * rel_variation * drand());
}
static double
aunif(double nominal_val, double abs_variation)
{
return (nominal_val + abs_variation * drand());
}
static double
limit(double nominal_val, double abs_variation)
{
return (nominal_val + (drand() > 0 ? abs_variation : -1. * abs_variation));
}
2013-11-07 21:42:02 +01:00
static const char *fmathS = /* all math functions */
2014-09-12 19:06:43 +02:00
"SQR SQRT SIN COS EXP LN ARCTAN ABS POW PWR MAX MIN INT LOG LOG10 SINH COSH"
2013-11-07 21:42:02 +01:00
" TANH TERNARY_FCN AGAUSS SGN GAUSS UNIF AUNIF LIMIT CEIL FLOOR"
" ASIN ACOS ATAN ASINH ACOSH ATANH TAN NINT";
2007-12-26 17:55:27 +01:00
enum {
2014-09-12 19:06:43 +02:00
XFU_SQR = 1, XFU_SQRT, XFU_SIN, XFU_COS, XFU_EXP, XFU_LN, XFU_ARCTAN, XFU_ABS, XFU_POW, XFU_PWR, XFU_MAX, XFU_MIN, XFU_INT, XFU_LOG, XFU_LOG10, XFU_SINH, XFU_COSH,
XFU_TANH, XFU_TERNARY_FCN, XFU_AGAUSS, XFU_SGN, XFU_GAUSS, XFU_UNIF, XFU_AUNIF, XFU_LIMIT, XFU_CEIL, XFU_FLOOR,
XFU_ASIN, XFU_ACOS, XFU_ATAN, XFU_ASINH, XFU_ACOSH, XFU_ATANH, XFU_TAN, XFU_NINT
};
2007-12-26 17:55:27 +01:00
static double
mathfunction(int f, double z, double x)
/* the list of built-in functions. Patch 'fmath', here and near line 888 to get more ...*/
2007-12-26 17:55:27 +01:00
{
double y;
switch (f)
{
case XFU_SQR:
y = x * x;
break;
case XFU_SQRT:
y = sqrt(x);
break;
case XFU_SIN:
y = sin(x);
break;
case XFU_COS:
y = cos(x);
break;
case XFU_EXP:
y = exp(x);
break;
case XFU_LN:
y = log(x);
break;
case XFU_ARCTAN:
y = atan(x);
break;
case XFU_ABS:
y = fabs(x);
break;
case XFU_POW:
y = pow(z, x);
break;
case XFU_PWR:
y = pow(fabs(z), x);
break;
case XFU_MAX:
y = MAX(x, z);
break;
case XFU_MIN:
y = MIN(x, z);
break;
case XFU_INT:
y = trunc(x);
break;
case XFU_NINT:
/* round to "nearest integer",
* round half-integers to the nearest even integer
* rely on default rounding mode of IEEE 754 to do so
*/
y = nearbyint(x);
break;
case XFU_LOG:
y = log(x);
break;
2014-09-12 19:06:43 +02:00
case XFU_LOG10:
y = log10(x);
break;
case XFU_SINH:
y = sinh(x);
2008-11-26 21:31:50 +01:00
break;
case XFU_COSH:
y = cosh(x);
2008-11-26 21:31:50 +01:00
break;
case XFU_TANH:
y = tanh(x);
2008-11-26 21:31:50 +01:00
break;
case XFU_SGN:
if (x > 0)
y = 1.;
else if (x == 0)
y = 0.;
else
y = -1.;
2010-03-07 16:59:08 +01:00
break;
case XFU_CEIL:
y = ceil(x);
2011-12-26 12:34:21 +01:00
break;
case XFU_FLOOR:
y = floor(x);
2011-12-26 12:34:21 +01:00
break;
case XFU_ASIN:
y = asin(x);
break;
case XFU_ACOS:
y = acos(x);
break;
case XFU_ATAN:
y = atan(x);
break;
case XFU_ASINH:
y = asinh(x);
break;
case XFU_ACOSH:
y = acosh(x);
break;
case XFU_ATANH:
y = atanh(x);
break;
case XFU_TAN:
y = tan(x);
break;
2007-12-26 17:55:27 +01:00
default:
y = x;
break;
2007-12-26 17:55:27 +01:00
}
return y;
2007-12-26 17:55:27 +01:00
}
#ifdef __GNUC__
2014-08-10 20:21:39 +02:00
static bool message(dico_t *dico, const char *fmt, ...)
__attribute__ ((format (__printf__, 2, 3)));
#endif
static bool
2014-08-10 20:21:39 +02:00
message(dico_t *dico, const char *fmt, ...)
2007-12-26 17:55:27 +01:00
{
va_list ap;
2014-08-10 20:21:39 +02:00
if (dico->srcline >= 0)
fprintf
(stderr,
"Original line no.: %d, new internal line no.: %d:\n",
2014-08-10 20:21:39 +02:00
dico->oldline, dico->srcline);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
2014-08-10 20:21:39 +02:00
dico->errcount++;
return 1; /* error! */
2007-12-26 17:55:27 +01:00
}
/************ the input text symbol table (dictionary) *************/
2007-12-26 17:55:27 +01:00
void
2014-08-10 20:21:34 +02:00
initdico(dico_t *dico)
2007-12-26 17:55:27 +01:00
{
int asize = 10; /* default allocation depth of the synbol stack */
COMPATMODE_T compat_mode;
dico->srcline = -1;
dico->errcount = 0;
spice_dstring_init(&(dico->lookup_buf));
dico->symbols = TMALLOC(NGHASHPTR, asize);
dico->inst_name = TMALLOC(char*, asize);
dico->max_stack_depth = asize;
dico->stack_depth = 0; /* top of the stack */
dico->symbols[0] = nghash_init(NGHASH_MIN_SIZE);
nghash_unique(dico->symbols[0], TRUE); /* no rewrite of global symbols */
dico->inst_symbols = NULL; /* instance qualified are lazily allocated */
compat_mode = ngspice_compat_mode();
if (compat_mode == COMPATMODE_HS)
dico->hs_compatibility = 1;
else
dico->hs_compatibility = 0;
2007-12-26 17:55:27 +01:00
}
void
2014-08-10 20:21:43 +02:00
dico_free_entry(entry_t *entry)
2010-02-25 22:43:03 +01:00
{
2014-08-10 20:21:43 +02:00
if (entry->symbol)
txfree(entry->symbol);
2011-02-19 16:04:09 +01:00
2014-08-10 20:21:43 +02:00
txfree(entry);
}
2010-02-25 22:43:03 +01:00
/* local semantics for parameters inside a subckt */
/* arguments as wll as .param expressions */
/* to do: scope semantics ?
"params:" and all new symbols should have local scope inside subcircuits.
redefinition of old symbols gives a warning message.
*/
static void
dicostack_push(dico_t *dico, char *inst_name)
/* push operation for nested subcircuit locals */
2007-12-26 17:55:27 +01:00
{
2014-08-09 19:38:29 +02:00
dico->stack_depth++;
if (dico->stack_depth >= dico->max_stack_depth) {
int asize = (dico->max_stack_depth *= 2);
dico->symbols = TREALLOC(NGHASHPTR, dico->symbols, asize);
2014-08-09 19:38:29 +02:00
dico->inst_name = TREALLOC(char*, dico->inst_name, asize);
}
2014-08-09 19:38:29 +02:00
/* lazy allocation - don't allocate space if we can help it */
dico->symbols[dico->stack_depth] = NULL;
dico->inst_name[dico->stack_depth] = inst_name;
}
static void
2014-08-10 20:21:34 +02:00
dicostack_pop(dico_t *dico)
/* pop operation for nested subcircuit locals */
{
char *inst_name; /* name of subcircuit instance */
char *param_p; /* qualified inst parameter name */
2014-08-10 20:21:43 +02:00
entry_t *entry; /* current entry */
NGHASHPTR htable_p; /* current hash table */
NGHASHITER iter; /* hash iterator - thread safe */
2014-08-09 19:38:29 +02:00
if (dico->stack_depth <= 0) {
message(dico, " Subckt Stack underflow.\n");
return;
}
/* -----------------------------------------------------------------
* Keep instance parameters around by transferring current local
* scope variables to an instance qualified hash table.
* ----------------------------------------------------------------- */
inst_name = dico->inst_name[dico->stack_depth];
htable_p = dico->symbols[dico->stack_depth];
2014-08-09 19:38:29 +02:00
if (htable_p) {
SPICE_DSTRING param_name; /* build a qualified name */
spice_dstring_init(&param_name);
NGHASH_FIRST(&iter);
2014-08-10 20:21:43 +02:00
for (entry = (entry_t *) nghash_enumerateRE(htable_p, &iter);
entry;
entry = (entry_t *) nghash_enumerateRE(htable_p, &iter))
2014-08-09 19:38:29 +02:00
{
spice_dstring_reinit(&param_name);
param_p = spice_dstring_print(&param_name, "%s.%s",
2014-08-10 20:21:43 +02:00
inst_name, entry->symbol);
nupa_add_inst_param(param_p, entry->vl);
dico_free_entry(entry);
2014-08-09 19:38:25 +02:00
}
2014-08-09 19:38:29 +02:00
nghash_free(htable_p, NULL, NULL);
spice_dstring_free(&param_name);
}
tfree(inst_name);
2014-08-09 19:38:25 +02:00
2014-08-09 19:38:29 +02:00
dico->inst_name[dico->stack_depth] = NULL;
dico->symbols[dico->stack_depth] = NULL;
2014-08-09 19:38:29 +02:00
dico->stack_depth--;
2007-12-26 17:55:27 +01:00
}
2007-12-26 17:55:27 +01:00
int
2014-08-10 20:21:34 +02:00
donedico(dico_t *dico)
2007-12-26 17:55:27 +01:00
{
int sze = nghash_get_size(dico->symbols[0]);
return sze;
2007-12-26 17:55:27 +01:00
}
2010-03-25 23:44:37 +01:00
/* -----------------------------------------------------------------
* Now entryb works on the given hash table hierarchy. First
* look thru the stack of local symbols and then look at the global
* symbols in that order.
* ----------------------------------------------------------------- */
2016-04-29 17:51:50 +02:00
entry_t *
2014-08-10 20:21:39 +02:00
entrynb(dico_t *dico, char *s)
2007-12-26 17:55:27 +01:00
{
int depth; /* stack depth */
2014-08-10 20:21:43 +02:00
entry_t *entry; /* search hash table */
NGHASHPTR htable_p; /* hash table */
/* look at the current scope and then backup the stack */
2014-08-10 20:21:39 +02:00
for (depth = dico->stack_depth; depth >= 0; depth--) {
htable_p = dico->symbols[depth];
if (htable_p) {
2014-08-10 20:21:43 +02:00
entry = (entry_t *) nghash_find(htable_p, s);
if (entry)
return (entry);
}
}
return NULL;
}
2007-12-26 17:55:27 +01:00
static double
fetchnumentry(dico_t *dico, char *s, bool *perr)
2007-12-26 17:55:27 +01:00
{
entry_t *entry = entrynb(dico, s);
if (entry && (entry->tp == NUPA_REAL))
return entry->vl;
*perr = message(dico, "Undefined number [%s]\n", s);
return 0.0;
2007-12-26 17:55:27 +01:00
}
/******* writing dictionary entries *********/
2014-08-10 20:21:29 +02:00
entry_t *
2014-08-10 20:21:39 +02:00
attrib(dico_t *dico, NGHASHPTR htable_p, char *t, char op)
2007-12-26 17:55:27 +01:00
{
/* seek or attribute dico entry number for string t.
Option op='N' : force a new entry, if tos>level and old is valid.
*/
2014-08-10 20:21:43 +02:00
entry_t *entry; /* symbol table entry */
2014-08-10 20:21:43 +02:00
entry = (entry_t *) nghash_find(htable_p, t);
if (entry && (op == 'N') &&
(entry->level < dico->stack_depth) && (entry->tp != NUPA_UNKNOWN))
{
2014-08-10 20:21:43 +02:00
entry = NULL;
}
2014-08-10 20:21:43 +02:00
if (!entry) {
entry = TMALLOC(entry_t, 1);
entry->symbol = strdup(t);
entry->tp = NUPA_UNKNOWN; /* signal Unknown */
2014-08-10 20:21:43 +02:00
entry->level = dico->stack_depth;
nghash_insert(htable_p, t, entry);
}
2014-08-10 20:21:43 +02:00
return entry;
2007-12-26 17:55:27 +01:00
}
/* user defined delete function:
* free the dictionary entries malloc'ed above
* will be called by nghash_free() in nupa_del_dicoS()
*/
void
2014-08-10 20:21:43 +02:00
del_attrib(void *entry_p)
{
2014-08-10 20:21:43 +02:00
entry_t *entry = (entry_t*) entry_p;
if(entry) {
tfree(entry->symbol);
tfree(entry);
}
}
static bool
2014-08-10 20:21:34 +02:00
nupa_define(dico_t *dico,
char *t, /* identifier to define */
char op, /* option */
nupa_type tpe, /* type marker */
double z, /* float value if any */
int w, /* integer value if any */
char *base) /* string pointer if any */
2007-12-26 17:55:27 +01:00
{
/*define t as real or integer,
opcode= 'N' impose a new item under local conditions.
check for pointers, too, in full macrolanguage version:
Call with 'N','P',0.0, ksymbol ... for VAR parameter passing.
Overwrite warning, beware: During 1st pass (macro definition),
we already make symbol entries which are dummy globals !
we mark each id with its subckt level, and warn if write at higher one.
*/
nupa_type c;
bool warn;
2014-08-10 20:21:43 +02:00
entry_t *entry; /* spice table entry */
NGHASHPTR htable_p; /* hash table */
/* can't be lazy anymore */
if (!(dico->symbols[dico->stack_depth]))
dico->symbols[dico->stack_depth] = nghash_init(NGHASH_MIN_SIZE);
2011-02-19 16:04:09 +01:00
htable_p = dico->symbols[dico->stack_depth];
2014-08-10 20:21:43 +02:00
entry = attrib(dico, htable_p, t, op);
if (!entry)
return message(dico, " Symbol table overflow\n");
c = entry->tp;
2017-10-25 21:33:13 +02:00
if ((c == NUPA_REAL) || (c == NUPA_STRING) || (c == NUPA_UNKNOWN)) {
2017-10-25 21:33:13 +02:00
entry->vl = z;
entry->tp = tpe;
entry->ivl = w;
entry->sbbase = base;
/* if ((c != '?') && (i <= dico->stack[dico->tos])) { */
if (c == NUPA_UNKNOWN)
2017-10-25 21:33:13 +02:00
entry->level = dico->stack_depth; /* promote! */
/* warn about re-write to a global scope! */
if (entry->level < dico->stack_depth)
warn = message(dico, "%s:%d overwritten.\n", t, entry->level);
} else {
/* suppress error message, resulting from multiple definition of
symbols (devices) in .model lines with same name, but in different subcircuits.
Subcircuit expansion is o.k., we have to deal with this numparam
behaviour later. (H. Vogt 090426)
*/
if (0)
message(dico, "%s: cannot redefine\n", t);
}
return 0;
2007-12-26 17:55:27 +01:00
}
bool
defsubckt(dico_t *dico, struct card *card, nupa_type categ)
/* called on 1st pass of spice source code,
to enter subcircuit (categ=U) and model (categ=O) names
*/
2007-12-26 17:55:27 +01:00
{
2016-05-08 19:02:34 +02:00
const char *s = card->line;
int w = card->linenum;
bool err;
2016-05-08 19:03:41 +02:00
const char *s_end;
2007-12-26 17:55:27 +01:00
2016-05-08 19:02:34 +02:00
while (*s && (*s != '.'))
s++; /* skip 1st dotword */
2007-12-26 17:55:27 +01:00
2016-05-08 19:02:34 +02:00
while (*s && (*s > ' '))
s++;
2007-12-26 17:55:27 +01:00
2016-05-08 19:02:34 +02:00
while (*s && (*s <= ' '))
s++; /* skip blank */
2007-12-26 17:55:27 +01:00
2016-05-08 19:03:41 +02:00
s_end = s;
2007-12-26 17:55:27 +01:00
2016-05-08 19:03:41 +02:00
while (*s_end && (*s_end > ' '))
s_end++;
2007-12-26 17:55:27 +01:00
2016-05-08 19:03:41 +02:00
if (s_end > s) {
SPICE_DSTRING ustr; /* temp user string */
spice_dstring_init(&ustr);
2016-05-08 19:03:41 +02:00
pscopy_up(&ustr, s, 0, (int) (s_end - s));
err = nupa_define(dico, spice_dstring_value(&ustr), ' ', categ, 0.0, w, NULL);
spice_dstring_free(&ustr);
} else {
2014-05-18 20:39:17 +02:00
err = message(dico, "Subcircuit or Model without name.\n");
}
2007-12-26 17:55:27 +01:00
return err;
2007-12-26 17:55:27 +01:00
}
2007-12-26 17:55:27 +01:00
int
2016-05-08 19:08:15 +02:00
findsubckt(dico_t *dico, const char * const s)
/* input: s is a subcircuit invocation line.
returns 0 if not found, else the stored definition line number value */
2007-12-26 17:55:27 +01:00
{
2017-11-18 19:05:51 +01:00
const char *name_e = skip_back_ws(s + strlen(s), s);
const char *name_b = skip_back_non_ws(name_e, s);
2016-05-08 19:10:49 +02:00
SPICE_DSTRING ustr; /* u= subckt name is last token in string s */
2017-11-18 17:51:49 +01:00
entry_t *entry; /* symbol table entry */
spice_dstring_init(&ustr);
2017-11-18 18:28:28 +01:00
pscopy_up(&ustr, name_b, 0, (int) (name_e - name_b));
2014-08-10 20:21:43 +02:00
entry = entrynb(dico, spice_dstring_value(&ustr));
if (entry && (entry->tp == NUPA_SUBCKT)) {
2017-11-18 17:51:49 +01:00
return entry->ivl;
} else {
2014-05-18 20:39:17 +02:00
message(dico, "Cannot find subcircuit.\n");
2017-11-18 17:51:49 +01:00
return 0;
}
2007-12-26 17:55:27 +01:00
}
/************ input scanner stuff **************/
2007-12-26 17:55:27 +01:00
static unsigned char
keyword(const char *keys, const char *s, const char *s_end)
2007-12-26 17:55:27 +01:00
{
/* return 0 if s not found in list keys, else the ordinal number */
unsigned char j = 1;
if (!*s)
return 0;
for (;;) {
const char *p = s;
2016-04-30 20:29:13 +02:00
while ((p < s_end) && (toupper_c(*p) == *keys))
p++, keys++;
if ((p >= s_end) && (*keys <= ' '))
return j;
keys = strchr(keys, ' ');
if (!keys)
return 0;
keys++;
j++;
}
2007-12-26 17:55:27 +01:00
}
2007-12-26 17:55:27 +01:00
static double
2013-10-08 18:57:37 +02:00
parseunit(const char *s)
/* the Spice suffixes */
2007-12-26 17:55:27 +01:00
{
switch (toupper_c(s[0]))
{
case 'T': return 1e12;
case 'G': return 1e9;
case 'K': return 1e3;
case 'M': return ci_prefix("MEG", s) ? 1e6 : 1e-3;
case 'U': return 1e-6;
case 'N': return 1e-9;
case 'P': return 1e-12;
case 'F': return 1e-15;
default : return 1;
}
2007-12-26 17:55:27 +01:00
}
2013-10-08 18:38:43 +02:00
static const char *
fetchid(const char *s, const char *s_end)
2007-12-26 17:55:27 +01:00
{
2015-11-07 19:11:54 +01:00
for (; s < s_end; s++)
if (!(alfanum(*s) || *s == '.'))
2015-10-10 17:11:04 +02:00
return s;
2015-10-10 16:33:10 +02:00
return s;
2007-12-26 17:55:27 +01:00
}
2007-12-26 17:55:27 +01:00
static double
2014-08-10 20:21:34 +02:00
fetchnumber(dico_t *dico, const char **pi, bool *perror)
/* parse a Spice number in string s */
2007-12-26 17:55:27 +01:00
{
double u;
int n = 0;
2013-10-08 18:38:43 +02:00
const char *s = *pi;
2007-12-26 17:55:27 +01:00
if (1 != sscanf(s, "%lG%n", &u, &n)) {
2014-05-18 20:39:17 +02:00
*perror = message(dico, "Number format error: \"%s\"\n", s);
2007-12-26 17:55:27 +01:00
return 0.0; /* FIXME return NaN */
2007-12-26 17:55:27 +01:00
}
u *= parseunit(s + n);
2007-12-26 17:55:27 +01:00
/* swallow unit
* FIXME `100MegBaz42' should emit an error message
* FIXME should we allow whitespace ? `100 MEG' ?
*/
2007-12-26 17:55:27 +01:00
while (s[n] && alfa(s[n]))
n++;
*pi += n;
return u;
2007-12-26 17:55:27 +01:00
}
2007-12-26 17:55:27 +01:00
static char
2014-08-10 20:21:34 +02:00
fetchoperator(dico_t *dico,
2013-10-08 18:38:43 +02:00
const char *s_end,
const char **pi,
unsigned char *pstate, unsigned char *plevel,
bool *perror)
/* grab an operator from string s and advance scan index pi.
each operator has: one-char alias, precedence level, new interpreter state.
*/
2007-12-26 17:55:27 +01:00
{
2013-10-08 18:38:43 +02:00
const char *iptr = *pi;
unsigned char state = *pstate;
unsigned char level = *plevel;
bool error = *perror;
char c, d;
2013-10-08 18:38:43 +02:00
c = *iptr++;
2013-10-08 18:38:43 +02:00
d = *iptr;
if (iptr >= s_end)
2011-02-19 15:56:40 +01:00
d = '\0';
if ((c == '!') && (d == '=')) {
c = '#';
2013-10-08 18:38:43 +02:00
iptr++;
} else if ((c == '<') && (d == '>')) {
c = '#';
2013-10-08 18:38:43 +02:00
iptr++;
} else if ((c == '<') && (d == '=')) {
c = 'L';
2013-10-08 18:38:43 +02:00
iptr++;
} else if ((c == '>') && (d == '=')) {
c = 'G';
2013-10-08 18:38:43 +02:00
iptr++;
} else if ((c == '*') && (d == '*')) {
c = '^';
2013-10-08 18:38:43 +02:00
iptr++;
} else if ((c == '=') && (d == '=')) {
2013-10-08 18:38:43 +02:00
iptr++;
} else if ((c == '&') && (d == '&')) {
c = 'A';
2013-10-08 18:38:43 +02:00
iptr++;
} else if ((c == '|') && (d == '|')) {
c = 'O';
2013-10-08 18:38:43 +02:00
iptr++;
2013-11-01 20:15:21 +01:00
}
if ((c == '+') || (c == '-')) {
state = S_binop; /* pending operator */
level = 4;
} else if ((c == '*') || (c == '/') || (c == '%') || (c == '\\')) {
state = S_binop;
level = 3;
} else if (c == '^') {
state = S_binop;
level = 2;
2016-04-30 12:20:35 +02:00
} else if (strchr("=<>#GL", c)) {
state = S_binop;
level = 5;
} else if (c == 'A') {
state = S_binop;
level = 6;
} else if (c == 'O') {
state = S_binop;
level = 7;
} else if (c == '!') {
state = S_unop;
} else if (c == '?') {
state = S_binop;
level = 9;
} else if (c == ':') {
state = S_binop;
level = 8;
} else {
state = S_init;
if (c > ' ')
2014-05-18 20:39:17 +02:00
error = message(dico, "Syntax error: letter [%c]\n", c);
2007-12-26 17:55:27 +01:00
}
2013-10-08 18:38:43 +02:00
*pi = iptr;
*pstate = state;
*plevel = level;
*perror = error;
return c;
2007-12-26 17:55:27 +01:00
}
2007-12-26 17:55:27 +01:00
static double
operate(char op, double x, double y)
2007-12-26 17:55:27 +01:00
{
/* execute operator op on a pair of reals */
/* bug: x:=x op y or simply x:=y for empty op? No error signalling! */
double u = 1.0;
double z = 0.0;
double t;
switch (op)
2007-12-26 17:55:27 +01:00
{
case ' ':
x = y; /* problem here: do type conversions ?! */
break;
2007-12-26 17:55:27 +01:00
case '+':
x = x + y;
break;
2007-12-26 17:55:27 +01:00
case '-':
x = x - y;
break;
2007-12-26 17:55:27 +01:00
case '*':
x = x * y;
break;
2007-12-26 17:55:27 +01:00
case '/':
x = x / y;
break;
case '^': /* power */
2013-10-08 18:38:43 +02:00
x = pow(fabs(x), y);
break;
case 'A': /* && */
x = ((x != 0.0) && (y != 0.0)) ? 1.0 : 0.0;
break;
case 'O': /* || */
x = ((x != 0.0) || (y != 0.0)) ? 1.0 : 0.0;
break;
2007-12-26 17:55:27 +01:00
case '=':
if (x == y)
x = u;
else
x = z;
break;
case '#': /* <> */
if (x != y)
x = u;
else
x = z;
break;
2007-12-26 17:55:27 +01:00
case '>':
if (x > y)
x = u;
else
x = z;
break;
2007-12-26 17:55:27 +01:00
case '<':
if (x < y)
x = u;
else
x = z;
break;
case 'G': /* >= */
if (x >= y)
x = u;
else
x = z;
break;
case 'L': /* <= */
if (x <= y)
x = u;
else
x = z;
break;
case '!': /* ! */
if (y == z)
x = u;
else
x = z;
break;
case '%': /* % */
2016-04-30 19:20:49 +02:00
t = trunc(x / y);
x = x - y * t;
break;
case '\\': /* / */
2016-04-30 19:20:49 +02:00
x = trunc(fabs(x / y));
break;
}
return x;
2007-12-26 17:55:27 +01:00
}
2016-03-05 21:07:56 +01:00
#define nprece 9 /* maximal nb of precedence levels */
2007-12-26 17:55:27 +01:00
static double
2014-08-10 20:21:34 +02:00
formula(dico_t *dico, const char *s, const char *s_end, bool *perror)
2007-12-26 17:55:27 +01:00
{
/* Expression parser.
s is a formula with parentheses and math ops +-* / ...
State machine and an array of accumulators handle operator precedence.
Parentheses handled by recursion.
Empty expression is forbidden: must find at least 1 atom.
Syntax error if no toggle between binoperator && (unop/state1) !
States : 1=atom, 2=binOp, 3=unOp, 4= stop-codon.
Allowed transitions: 1->2->(3,1) and 3->(3,1).
*/
bool error = *perror;
bool negate = 0;
unsigned char state, oldstate, topop, ustack, level, fu;
2013-10-08 18:38:43 +02:00
double u = 0.0;
double accu[nprece + 1];
char oper[nprece + 1];
char uop[nprece + 1];
2013-10-08 18:38:43 +02:00
int i, natom;
bool ok;
SPICE_DSTRING tstr;
2013-10-08 18:38:43 +02:00
const char *s_orig = s;
spice_dstring_init(&tstr);
for (i = 0; i <= nprece; i++) {
accu[i] = 0.0;
oper[i] = ' ';
}
2013-10-08 18:38:43 +02:00
/* trim trailing whitespace */
while ((s_end > s) && (s_end[-1] <= ' '))
s_end--;
state = S_init;
natom = 0;
ustack = 0;
topop = 0;
oldstate = S_init;
fu = 0;
error = 0;
level = 0;
2013-10-08 18:38:43 +02:00
while ((s < s_end) && !error) {
char c = *s;
if (c == '(') {
/* sub-formula or math function */
2013-10-08 18:38:43 +02:00
double v = 1.0, w = 0.0;
/* new: must support multi-arg functions */
2013-10-08 18:38:43 +02:00
const char *kptr = ++s;
const char *arg2 = NULL;
const char *arg3 = NULL;
2013-10-08 18:38:43 +02:00
level = 1;
2016-05-14 12:34:21 +02:00
for (; kptr < s_end; kptr++)
{
char d = *kptr;
if (d == '(')
level++;
else if (d == ')')
level--;
if ((d == ',') && (level == 1)) {
2013-10-08 18:38:43 +02:00
if (arg2 == NULL)
arg2 = kptr;
else
arg3 = kptr; /* kludge for more than 2 args (ternary expression) */
} /* comma list? */
2016-05-14 12:13:29 +02:00
if ((d == ')') && (level <= 0)) {
break;
}
}
2013-10-08 18:38:43 +02:00
// fixme, here level = 0 !!!!! (almost)
if (kptr >= s_end) {
2014-05-18 20:39:17 +02:00
error = message(dico, "Closing \")\" not found.\n");
natom++; /* shut up other error message */
} else {
if (arg2 >= s) {
v = formula(dico, s, arg2, &error);
s = arg2 + 1;
}
if (arg3 >= s) {
w = formula(dico, s, arg3, &error);
s = arg3 + 1;
}
u = formula(dico, s, kptr, &error);
state = S_atom;
if (fu > 0) {
if ((fu == XFU_TERNARY_FCN))
u = ternary_fcn(v, w, u);
else if ((fu == XFU_AGAUSS))
u = agauss(v, w, u);
else if ((fu == XFU_GAUSS))
u = gauss(v, w, u);
else if ((fu == XFU_UNIF))
u = unif(v, u);
else if ((fu == XFU_AUNIF))
u = aunif(v, u);
else if ((fu == XFU_LIMIT))
u = limit(v, u);
else
u = mathfunction(fu, v, u);
}
}
s = kptr + 1;
fu = 0;
} else if (alfa(c)) {
const char *s_next = fetchid(s, s_end);
fu = keyword(fmathS, s, s_next); /* numeric function? */
if (fu > 0) {
state = S_init; /* S_init means: ignore for the moment */
} else {
spice_dstring_reinit(&tstr);
2015-11-07 19:11:54 +01:00
while (s < s_next)
2016-04-30 20:29:13 +02:00
cadd(&tstr, toupper_c(*s++));
u = fetchnumentry(dico, spice_dstring_value(&tstr), &error);
state = S_atom;
2013-10-08 18:38:43 +02:00
}
s = s_next;
} else if (((c == '.') || ((c >= '0') && (c <= '9')))) {
2013-10-08 18:38:43 +02:00
u = fetchnumber(dico, &s, &error);
if (negate) {
u = -1 * u;
negate = 0;
}
state = S_atom;
} else {
2013-10-08 18:38:43 +02:00
c = fetchoperator(dico, s_end, &s, &state, &level, &error);
}
/* may change c to some other operator char! */
/* control chars <' ' ignored */
ok = (oldstate == S_init) || (state == S_init) ||
((oldstate == S_atom) && (state == S_binop)) ||
((oldstate != S_atom) && (state != S_binop));
if (oldstate == S_binop && state == S_binop && c == '-') {
ok = 1;
negate = 1;
continue;
}
2007-12-26 17:55:27 +01:00
if (!ok)
2014-05-18 20:39:17 +02:00
error = message(dico, " Misplaced operator\n");
2007-12-26 17:55:27 +01:00
if (state == S_unop) {
/* push unary operator */
2013-10-08 18:38:43 +02:00
uop[++ustack] = c;
} else if (state == S_atom) {
/* atom pending */
natom++;
2013-10-08 18:38:43 +02:00
if (s >= s_end) {
state = S_stop;
level = topop;
} /* close all ops below */
2013-10-08 18:38:43 +02:00
while (ustack > 0)
u = operate(uop[ustack--], u, u);
accu[0] = u; /* done: all pending unary operators */
}
2007-12-26 17:55:27 +01:00
if ((state == S_binop) || (state == S_stop)) {
/* do pending binaries of priority Upto "level" */
2013-10-08 18:38:43 +02:00
for (i = 1; i <= level; i++) {
if (i < level && oper[i] == ':' && (oper[i+1] == '?' || oper[i+1] == 'x')) {
if (oper[i+1] == 'x') {
/* this is a `first-of-triple' op */
accu[i+1] = accu[i+1];
c = 'x'; /* transform next '?' to 'first-of-triple' */
} else if (accu[i+1] != 0.0) {
/* this is a `true' ternary */
accu[i+1] = accu[i];
c = 'x'; /* transform next '?' to `first-of-triple' */
} else {
/* this is a `false' ternary */
accu[i+1] = accu[i-1];
}
accu[i-1] = 0.0;
oper[i] = ' '; /* reset intermediates */
i++;
accu[i-1] = 0.0;
oper[i] = ' '; /* reset intermediates */
} else {
2013-11-07 21:42:02 +01:00
/* not yet speed optimized! */
accu[i] = operate(oper[i], accu[i], accu[i-1]);
accu[i-1] = 0.0;
oper[i] = ' '; /* reset intermediates */
}
}
oper[level] = c;
if (topop < level)
topop = level;
}
if (state != S_init)
oldstate = state;
}
if ((natom == 0) || (oldstate != S_stop))
2014-05-18 20:39:17 +02:00
error = message(dico, " Expression err: %s\n", s_orig);
2007-12-26 17:55:27 +01:00
if (negate == 1)
error = message(dico,
2014-05-18 20:39:17 +02:00
" Problem with formula eval -- wrongly determined negation!\n");
2007-12-26 17:55:27 +01:00
*perror = error;
2007-12-26 17:55:27 +01:00
spice_dstring_free(&tstr);
2009-03-22 22:03:02 +01:00
if (error)
return 1.0;
else
return accu[topop];
}
/* stupid, produce a string representation of a given double
* to be spliced back into the circuit deck
* we want *exactly* 25 chars, we have
* sign, leading digit, '.', 'e', sign, upto 3 digits exponent
* ==> 8 chars, thus we have 17 left for precision
* don't print a leading '+', something choked
*/
static void
double_to_string(SPICE_DSTRINGPTR qstr_p, double value)
{
char buf[ACT_CHARACTS + 1];
if (snprintf(buf, sizeof(buf), "% 25.17e", value) != ACT_CHARACTS) {
fprintf(stderr, "ERROR: xpressn.c, %s(%d)\n", __FUNCTION__, __LINE__);
controlled_exit(1);
}
scopys(qstr_p, buf);
}
/* expand parameter in string `t' to result `q' */
static bool
evaluate_variable(dico_t *dico, SPICE_DSTRINGPTR qstr_p, const char * const t, const char * const t_end)
2007-12-26 17:55:27 +01:00
{
2014-08-10 20:21:43 +02:00
entry_t *entry;
spice_dstring_reinit(qstr_p);
char *tx = copy_substring(t, t_end);
strtoupper(tx);
entry = entrynb(dico, tx);
tfree(tx);
if (!entry)
return message(dico,
"\"%.*s\" not evaluated. Lookup failure.\n", (int) (t_end - t), t);
if (entry->tp == NUPA_REAL) {
double_to_string(qstr_p, entry->vl);
}
else if (entry->tp == NUPA_STRING) {
/* suppose source text "..." at */
int j = entry->ivl + 1;
for (;;) {
char c = entry->sbbase[j++];
if ((c == '\"') || (c < ' '))
return 0;
cadd(qstr_p, c);
}
}
return 0;
}
/* transform exression in string `t' to result q */
static bool
evaluate_expr(dico_t *dico, SPICE_DSTRINGPTR qstr_p, const char *t, const char * const t_end)
{
bool err = 0;
double u;
spice_dstring_reinit(qstr_p);
u = formula(dico, t, t_end, &err);
if (err)
return err;
double_to_string(qstr_p, u);
return 0;
2007-12-26 17:55:27 +01:00
}
/********* interface functions for spice3f5 extension ***********/
2007-12-26 17:55:27 +01:00
static char *
2016-05-01 17:55:11 +02:00
insertnumber(dico_t *dico, char * const s, SPICE_DSTRINGPTR ustr_p)
/* insert u in string s in place of the next placeholder number */
2007-12-26 17:55:27 +01:00
{
const char *u = spice_dstring_value(ustr_p);
char buf[ACT_CHARACTS+1];
long id = 0;
int n = 0;
2007-12-26 17:55:27 +01:00
2016-05-01 17:55:11 +02:00
char *p = strstr(s, "numparm__________");
2007-12-26 17:55:27 +01:00
if (p &&
(1 == sscanf(p, "numparm__________%8lx%n", &id, &n)) &&
(n == ACT_CHARACTS) &&
(id > 0) && (id < dynsubst + 1) &&
(snprintf(buf, sizeof(buf), "%-25s", u) == ACT_CHARACTS))
2007-12-26 17:55:27 +01:00
{
memcpy(p, buf, ACT_CHARACTS);
return p + ACT_CHARACTS;
2007-12-26 17:55:27 +01:00
}
message
(dico,
"insertnumber: fails.\n"
" s = \"%s\" u=\"%s\" id=%ld\n",
2016-05-01 17:55:11 +02:00
s, u, id);
2007-12-26 17:55:27 +01:00
/* swallow everything on failure */
return s + strlen(s);
2007-12-26 17:55:27 +01:00
}
bool
2017-11-17 19:26:37 +01:00
nupa_substitute(dico_t *dico, const char *s, char *r)
/* s: pointer to original source line.
r: pointer to result line, already heavily modified wrt s
anywhere we find a 10-char numstring in r, substitute it.
bug: wont flag overflow!
*/
2007-12-26 17:55:27 +01:00
{
const char * const s_end = s + strlen(s);
bool err = 0;
SPICE_DSTRING qstr; /* temp result dynamic string */
spice_dstring_init(&qstr);
while (s < s_end) {
2016-05-01 15:53:22 +02:00
char c = *s++;
2016-05-05 12:11:20 +02:00
if (c == '{') {
2011-05-29 12:07:10 +02:00
/* try ps expression syntax */
const char *kptr = s;
int nnest = 1;
2016-05-14 15:46:48 +02:00
for (; *kptr; kptr++) {
2016-05-14 15:40:22 +02:00
char d = *kptr;
2016-05-01 15:53:22 +02:00
if (d == '{')
nnest++;
else if (d == '}')
nnest--;
2016-05-14 15:48:18 +02:00
if (nnest == 0)
break;
}
if (*kptr == '\0') {
2014-05-18 20:39:17 +02:00
err = message(dico, "Closing \"}\" not found.\n");
goto Lend;
}
2017-11-18 21:43:40 +01:00
/* exeption made for .meas */
if (s + 4 == kptr && strncasecmp(s, "LAST", 4) == 0) {
spice_dstring_reinit(&qstr);
sadd(&qstr, "last");
} else {
err = evaluate_expr(dico, &qstr, s, kptr);
if (err) {
err = message(dico, "Cannot compute substitute\n");
goto Lend;
}
2017-11-18 21:43:40 +01:00
}
s = kptr + 1;
2017-11-17 19:26:37 +01:00
r = insertnumber(dico, r, &qstr);
} else if (c == Intro) {
/* skip "&&" which may occur in B source */
if ((s < s_end - 1) && (*s == Intro)) {
2016-05-01 15:46:47 +02:00
s++;
2010-02-28 17:00:40 +01:00
continue;
}
while ((s < s_end - 1) && (*s <= ' '))
2016-05-01 15:46:47 +02:00
s++;
if (*s == '(') {
/* sub-formula */
2016-05-15 19:40:15 +02:00
const char *kptr = s + 1;
int level = 1;
for (; *kptr; kptr++) {
2016-05-14 15:46:48 +02:00
char d = *kptr;
2016-05-01 15:53:22 +02:00
if (d == '(')
level++;
else if (d == ')')
level--;
2016-05-14 15:48:18 +02:00
if ((d == ')') && (level <= 0))
break;
}
if (*kptr == '\0') {
2014-05-18 20:39:17 +02:00
err = message(dico, "Closing \")\" not found.\n");
goto Lend;
}
2016-05-15 19:40:15 +02:00
err = evaluate_expr(dico, &qstr, s + 1, kptr);
if (err) {
message(dico, "Cannot compute &(expression)\n");
goto Lend;
}
s = kptr + 1;
} else {
/* simple identifier may also be string? */
/* fixme, kptr might point behind the terminating '\0' here
* causing serious troubles in evaluate_variable()
* and/or when updating s
*/
const char *kptr = s + 1;
2016-05-14 15:47:28 +02:00
for (; kptr < s_end; kptr++)
if (*kptr <= ' ')
break;
err = evaluate_variable(dico, &qstr, s, kptr);
if (err) {
message(dico, "Cannot compute &identifier\n");
goto Lend;
}
2016-05-14 15:48:18 +02:00
s = kptr;
}
2017-11-17 19:26:37 +01:00
r = insertnumber(dico, r, &qstr);
}
}
Lend:
spice_dstring_free(&qstr);
return err;
2007-12-26 17:55:27 +01:00
}
2017-11-12 21:13:26 +01:00
static const char *
getword(const char *s, SPICE_DSTRINGPTR tstr_p)
2007-12-26 17:55:27 +01:00
{
const char *s_end = s + strlen(s);
2016-05-01 17:36:12 +02:00
while ((s < s_end - 1) && !alfa(*s))
2016-05-06 20:08:06 +02:00
s++;
2007-12-26 17:55:27 +01:00
spice_dstring_reinit(tstr_p);
2007-12-26 17:55:27 +01:00
2016-05-01 17:32:56 +02:00
while ((s < s_end) && (alfa(*s) || isdigit_c(*s)))
cadd(tstr_p, toupper_c(*s++));
2016-05-22 14:52:07 +02:00
return s;
2007-12-26 17:55:27 +01:00
}
static char *
getexpress(nupa_type *type, SPICE_DSTRINGPTR tstr_p, const char *s)
/* returns expression-like string until next separator
Input i=position before expr, output i=just after expr, on separator.
returns tpe=='R' if (numeric, 'S' if (string only
*/
2007-12-26 17:55:27 +01:00
{
2017-11-18 11:54:53 +01:00
const char * const s_end = s + strlen(s);
const char *p;
nupa_type tpe;
2017-11-18 11:54:53 +01:00
while ((s < s_end - 1) && (*s <= ' '))
s++; /*white space ? */
2017-11-18 11:54:53 +01:00
if (*s == '"') { /* string constant */
s++;
p = s;
2017-11-18 11:54:53 +01:00
while ((p < s_end - 1) && (*p != '"'))
2017-11-18 12:07:25 +01:00
p++;
2011-02-13 16:16:48 +01:00
do
2017-11-18 12:07:25 +01:00
p++;
2017-11-18 11:54:53 +01:00
while ((p < s_end) && (*p <= ' '));
2017-11-18 11:54:53 +01:00
tpe = NUPA_STRING;
} else {
2007-12-26 17:55:27 +01:00
if (*s == '{')
s++;
2007-12-26 17:55:27 +01:00
2017-11-18 11:54:53 +01:00
p = s;
2017-11-18 11:54:53 +01:00
for (; p < s_end; p++) {
2017-11-18 11:54:53 +01:00
if (strchr(",;)}", *p)) /* legal separators */
break;
2017-11-18 11:54:53 +01:00
if (*p == '(') {
/* sub-formula */
2017-11-18 12:20:38 +01:00
int level = 1;
2017-11-18 12:28:56 +01:00
p++;
2017-11-18 11:54:53 +01:00
for (; p < s_end; p++) {
2017-11-18 12:21:32 +01:00
char d = *p;
if (d == '(')
level++;
else if (d == ')')
level--;
if (level <= 0)
2016-05-01 15:07:02 +02:00
break;
2017-11-18 12:28:56 +01:00
}
}
2017-11-18 12:34:42 +01:00
}
2007-12-26 17:55:27 +01:00
tpe = NUPA_REAL;
2007-12-26 17:55:27 +01:00
}
pscopy(tstr_p, s, 0, (int) (p - s));
2007-12-26 17:55:27 +01:00
2017-11-18 12:08:48 +01:00
if (*p == '}')
2017-11-18 12:07:25 +01:00
p++;
2007-12-26 17:55:27 +01:00
if (tpe == NUPA_STRING)
2017-11-18 12:07:25 +01:00
p++; /* beyond quote */
2007-12-26 17:55:27 +01:00
if (type)
*type = tpe;
2017-11-18 12:50:09 +01:00
return (char *) p;
2007-12-26 17:55:27 +01:00
}
bool
nupa_assignment(dico_t *dico, const char * const s, char mode)
/* is called for all 'Param' lines of the input file.
is also called for the params: section of a subckt .
mode='N' define new local variable, else global...
bug: we cannot rely on the transformed line, must re-parse everything!
*/
2007-12-26 17:55:27 +01:00
{
/* s has the format: ident = expression; ident= expression ... */
2017-11-18 15:54:18 +01:00
const char * const s_end = s + strlen(s);
2017-11-18 15:27:00 +01:00
const char *p = s;
2017-11-18 15:36:40 +01:00
bool error = 0;
nupa_type dtype;
int wval = 0;
double rval = 0.0;
char *t_p; /* dstring contents value */
2017-11-18 15:36:40 +01:00
SPICE_DSTRING tstr; /* temporary dstring */
SPICE_DSTRING ustr; /* temporary dstring */
spice_dstring_init(&tstr);
spice_dstring_init(&ustr);
2017-11-18 15:33:31 +01:00
while ((p < s_end) && (*p <= ' '))
2017-11-18 15:27:00 +01:00
p++;
2017-11-18 15:28:10 +01:00
if (*p == Intro)
2017-11-18 15:27:00 +01:00
p++;
2017-11-18 15:28:10 +01:00
if (*p == '.') /* skip any dot keyword */
while (*p > ' ')
2017-11-18 15:27:00 +01:00
p++;
2007-12-26 17:55:27 +01:00
while (p < s_end) {
2017-11-18 15:39:22 +01:00
p = getword(p, &tstr);
t_p = spice_dstring_value(&tstr);
if (t_p[0] == '\0') {
2014-05-18 20:39:17 +02:00
error = message(dico, " Identifier expected\n");
break;
}
2007-12-26 17:55:27 +01:00
2017-11-18 15:36:40 +01:00
/* assignment expressions */
2017-11-18 15:39:22 +01:00
while ((p < s_end) && (*p != '='))
2017-11-18 15:36:40 +01:00
p++;
2017-11-18 15:39:22 +01:00
if (p >= s_end) {
2017-11-18 15:36:40 +01:00
error = message(dico, " = sign expected.\n");
break;
}
2017-11-18 15:39:22 +01:00
p = getexpress(&dtype, &ustr, p + 1) + 1;
2017-11-18 15:36:40 +01:00
if (dtype == NUPA_REAL) {
const char *tmp = spice_dstring_value(&ustr);
rval = formula(dico, tmp, tmp + strlen(tmp), &error);
if (error) {
message(dico,
" Formula() error.\n"
" %s\n", s);
break;
}
2017-11-18 15:36:40 +01:00
} else if (dtype == NUPA_STRING) {
wval = (int) (p - s);
}
2017-11-18 15:36:40 +01:00
error = nupa_define(dico, spice_dstring_value(&tstr), mode /* was ' ' */ ,
dtype, rval, wval, NULL);
if (error)
break;
2007-12-26 17:55:27 +01:00
if ((p < s_end) && (p[-1] != ';')) {
2014-05-18 20:39:17 +02:00
error = message(dico, " ; sign expected.\n");
break;
}
2007-12-26 17:55:27 +01:00
}
spice_dstring_free(&tstr);
spice_dstring_free(&ustr);
return error;
2007-12-26 17:55:27 +01:00
}
bool
2017-11-25 15:11:52 +01:00
nupa_subcktcall(dico_t *dico, char *s, char * const x, char * const inst_name)
/* s= a subckt define line, with formal params.
x= a matching subckt call line, with actual params
*/
2007-12-26 17:55:27 +01:00
{
int n, narg = 0;
SPICE_DSTRING subname;
SPICE_DSTRING tstr;
SPICE_DSTRING ustr;
SPICE_DSTRING vstr;
SPICE_DSTRING idlist;
SPICE_DSTRING parsebuf;
bool err = 0;
spice_dstring_init(&subname);
spice_dstring_init(&tstr);
spice_dstring_init(&ustr);
spice_dstring_init(&vstr);
spice_dstring_init(&idlist);
/***** first, analyze the subckt definition line */
n = 0; /* number of parameters if any */
scopy_up(&tstr, s);
const char *j2 = strstr(spice_dstring_value(&tstr), "SUBCKT");
if (j2) {
j2 = j2 + 6; /* fetch its name - skip subckt */
while (*j2 && (*j2 <= ' '))
j2++;
while (*j2 && (*j2 != ' ')) {
cadd(&subname, *j2);
j2++;
}
} else {
2014-05-18 20:39:17 +02:00
err = message(dico, " ! a subckt line!\n");
2007-12-26 17:55:27 +01:00
}
const char *i2 = strstr(spice_dstring_value(&tstr), "PARAMS:");
2007-12-26 17:55:27 +01:00
if (i2) {
const char *optr, *jptr;
pscopy(&tstr, i2 + 7, 0, (int) strlen(i2 + 7));
/* search identifier to the left of '=' assignments */
for (optr = spice_dstring_value(&tstr);
(jptr = strchr(optr, '=')) != NULL;
optr = jptr + 1)
{
const char *kptr, *hptr;
/* skip "==" */
if (jptr[1] == '=') {
jptr++;
continue;
}
/* skip "<=" ">=" "!=" */
if (jptr > optr && strchr("<>!", jptr[-1]))
continue;
2007-12-26 17:55:27 +01:00
kptr = jptr;
while (--kptr >= optr && isspace_c(*kptr))
;
2007-12-26 17:55:27 +01:00
hptr = kptr;
while (hptr >= optr && alfanum(*hptr))
hptr--;
2007-12-26 17:55:27 +01:00
if (hptr < kptr && alfa(hptr[1])) {
while (hptr++ < kptr)
cadd(&idlist, *hptr);
2007-12-26 17:55:27 +01:00
sadd(&idlist, "=$;");
n++;
} else {
2014-05-18 20:39:17 +02:00
message(dico, "identifier expected.\n");
}
}
}
/***** next, analyze the circuit call line */
if (!err) {
/*
skip over instance name -- fixes bug where instance 'x1' is
same name as subckt 'x1'
*/
2017-11-25 15:11:52 +01:00
scopy_up(&tstr, skip_non_ws(x));
int j0 = 0;
2007-12-26 17:55:27 +01:00
char * const t_p = spice_dstring_value(&tstr);
2016-05-16 11:55:06 +02:00
char * const ls_ptr = t_p + spice_dstring_length(&tstr);
spice_dstring_init(&parsebuf);
scopyd(&parsebuf, &tstr);
char * const buf = spice_dstring_value(&parsebuf);
int found = 0, found_j = 0;
char *token = strtok(buf, " "); /* a bit more exact - but not sufficient everytime */
j0 = j0 + (int) strlen(token) + 1;
if (strcmp(token, spice_dstring_value(&subname)))
while ((token = strtok(NULL, " ")) != NULL) {
if (!strcmp(token, spice_dstring_value(&subname))) {
found = 1;
found_j = j0;
}
j0 = j0 + (int) strlen(token) + 1;
}
j0 = found_j; /* last occurence of subname in buf */
spice_dstring_free(&parsebuf);
/* make sure that subname followed by space */
if (found) {
2017-11-25 13:20:12 +01:00
char *jp = t_p + j0 + spice_dstring_length(&subname) + 1; /* 1st position of arglist: jp */
2016-05-16 12:00:28 +02:00
while ((jp < ls_ptr) && ((*jp <= ' ') || (*jp == ',')))
2017-11-25 13:20:12 +01:00
jp++;
2016-05-16 12:00:28 +02:00
while (jp < ls_ptr) {
/* try to fetch valid arguments */
2016-05-16 12:00:28 +02:00
char *kp = jp;
spice_dstring_reinit(&ustr);
2016-05-16 12:00:28 +02:00
if (*kp == Intro) {
/* handle historical syntax... */
2016-05-16 12:00:28 +02:00
if (alfa(kp[1])) {
2016-05-16 11:45:51 +02:00
kp++;
2016-05-16 12:00:28 +02:00
} else if (kp[1] == '(') {
/* transform to braces... */
2016-05-16 11:45:51 +02:00
kp++;
2016-05-16 12:00:28 +02:00
*kp = '{';
char *gp = kp;
int nest = 1;
2016-05-16 12:00:28 +02:00
while ((nest > 0) && (gp < ls_ptr)) {
2017-11-17 21:20:15 +01:00
gp++;
2016-05-16 12:00:28 +02:00
if (*gp == '(')
nest++;
2016-05-16 12:00:28 +02:00
else if (*gp == ')')
nest--;
}
2016-05-16 12:00:28 +02:00
if ((gp < ls_ptr) && (nest == 0))
*gp = '}';
}
}
2016-05-16 12:00:28 +02:00
if (alfanum(*kp) || *kp == '.') {
/* number, identifier */
2016-05-16 12:00:28 +02:00
char *hp = kp;
while (*kp > ' ')
2016-05-16 11:45:51 +02:00
kp++;
2016-05-16 12:15:42 +02:00
pscopy(&ustr, hp, 0, (int) (kp - hp));
2016-05-16 12:00:28 +02:00
jp = kp;
} else if (*kp == '{') {
jp = getexpress(NULL, &ustr, jp);
} else {
2017-11-25 13:20:12 +01:00
jp++;
2016-05-16 12:00:28 +02:00
if (*kp > ' ')
message(dico, "Subckt call, symbol %c not understood\n", *kp);
}
char * const u_p = spice_dstring_value(&ustr);
if (u_p[0]) {
2016-05-16 21:40:50 +02:00
char * const idlist_p = spice_dstring_value(&idlist);
char *dollar = strchr(idlist_p, '$');
2016-04-30 12:20:35 +02:00
if (dollar) {
2016-05-16 21:40:50 +02:00
int kk = (int) (dollar - idlist_p);
/* replace dollar with expression string u */
2016-05-16 21:40:50 +02:00
pscopy(&vstr, idlist_p, 0, kk);
sadd(&vstr, spice_dstring_value(&ustr));
2016-05-16 21:40:50 +02:00
sadd(&vstr, idlist_p + kk + 1);
scopyd(&idlist, &vstr);
}
narg++;
}
}
} else {
2014-05-18 20:39:17 +02:00
message(dico, "Cannot find called subcircuit\n");
}
}
/***** finally, execute the multi-assignment line */
dicostack_push(dico, inst_name); /* create local symbol scope */
if (narg != n) {
err = message(dico,
2014-05-18 20:39:17 +02:00
" Mismatch: %d formal but %d actual params.\n"
"%s\n",
n, narg, spice_dstring_value(&idlist));
/* ;} else { debugwarn(dico, idlist) */
}
err = nupa_assignment(dico, spice_dstring_value(&idlist), 'N');
spice_dstring_free(&subname);
spice_dstring_free(&tstr);
spice_dstring_free(&ustr);
spice_dstring_free(&vstr);
spice_dstring_free(&idlist);
return err;
2007-12-26 17:55:27 +01:00
}
2007-12-26 17:55:27 +01:00
void
2014-08-10 20:21:34 +02:00
nupa_subcktexit(dico_t *dico)
2007-12-26 17:55:27 +01:00
{
dicostack_pop(dico);
2007-12-26 17:55:27 +01:00
}
struct nupa_type { /* used as a type-checked enum */
const char *name;
};
const struct nupa_type S_nupa_real = { "NUPA_REAL" };
const struct nupa_type S_nupa_string = { "NUPA_STRING" };
const struct nupa_type S_nupa_subckt = { "NUPA_SUBCKT" };
const struct nupa_type S_nupa_unknown = { "NUPA_UNKNOWN" };
const struct nupa_type S_nupa_model = { "NUPA_MODEL" };