diff --git a/configure.ac b/configure.ac index c3c4a4898..47ecc9827 100644 --- a/configure.ac +++ b/configure.ac @@ -1180,6 +1180,7 @@ AC_CONFIG_FILES([Makefile tests/regression/subckt-processing/Makefile tests/regression/lib-processing/Makefile tests/regression/parser/Makefile + tests/regression/func/Makefile tests/sensitivity/Makefile tests/transient/Makefile tests/transmission/Makefile diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index f6c23cf11..025ee3a6f 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -75,6 +75,7 @@ struct function_env char *macro; char *params[N_PARAMS]; int num_parameters; + const char *accept; } *functions; }; @@ -2888,6 +2889,17 @@ inp_get_func_from_line(struct function_env *env, char *line) temp_buf[str_len++] = '\0'; function->macro = strdup(temp_buf); + + { + int i; + + char *accept = TMALLOC(char, function->num_parameters + 1); + for (i = 0; i < function->num_parameters; i++) + accept[i] = function->params[i][0]; + accept[i] = '\0'; + + function->accept = accept; + } } @@ -2925,115 +2937,100 @@ inp_grab_func(struct function_env *env, struct line *c) static char* -inp_do_macro_param_replace(struct function *fcn, char *params[]) +search_func_arg(char *str, struct function *fcn, int *which, char *str_begin) { - char *param_ptr, *curr_ptr, *new_str, *curr_str = NULL, *search_ptr; - char keep, before, after; - int i; + for (; (str = strpbrk(str, fcn->accept)) != NULL; str++) { + char before; - if (fcn->num_parameters == 0) - return strdup(fcn->macro); + if (str > str_begin) + before = str[-1]; + else + before = '\0'; - for (i = 0; i < fcn->num_parameters; i++) { - - if (curr_str == NULL) { - search_ptr = curr_ptr = strdup(fcn->macro); - } else { - search_ptr = curr_ptr = curr_str; - curr_str = NULL; - } - - while ((param_ptr = strstr(search_ptr, fcn->params[i])) != NULL) { - char *op_ptr = NULL, *cp_ptr = NULL; - int is_vi = 0; - - /* make sure actually have the parameter name */ - if (param_ptr == search_ptr) /* no valid 'before' */ - before = '\0'; - else - before = *(param_ptr-1); - after = param_ptr [ strlen(fcn->params[i]) ]; - if (!(is_arith_char(before) || isspace(before) || - before == ',' || before == '=' || (param_ptr-1) < curr_ptr) || - !(is_arith_char(after) || isspace(after) || - after == ',' || after == '=' || after == '\0')) - { - search_ptr = param_ptr + 1; - continue; - } - - /* exclude v(nn, parameter), v(parameter, nn), v(parameter), - and i(parameter) if here 'parameter' is also a node name */ - if (before != '\0') { - /* go backwards from 'parameter' and find '(' */ - for (op_ptr = param_ptr-1; op_ptr > curr_ptr; op_ptr--) { - if (*op_ptr == ')') { - is_vi = 0; - break; - } - if ((*op_ptr == '(') && (op_ptr - 2 > curr_ptr) && - ((*(op_ptr - 1) == 'v') || (*(op_ptr - 1) == 'i')) && - (is_arith_char(*(op_ptr - 2)) || isspace(*(op_ptr - 2)) || - *(op_ptr - 2) == ',' || *(op_ptr - 2) == '=' )) { - is_vi = 1; - break; + if (is_arith_char(before) || isspace(before) || strchr(",=", before)) { + int i; + for (i = 0; i < fcn->num_parameters; i++) { + size_t len = strlen(fcn->params[i]); + if (strncmp(str, fcn->params[i], len) == 0) { + char after = str[len]; + if (is_arith_char(after) || isspace(after) || strchr(",=", after)) { + *which = i; + return str; } } - /* We have a true v( or i( */ - if (is_vi) { - cp_ptr = param_ptr; - /* go forward and find closing ')' */ - while (*cp_ptr) { - cp_ptr++; - if (*cp_ptr == '(') { - is_vi = 0; - break; - } - if (*cp_ptr == ')') - break; - } - if (*cp_ptr == '\0') - is_vi = 0; - } - /* We have a true v(...) or i(...), - so skip it, and continue searching for new 'parameter' */ - if (is_vi) { - search_ptr = cp_ptr; - continue; - } - } - - keep = *param_ptr; - *param_ptr = '\0'; - - { - size_t curr_str_len = curr_str ? strlen(curr_str) : 0; - size_t len = strlen(curr_ptr) + strlen(params[i]) + 1; - if (str_has_arith_char(params[i])) { - curr_str = TREALLOC(char, curr_str, curr_str_len + len + 2); - sprintf(curr_str + curr_str_len, "%s(%s)", curr_ptr, params[i]); - } else { - curr_str = TREALLOC(char, curr_str, curr_str_len + len); - sprintf(curr_str + curr_str_len, "%s%s", curr_ptr, params[i]); - } - } - - *param_ptr = keep; - search_ptr = curr_ptr = param_ptr + strlen(fcn->params[i]); - } - - if (param_ptr == NULL) { - if (curr_str == NULL) { - curr_str = curr_ptr; - } else { - new_str = tprintf("%s%s", curr_str, curr_ptr); - tfree(curr_str); - curr_str = new_str; } } } - return curr_str; + return NULL; +} + + +static char* +inp_do_macro_param_replace(struct function *fcn, char *params[]) +{ + char *str = strdup(fcn->macro); + int i; + + char *collect_ptr = NULL; + char *arg_ptr = str; + char *rest = str; + + while ((arg_ptr = search_func_arg(arg_ptr, fcn, &i, str)) != NULL) { + char *p; + int is_vi = 0; + + /* exclude v(nn, parameter), v(parameter, nn), v(parameter), + and i(parameter) if here 'parameter' is also a node name */ + + /* go backwards from 'parameter' and find '(' */ + for (p = arg_ptr; --p > str; ) + if (*p == '(' || *p == ')') { + if ((*p == '(') && strchr("vi", p[-1]) && + (p - 2 < str || is_arith_char(p[-2]) || isspace(p[-2]) || strchr(",=", p[-2]))) + is_vi = 1; + break; + } + + /* if we have a true v( or i( */ + if (is_vi) { + /* go forward and find closing ')' */ + for (p = arg_ptr + 1; *p; p++) + if (*p == '(' || *p == ')') + break; + /* We have a true v(...) or i(...), + so skip it, and continue searching for new 'parameter' */ + if (*p == ')') { + arg_ptr = p; + continue; + } + } + + { + size_t collect_ptr_len = collect_ptr ? strlen(collect_ptr) : 0; + size_t len = strlen(rest) + strlen(params[i]) + 1; + int prefix_len = (int) (arg_ptr - rest); + if (str_has_arith_char(params[i])) { + collect_ptr = TREALLOC(char, collect_ptr, collect_ptr_len + len + 2); + sprintf(collect_ptr + collect_ptr_len, "%.*s(%s)", prefix_len, rest, params[i]); + } else { + collect_ptr = TREALLOC(char, collect_ptr, collect_ptr_len + len); + sprintf(collect_ptr + collect_ptr_len, "%.*s%s", prefix_len, rest, params[i]); + } + } + + arg_ptr += strlen(fcn->params[i]); + rest = arg_ptr; + } + + if (collect_ptr) { + char *new_str = tprintf("%s%s", collect_ptr, rest); + tfree(collect_ptr); + tfree(str); + str = new_str; + } + + return str; } diff --git a/src/misc/string.c b/src/misc/string.c index 6035c8d95..44ef01f92 100644 --- a/src/misc/string.c +++ b/src/misc/string.c @@ -647,8 +647,7 @@ isquote( char ch ) bool is_arith_char( char c ) { - if ( c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '<' || - c == '>' || c == '?' || c == '|' || c == '&' || c == '^') + if (c != '\0' && strchr("+-*/()<>?:|&^!%\\", c)) return TRUE; else return FALSE; diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index 9fd1a3d71..41efde2c8 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -1,5 +1,5 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = lib-processing parser subckt-processing +SUBDIRS = lib-processing parser subckt-processing func MAINTAINERCLEANFILES = Makefile.in diff --git a/tests/regression/func/Makefile.am b/tests/regression/func/Makefile.am new file mode 100644 index 000000000..900784d41 --- /dev/null +++ b/tests/regression/func/Makefile.am @@ -0,0 +1,12 @@ +## Process this file with automake to produce Makefile.in + + +TESTS = func-1.cir + +TESTS_ENVIRONMENT = ngspice_vpath=$(srcdir) $(SHELL) $(top_srcdir)/tests/bin/check.sh $(top_builddir)/src/ngspice + +EXTRA_DIST = \ + $(TESTS) \ + $(TESTS:.cir=.out) + +MAINTAINERCLEANFILES = Makefile.in diff --git a/tests/regression/func/func-1.cir b/tests/regression/func/func-1.cir new file mode 100644 index 000000000..25f3982e6 --- /dev/null +++ b/tests/regression/func/func-1.cir @@ -0,0 +1,124 @@ +* 'func-1' check .func processing + +* (exec-spice "ngspice -b %s") +* (tests-aux-renumber) + + +* ---------------------------------------- +* introduce some nodenames and instancenames +* intentionally meant to collide with parameter names + +vncol1 n 0 101.0 +vncol2 p 0 102.0 +vncol3 x 0 103.0 + +vp 1 0 105.0 +rp 1 0 1.0 + +* ---------------------------------------- +* arbitrary tests + +.func foo0() '1013.0' + +v1001_t n1001_t 0 '100*foo0()' +v1001_g n1001_g 0 '101300.0' + +.func bar1(p) 'p' + +v1002_t n1002_t 0 'bar1(117.0)' +v1003_t n1003_t 0 '2*bar1(117.0+2.0)' + +v1002_g n1002_g 0 '117.0' +v1003_g n1003_g 0 '238.0' + +.func bar2(p) 'v(p)+p' + +b1004_t n1004_t 0 v = 'bar2(17.0)' +v1004_g n1004_g 0 '119.0' + +.func bar3(p) 'p+v(p)' + +b1005_t n1005_t 0 v = 'bar3(17.0)' +v1005_g n1005_g 0 '119.0' + +.func bar4(p) 'p+v(p)+p' + +b1006_t n1006_t 0 v = 'bar4(17.0)' +v1006_g n1006_g 0 '136.0' + +.func baz1(n,vp) 'n+i(vp)+vp' + +b1007_t n1007_t 0 v = 'baz1(17.0,10000)' +v1007_g n1007_g 0 '9912' + +.func baz2(p,n) 'v(p)+p+v(n)' + +b1008_t n1008_t 0 v = 'baz2(1000.0,1e20)' +v1008_g n1008_g 0 '1203.0' + +.func baz3(x) 'x+x' + +v1009_t n1009_t 0 '17*baz3(100.0)' +v1009_g n1009_g 0 '3400.0' + +.func moo1(x,p,n) 'x
n?n:x' +v1012_t n1012_t 0 '10*moo2(4,3,5)*100' +v1012_g n1012_g 0 '4000' + +v1013_t n1013_t 0 '10*moo2(2,3,5)*100' +v1013_g n1013_g 0 '3000' + +v1014_t n1014_t 0 '10*moo2(6,3,5)*100' +v1014_g n1014_g 0 '5000' + +* ---------------------------------------- + +.param xoo = 100 +.func fun1(a, xoo) 'a*xoo' + +v1015_t n1015_t 0 'fun1(xoo,2)' +v1015_g n1015_g 0 '200' + +* ---------------------------------------- + +.control + +define mismatch(a,b,err) abs(a-b)>err + +op + +let total_count = 0 +let fail_count = 0 + +let tests = 1001 + vector(15) + +foreach n $&tests + set n_test = "n{$n}_t" + set n_gold = "n{$n}_g" + if mismatch(v($n_test), v($n_gold), 1e-9) + let v_test = v($n_test) + let v_gold = v($n_gold) + echo "ERROR, test failure, v($n_test) = $&v_test but should be $&v_gold" + let fail_count = fail_count + 1 + end + let total_count = total_count + 1 +end + +if fail_count > 0 + echo "ERROR: $&fail_count of $&total_count tests failed" + quit 1 +else + echo "INFO: $&fail_count of $&total_count tests failed" + quit 0 +end + +.endc + +.end diff --git a/tests/regression/func/func-1.out b/tests/regression/func/func-1.out new file mode 100644 index 000000000..4f6a2ad23 --- /dev/null +++ b/tests/regression/func/func-1.out @@ -0,0 +1,9 @@ + +Circuit: * 'xpressn-1' check xpressn.c parser + +Doing analysis at TEMP = 27.000000 and TNOM = 27.000000 + + + +No. of Data Rows : 1 +INFO: 0 of 15 tests failed