diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index ccfa6ea64..413cccff5 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -2869,8 +2869,10 @@ static char *inp_spawn_brace(char *s) removes " " quotes, returns lower case letters, replaces non-printable characters with '_', however if non-printable character is the only character in a line, - replace it by '*'. If there is a XSPICE code model .model - line with file input, keep quotes and case for the file path. + replace it by '*'. Leave quotes in .param, .subckt and x + (subcircuit instance) cards to allow string-valued parameters. + If there is a XSPICE code model .model line with file input, + keep quotes and case for the file path. *-------------------------------------------------------------------------*/ void inp_casefix(char *string) @@ -2883,20 +2885,23 @@ void inp_casefix(char *string) return; } if (string) { + bool keepquotes; + #ifdef XSPICE - /* special treatment of code model file input */ char* tmpstr = NULL; - bool keepquotes = ciprefix(".model", string); - if (keepquotes){ + + /* Special treatment of code model file input. */ + + if (ciprefix(".model", string)) tmpstr = strstr(string, "file="); - keepquotes = keepquotes && tmpstr; - } #endif + keepquotes = ciprefix(".param", string); // Allow string params + while (*string) { #ifdef XSPICE /* exclude file name inside of quotes from getting lower case, keep quotes to enable spaces in file path */ - if (keepquotes && string == tmpstr) { + if (string == tmpstr) { string = string + 6; // past first quote while (*string && *string != '"') string++; @@ -2907,12 +2912,13 @@ void inp_casefix(char *string) } #endif if (*string == '"') { - *string++ = ' '; + if (!keepquotes) + *string++ = ' '; while (*string && *string != '"') string++; if (*string == '\0') continue; /* needed if string is "something ! */ - if (*string == '"') + if (*string == '"' && !keepquotes) *string = ' '; } if (*string && !isspace_c(*string) && !isprint_c(*string)) @@ -5054,24 +5060,40 @@ static int inp_split_multi_param_lines(struct card *card, int line_num) char *beg_param, *end_param; - bool get_expression = FALSE; - bool get_paren_expression = FALSE; + int expression_depth = 0; + int paren_depth = 0; beg_param = skip_back_ws(equal_ptr, curr_line); beg_param = skip_back_non_ws(beg_param, curr_line); end_param = skip_ws(equal_ptr + 1); - while (*end_param != '\0' && - (!isspace_c(*end_param) || get_expression || - get_paren_expression)) { - if (*end_param == '{') - get_expression = TRUE; - if (*end_param == '(') - get_paren_expression = TRUE; - if (*end_param == '}') - get_expression = FALSE; - if (*end_param == ')') - get_paren_expression = FALSE; - end_param++; + while (*end_param && !isspace_c(*end_param)) { + /* Advance over numeric or string expression. */ + + if (*end_param == '"') { + /* RHS is quoted string. */ + + end_param++; + while (*end_param != '\0' && *end_param != '"') + end_param++; + if (*end_param == '"') + end_param++; + } else { + while (*end_param != '\0' && *end_param != '"' && + (!isspace_c(*end_param) || + expression_depth || paren_depth)) { + if (*end_param == ',' && paren_depth == 0) + break; + if (*end_param == '{') + ++expression_depth; + if (*end_param == '(') + ++paren_depth; + if (*end_param == '}' && expression_depth > 0) + --expression_depth; + if (*end_param == ')' && paren_depth > 0) + --paren_depth; + end_param++; + } + } } if (end_param[-1] == ',') diff --git a/src/frontend/numparam/general.h b/src/frontend/numparam/general.h index a17c26d9a..a0bf7aeaf 100644 --- a/src/frontend/numparam/general.h +++ b/src/frontend/numparam/general.h @@ -9,6 +9,7 @@ #include "ngspice/bool.h" +void pscat(DSTRINGPTR s, const char *str, const char *stop); void pscopy(DSTRINGPTR s, const char *str, const char *stop); void scopyd(DSTRINGPTR dst, const DSTRINGPTR src); void scopys(DSTRINGPTR a, const char *b); diff --git a/src/frontend/numparam/mystring.c b/src/frontend/numparam/mystring.c index fe58576e1..c16043877 100644 --- a/src/frontend/numparam/mystring.c +++ b/src/frontend/numparam/mystring.c @@ -123,21 +123,26 @@ scopys(DSTRINGPTR s, const char *t) /* returns success flag */ } -/* Copy until stop char (exclusive) or end of string if none given */ +/* Concatentate until stop char (exclusive) or end of string if none given */ + void -pscopy(DSTRINGPTR dstr_p, const char *t, const char *stop) +pscat(DSTRINGPTR dstr_p, const char *t, const char *stop) { if (!stop) { /* locate end of string if no stop char given */ stop = strchr(t, '\0'); } - - ds_clear(dstr_p); if (ds_cat_mem(dstr_p, t, (size_t) (stop - t)) != DS_E_OK) { fprintf(stderr, "Error: DS could not copy partially string %s\n", t); controlled_exit(-1); } +} /* end of function pscat */ - return; +/* Copy until stop char (exclusive) or end of string if none given */ +void +pscopy(DSTRINGPTR dstr_p, const char *t, const char *stop) +{ + ds_clear(dstr_p); + pscat(dstr_p, t, stop); } /* end of function pscopy */ diff --git a/src/frontend/numparam/numpaif.h b/src/frontend/numparam/numpaif.h index 800f4c701..560a70da2 100644 --- a/src/frontend/numparam/numpaif.h +++ b/src/frontend/numparam/numpaif.h @@ -18,9 +18,9 @@ extern int nupa_eval(struct card *card); extern void nupa_signal(int sig); extern void nupa_scan(const struct card *card); extern void nupa_list_params(FILE *cp_out); -extern double nupa_get_param(char *param_name, int *found); +extern double nupa_get_param(const char *param_name, int *found); +extern const char *nupa_get_string_param(const char *param_name); extern void nupa_add_param(char *param_name, double value); -extern void nupa_add_inst_param(char *param_name, double value); extern void nupa_copy_inst_dico(void); extern void nupa_del_dicoS(void); extern int nupa_add_dicoslist(void); diff --git a/src/frontend/numparam/numparam.h b/src/frontend/numparam/numparam.h index e5076a4c2..a294e2a2e 100644 --- a/src/frontend/numparam/numparam.h +++ b/src/frontend/numparam/numparam.h @@ -68,3 +68,4 @@ void nupa_subcktexit(dico_t *); entry_t *entrynb(dico_t *dico, char *s); entry_t *attrib(dico_t *, NGHASHPTR htable, char *t, char op); void del_attrib(void *); +void nupa_copy_inst_entry(char *param_name, entry_t *proto); diff --git a/src/frontend/numparam/spicenum.c b/src/frontend/numparam/spicenum.c index 338e83464..ce1af5153 100644 --- a/src/frontend/numparam/spicenum.c +++ b/src/frontend/numparam/spicenum.c @@ -415,6 +415,9 @@ dump_symbol_table(NGHASHPTR htable_p, FILE *fp) { if (entry->tp == NUPA_REAL) fprintf(fp, " ---> %s = %g\n", entry->symbol, entry->vl); + else if (entry->tp == NUPA_STRING) + fprintf(fp, " ---> %s = \"%s\"\n", + entry->symbol, entry->sbbase); } } @@ -456,8 +459,7 @@ nupa_list_params(FILE *fp) * Otherwise, we have to exhaust all of the tables including the global * table. * ----------------------------------------------------------------- */ -double -nupa_get_param(char *param_name, int *found) +static entry_t *nupa_get_entry(const char *param_name) { dico_t *dico = dicoS; /* local copy for speed */ int depth; /* nested subcircit depth */ @@ -465,21 +467,40 @@ nupa_get_param(char *param_name, int *found) for (depth = dico->stack_depth; depth >= 0; depth--) { NGHASHPTR htable_p = dico->symbols[depth]; if (htable_p) { - entry_t *entry = (entry_t *) nghash_find(htable_p, param_name); - if (entry) { - *found = 1; - return entry->vl; - } + entry_t *entry; + + entry = (entry_t *)nghash_find(htable_p, (void *)param_name); + if (entry) + return entry; } } + return NULL; +} +double +nupa_get_param(const char *param_name, int *found) +{ + entry_t *entry = nupa_get_entry(param_name); + if (entry && entry->tp == NUPA_REAL) { + *found = 1; + return entry->vl; + } *found = 0; return 0; } +const char * +nupa_get_string_param(const char *param_name) +{ + entry_t *entry = nupa_get_entry(param_name); + if (entry && entry->tp == NUPA_STRING) + return entry->sbbase; + return NULL; +} -void -nupa_add_param(char *param_name, double value) + +static void +nupa_copy_entry(entry_t *proto) { dico_t *dico = dicoS; /* local copy for speed */ entry_t *entry; /* current entry */ @@ -491,18 +512,32 @@ nupa_add_param(char *param_name, double value) htable_p = dico->symbols[dico->stack_depth]; - entry = attrib(dico, htable_p, param_name, 'N'); + entry = attrib(dico, htable_p, proto->symbol, 'N'); if (entry) { - entry->vl = value; - entry->tp = NUPA_REAL; - entry->ivl = 0; - entry->sbbase = NULL; + entry->vl = proto->vl; + entry->tp = proto->tp; + entry->ivl = proto->ivl; + entry->sbbase = proto->sbbase; } } void -nupa_add_inst_param(char *param_name, double value) +nupa_add_param(char *param_name, double value) +{ + entry_t entry; + + entry.symbol = param_name; + entry.vl = value; + entry.tp = NUPA_REAL; + entry.ivl = 0; + entry.sbbase = NULL; + nupa_copy_entry(&entry); +} + + +void +nupa_copy_inst_entry(char *param_name, entry_t *proto) { dico_t *dico = dicoS; /* local copy for speed */ entry_t *entry; /* current entry */ @@ -512,10 +547,10 @@ nupa_add_inst_param(char *param_name, double value) entry = attrib(dico, dico->inst_symbols, param_name, 'N'); if (entry) { - entry->vl = value; - entry->tp = NUPA_REAL; - entry->ivl = 0; - entry->sbbase = NULL; + entry->vl = proto->vl; + entry->tp = proto->tp; + entry->ivl = proto->ivl; + entry->sbbase = proto->sbbase; } } @@ -542,7 +577,7 @@ nupa_copy_inst_dico(void) entry; entry = (entry_t *) nghash_enumerateRE(dico->inst_symbols, &iter)) { - nupa_add_param(entry->symbol, entry->vl); + nupa_copy_entry(entry); dico_free_entry(entry); } diff --git a/src/frontend/numparam/xpressn.c b/src/frontend/numparam/xpressn.c index 1f523f66c..fd0749bc3 100644 --- a/src/frontend/numparam/xpressn.c +++ b/src/frontend/numparam/xpressn.c @@ -232,7 +232,6 @@ message(dico_t *dico, const char *fmt, ...) dico->oldline); } } - va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); @@ -275,7 +274,6 @@ dico_free_entry(entry_t *entry) { if (entry->symbol) txfree(entry->symbol); - txfree(entry); } @@ -339,7 +337,7 @@ dicostack_pop(dico_t *dico) fprintf(stderr, "Error: DS could not add string %s\n", inst_name); controlled_exit(-1); } - nupa_add_inst_param(ds_get_buf(¶m_name), entry->vl); + nupa_copy_inst_entry(ds_get_buf(¶m_name), entry); dico_free_entry(entry); } nghash_free(htable_p, NULL, NULL); @@ -440,6 +438,8 @@ del_attrib(void *entry_p) entry_t *entry = (entry_t*) entry_p; if(entry) { tfree(entry->symbol); + if (entry->sbbase) + tfree(entry->sbbase); tfree(entry); } } @@ -1085,6 +1085,79 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror) } +/* Check for a string expression, return end pointer or NULL. + * A string expression is a sequence of quoted strings and string + * variables, optionally enclosed by '{}' with no interventing space. + * If successful return pointer to next char, otherwise NULL. + * Evaluated string is returned in *qstr_p (may be NULL). + */ + +static char *string_expr(dico_t *dico, DSTRINGPTR qstr_p, + const char *t, const char *t_end) +{ + const char *tie; + bool ok = FALSE; + + while (isblank(*t) && t < t_end) + ++t; + if (qstr_p) + ds_clear(qstr_p); + for (; t < t_end; ) { + if (*t == '"') { + /* String constant. */ + + tie = ++t; + while (*t != '"' && t < t_end) + ++t; + if (qstr_p) + pscat(qstr_p, tie, t); + if (*t == '"') + ++t; + ok = TRUE; + continue; + } + if (*t == '{') { + /* Isolate and check wrapped identifier. */ + + tie = ++t; + while (t < t_end) { + if (*t == '}') + break; + ++t; + } + } else { + /* Last option: naked string-valued param. */ + + tie = t; + t = fetchid(t, t_end); + if (t == tie ) + return NULL; + } + /* Now pointers tie, t should bracket an identifier. */ + + { + DS_CREATE(lcl_str, 200); + entry_t *entry; + + /* Formula is a single identifier. */ + + pscopy(&lcl_str, tie, t); + entry = entrynb(dico, ds_get_buf(&lcl_str)); + ds_free(&lcl_str); + if (entry && (entry->tp == NUPA_STRING)) { + if (qstr_p) + pscat(qstr_p, entry->sbbase, NULL); + ok = TRUE; + } else { + return NULL; + } + } + if (*t == '}') + ++t; + } + return ok ? (char *)t : NULL; +} + /* stupid, produce a string representation of a given double * to be spliced back into the circuit deck * we want *exactly* 25 chars, we have @@ -1113,7 +1186,8 @@ evaluate_expr(dico_t *dico, DSTRINGPTR qstr_p, const char *t, const char * const double u; ds_clear(qstr_p); - + if (string_expr(dico, qstr_p, t, t_end)) + return 0; u = formula(dico, t, t_end, &err); if (err) return err; @@ -1247,38 +1321,31 @@ getword(const char *s, DSTRINGPTR tstr_p) static char * -getexpress(nupa_type *type, DSTRINGPTR tstr_p, const char *s) +getexpress(dico_t *dico, nupa_type *type, 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 */ { - const char * const s_end = s + strlen(s); + const char *s_end = s + strlen(s); const char *p; nupa_type tpe; while ((s < s_end - 1) && ((unsigned char)(* s) <= ' ')) s++; /*white space ? */ - if (*s == '"') { /* string constant */ - - s++; - p = s; - - while ((p < s_end - 1) && (*p != '"')) - p++; - - do - p++; - while ((p < s_end) && ((unsigned char)(*p) <= ' ')); + /* Check for injected semicolon separator in assignment list. */ + p = strchr(s, ';'); + if (p) + s_end = p; + p = string_expr(dico, NULL, s, s_end); + if (p) { tpe = NUPA_STRING; - } else { if (*s == '{') s++; - p = s; for (; p < s_end; p++) { @@ -1304,7 +1371,6 @@ getexpress(nupa_type *type, DSTRINGPTR tstr_p, const char *s) } } } - tpe = NUPA_REAL; } @@ -1313,9 +1379,6 @@ getexpress(nupa_type *type, DSTRINGPTR tstr_p, const char *s) if (*p == '}') p++; - if (tpe == NUPA_STRING) - p++; /* beyond quote */ - if (type) *type = tpe; @@ -1334,7 +1397,8 @@ nupa_assignment(dico_t *dico, const char *s, char mode) /* s has the format: ident = expression; ident= expression ... */ const char * const s_end = s + strlen(s); const char *p = s; - + const char *tmp; + char *sval = NULL; bool error = 0; nupa_type dtype; int wval = 0; @@ -1369,23 +1433,26 @@ nupa_assignment(dico_t *dico, const char *s, char mode) break; } - p = getexpress(&dtype, &ustr, p + 1) + 1; + p = getexpress(dico, &dtype, &ustr, p + 1) + 1; + tmp = ds_get_buf(&ustr); if (dtype == NUPA_REAL) { - const char *tmp = ds_get_buf(&ustr); rval = formula(dico, tmp, tmp + strlen(tmp), &error); if (error) { message(dico, " Formula() error.\n" - " %s\n", s); + " |%s| : |%s|=|%s|\n", s, ds_get_buf(&tstr), ds_get_buf(&ustr)); break; } } else if (dtype == NUPA_STRING) { - wval = (int) (p - s); + DS_CREATE(sstr, 200); + string_expr(dico, &sstr, tmp, tmp + strlen(tmp)); + sval = copy(ds_get_buf(&sstr)); + ds_free(&sstr); } error = nupa_define(dico, ds_get_buf(&tstr), mode /* was ' ' */ , - dtype, rval, wval, NULL); + dtype, rval, wval, sval); if (error) break; @@ -1445,9 +1512,10 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, /***** first, analyze the subckt definition line */ n = 0; /* number of parameters if any */ - scopys(&tstr, s); + /* Get the subcircuit name in subname. */ + const char *j2 = strstr(ds_get_buf(&tstr), "subckt"); if (j2) { j2 = skip_ws(j2 + 6); /* skip subckt and whitespace */ @@ -1456,6 +1524,8 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, err = message(dico, " ! a subckt line!\n"); } + /* Scan the .subckt line for assignments, copying templates to idlist. */ + const char *i2 = strstr(ds_get_buf(&tstr), "params:"); if (i2) { @@ -1510,7 +1580,7 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, char * const t_p = ds_get_buf(&tstr); char *jp = NULL; - /* search for the last occurence of `subname' in the given line */ + /* Search for the last occurence of `subname' in the call line. */ for (;;) { char *next_p = search_isolated_identifier(jp ? jp + 1 : t_p, ds_get_buf(&subname)); @@ -1528,7 +1598,6 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, /* jp is pointing to the 1st position of arglist now */ while (*jp) { - /* try to fetch valid arguments */ char *kp = jp; ds_clear(&ustr); @@ -1538,13 +1607,16 @@ nupa_subcktcall(dico_t *dico, const char *s, const char *x, jp = skip_non_ws(kp); pscopy(&ustr, kp, jp); } else if (*kp == '{') { - jp = getexpress(NULL, &ustr, jp); + jp = getexpress(dico, NULL, &ustr, jp); } else { jp++; if ((unsigned char) (*kp) > ' ') message(dico, "Subckt call, symbol %c not understood\n", *kp); } + /* Substitute the parameter for one of the '$' characters + * in idlist. + */ char * const u_p = ds_get_buf(&ustr); if (*u_p) { char * const idlist_p = ds_get_buf(&idlist);