Added numparam library (contributed by Georg Post) to add .param to spice
netlists.
This commit is contained in:
parent
cea675e1a2
commit
feedbaf092
|
|
@ -0,0 +1,13 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
noinst_LIBRARIES = libnumparam.a
|
||||
|
||||
libnumparam_a_SOURCES = \
|
||||
spicenum.c \
|
||||
xpressn.c \
|
||||
mystring.c \
|
||||
general.h \
|
||||
numparam.h
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/src/include
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
! downgrad.txt, Use with 'washprog.c'
|
||||
! opcodes: x=exclusion, m=macro, w=wordsubst s= general subst
|
||||
! Macros to keep: Cconst Hi Lo Str Strbig Use ...?
|
||||
|
||||
w Proc void
|
||||
w Begin {
|
||||
w EndProc ;}
|
||||
w Func ""
|
||||
w EndFunc ;}
|
||||
w If "if ("
|
||||
w Then ") {"
|
||||
w Else ";} else {"
|
||||
w ElsIf ";} else if ("
|
||||
w EndIf ;}
|
||||
w While "while ("
|
||||
w Do ") {"
|
||||
w Done ;}
|
||||
w Repeat "do {"
|
||||
w Until ";} while ( !("
|
||||
w EndRep ));
|
||||
w For "for ("
|
||||
w Switch "switch ("
|
||||
w CaseOne ") { case"
|
||||
w Case "; break; } case"
|
||||
w AndCase ":; case"
|
||||
w Is :{
|
||||
w Default "; break;} default: {"
|
||||
w EndSw ";break;} }"
|
||||
|
||||
m Const(1,2) "const short 1 = 2;"
|
||||
|
||||
m Record(1) "typedef struct _t1 {"
|
||||
m RecPtr(1) "typedef struct _t1 *"
|
||||
m EndRec(1) "} 1;"
|
||||
m Addr(1) &1
|
||||
|
||||
w False 0
|
||||
w True 1
|
||||
w Not !
|
||||
w And &&
|
||||
w Or ||
|
||||
w Div /
|
||||
w Mod %
|
||||
|
||||
w Shl <<
|
||||
w Shr >>
|
||||
w AND &
|
||||
w OR |
|
||||
w XOR \^
|
||||
w NOT ~
|
||||
w AT *
|
||||
|
||||
m Inc(1) 1++
|
||||
m Dec(1) 1--
|
||||
w Null NULL
|
||||
w Void void
|
||||
m Table(1) "[1]= {"
|
||||
w EndTab };
|
||||
|
||||
m chr(1) (char)(1)
|
||||
m Zero(1) (!(1))
|
||||
m NotZ(1) (1)
|
||||
|
||||
w Pointer "void *"
|
||||
w Pfile "FILE *"
|
||||
|
||||
w Char "unsigned char"
|
||||
w Byte "unsigned char"
|
||||
w Bool "unsigned char"
|
||||
w Word "unsigned short"
|
||||
w Pchar "char *"
|
||||
|
||||
w Intern static
|
||||
w Extern extern
|
||||
m Tarray(1,2,3) "typedef 2 1[3];"
|
||||
m Tarray\2(1,2,3,4) "typedef 2 1[3][4];"
|
||||
m Darray(1,2,3) "2 1[3];"
|
||||
|
||||
!m Cconst(1,2) "typedef enum {1 = 2} _n1;"
|
||||
!m Str(1,2) "char 2[1+03]={00,00,(char)1}"
|
||||
!m Strbig(1,2) "char 2[1+04]={00, (char)Hi(1), (char)Lo(1)}"
|
||||
|
||||
w Aconst(1,2,3) "2 1[3] ={"
|
||||
w EndAco "};"
|
||||
m Sini(1) "sini(1,sizeof(1)-04)"
|
||||
m New(1) "(1 *)new(sizeof(1))"
|
||||
m Dispose(1) "dispose((void *)1)"
|
||||
m NewArr(1,2) "(1 *)new(sizeof(1)*2)"
|
||||
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
/* general.h */
|
||||
|
||||
/*** Part 1: the C language redefined for quiche eaters ****
|
||||
*
|
||||
* Real Hackers: undo all these macros with the 'washprog.c' utility !
|
||||
*/
|
||||
|
||||
/* Proc ...... Begin .... EndProc */
|
||||
#define Proc void
|
||||
#define Begin {
|
||||
#define EndProc ;}
|
||||
/* Func short ...(...) Begin...EndFunc */
|
||||
#define Func
|
||||
#define EndFunc ;}
|
||||
/* If ... Then...ElsIf..Then...Else...EndIf */
|
||||
#define If if(
|
||||
#define Then ){
|
||||
#define Else ;}else{
|
||||
#define ElsIf ;}else if(
|
||||
#define EndIf ;}
|
||||
/* While...Do...Done */
|
||||
#define While while(
|
||||
#define Do ){
|
||||
#define Done ;}
|
||||
/* Repeat...Until...EndRep */
|
||||
#define Repeat do{
|
||||
#define Until ;}while(!(
|
||||
#define EndRep ));
|
||||
/* For i=1;i<=10; Inc(i) Do...Done */
|
||||
#define For for(
|
||||
/* Switch...CaseOne...Is...Case..Is...Default...EndSw */
|
||||
#define Switch switch(
|
||||
#define CaseOne ){ case
|
||||
#define Case ;break;}case
|
||||
#define AndCase :; case
|
||||
#define Is :{
|
||||
#define Default ;break;}default:{
|
||||
#define EndSw ;break;}}
|
||||
|
||||
#define Record(x) typedef struct _t ## x {
|
||||
#define RecPtr(x) typedef struct _t ## x *
|
||||
#define EndRec(x) } x;
|
||||
#define Addr(x) &x
|
||||
|
||||
#define False 0
|
||||
#define True 1
|
||||
#define Not !
|
||||
#define And &&
|
||||
#define Or ||
|
||||
#define Div /
|
||||
#define Mod %
|
||||
|
||||
#define Shl <<
|
||||
#define Shr >>
|
||||
#define AND &
|
||||
#define OR |
|
||||
#define XOR ^
|
||||
#define NOT ~
|
||||
#define AT *
|
||||
|
||||
#define Inc(p) (p)++
|
||||
#define Dec(p) (p)--
|
||||
|
||||
/* see screened versions below:
|
||||
#define New(t) (t*)malloc(sizeof(t))
|
||||
#define Dispose(p) free((void*)p)
|
||||
*/
|
||||
|
||||
#ifdef NULL
|
||||
#define Null NULL
|
||||
#else
|
||||
#define Null (void *)0L
|
||||
#endif
|
||||
|
||||
#define chr(x) (char)(x)
|
||||
#define Zero(x) (!(x))
|
||||
#define NotZ(x) (x)
|
||||
|
||||
typedef void* Pointer;
|
||||
#define Type(a,b) typedef b a;
|
||||
|
||||
#ifdef _STDIO_H /* somebody pulled stdio */
|
||||
Type(Pfile, FILE AT)
|
||||
#else
|
||||
#ifdef __STDIO_H /* Turbo C */
|
||||
Type(Pfile, FILE AT)
|
||||
#else
|
||||
Type(Pfile, Pointer)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Type(Char, unsigned char)
|
||||
Type(Byte, unsigned char)
|
||||
#ifndef Bool
|
||||
Type(Bool, unsigned char)
|
||||
#endif
|
||||
Type(Word, unsigned short)
|
||||
Type(Pchar, char AT)
|
||||
|
||||
#define Intern static
|
||||
#define Extern extern
|
||||
#define Tarray(a,d,n) typedef d a[n];
|
||||
#define Tarray2(a,d,n,m) typedef d a[n][m];
|
||||
#define Darray(a,d,n) d a[n];
|
||||
|
||||
#define Const(x,y) const short x=y;
|
||||
#define Cconst(x,y) typedef enum {x=y} _n ## x;
|
||||
|
||||
#define Aconst(a,tp,sze) tp a[sze] ={
|
||||
#define EndAco };
|
||||
|
||||
/* the following require the 'mystring' mini-library */
|
||||
|
||||
#define Mcopy(a,b) rawcopy((Pchar)a, (Pchar)b, sizeof(a),sizeof(b))
|
||||
#define Rcopy(a,b) rawcopy((Pchar)(&a), (Pchar)(&b), sizeof(&a),sizeof(&b))
|
||||
#define New(tp) (tp *)new(sizeof(tp))
|
||||
#define Dispose(p) dispose((void *)p)
|
||||
#define NewArr(t,n) (t *)new(sizeof(t)*n)
|
||||
|
||||
|
||||
/*** Part 2: common 'foolproof' string library ******/
|
||||
/*
|
||||
include beforehand the following:
|
||||
#include <stdio.h> // NULL FILE fopen feof fgets fclose fputs fputc gets
|
||||
#include <stdlib.h>
|
||||
the function code is in 'mystring.c' .
|
||||
*/
|
||||
|
||||
#define Use(x) x=0;x=x
|
||||
#define Uses(s) s=s
|
||||
#define Usep(x) x=x
|
||||
#define Hi(x) (((x) Shr 8) AND 0xff)
|
||||
#define Lo(x) ((x) AND 0xff)
|
||||
|
||||
#define Strbig(n,a) char a[n+4]={0, (char)Hi(n), (char)Lo(n)}
|
||||
#define Str(n,a) char a[n+3]={0,0,(char)n} /* n<255 ! */
|
||||
#define Sini(s) sini(s,sizeof(s)-4)
|
||||
|
||||
Cconst(Maxstr,2004) /* was 255, string maxlen, may be up to 32000 or so */
|
||||
|
||||
typedef char string[258];
|
||||
|
||||
Cconst(Esc, 27)
|
||||
Cconst(Tab, 9)
|
||||
Cconst(Bs, 8)
|
||||
Cconst(Lf, 10)
|
||||
Cconst(Cr, 13)
|
||||
|
||||
Proc sini( Pchar s, short i);
|
||||
Proc sfix(Pchar s, short i, short max);
|
||||
Func short maxlen(Pchar s);
|
||||
Func Pchar pscopy( Pchar s, Pchar a, short i,short j);
|
||||
Func Bool scopy( Pchar a, Pchar b);
|
||||
Func Bool ccopy( Pchar a, char c);
|
||||
Func Bool sadd( Pchar s, Pchar t);
|
||||
Func Bool nadd( Pchar s, long n);
|
||||
Func Bool cadd( Pchar s, char c);
|
||||
Func Bool sins( Pchar s, Pchar t);
|
||||
Func Bool cins( Pchar s, char c);
|
||||
Func short cpos( char c, Pchar s);
|
||||
Func short spos( Pchar sub, Pchar s);
|
||||
|
||||
Func short length(Pchar s);
|
||||
Func Bool steq(Pchar s, Pchar t);
|
||||
Func Bool stne(Pchar s, Pchar t);
|
||||
Func short scompare(Pchar a, Pchar b);
|
||||
Func short ord(char c);
|
||||
Func short pred(short i);
|
||||
Func short succ(short i);
|
||||
Proc stri(long n, Pchar s);
|
||||
Proc strif(long n, short f, Pchar s);
|
||||
Proc strf(double x, short a, short b, Pchar s); /* float -> string */
|
||||
Func long ival(Pchar s, short *err);
|
||||
Func double rval(Pchar s, short *err);
|
||||
|
||||
Func char upcase(char c);
|
||||
Func char lowcase(char c);
|
||||
Func short hi(long w);
|
||||
Func short lo(long w);
|
||||
Func Bool odd(long x);
|
||||
Func Bool alfa(char c);
|
||||
Func Bool num(char c);
|
||||
Func Bool alfanum(char c);
|
||||
Func Pchar stupcase( Pchar s);
|
||||
|
||||
/***** primitive input-output ***/
|
||||
Proc wc(char c);
|
||||
Proc wln(void);
|
||||
Proc ws( Pchar s);
|
||||
Proc wi(long i);
|
||||
Proc rs( Pchar s);
|
||||
Func char rc(void);
|
||||
|
||||
Func short freadstr(Pfile f, Pchar s, short max);
|
||||
Func char freadc(Pfile f);
|
||||
Func long freadi(Pfile f);
|
||||
|
||||
Func long round(double d);
|
||||
Func long trunc(double x);
|
||||
Func double sqr(double x);
|
||||
Func double absf(double x); /* abs */
|
||||
Func long absi( long i);
|
||||
Func double frac(double x);
|
||||
|
||||
Func Bool reset(Pfile f);
|
||||
Func Bool rewrite(Pfile f);
|
||||
Proc rawcopy(Pointer a, Pointer b, short la, short lb);
|
||||
Func Pointer new(long sz);
|
||||
Proc dispose(Pointer p);
|
||||
Func Pchar newstring(short n);
|
||||
|
||||
|
|
@ -0,0 +1,881 @@
|
|||
/* mystring.c Copyright (C) 2002 Georg Post
|
||||
*
|
||||
* This file is part of Numparam, see: readme.txt
|
||||
* Free software under the terms of the GNU Lesser General Public License
|
||||
*/
|
||||
|
||||
#ifdef __TURBOC__
|
||||
extern unsigned _stklen= 32000; /* Turbo C default was only 4 K */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/* #include <math.h> -- ceil floor */
|
||||
|
||||
#include "general.h"
|
||||
|
||||
#define Getmax(s,ls) (((Byte)(s[ls+1])) Shl 8) + (Byte)(s[ls+2])
|
||||
|
||||
/***** primitive input-output ***/
|
||||
|
||||
Proc wc(char c)
|
||||
Begin
|
||||
fputc(c, stdout)
|
||||
EndProc
|
||||
|
||||
Proc wln(void)
|
||||
Begin wc('\n') EndProc
|
||||
|
||||
Proc ws( Pchar s)
|
||||
Begin
|
||||
short k=0;
|
||||
While s[k] !=0 Do
|
||||
wc(s[k]); Inc(k)
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Proc wi(long i)
|
||||
Begin
|
||||
Str(16,s);
|
||||
nadd(s,i);
|
||||
ws(s)
|
||||
EndProc
|
||||
|
||||
Proc rs( Pchar s)
|
||||
Begin /*basic line input, limit= 80 chars */
|
||||
short max,i;
|
||||
char c;
|
||||
max=maxlen(s);
|
||||
i=0; sini(s,max);
|
||||
If max>80 Then max=80 EndIf
|
||||
Repeat
|
||||
c=fgetc(stdin);
|
||||
If (i<max) And (c>=' ') Then
|
||||
cadd(s,c); Inc(i)
|
||||
EndIf
|
||||
Until (c==Cr) Or (c=='\n') EndRep
|
||||
/* return i */
|
||||
EndFunc
|
||||
|
||||
Func char rc(void)
|
||||
Begin
|
||||
short ls;
|
||||
Str(80,s);
|
||||
rs(s); ls=length(s);
|
||||
If ls>0 Then
|
||||
return s[ls-1]
|
||||
Else
|
||||
return 0
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
/******* Strings ************
|
||||
* are 0-terminated char arrays with a 2-byte trailer: max length.
|
||||
* the string mini-library is "overflow-safe" under these conditions:
|
||||
* use Str(n,s) macro: define and initialize a string s of maxlen n<255
|
||||
* use sini() to initialize empty strings; sfix() for non-empty ones.
|
||||
* the Sini() macro does automatic sizing, for automatic char arrays
|
||||
* to allocate a string on the heap, use newstring(n).
|
||||
* use maxlen() and length() to retrieve string max and actual length
|
||||
* use: cadd, cins, sadd, sins, scopy, pscopy to manipulate them
|
||||
* never put '\x0' characters inside strings !
|
||||
*
|
||||
* the 'killer idea' is the following:
|
||||
* on string overflow and/or on heap allocation failure, a program
|
||||
* MUST die.
|
||||
*/
|
||||
|
||||
Intern
|
||||
Proc stringbug(Pchar op, Pchar s, Pchar t, char c)
|
||||
/* we brutally stop the program on string overflow */
|
||||
Begin
|
||||
char rep=' ';
|
||||
ws(" STRING overflow ");
|
||||
ws(op); wln();
|
||||
ws(" Operand1: ");
|
||||
ws(s); wln();
|
||||
If t != Null Then
|
||||
ws(" Operand2: ");
|
||||
ws(t); wln();
|
||||
EndIf
|
||||
If c != 0 Then
|
||||
wc('{'); wc(c); wc('}')
|
||||
EndIf
|
||||
ws(" [A]bort [I]gnore ? ");
|
||||
rep=rc();
|
||||
If upcase(rep)=='A' Then exit(1) EndIf
|
||||
EndProc
|
||||
|
||||
Proc sini(Pchar s, short max) /* suppose s is allocated */
|
||||
Begin
|
||||
If max<1 Then
|
||||
max=1
|
||||
ElsIf max>Maxstr Then
|
||||
max=Maxstr
|
||||
EndIf
|
||||
s[0]=0;
|
||||
s[1]= Hi(max); s[2]= Lo(max);
|
||||
EndProc
|
||||
|
||||
Proc sfix(Pchar s, short i, short max)
|
||||
/* suppose s is allocated and filled with non-zero stuff */
|
||||
Begin
|
||||
short j;
|
||||
If max<1 Then
|
||||
max=1
|
||||
ElsIf max>Maxstr Then
|
||||
max=Maxstr
|
||||
EndIf
|
||||
If i>max Then
|
||||
i=max
|
||||
ElsIf i<0 Then
|
||||
i=0
|
||||
EndIf
|
||||
s[i]=0;
|
||||
s[i+1]= Hi(max); s[i+2]= Lo(max);
|
||||
For j=0;j<i; Inc(j) Do /* eliminate null characters ! */
|
||||
If s[j]==0 Then s[j]=1 EndIf
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Intern
|
||||
Proc inistring(Pchar s, char c, short max)
|
||||
/* suppose s is allocated. empty it if c is zero ! */
|
||||
Begin
|
||||
short i=0;
|
||||
s[i]=c;
|
||||
If c!=0 Then
|
||||
Inc(i); s[i]=0
|
||||
EndIf
|
||||
If max<1 Then
|
||||
max=1
|
||||
ElsIf max>Maxstr Then
|
||||
max=Maxstr
|
||||
EndIf
|
||||
s[i+1]= Hi(max); s[i+2]= Lo(max);
|
||||
EndProc
|
||||
|
||||
Func short length(Pchar s)
|
||||
Begin
|
||||
short lg=0;
|
||||
While NotZ(s[lg]) Do Inc(lg) Done
|
||||
return lg
|
||||
EndFunc
|
||||
|
||||
Func short maxlen(Pchar s)
|
||||
Begin
|
||||
short ls= length(s);
|
||||
return Getmax(s,ls)
|
||||
EndFunc
|
||||
|
||||
Func Bool sadd( Pchar s, Pchar t)
|
||||
Begin
|
||||
Bool ok;
|
||||
short i=0, max, ls= length(s);
|
||||
max= Getmax(s,ls);
|
||||
While (t[i] !=0) And (ls<max) Do
|
||||
s[ls]= t[i];
|
||||
Inc(i); Inc(ls);
|
||||
Done
|
||||
s[ls]=0;
|
||||
s[ls+1]= Hi(max); s[ls+2]= Lo(max);
|
||||
ok= (t[i]==0); /* end of t is reached */
|
||||
If Not ok Then
|
||||
stringbug("sadd",s,t,0)
|
||||
EndIf
|
||||
return ok
|
||||
EndProc
|
||||
|
||||
Func Bool cadd( Pchar s, char c)
|
||||
Begin
|
||||
short max, ls= length(s);
|
||||
Bool ok;
|
||||
max= Getmax(s,ls);
|
||||
ok= (ls<max);
|
||||
If ok Then
|
||||
s[ls+3]= s[ls+2]; s[ls+2]=s[ls+1];
|
||||
s[ls+1]=0; s[ls]=c
|
||||
EndIf
|
||||
If Not ok Then
|
||||
stringbug("cadd",s, Null,c)
|
||||
EndIf
|
||||
return ok
|
||||
EndProc
|
||||
|
||||
Func Bool cins( Pchar s, char c)
|
||||
Begin
|
||||
short i, max, ls= length(s);
|
||||
Bool ok;
|
||||
max= Getmax(s,ls);
|
||||
ok= (ls<max);
|
||||
If ok Then
|
||||
For i=ls+2; i>=0; Dec(i) Do s[i+1]=s[i] Done;
|
||||
s[0]=c;
|
||||
EndIf
|
||||
If Not ok Then
|
||||
stringbug("cins",s, Null,c)
|
||||
EndIf
|
||||
return ok
|
||||
EndProc
|
||||
|
||||
Func Bool sins( Pchar s, Pchar t)
|
||||
Begin
|
||||
short i, max, ls= length(s), lt=length(t);
|
||||
Bool ok;
|
||||
max= Getmax(s,ls);
|
||||
ok= ((ls+lt) < max);
|
||||
If ok Then
|
||||
For i=ls+2; i>=0; Dec(i) Do s[i+lt]=s[i] Done;
|
||||
For i=0; i<lt; Inc(i) Do s[i]=t[i] Done;
|
||||
EndIf
|
||||
If Not ok Then
|
||||
stringbug("sins",s, t,0)
|
||||
EndIf
|
||||
return ok
|
||||
EndProc
|
||||
|
||||
Func short cpos(char c, Pchar s)
|
||||
/* return position of c in s, or 0 if not found.
|
||||
* BUG, Pascal inherited: first char is at 1, not 0 !
|
||||
*/
|
||||
Begin
|
||||
short i=0;
|
||||
While (s[i] !=c) And (s[i] !=0) Do Inc(i) Done
|
||||
If s[i]==c Then
|
||||
return (i+1)
|
||||
Else
|
||||
return 0
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func char upcase(char c)
|
||||
Begin
|
||||
If (c>='a')And(c<='z') Then
|
||||
return c+'A'-'a'
|
||||
Else
|
||||
return c
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func Bool scopy(Pchar s, Pchar t) /* returns success flag */
|
||||
Begin
|
||||
Bool ok;
|
||||
short i,max, ls= length(s);
|
||||
max= Getmax(s,ls);
|
||||
i=0;
|
||||
While (t[i] !=0) And (i<max) Do
|
||||
s[i]= t[i]; Inc(i);
|
||||
Done
|
||||
s[i]=0;
|
||||
s[i+1]= Hi(max); s[i+2]= Lo(max);
|
||||
ok= (t[i]==0); /* end of t is reached */
|
||||
If Not ok Then
|
||||
stringbug("scopy",s, t,0)
|
||||
EndIf
|
||||
return ok
|
||||
EndProc
|
||||
|
||||
Func Bool ccopy(Pchar s, char c) /* returns success flag */
|
||||
Begin
|
||||
short max, ls= length(s);
|
||||
Bool ok=False;
|
||||
max= Getmax(s,ls);
|
||||
If max>0 Then
|
||||
s[0]=c; sfix(s,1,max);
|
||||
ok=True
|
||||
EndIf
|
||||
If Not ok Then
|
||||
stringbug("ccopy",s, Null,c)
|
||||
EndIf
|
||||
return ok
|
||||
EndProc
|
||||
|
||||
Func Pchar pscopy(Pchar s, Pchar t, short start, short leng)
|
||||
/* partial string copy, with Turbo Pascal convention for "start" */
|
||||
/* BUG: position count starts at 1, not 0 ! */
|
||||
Begin
|
||||
short max= maxlen(s); /* keep it for later */
|
||||
short stop= length(t);
|
||||
short i;
|
||||
Bool ok= (max>=0) And (max<=Maxstr);
|
||||
If Not ok Then
|
||||
stringbug("copy target non-init", s, t, 0)
|
||||
EndIf
|
||||
If leng>max Then
|
||||
leng=max; ok=False
|
||||
EndIf
|
||||
If start>stop Then /* nothing! */
|
||||
ok=False;
|
||||
inistring(s,0,max)
|
||||
Else
|
||||
If (start+leng-1)>stop Then
|
||||
leng = stop-start+1;
|
||||
ok=False
|
||||
EndIf
|
||||
For i=0; i<leng; Inc(i) Do s[i]= t[start+i -1] Done
|
||||
i=leng; s[i]=0;
|
||||
s[i+1]= Hi(max); s[i+2]= Lo(max);
|
||||
EndIf
|
||||
/* If Not ok Then stringbug("copy",s, t, 0) EndIf */
|
||||
/* If ok Then return s Else return Null EndIf */
|
||||
ok=ok;
|
||||
return s
|
||||
EndProc
|
||||
|
||||
Func short ord(char c)
|
||||
Begin
|
||||
return c AND 0xff
|
||||
EndFunc /* strip high byte */
|
||||
|
||||
Func short pred(short i)
|
||||
Begin
|
||||
return (--i)
|
||||
EndFunc
|
||||
|
||||
Func short succ(short i)
|
||||
Begin
|
||||
return (++i)
|
||||
EndFunc
|
||||
|
||||
Func Bool nadd( Pchar s, long n)
|
||||
/* append a decimal integer to a string */
|
||||
Begin
|
||||
short d[25];
|
||||
short j,k,ls,len;
|
||||
char sg; /* the sign */
|
||||
Bool ok;
|
||||
k=0;
|
||||
len=maxlen(s);
|
||||
If n<0 Then
|
||||
n= -n; sg='-'
|
||||
Else
|
||||
sg='+'
|
||||
EndIf
|
||||
While n>0 Do
|
||||
d[k]=n Mod 10; Inc(k);
|
||||
n= n Div 10
|
||||
Done
|
||||
If k==0 Then
|
||||
ok=cadd(s,'0')
|
||||
Else
|
||||
ls=length(s);
|
||||
ok= (len-ls)>k;
|
||||
If ok Then
|
||||
If sg=='-' Then
|
||||
s[ls]=sg; Inc(ls)
|
||||
EndIf
|
||||
For j=k-1; j>=0; Dec(j) Do
|
||||
s[ls]=d[j]+'0'; Inc(ls)
|
||||
Done
|
||||
sfix(s,ls,len);
|
||||
EndIf
|
||||
EndIf
|
||||
If Not ok Then
|
||||
stringbug("nadd",s, Null,sg)
|
||||
EndIf
|
||||
return ok
|
||||
EndProc
|
||||
|
||||
Proc stri( long n, Pchar s)
|
||||
/* convert integer to string */
|
||||
Begin
|
||||
sini(s, maxlen(s));
|
||||
nadd(s,n)
|
||||
EndProc
|
||||
|
||||
Proc rawcopy(Pointer a, Pointer b, short la, short lb)
|
||||
/* dirty binary copy */
|
||||
Begin
|
||||
short j,n;
|
||||
If lb<la Then
|
||||
n=lb
|
||||
Else
|
||||
n=la
|
||||
EndIf
|
||||
For j=0; j<n; Inc(j) Do
|
||||
((Pchar)a)[j]=((Pchar)b)[j]
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Func short scompare(Pchar a, Pchar b)
|
||||
Begin
|
||||
Word j=0;
|
||||
short k=0;
|
||||
While (a[j]==b[j]) And (a[j]!=0) And (b[j]!=0) Do Inc(j) Done;
|
||||
If a[j]<b[j] Then
|
||||
k= -1
|
||||
ElsIf a[j]>b[j] Then
|
||||
k=1
|
||||
EndIf
|
||||
return k
|
||||
EndFunc
|
||||
|
||||
Func Bool steq(Pchar a, Pchar b) /* string a==b test */
|
||||
Begin
|
||||
Word j=0;
|
||||
While (a[j]==b[j]) And (a[j]!=0) And (b[j]!=0) Do Inc(j) Done;
|
||||
return ((a[j]==0) And (b[j]==0)) /* string equality test */
|
||||
EndFunc
|
||||
|
||||
Func Bool stne(Pchar s, Pchar t)
|
||||
Begin
|
||||
return scompare(s,t) !=0
|
||||
EndFunc
|
||||
|
||||
Func short hi(long w)
|
||||
Begin
|
||||
return (w AND 0xff00) Shr 8
|
||||
EndFunc
|
||||
|
||||
Func short lo(long w)
|
||||
Begin
|
||||
return (w AND 0xff)
|
||||
EndFunc
|
||||
|
||||
Func char lowcase(char c)
|
||||
Begin
|
||||
If (c>='A')And(c<='Z') Then
|
||||
return (char)(c-'A' +'a')
|
||||
Else
|
||||
return c
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func Bool alfa( char c)
|
||||
Begin
|
||||
return ((c>='a') And (c<='z')) Or ((c>='A') And (c<='Z'));
|
||||
EndFunc
|
||||
|
||||
Func Bool num( char c)
|
||||
Begin
|
||||
return (c>='0') And (c<='9');
|
||||
EndFunc
|
||||
|
||||
Func Bool alfanum(char c)
|
||||
Begin
|
||||
return
|
||||
((c>='a') And (c<='z')) Or ((c>='A')And(c<='Z'))
|
||||
Or ((c>='0')And(c<='9'))
|
||||
Or (c=='_')
|
||||
EndFunc
|
||||
|
||||
Func short freadstr(Pfile f, Pchar s, short max)
|
||||
/* read a line from a file.
|
||||
BUG: long lines truncated without warning, ctrl chars are dumped.
|
||||
*/
|
||||
Begin
|
||||
char c;
|
||||
short i=0, mxlen=maxlen(s);
|
||||
If mxlen<max Then max=mxlen EndIf
|
||||
Repeat
|
||||
c=fgetc(f); /* tab is the only control char accepted */
|
||||
If ((c>=' ') Or (c<0) Or (c==Tab)) And (i<max) Then
|
||||
s[i]=c; Inc(i)
|
||||
EndIf
|
||||
Until feof(f) Or (c=='\n') EndRep
|
||||
s[i]=0;
|
||||
s[i+1]= Hi(mxlen); s[i+2]= Lo(mxlen);
|
||||
return i
|
||||
EndProc
|
||||
|
||||
Func char freadc(Pfile f)
|
||||
Begin
|
||||
return fgetc(f)
|
||||
EndFunc
|
||||
|
||||
Func long freadi(Pfile f)
|
||||
/* reads next integer, but returns 0 if none found. */
|
||||
Begin
|
||||
long z=0;
|
||||
Bool minus=False;
|
||||
char c;
|
||||
Repeat c=fgetc(f)
|
||||
Until feof(f) Or Not ((c>0) And (c<=' ')) EndRep /* skip space */
|
||||
If c=='-' Then
|
||||
minus=True; c=fgetc(f)
|
||||
EndIf
|
||||
While num(c) Do
|
||||
z= 10*z + c-'0'; c=fgetc(f)
|
||||
Done
|
||||
ungetc(c,f) ; /* re-push character lookahead */
|
||||
If minus Then z= -z EndIf;
|
||||
return z
|
||||
EndFunc
|
||||
|
||||
Func Pchar stupcase( Pchar s)
|
||||
Begin
|
||||
short i=0;
|
||||
While s[i] !=0 Do
|
||||
s[i]= upcase(s[i]); Inc(i)
|
||||
Done
|
||||
return s
|
||||
EndFunc
|
||||
|
||||
/***** pointer tricks: app won't use naked malloc(), free() ****/
|
||||
|
||||
Proc dispose(Pointer p)
|
||||
Begin
|
||||
If p != Null Then free(p) EndIf
|
||||
EndProc
|
||||
|
||||
Func Pointer new(long sz)
|
||||
Begin
|
||||
Pointer p;
|
||||
If sz<=0 Then
|
||||
return Null
|
||||
Else
|
||||
#ifdef __TURBOC__
|
||||
/* truncate to 64 K ! */
|
||||
If sz> 0xffff Then sz= 0xffff EndIf
|
||||
p= malloc((Word)sz);
|
||||
#else
|
||||
p= malloc(sz);
|
||||
#endif
|
||||
If p==Null Then /* fatal error */
|
||||
ws(" new() failure. Program halted.\n");
|
||||
exit(1);
|
||||
EndIf
|
||||
return p
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func Pchar newstring(short n)
|
||||
Begin
|
||||
Pchar s= (Pchar)new(n+4);
|
||||
sini(s, n);
|
||||
return s
|
||||
EndFunc
|
||||
|
||||
/***** elementary math *******/
|
||||
|
||||
Func double sqr(double x)
|
||||
Begin
|
||||
return x*x
|
||||
EndFunc
|
||||
|
||||
Func double absf(double x)
|
||||
Begin
|
||||
If x<0.0 Then
|
||||
return -x
|
||||
Else
|
||||
return x
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func long absi(long i)
|
||||
Begin
|
||||
If i>=0 Then
|
||||
return(i)
|
||||
Else
|
||||
return(-i)
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Proc strif(long i, short f, Pchar s)
|
||||
/* formatting like str(i:f,s) in Turbo Pascal */
|
||||
Begin
|
||||
short j,k,n,max;
|
||||
char cs;
|
||||
char t[32];
|
||||
k=0;
|
||||
max=maxlen(s);
|
||||
If i<0 Then
|
||||
i= -i; cs='-'
|
||||
Else
|
||||
cs=' '
|
||||
EndIf;
|
||||
While i>0 Do
|
||||
j=(short)(i Mod 10);
|
||||
i=(long)(i Div 10);
|
||||
t[k]=chr('0'+j); Inc(k)
|
||||
Done
|
||||
If k==0 Then
|
||||
t[k]='0'; Inc(k)
|
||||
EndIf
|
||||
If cs=='-' Then
|
||||
t[k]=cs
|
||||
Else
|
||||
Dec(k)
|
||||
EndIf;
|
||||
/* now the string is in 0...k in reverse order */
|
||||
For j=1; j<=k; Inc(j) Do t[k+j]=t[k-j] Done /* mirror image */
|
||||
t[2*k+1]=0; /* null termination */
|
||||
n=0;
|
||||
If (f>k) And (f<40) Then /* reasonable format */
|
||||
For j=k+2; j<=f; Inc(j) Do
|
||||
s[n]=' '; Inc(n)
|
||||
Done
|
||||
EndIf
|
||||
For j=0; j<=k+1; Inc(j) Do s[n+j]=t[k+j] Done; /* shift t down */
|
||||
k=length(s);
|
||||
sfix(s,k,max);
|
||||
EndProc
|
||||
|
||||
Func Bool odd(long x)
|
||||
Begin
|
||||
return NotZ(x AND 1)
|
||||
EndFunc
|
||||
|
||||
Func short vali(Pchar s, long * i)
|
||||
/* convert s to integer i. returns error code 0 if Ok */
|
||||
/* BUG: almost identical to ival() with arg/return value swapped ... */
|
||||
Begin
|
||||
short k=0, digit=0, ls;
|
||||
long z=0;
|
||||
Bool minus=False, ok=True;
|
||||
char c;
|
||||
ls=length(s);
|
||||
Repeat
|
||||
c=s[k]; Inc(k)
|
||||
Until (k>=ls) Or Not ((c>0) And (c<=' ')) EndRep /* skip space */
|
||||
If c=='-' Then
|
||||
minus=True;
|
||||
c=s[k]; Inc(k)
|
||||
EndIf
|
||||
While num(c) Do
|
||||
z= 10*z + c-'0';
|
||||
c=s[k]; Inc(k);
|
||||
Inc(digit)
|
||||
Done
|
||||
If minus Then z= -z EndIf;
|
||||
*i= z;
|
||||
ok= (digit>0) And (c==0); /* successful end of string */
|
||||
If ok Then
|
||||
return 0
|
||||
Else
|
||||
return k /* one beyond error position */
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Intern
|
||||
Func Bool match
|
||||
(Pchar s, Pchar t, short n, short tstart, Bool testcase)
|
||||
Begin
|
||||
/* returns 0 If tstart is out of range. But n may be 0 ? */
|
||||
/* True if s matches t[tstart...tstart+n] */
|
||||
short i,j,lt;
|
||||
Bool ok;
|
||||
char a,b;
|
||||
i=0; j=tstart;
|
||||
lt= length(t);
|
||||
ok=(tstart<lt);
|
||||
While ok And (i<n) Do
|
||||
a=s[i]; b=t[j];
|
||||
If Not testcase Then
|
||||
a=upcase(a); b=upcase(b)
|
||||
EndIf
|
||||
ok= (j<lt) And (a==b);
|
||||
Inc(i); Inc(j);
|
||||
Done
|
||||
return ok
|
||||
EndFunc
|
||||
|
||||
Intern
|
||||
Func short posi(Pchar sub, Pchar s, short opt)
|
||||
/* find position of substring in s */
|
||||
Begin
|
||||
/* opt=0: like Turbo Pascal */
|
||||
/* opt=1: like Turbo Pascal Pos, but case insensitive */
|
||||
/* opt=2: position in space separated wordlist for scanners */
|
||||
short a,b,k,j;
|
||||
Bool ok, tstcase;
|
||||
Str(250,t);
|
||||
ok=False;
|
||||
tstcase=( opt==0);
|
||||
If opt<=1 Then
|
||||
scopy(t,sub)
|
||||
Else
|
||||
cadd(t,' '); sadd(t,sub); cadd(t,' ');
|
||||
EndIf
|
||||
a= length(t);
|
||||
b= (short)(length(s)-a);
|
||||
k=0; j=1;
|
||||
If a>0 Then /*Else return 0*/
|
||||
While (k<=b) And (Not ok) Do
|
||||
ok=match(t,s, a,k, tstcase); /* we must start at k=0 ! */
|
||||
Inc(k);
|
||||
If s[k]==' ' Then Inc(j) EndIf /* word counter */
|
||||
Done
|
||||
EndIf
|
||||
If opt==2 Then k=j EndIf
|
||||
If ok Then
|
||||
return k
|
||||
Else
|
||||
return 0
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func short spos(Pchar sub, Pchar s)
|
||||
/* equivalent to Turbo Pascal pos().
|
||||
BUG: counts 1 ... length(s), not from 0 like C
|
||||
*/
|
||||
Begin
|
||||
return posi( sub, s, 0)
|
||||
EndFunc
|
||||
|
||||
/**** float formatting with printf/scanf ******/
|
||||
|
||||
Func short valr(Pchar s, double *r)
|
||||
/* returns 0 if ok, else length of partial string ? */
|
||||
Begin
|
||||
short n=sscanf(s, "%lG", r);
|
||||
If n==1 Then
|
||||
return(0)
|
||||
Else
|
||||
return(1)
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Proc strf( double x, short f1, short f2, Pchar t)
|
||||
/* e-format if f2<0, else f2 digits after the point, total width=f1 */
|
||||
/* if f1=0, also e-format with f2 digits */
|
||||
Begin /*default f1=17, f2=-1*/
|
||||
Str(30,fmt);
|
||||
short n,mlt;
|
||||
mlt=maxlen(t);
|
||||
cadd(fmt,'%');
|
||||
If f1>0 Then
|
||||
nadd(fmt , f1); /* f1 is the total width */
|
||||
If f2<0 Then
|
||||
sadd(fmt,"lE") /* exponent format */
|
||||
Else
|
||||
cadd(fmt,'.');
|
||||
nadd(fmt,f2);
|
||||
sadd(fmt,"lf")
|
||||
EndIf
|
||||
Else
|
||||
cadd(fmt,'.');
|
||||
nadd(fmt, absi(f2-6)); /* note the 6 surplus positions */
|
||||
cadd(fmt,'e');
|
||||
EndIf
|
||||
n=sprintf(t, fmt, x);
|
||||
sfix(t,n, mlt);
|
||||
EndProc
|
||||
|
||||
Func double rval(Pchar s, short *err)
|
||||
/* returns err=0 if ok, else length of partial string ? */
|
||||
Begin
|
||||
double r= 0.0;
|
||||
short n=sscanf(s, "%lG", &r);
|
||||
If n==1 Then
|
||||
(*err)=0
|
||||
Else
|
||||
(*err)=1
|
||||
EndIf
|
||||
return r;
|
||||
EndFunc
|
||||
|
||||
Func long ival(Pchar s, short *err)
|
||||
/* value of s as integer string. error code err= 0 if Ok */
|
||||
Begin
|
||||
short k=0, digit=0, ls;
|
||||
long z=0;
|
||||
Bool minus=False, ok=True;
|
||||
char c;
|
||||
ls=length(s);
|
||||
Repeat
|
||||
c=s[k]; Inc(k)
|
||||
Until (k>=ls) Or Not ((c>0) And (c<=' ')) EndRep /* skip space */
|
||||
If c=='-' Then
|
||||
minus=True;
|
||||
c=s[k]; Inc(k)
|
||||
EndIf
|
||||
While num(c) Do
|
||||
z= 10*z + c-'0';
|
||||
c=s[k]; Inc(k);
|
||||
Inc(digit)
|
||||
Done
|
||||
If minus Then z= -z EndIf;
|
||||
ok= (digit>0) And (c==0); /* successful end of string */
|
||||
If ok Then
|
||||
(*err)= 0
|
||||
Else
|
||||
(*err)= k /* one beyond error position */
|
||||
EndIf
|
||||
return z
|
||||
EndFunc
|
||||
|
||||
#ifndef _MATH_H
|
||||
|
||||
Func long round(double x)
|
||||
/* using <math.h>, it would be simpler: floor(x+0.5) */
|
||||
Begin
|
||||
double u;
|
||||
long z;
|
||||
short n;
|
||||
Str(40,s);
|
||||
u=2e9;
|
||||
If x>u Then
|
||||
x=u
|
||||
ElsIf x< -u Then
|
||||
x= -u
|
||||
EndIf
|
||||
n=sprintf(s,"%-12.0f", x);
|
||||
s[n]=0;
|
||||
sscanf(s,"%ld", Addr(z));
|
||||
return z
|
||||
EndFunc
|
||||
|
||||
Func long trunc(double x)
|
||||
Begin
|
||||
long n=round(x);
|
||||
If (n>x) And (x>=0.0) Then
|
||||
Dec(n)
|
||||
ElsIf (n<x) And (x<0.0) Then
|
||||
Inc(n)
|
||||
EndIf
|
||||
return n
|
||||
EndFunc
|
||||
|
||||
Func double frac(double x)
|
||||
Begin
|
||||
return x- trunc(x)
|
||||
EndFunc
|
||||
|
||||
Func double intp(double x)
|
||||
Begin
|
||||
double u=2e9;
|
||||
If (x>u) Or (x< -u) Then
|
||||
return x
|
||||
Else
|
||||
return trunc(x)
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
#else /* use floor() and ceil() */
|
||||
|
||||
Func long round(double r)
|
||||
Begin
|
||||
return (long)floor(r+0.5)
|
||||
EndFunc
|
||||
|
||||
Func long trunc(double r)
|
||||
Begin
|
||||
If r>=0.0 Then
|
||||
return (long)floor(r)
|
||||
Else
|
||||
return (long)ceil(r)
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func double frac(double x)
|
||||
Begin
|
||||
If x>=0.0 Then
|
||||
return(x - floor(x))
|
||||
Else
|
||||
return(x - ceil(x))
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func double intp(double x) /* integral part */
|
||||
Begin
|
||||
If x>=0.0 Then
|
||||
return floor(x)
|
||||
Else
|
||||
return ceil(x)
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
#endif /* _MATH_H */
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
|
||||
# ngconfig.sh
|
||||
# configure options for ngspice with numparam add-on
|
||||
# run this in ngspice's top-level directory
|
||||
|
||||
# specify your Numparam directory
|
||||
HACK=/home/post/spice3f5/hack
|
||||
|
||||
# over-write the original subckt.c
|
||||
cp -biv $HACK/ngsubckt.c src/frontend/subckt.c
|
||||
|
||||
# my box needs CFLAGS on 1st run, else 'terminal.c' wont find 'termcap.h' ?
|
||||
|
||||
CFLAGS=-I/usr/include/ncurses \
|
||||
LIBS=$HACK/libnupa.a \
|
||||
./configure --without-x --prefix=/usr/local/ngsp
|
||||
|
||||
#### end of sample script ####
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/* numparam.h */
|
||||
|
||||
/*** interface to spice frontend subckt.c ***/
|
||||
|
||||
#define NUPADECKCOPY 0
|
||||
#define NUPASUBSTART 1
|
||||
#define NUPASUBDONE 2
|
||||
#define NUPAEVALDONE 3
|
||||
|
||||
extern char * nupa_copy(char *s, int linenum);
|
||||
extern int nupa_eval(char *s, int linenum);
|
||||
extern int nupa_signal(int sig, char *info);
|
||||
|
||||
/***** numparam internals ********/
|
||||
|
||||
#define ln(x) log(x)
|
||||
#define trunc(x) floor(x)
|
||||
|
||||
Cconst(Nul, 0)
|
||||
Cconst(Nodekey,'#') /*introduces node symbol*/
|
||||
Cconst(Intro ,'&') /*introduces preprocessor tokens*/
|
||||
Cconst(Comment,'*') /*Spice Comment lines*/
|
||||
Cconst(Pspice,'{') /*Pspice expression */
|
||||
Cconst(Maxdico,200) /*size of symbol table*/
|
||||
|
||||
Cconst(Llen,250) /* maximum composite input line length */
|
||||
typedef char str20 [24];
|
||||
typedef char str80 [84];
|
||||
|
||||
Cconst(Maxline, 1000) /* size of initial unexpanded circuit code */
|
||||
Cconst(Maxckt, 5000) /* size of expanded circuit code */
|
||||
|
||||
|
||||
typedef Pchar auxtable; /* dummy */
|
||||
|
||||
Record(entry)
|
||||
char tp; /* type: I)nt R)eal S)tring F)unction M)acro P)ointer */
|
||||
str20 nom;
|
||||
short level; /* subckt nesting level */
|
||||
double vl; /* float value if defined */
|
||||
Word ivl; /*int value or string buffer index*/
|
||||
Pchar sbbase; /* string buffer base address if any */
|
||||
EndRec(entry)
|
||||
|
||||
Record(fumas) /*funtion,macro,string*/
|
||||
Word start /*,stop*/ ; /*buffer index or location */
|
||||
EndRec(fumas)
|
||||
|
||||
Record(tdico)
|
||||
/* the input scanner data structure */
|
||||
str80 srcfile; /* last piece of source file name */
|
||||
short srcline;
|
||||
short errcount;
|
||||
entry dat[Maxdico+1];
|
||||
short nbd; /* number of data entries */
|
||||
fumas fms[101];
|
||||
short nfms; /* number of functions & macros */
|
||||
short stack[20];
|
||||
short tos; /* top of stack index for symbol mark/release mechanics */
|
||||
str20 option; /* one-character translator options */
|
||||
auxtable nodetab;
|
||||
Darray(refptr, Pchar, Maxline) /* pointers to source code lines */
|
||||
Darray(category, char, Maxline) /* category of each line */
|
||||
EndRec(tdico)
|
||||
|
||||
Proc initdico(tdico * dico);
|
||||
Func short donedico(tdico * dico);
|
||||
Func Bool defsubckt( tdico *dico, Pchar s, Word w, char categ);
|
||||
Func short findsubckt( tdico *dico, Pchar s, Pchar subname);
|
||||
Func Bool nupa_substitute( tdico *dico, Pchar s, Pchar r, Bool err);
|
||||
Func Bool nupa_assignment( tdico *dico, Pchar s, char mode);
|
||||
Func Bool nupa_subcktcall( tdico *dico, Pchar s, Pchar x, Bool err);
|
||||
Proc nupa_subcktexit( tdico *dico);
|
||||
Func tdico * nupa_fetchinstance(void);
|
||||
Func char getidtype( tdico *d, Pchar s);
|
||||
|
||||
|
|
@ -0,0 +1,453 @@
|
|||
/* nupatest.c Copyright (C) 2002 Georg Post
|
||||
*
|
||||
* This file is part of Numparam, see: readme.txt
|
||||
* Free software under the terms of the GNU Lesser General Public License
|
||||
*/
|
||||
|
||||
/**** test executable for the numparam library ****/
|
||||
/**** usage: nupatest <filename.cir> ****/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "general.h"
|
||||
#include "numparam.h"
|
||||
|
||||
Cconst(pfxsep,'_') /* official prefix separator is ':' not '_' ! */
|
||||
|
||||
Darray(buff, Pchar, Maxline) /* input lines */
|
||||
Darray(buf2, Pchar, Maxline) /* stripped lines */
|
||||
Darray(pxbuf, Pchar, Maxline) /* prefix for subnodes */
|
||||
Darray(runbuf, short, Maxckt) /* index list of expanded circuit */
|
||||
Darray(pindex, short, Maxckt) /* prefix index list */
|
||||
short irunbuf= 0; /* count lines of runbuf */
|
||||
short ipx=0; /* count prefixes in pxbuf */
|
||||
|
||||
/*
|
||||
this toy imitates the Spice subcircuit expansion.
|
||||
To ckeck against Genuine Spice, use the 'listing expand' JDML command
|
||||
Bug1: model or device call with parameters: incorrectly parsed
|
||||
needs a database about _optional_ model/device pars...
|
||||
better: Enter all .model identifiers in the symbol table !
|
||||
Bug2: nested subckt declarations, local .models: might be dangerous.
|
||||
expanded circuit lines, device/node names:
|
||||
any line that starts with a letter (device): splice the prefix in
|
||||
any node that isnt a formal param: add the prefix
|
||||
any formal param node: substitute actual params and their prefixes
|
||||
|
||||
Node and subdevice references for prefixing:
|
||||
|
||||
deviceletter[n] is a device type prefix
|
||||
nbofnodes [n] is the number of "node-type" arguments that follow.
|
||||
nbsubdevice [n] is the number of "subdevices" for splice-in prefix.
|
||||
|
||||
To solve the Q ambiguity, forbid any model identifiers as node names.
|
||||
|
||||
Bug3:
|
||||
In arbitrary dependent sources, we must parse v(,) and i(,) expressions
|
||||
and substitute node/device name arguments.
|
||||
|
||||
*/
|
||||
|
||||
Func short runscript( tdico *dico, Pchar prefix,
|
||||
short istart, short istop, short maxnest)
|
||||
/* recursive top-down expansion: circuit --> list of line numbers */
|
||||
/* keep it simple,stupid compared to Spice's code */
|
||||
/* prefix: inherited string for node & device prefixing */
|
||||
/* istart, istop: allowed interval in table buf[], buf2[]. */
|
||||
/* return value: number of lines included */
|
||||
Begin
|
||||
short i,j, idef, nnest, nline, dn, myipx;
|
||||
Str(250, subpfx); /* subckt prefix */
|
||||
Str(80, subname);
|
||||
char c;
|
||||
Bool done= False;
|
||||
i=istart;
|
||||
nline=0;
|
||||
Inc(ipx); myipx= ipx; /* local copy */
|
||||
pxbuf[ipx]= newstring( length(prefix));
|
||||
scopy( pxbuf[ipx], prefix);
|
||||
While (maxnest>0) And (i<istop) And (Not done) Do
|
||||
c= dico->category[i];
|
||||
If c=='U' Then
|
||||
done=True; /* subcircuit end. Keep as a comment? */
|
||||
buf2[i][0]='#';
|
||||
EndIf
|
||||
If c=='S' Then /* skip nested subcircuits */
|
||||
nnest=1;
|
||||
Repeat
|
||||
Inc(i); c= dico->category[i];
|
||||
If c=='S' Then
|
||||
Inc(nnest)
|
||||
ElsIf c=='U' Then
|
||||
Dec(nnest)
|
||||
EndIf
|
||||
Until (nnest<=0) Or (i>=istop) EndRep
|
||||
ElsIf c=='X' Then /* recursion here ! */
|
||||
runbuf[irunbuf]= i;
|
||||
pindex[irunbuf]= myipx;
|
||||
Inc(irunbuf); Inc(nline);
|
||||
/* keep out-commented X line for parameter passing */
|
||||
idef = findsubckt( dico, buf2[i], subname);
|
||||
buf2[i][0]= '*';
|
||||
If idef>0 Then
|
||||
scopy(subpfx, prefix);
|
||||
cadd(subpfx, pfxsep);
|
||||
j=1; /* add the instance name from buf2[i] */
|
||||
While buf2[i][j] > ' ' Do
|
||||
cadd( subpfx, buf2[i][j]); Inc(j)
|
||||
Done
|
||||
dn= runscript(dico, subpfx, idef+1, istop, maxnest-1);
|
||||
nline= nline+dn;
|
||||
Else /* FIXME: error message here! */
|
||||
ws("cannot find subckt "); ws(buf2[i]); wln();
|
||||
EndIf
|
||||
ElsIf (c != '?') And NotZ(buf2[i][0]) Then
|
||||
/* keep any other valid non-empty line, and its prefix pointer */
|
||||
runbuf[irunbuf]= i;
|
||||
pindex[irunbuf]= myipx;
|
||||
Inc(irunbuf); Inc(nline);
|
||||
EndIf
|
||||
Inc(i);
|
||||
Done
|
||||
return nline
|
||||
EndProc
|
||||
|
||||
Proc gluepluslines( short imax)
|
||||
/* general sweep to eliminate continuation lines */
|
||||
Begin
|
||||
short i,j,k, ls, p;
|
||||
Str(250,s);
|
||||
i=1;
|
||||
While i<= imax Do
|
||||
If (buff[i][0]=='+') And (i>1) Then
|
||||
j= i-1;
|
||||
While (i < imax) And (buff[i+1][0]=='+') Do Inc(i) Done
|
||||
/* the lines j+1 ... i are continuation lines to j */
|
||||
For k=j; k<=i; Inc(k) Do
|
||||
ls=length(s);
|
||||
sadd(s, buff[k]);
|
||||
p= spos("//",s);
|
||||
If p>0 Then pscopy(s,s, 1,p-1) EndIf
|
||||
If ls>0 Then s[ls]=' ' EndIf /* erase the + */
|
||||
Done
|
||||
ls= length(s);
|
||||
If ls> 80 Then
|
||||
Dispose(buff[j]);
|
||||
buff[j]=newstring(ls)
|
||||
EndIf
|
||||
scopy(buff[j], s)
|
||||
EndIf
|
||||
Inc(i)
|
||||
Done
|
||||
EndProc
|
||||
|
||||
#if 0
|
||||
Proc rs(Pchar s) /* 78 coumn limit */
|
||||
Begin
|
||||
short i;
|
||||
Bool done;
|
||||
char c;
|
||||
short max=maxlen(s);
|
||||
If max>78 Then max=78 EndIf
|
||||
i=0; done=False;
|
||||
scopy(s,"");
|
||||
While Not done Do
|
||||
c=fgetc(stdin);
|
||||
If (c>=' ')And(c<='~') And (i<max) Then
|
||||
cadd(s,c); Inc(i)
|
||||
EndIf
|
||||
done= (c==Lf) Or (c==Cr)
|
||||
Done
|
||||
EndProc
|
||||
#endif
|
||||
|
||||
Proc fwrites(Pfile f, Pchar s)
|
||||
Begin
|
||||
fputs(s,f)
|
||||
EndProc
|
||||
|
||||
Proc fwriteln(Pfile f)
|
||||
Begin
|
||||
fputc('\n',f)
|
||||
EndProc
|
||||
|
||||
Intern
|
||||
Proc freadln(Pfile f, Pchar s, short max)
|
||||
Begin
|
||||
short ls;
|
||||
freadstr(f,s,max);
|
||||
ls=length(s);
|
||||
If feof(f) And (ls>0) Then
|
||||
pscopy(s,s,1,ls-1)
|
||||
EndIf /* kill EOF character */
|
||||
EndProc
|
||||
|
||||
Proc wordinsert(Pchar s, Pchar w, short i)
|
||||
/* insert w before s[i] */
|
||||
Begin
|
||||
Str(250,t);
|
||||
short ls=length(s);
|
||||
pscopy(t,s,i+1,ls); pscopy(s,s,1,i);
|
||||
sadd(s,w); sadd(s,t);
|
||||
EndProc
|
||||
|
||||
Func short worddelete(Pchar s, short i)
|
||||
/* delete word starting at s[i] */
|
||||
Begin
|
||||
Str(250,t);
|
||||
short ls= length(s);
|
||||
short j=i;
|
||||
While (j<ls) And (s[j]>' ') Do Inc(j) Done
|
||||
pscopy(t,s,j+1,ls);
|
||||
pscopy(s,s,1,i);
|
||||
sadd(s,t);
|
||||
return j-i /* nb of chars deleted */
|
||||
EndProc
|
||||
|
||||
Func short getnextword(Pchar s, Pchar u, short j)
|
||||
Begin
|
||||
short ls,k;
|
||||
ls= length(s);
|
||||
k=j;
|
||||
While (j<ls) And (s[j] > ' ') Do Inc(j) Done /* skip current word */
|
||||
pscopy(u, s, k+1, j-k);
|
||||
While (j<ls) And (s[j] <= ' ') Do Inc(j) Done
|
||||
return j
|
||||
EndFunc
|
||||
|
||||
Func short inwordlist(Pchar u, Pchar wl)
|
||||
/* suppose wl is single-space separated, plus 1 space at start and end. */
|
||||
Begin
|
||||
short n,p,k;
|
||||
Str(80,t);
|
||||
n=0;
|
||||
ccopy(t,' '); sadd(t,u); cadd(t,' ');
|
||||
p= spos(t,wl);
|
||||
If p>0 Then
|
||||
For k=0; k<p; Inc(k) Do
|
||||
If wl[k] <= ' ' Then Inc(n) EndIf
|
||||
Done
|
||||
EndIf
|
||||
return n
|
||||
EndFunc
|
||||
|
||||
Proc takewordlist(Pchar u, short k, Pchar wl)
|
||||
Begin
|
||||
short i,j,lwl;
|
||||
lwl= length(wl);
|
||||
i=0; j=0;
|
||||
scopy(u,"");
|
||||
While (i<lwl) And (j<k ) Do
|
||||
If wl[i] <= ' ' Then Inc(j) EndIf
|
||||
Inc(i)
|
||||
Done
|
||||
If j==k Then /* word has been found and starts at i */
|
||||
While wl[i]>' ' Do
|
||||
cadd(u,wl[i]); Inc(i)
|
||||
Done
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
Pchar deviceletter= "RLCVIBSGETOUWFHDQKJZM";
|
||||
Pchar nbofnodes = "222222444443222240334";
|
||||
Pchar nbsubdevice = "000000000000111002000";
|
||||
|
||||
Proc prefixing(Pchar s, Pchar p, Pchar formals, Pchar actuals,
|
||||
char categ, tdico *dic)
|
||||
/* s is a line in expanded subcircuit.
|
||||
p is the prefix to be glued anywhere .
|
||||
assume that everything except first and last word in s may be a node.
|
||||
formals: node parameter list of a subckt definition line
|
||||
actuals: substitutes from the last X... call line (commented-out)
|
||||
subdevices (L belonging to a K line, for example) must be within the
|
||||
same subckt, they get the same prefix splice-in.
|
||||
There is a kludge for Q lines (may have 3 or 4 nodes, you never know).
|
||||
Reminder on Numparam symbols:
|
||||
naming convention: subckt,model,numparam and node names must be unique.
|
||||
cannot re-use a model name as a param name elsewhere, for example.
|
||||
*/
|
||||
Begin
|
||||
short i,j,k,ls, jnext, dsize;
|
||||
short dtype, nodes, subdv;
|
||||
Bool done;
|
||||
char leadchar;
|
||||
Str(80,u); Str(80,v); Str(80,pfx);
|
||||
i=0; ls=length(s);
|
||||
While (i<ls) And (s[i]<=' ') Do Inc(i) Done
|
||||
If alfa(s[i]) Or (categ=='X') Then /* splice in the prefix and nodelist */
|
||||
wordinsert(s,p, i+1);
|
||||
j= getnextword(s,u,i);
|
||||
done=False;
|
||||
If p[0]== pfxsep Then
|
||||
pscopy(pfx,p, 2, length(p))
|
||||
Else
|
||||
scopy(pfx,p)
|
||||
EndIf
|
||||
leadchar=upcase(s[i]);
|
||||
dtype= cpos( leadchar, deviceletter) -1 ;
|
||||
If dtype >= 0 Then
|
||||
nodes= nbofnodes[dtype] - '0';
|
||||
subdv= nbsubdevice[dtype] - '0';
|
||||
Else
|
||||
nodes=999; subdv=0;
|
||||
EndIf
|
||||
While Not done Do
|
||||
jnext= getnextword(s,u,j);
|
||||
done=(jnext >= length(s)); /* was the last one, do not transform */
|
||||
/* bug: are there semilocal nodes ? in nested subckt declarations ? */
|
||||
If (leadchar=='Q') And (Not done) Then /* BJT: watch non-node name */
|
||||
scopy(v,u); stupcase(v);
|
||||
done= getidtype(dic, v) == 'O'; /* a model name stops the node list */
|
||||
EndIf
|
||||
If (Not done) And (nodes>0) Then /* transform a node name */
|
||||
k= inwordlist(u, formals);
|
||||
If (k>0) Then /* parameter node */
|
||||
dsize= - worddelete(s,j);
|
||||
takewordlist(u,k, actuals);
|
||||
wordinsert(s,u,j);
|
||||
dsize= dsize + length(u);
|
||||
ElsIf stne(u,"0") Then /* local node */
|
||||
wordinsert(s,pfx,j);
|
||||
dsize= length(pfx);
|
||||
Else dsize=0 EndIf
|
||||
ElsIf (Not done) And (subdv >0) Then /* splice a subdevice name */
|
||||
wordinsert(s,p,j+1);
|
||||
dsize= length(p);
|
||||
EndIf
|
||||
j= jnext + dsize; /* jnext did shift ...*/
|
||||
If nodes >0 Then Dec(nodes)
|
||||
ElsIf subdv >0 Then Dec(subdv)
|
||||
EndIf
|
||||
done= done Or (Zero(nodes) And Zero(subdv));
|
||||
Done
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
Proc getnodelist(Pchar form, Pchar act, Pchar s, tdico *dic, short k)
|
||||
/* the line s contains the actual node parameters, between 1st & last word */
|
||||
Begin
|
||||
short j,ls, idef;
|
||||
Str(80,u); Str(250,t);
|
||||
ccopy(act,' '); ccopy(form,' ');
|
||||
j=0; ls= length(s);
|
||||
j= getnextword(s,u,j);
|
||||
While j<ls Do
|
||||
j= getnextword(s,u,j);
|
||||
If j<ls Then sadd(act,u); cadd(act,' ') EndIf
|
||||
Done
|
||||
/* now u already holds the subckt name if all is ok ? */
|
||||
idef = findsubckt( dic, buf2[k], u);
|
||||
/* line buf2[idef] contains: .subckt name < formal list > */
|
||||
If idef>0 Then
|
||||
scopy(t, buf2[idef])
|
||||
Else
|
||||
ws("Subckt call error: "); ws(s); wln();
|
||||
EndIf
|
||||
j=0; ls= length(t);
|
||||
j= getnextword(t,u,j);
|
||||
j= getnextword(t,u,j);
|
||||
While j<ls Do
|
||||
j= getnextword(t,u,j);
|
||||
sadd(form,u); cadd(form,' ');
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Proc nupa_test(Pchar fname, char mode)
|
||||
/* debugging circuit expansion run. mode='w': write ouput file */
|
||||
/* bugs in nupa_eval(), and for nested subckt definitions !?! */
|
||||
Begin
|
||||
Pfile tf, fout;
|
||||
tdico * dic; /* dictionary data pointer */
|
||||
Str(250,s);
|
||||
Str(80, prefix);
|
||||
/* Str(250, formals); Str(250,actuals); */
|
||||
Darray(formals, Pchar, 10)
|
||||
Darray(actuals, Pchar, 10)
|
||||
short i, j, k, nline, parstack;
|
||||
For i=0; i<Maxline; Inc(i) Do /* allocate string storage */
|
||||
buff[i]= newstring(80);
|
||||
buf2[i]= Null;
|
||||
pxbuf[i]= Null
|
||||
Done
|
||||
For i=0; i<10; Inc(i) Do
|
||||
formals[i]= newstring(250);
|
||||
actuals[i]= newstring(250);
|
||||
Done
|
||||
i=0; parstack=0;
|
||||
tf=fopen( fname, "r");
|
||||
If tf != Null Then
|
||||
While (Not feof(tf)) And ((i+1) < Maxline) Do
|
||||
Inc(i);
|
||||
freadln(tf, buff[i], 80); /* original data */
|
||||
Done
|
||||
fclose(tf);
|
||||
Else
|
||||
ws("Cannot find "); ws(fname); wln();
|
||||
EndIf
|
||||
/* continuation lines are glued at this stage, so they can be ignored
|
||||
in all the subsequent manipulations.
|
||||
*/
|
||||
gluepluslines(i); /* must re-allocate certain buff[i] */
|
||||
nupa_signal(NUPADECKCOPY, fname);
|
||||
dic= nupa_fetchinstance(); /* bug: should have a task handle as arg */
|
||||
For j=1; j<=i; Inc(j) Do
|
||||
buf2[j]= nupa_copy(buff[j], j); /* transformed data */
|
||||
Done
|
||||
nupa_signal(NUPASUBDONE, Null);
|
||||
nline= runscript(dic, "", 1,i, 20); /* our own subckt expansion */
|
||||
/* putlogfile(' ',nline," expanded lines"); */
|
||||
If mode=='w' Then
|
||||
i= cpos('.', fname);
|
||||
pscopy(s, fname, 1, i);
|
||||
sadd(s,"out");
|
||||
fout= fopen(s, "w");
|
||||
Else
|
||||
fout= Null
|
||||
EndIf
|
||||
For j=0; j<irunbuf; Inc(j) Do
|
||||
k= runbuf[j];
|
||||
If buf2[k] != Null Then
|
||||
scopy(s, buf2[k]);
|
||||
nupa_eval(s, k);
|
||||
scopy(prefix,pxbuf[pindex[j]]);
|
||||
If NotZ(prefix[0]) Then cadd(prefix, pfxsep) EndIf
|
||||
prefixing(s, prefix, formals[parstack], actuals[parstack],
|
||||
dic->category[k], dic);
|
||||
If dic->category[k] == 'X' Then
|
||||
If parstack< (10-1) Then Inc(parstack) EndIf
|
||||
getnodelist(formals[parstack], actuals[parstack], s, dic,k);
|
||||
/*dbg: ws("Form: "); ws(formals[parstack] ); wln(); */
|
||||
/*dbg: ws("Actu: "); ws(actuals[parstack]); wln(); */
|
||||
ElsIf dic->category[k]=='U' Then /* return from subckt */
|
||||
If parstack>0 Then Dec(parstack) EndIf
|
||||
EndIf
|
||||
If fout != Null Then
|
||||
fwrites(fout, s); fwriteln(fout)
|
||||
EndIf
|
||||
EndIf
|
||||
Done
|
||||
If fout != Null Then fclose(fout) EndIf
|
||||
nupa_signal(NUPAEVALDONE, Null); /* frees the buff[i] */
|
||||
For i= 10-1; i>=0; Dec(i) Do
|
||||
Dispose(actuals[i]);
|
||||
Dispose(formals[i]);
|
||||
Done
|
||||
For i= Maxline -1; i>=0; Dec(i) Do
|
||||
Dispose(pxbuf[i]);
|
||||
Dispose(buf2[i]);
|
||||
/* Dispose(buff[i]) done elsewhere */
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Func int main(int argc, Pchar argv[])
|
||||
Begin
|
||||
Str(80,fname);
|
||||
If argc>1 Then
|
||||
scopy(fname, argv[1])
|
||||
Else
|
||||
scopy(fname,"testfile.nup")
|
||||
EndIf
|
||||
nupa_test(fname, 'w');
|
||||
return 0
|
||||
EndFunc
|
||||
|
||||
|
|
@ -0,0 +1,674 @@
|
|||
********************************************************
|
||||
README.TXT the minimal Numparam documentation
|
||||
********************************************************
|
||||
|
||||
Numparam: an add-on library for electronic circuit analysis front-ends
|
||||
Copyright (C) 2002 Georg Post
|
||||
|
||||
This library 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 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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 library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
CONTENTS
|
||||
|
||||
A. The Numparam library
|
||||
B. Circuit description with Numparam (in lieu of a tutorial)
|
||||
C. Reference section
|
||||
D. Installation
|
||||
E. Theory of operation
|
||||
F. Files in this package
|
||||
G. Known bugs
|
||||
|
||||
|
||||
A. The Numparam library:
|
||||
|
||||
The spice-3f(x) front end lacks some features of commercial derivatives,
|
||||
such as the ability to define numerical attributes of devices by symbols
|
||||
or by constant (at circuit-expansion time) numerical expressions.
|
||||
Numerical parameters - other than node names - for subcircuits are also
|
||||
commonly available in these non-free Spices.
|
||||
|
||||
This library provides a retrofit to the Spice circuit description language
|
||||
to add these features. By design, the new code is organized as an independent
|
||||
library which does not import any Spice header files and whose only interface
|
||||
to Spice is a set of three function headers. The only place where these
|
||||
functions are called - if a new compile-time option for Spice is set - is
|
||||
the code file src/lib/fte/subckt.c. There are no patches anywhere else.
|
||||
|
||||
The purpose of this minimal coupling was to freely license the additional code.
|
||||
It is distributed under the GNU-LGPL and thus can be linked to the original
|
||||
Spice which is open-source software but maintains a special license.
|
||||
(As I read somewhere, Berkeley Spice is reserved to people friendly to the USA).
|
||||
Due to GPL-type requirements, I cannot merge any lines from Spice with
|
||||
the new code, and I cannot avoid redundancies, such as one more symbol table
|
||||
manager and the umpteenth arithmetic expression parser.
|
||||
|
||||
Coding style of my add-on is a bit personal. Using a set of keyword-hiding
|
||||
macros, it is closer to Pascal and/or Basic than to authentic C programs.
|
||||
Most of it originated from my initial Turbo Pascal preprocessors that have been
|
||||
translated to C. After all, I'm definitely not "friendly to the C language".
|
||||
Even in disguise, the code is pure Ansi-C and compiles without a warning
|
||||
under the severest settings of gcc (under GNU/Linux) or Turbo C++ (under
|
||||
MS-DOS). For C hardliners, I include the 'washprog' utility which downgrades
|
||||
the source code to the common C look-&-feel. Extreme purists might apply
|
||||
some appropriate "indent" utility, in addition.
|
||||
|
||||
|
||||
B. Circuit description with Numparam (in lieu of a tutorial).
|
||||
|
||||
As we now know, all the text entry to Spice comes in two separate languages:
|
||||
- a circuit description language (CDL) which defines the electronic hardware.
|
||||
- an analysis job and data management language (JDML) which may be used
|
||||
as an interactive shell or in batch files.
|
||||
|
||||
In the circuit description "CDL" file, the design is typically organized
|
||||
as a hierarchical set of subcircuits which are connected together in
|
||||
the 'main' circuit (they are "called", like procedures in a general-purpose
|
||||
programming language). CDL is line-oriented, with a recognizer role assigned
|
||||
to the first non-space character of a line.
|
||||
(In the ancient times of data processing, a line was called a "card".)
|
||||
For example, '*' introduces comment lines. 'C' places a capacitor
|
||||
device in the circuit, 'R' a resistor, 'L' an inductor, and so on.
|
||||
'X' signals the "call" of a subcircuit. Character '+' introduces a continuation
|
||||
line (the way to split exceedingly long lines into pieces).
|
||||
|
||||
A special class of lines that start with a dot '.' have control functions
|
||||
inside CDL: they do not add physical circuit elements.
|
||||
For instance, the control pair '.subckt' and '.ends' brackets a subcircuit
|
||||
definition section in CDL.
|
||||
In the old days of Spice-2, some of the functions now assigned to JDML were
|
||||
also inserted as dot cards. You can still insert pieces of JDML inside
|
||||
a CDL file, as a section enclosed within lines '.control' and '.endc' .
|
||||
|
||||
Example CDL file, a netlist of some double T RC-filter:
|
||||
|
||||
* First-example
|
||||
.subckt myfilter in out
|
||||
Ra in p1 2k
|
||||
Rb p1 out 2k
|
||||
C1 p1 0 2nF
|
||||
Ca in p2 1nF
|
||||
Cb p2 out 1nF
|
||||
R1 p2 0 1k
|
||||
.ends myfilter
|
||||
|
||||
X1 input output myfilter
|
||||
V1 input 0 AC 1V
|
||||
|
||||
|
||||
Let us recall what the Spice "front-end" essentially does to your
|
||||
circuit-description (CDL) file whenever it is submitted, either at program
|
||||
start-up or after some interactive JDML commands like 'edit' or 'source'.
|
||||
First, all the JDML sections in the file are sorted out and kept for
|
||||
later use (unless the file is pure JDML and thus immediately executed).
|
||||
Next, the hierarchy of subcircuits is expanded and an internal representation
|
||||
of the flattened circuit is stored, i.e. a set of CDL lines without any more
|
||||
'X' and '.subckt' lines. This flat circuit is also known as the "netlist".
|
||||
Then, the netlist is translated into the internal data structures of Spice,
|
||||
essentially a sparse matrix of elements indexed by pairs of circuit nodes.
|
||||
|
||||
Finally, the mathematical analysis is carried out under the control of JDML,
|
||||
and output data may be stored, printed, plotted, compared, and so on.
|
||||
Analyses may be repeated under varying bias/frequency/time... conditions.
|
||||
But to change the circuit topology, the CDL must be edited and re-compiled.
|
||||
|
||||
Numparam-specific features of CDL :
|
||||
|
||||
The Numparam library is an enhancement of the Spice front-end which adds
|
||||
clarity and arithmetic functionality to the circuit description language.
|
||||
|
||||
The most wanted feature of a language is to have word-like symbols that
|
||||
take the place of specific values or objects. The dot-line
|
||||
|
||||
.param <identifier> = <expression>
|
||||
|
||||
defines such symbols. For example, to describe a triple RC filter
|
||||
with identical values of components, we do not need to explicitly
|
||||
repeat constant numbers. The CDL code may go like this:
|
||||
|
||||
.param res= 1kohm // numparam allows comment tails like in C++
|
||||
.param tau= 10ns // we want a time constant
|
||||
.param cap= tau/res // tau= RC, of course
|
||||
|
||||
.subckt triplerc in out
|
||||
R1 in p1 {res}
|
||||
C1 p1 0 {cap}
|
||||
R2 p1 p2 {res}
|
||||
C2 p2 0 {cap}
|
||||
R3 p2 out {res}
|
||||
C3 out 0 {cap}
|
||||
.ends
|
||||
|
||||
As you can see, the use of symbols anywhere in the circuit description
|
||||
requires the curly braces :
|
||||
{ <expression> }
|
||||
This coding style is even more interesting if circuit elements have known
|
||||
fixed ratios (Butterworth filters and the like) and we only need to
|
||||
touch one value (a time constant) to tune the circuit.
|
||||
|
||||
Only numerical constants such as '30pF' may be used without enclosing
|
||||
braces. It is the braces that tell our CDL front-end to look up
|
||||
symbols and to crunch arithmetic expressions inside.
|
||||
|
||||
Obviously, it was restrictive that subcircuit definitions could only
|
||||
accept interface node names as symbolic parameters. With the following
|
||||
syntax of the .subckt line, we add numerical parameters:
|
||||
|
||||
.subckt <ckt-ident> <node> ... params: <id>=<value> <id>=<value> ...
|
||||
|
||||
Example, a parameterized filter:
|
||||
|
||||
.subckt triplerc2 in out params: res=1kohm cap=50pF
|
||||
* all the following lines as above.
|
||||
* the specified default values are always overridden with X lines.
|
||||
|
||||
To call variants of such a subcircuit later on, we may write:
|
||||
|
||||
X1 in out1 triplerc2 {r1} {c1}
|
||||
X2 in out2 triplerc2 {2*r1} {c1/2}
|
||||
X3 in out3 triplerc2 {3*r1} {c1/3}
|
||||
|
||||
where the r1 and c1 symbols are defined in .param lines.
|
||||
So, we can use subcircuits with one or more parameters, the same way
|
||||
as Spice2 already allowed an area parameter for diodes and transistors.
|
||||
|
||||
|
||||
Here is the first example, rewritten with parameters:
|
||||
|
||||
* Param-example
|
||||
.param amplitude= 1V
|
||||
|
||||
.subckt myfilter in out
|
||||
+ params: rval=100k cval= 100nF
|
||||
Ra in p1 {2*rval}
|
||||
Rb p1 out {2*rval}
|
||||
C1 p1 0 {2*cval}
|
||||
Ca in p2 {cval}
|
||||
Cb p2 out {cval}
|
||||
R1 p2 0 {rval}
|
||||
.ends myfilter
|
||||
|
||||
X1 input output myfilter 1k 1nF
|
||||
V1 input 0 AC {amplitude}
|
||||
|
||||
|
||||
Note:
|
||||
Now, there is some possible confusion in Spice because of multiple numerical
|
||||
expression features. The .param lines and the braces expressions are
|
||||
evaluated in the front-end, that is, just after the subcircuit expansion.
|
||||
(Technically, the X lines are kept as comments in the expanded circuit
|
||||
so that the actual parameters can correctly be substituted ).
|
||||
So, after the netlist expansion and before the internal data setup, all
|
||||
number attributes in the circuit are known constants.
|
||||
However, there are some circuit elements in Spice which accept arithmetic
|
||||
expressions that are NOT evaluated at this point, but only later during
|
||||
circuit analysis. These are the arbitrary current and voltage sources.
|
||||
The syntactic difference is that "compile-time" expressions are
|
||||
within braces, but "run-time" expressions have no braces.
|
||||
To make things more complicated, the backend language JDML also accepts
|
||||
arithmetic/logic expressions that operate on its own scalar or vector data sets.
|
||||
|
||||
It would be desirable to have the same expression syntax, operator and function
|
||||
set, and precedence rules, for the three contexts mentioned above.
|
||||
In the current Numparam implementation, that goal is not yet achieved...
|
||||
|
||||
|
||||
C. Reference section:
|
||||
|
||||
The Numparam add-on supports the following elements in the circuit description
|
||||
language.
|
||||
|
||||
1. '.param' control lines to define symbolic numbers
|
||||
2. arithmetic expressions in place of any numeric constant
|
||||
3. formal and actual numeric parameters for subcircuit definition and 'call'.
|
||||
|
||||
NOT YET IMPLEMENTED:
|
||||
To activate the additional functions, put a line near the top of the CDL file:
|
||||
.option numparam
|
||||
|
||||
In the syntax description,
|
||||
<ident> means an alphanumeric identifier (<20 chars, starting with a letter)
|
||||
<expr> means an expression, composed of <ident>s, Spice numbers, and operators.
|
||||
|
||||
1. The .param line:
|
||||
Syntax: .param <ident> = <expr> ; <ident> = <expr> ....
|
||||
|
||||
This line assigns numerical values to identifiers. More than one assignment
|
||||
per line is possible using the ';' separator.
|
||||
The .param lines inside subcircuits are copied per call, like any other line.
|
||||
All assignments are executed sequentially through the expanded circuit.
|
||||
Before its first use, a name must have been assigned a value.
|
||||
|
||||
2. Brace expressions in cicuit elements:
|
||||
Syntax: { <expr> }
|
||||
|
||||
These are allowed in .model lines and in device lines, wherever only constant
|
||||
Spice numbers could be used in spice2/3. A Spice number is a floating
|
||||
point number with an optional scaling suffix, immediately glued to the
|
||||
numeric tokens (see below).
|
||||
Warning: {..} cannot be used to 'parameterize' node names or parts of names.
|
||||
( We are not into obfuscated shell scripting ...)
|
||||
All identifiers used within an <expr> must have known values at the time
|
||||
when the line is evaluated, else an error is flagged.
|
||||
|
||||
3. Subcircuit parameters:
|
||||
The syntax of a subcircuit definition header is:
|
||||
.subckt <ident> node node ... params: <ident>= <value> <ident>=<value>...
|
||||
|
||||
node is an integer number or an identifier, for one of the external nodes.
|
||||
The 'params:' keyword introduces an optional section of the line.
|
||||
Each <ident> is a formal parameter, and each <value> is either a Spice
|
||||
number or a brace expression.
|
||||
Inside the '.subckt' ... '.ends' context, each formal parameter may be
|
||||
used like any identifier that was defined on a .param control line.
|
||||
The <value> parts are supposed to be default values of the parameters.
|
||||
However, in the current version of Numparam, they are not used and each
|
||||
invocation of the subcircuit must supply the _exact_ number of actual
|
||||
parameters.
|
||||
|
||||
The syntax of a subcircuit call (invocation) is:
|
||||
X<name> node node ... <ident> <value> <value> ....
|
||||
|
||||
Here <name> is the symbolic name given to that instance of the subcircuit,
|
||||
<ident> is the name of a subcircuit defined beforehand. node node ... is
|
||||
the list of actual nodes where the subcircuit is connected.
|
||||
<value> is either a Spice number or a brace expression { <expr> } .
|
||||
The sequence of <value> items on the X line must exactly match the number
|
||||
and the order of formal parameters of the subcircuit.
|
||||
|
||||
4. Symbol scope
|
||||
|
||||
All Subcircuit and Model names are considered global and must be unique.
|
||||
The .param symbols that are defined outside of any '.subckt' ... '.ends'
|
||||
section are global. Inside such a section, the pertaining 'params:'
|
||||
symbols and any .param assignments are considered local: they mask any
|
||||
global identical names, until the .ends line is encountered.
|
||||
You cannot reassign to a global number inside a .subckt, a local copy is
|
||||
created instead. Scope nesting works up to a level of 10. For example,
|
||||
if the main circuit calls A which has a formal parameter xx, A calls B
|
||||
which has a param. xx, and B calls C which also has a formal param. xx,
|
||||
there will be three versions of 'xx' in the symbol table but only the most
|
||||
local one - belonging to C - is visible.
|
||||
|
||||
5. Syntax of expressions <expr> ( optional parts within [ ...] ):
|
||||
|
||||
An expression may be one of:
|
||||
<atom> where <atom> is either a Spice number or an identifier
|
||||
<unary-operator> <atom>
|
||||
<function-name> ( <expr> [ , <expr> ...] )
|
||||
<atom> <binary-operator> <expr>
|
||||
( <expr> )
|
||||
|
||||
As expected, atoms, builtin function calls and stuff within parentheses
|
||||
are evaluated before the other operators. The operators are evaluated
|
||||
following a list of precedence close to the one of the C language.
|
||||
For equal precedence binary ops, evaluation goes left to right.
|
||||
|
||||
Operators: Alias Internal symb. Precedence
|
||||
|
||||
- - 1 (unary -)
|
||||
not ! ! 1 (unary not)
|
||||
** ^ ^ 2 (power)
|
||||
* * 3 (multiply)
|
||||
/ / 3 (divide)
|
||||
mod % % 3 (modulo)
|
||||
div \ \ 3 (integer divide)
|
||||
+ + 4 (add)
|
||||
- - 4 (subtract)
|
||||
== = 5 (equality)
|
||||
<> != # 5 (un-equal)
|
||||
<= L 5 (less or equal)
|
||||
>= G 5 (greater or equal)
|
||||
< < 5 (less than)
|
||||
> > 5 (greater than)
|
||||
and && & 6 (and)
|
||||
or || | 7 (or)
|
||||
|
||||
The result of logical operators is 1 or 0 , for True or False.
|
||||
|
||||
|
||||
Builtin functions: Internal ref.
|
||||
|
||||
defined 0 (returns 1 if symbol is defined, else 0)
|
||||
sqr 1
|
||||
sqrt 2
|
||||
sin 3
|
||||
cos 4
|
||||
exp 5
|
||||
ln 6
|
||||
arctan 7
|
||||
abs 8
|
||||
pwr 9
|
||||
|
||||
Scaling suffixes (any decorative alphanum. string may follow ...)
|
||||
|
||||
g 1e9
|
||||
meg 1e6
|
||||
k 1e3
|
||||
m 1e-3
|
||||
u 1e-6
|
||||
n 1e-9
|
||||
p 1e-12
|
||||
f 1e-15
|
||||
|
||||
Note: there are intentional redundancies in expression syntax, e.g.
|
||||
x^y , x**y and pwr(x,y) all have nearly the same result.
|
||||
|
||||
6. Reserved words
|
||||
In addition to the above function names and to the verbose operators
|
||||
( not and or div mod ), other words are reserved and cannot be used
|
||||
as parameter names. Historically, they come from a version of Numparam
|
||||
that was a full-blown macro language. I won't link that one to Spice,
|
||||
not before somebody proves to me that such a thing could be useful...
|
||||
|
||||
and or not div mod if else end while macro funct defined
|
||||
include for to downto is var
|
||||
sqr sqrt sin cos exp ln arctan abs pwr
|
||||
|
||||
|
||||
7. Alternative syntax
|
||||
the & sign is tolerated to provide some 'historical' parameter notation:
|
||||
& as the first character of a line is equivalent to: .param
|
||||
Inside a line, the notation &(....) is equivalent to {....}, and
|
||||
&identifier means the same thing as {identifier} .
|
||||
|
||||
This notation exists a bit for the same reason as my macros which wipe
|
||||
the curly braces out of the C language: entering those signs is a pain in
|
||||
the neck on IBM French-type keyboards. You hit, among others, a vanishingly
|
||||
small AltGr key which is squeezed by superfluous buttons that show ugly
|
||||
office-software logos...
|
||||
|
||||
Comments in the style of C++ line trailers (//) are detected and erased.
|
||||
Warning: this is NOT possible in embedded .control parts of a source
|
||||
file, these JDML lines are outside of Numparam's scope. DOS-style
|
||||
carriage returns at line ends are difficult for JDML, too.
|
||||
|
||||
|
||||
D. Installation
|
||||
|
||||
There are two versions of Spice on which this library has been tried:
|
||||
a. a 1997 version spice3f5 that was arranged for Red Hat Linux
|
||||
b. the version 14 of ngspice (will now be privileged for development)
|
||||
|
||||
On my system, the size of libnupa.a is about 47k, so this is the additional
|
||||
bloat that the spice3 and nutmeg binary programs will accumulate.
|
||||
( The numparam source tarball weighs in for some 70k )
|
||||
|
||||
The common part to build the Numparam library is this:
|
||||
|
||||
0. choose any directory you like for Numparam, let's call it $HACK.
|
||||
1. un-tar the .c and .h files and the rest, in Numparam's directory :
|
||||
tar xzvf numparam.tgz
|
||||
|
||||
2. compile the lib sources with gcc -c -Wall:
|
||||
|
||||
gcc -c -ansi -pedantic -Wall spicenum.c nupatest.c xpressn.c mystring.c
|
||||
|
||||
3. pre-link together the library part to numparam.o and libnupa.a:
|
||||
|
||||
ld -r -o numparam.o spicenum.o xpressn.o mystring.o
|
||||
ar -rcs libnupa.a spicenum.o xpressn.o mystring.o
|
||||
|
||||
4. make the test executable nupatest:
|
||||
|
||||
gcc -o nupatest nupatest.o spicenum.o xpressn.o mystring.o -lm
|
||||
|
||||
The script file 'mknumpar.sh' does all this (2-4).
|
||||
|
||||
|
||||
5a. Link with the "third version of Spice3f5 for RedHat Linux 2.6" (1997)
|
||||
|
||||
do the following in the spice3f5 top-level directory:
|
||||
|
||||
1. patch the file src/lib/fte/subckt.c :
|
||||
cp -biv $HACK/rhsubckt.c src/lib/fte/subckt.c
|
||||
2. edit src/bin/makeops , to add $HACK/libnupa.a to LIBS and LIBN.
|
||||
3. ./util/build linux
|
||||
4. ./util/build linux install
|
||||
|
||||
|
||||
5b. Link procedure for ngspice version 14
|
||||
|
||||
I haven't yet a working knowledge of the 'automake/autoconf' system, so I'll
|
||||
describe the pedestrian hacks to get Numparam in. That's evil; the right way
|
||||
would need a configuration flag that chooses to make and to link the library.
|
||||
Only the top level files 'configure.in' and 'Makefile.am' should be revised
|
||||
to process the numparam option. (?)
|
||||
Help!
|
||||
|
||||
1. replace the file src/frontend/subckt.c with Numparam's patched version :
|
||||
cp -biv $HACK/ngsubckt.c src/frontend/subckt.c
|
||||
2. run ./configure with a "LIBS prefix" to include numparam (see below)
|
||||
3. make
|
||||
4. make install
|
||||
|
||||
Here is one "prefixed" ngspice configure script that works on my system:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
# ngconfig.sh
|
||||
# configure options for ngspice with numparam add-on
|
||||
# run this in ngspice's top-level directory
|
||||
|
||||
# specify your Numparam directory
|
||||
HACK=/home/post/spice3f5/hack
|
||||
|
||||
# over-write the original subckt.c
|
||||
cp -biv $HACK/ngsubckt.c src/frontend/subckt.c
|
||||
|
||||
# my box needs CFLAGS on 1st run, else 'terminal.c' wont find 'termcap.h' ?
|
||||
|
||||
CFLAGS=-I/usr/include/ncurses \
|
||||
LIBS=$HACK/libnupa.a \
|
||||
./configure --without-x --prefix=/usr/local/ngsp
|
||||
|
||||
#### end of sample script ####
|
||||
|
||||
|
||||
E. Theory of operation
|
||||
|
||||
Spice's front end does a lot of malloc/free type memory gymnastics and does not
|
||||
seem to care much about small leaks here and there. Numparam will do some
|
||||
malloc'ing in place of Spice (essentially the translated strings of the input
|
||||
deck) and rely on Spice to clean it up - or not - later on. My library will
|
||||
clean up its private space only (the symbol tables) and will make some
|
||||
assumptions about the interface function calls coming from Spice.
|
||||
Here is the scenario supposed to be followed by Spice and Numparam:
|
||||
|
||||
0. the patched codefile subckt.c imports the following header lines:
|
||||
|
||||
#define NUPADECKCOPY 0
|
||||
#define NUPASUBSTART 1
|
||||
#define NUPASUBDONE 2
|
||||
#define NUPAEVALDONE 3
|
||||
|
||||
extern char * nupa_copy(char *s, int linenum);
|
||||
extern int nupa_eval(char *s, int linenum);
|
||||
extern int nupa_signal(int sig);
|
||||
|
||||
These are the three library functions called, i.e.
|
||||
|
||||
- nupa_copy by inp_subcktexpand to preprocess all extended-syntax lines.
|
||||
- nupa_eval by inp_subcktexpand to do the parameter substitutions
|
||||
- nupa_signal with one of the 4 signals, from various places to
|
||||
send state information to the Numparam library.
|
||||
|
||||
The only places with numparam patches are the functions
|
||||
inp_subcktexpand() and its recursive subroutine doit(), in the
|
||||
file subckt.c . At this stage, we suppose that:
|
||||
- any .control sections are filtered out
|
||||
- any .include are expanded
|
||||
- any + continuation line chunks are glued together
|
||||
|
||||
1. In the first phase, Numparam runs through the deck (whose .control sections
|
||||
have already been removed by Spice) to create copies of the lines
|
||||
without the extended syntax. Pointers to the original deck lines are kept
|
||||
and the copies are traditional Spice, with placeholders for
|
||||
symbols and expressions. Spice loses the originals and gets the bleached-out
|
||||
copies.
|
||||
|
||||
2. The "doit()" circuit expansions are modified to keep more information.
|
||||
Contrary to the initial Spice code, now the subcircuit invocation
|
||||
lines are preserved as comments, which allows Numparam to update
|
||||
symbolic subcircuit parameters a bit later. Subcircuit exit lines are also
|
||||
copied and out-commented, to keep track of identifier scopes during
|
||||
the final pass.
|
||||
|
||||
If this seems waste of storage, just consider all those sloppy memory
|
||||
leaks in the circuit expansion code...
|
||||
|
||||
3. The final wash-up is a sequential call to the library (nupa_eval())
|
||||
line-by-line through the expanded circuit. By using its pointers
|
||||
to the original lines, Numparam recovers the added syntax features.
|
||||
It triggers all the symbol value computations and inserts constant
|
||||
numbers into the circuit definition lines, whose length must not change!
|
||||
This option is a kludge to avoid memory reallocation [ my intuitive
|
||||
fear is that these free() malloc() realloc() and friends swallow a lot of
|
||||
CPU time ? ].
|
||||
|
||||
4. The termination signal at the end of inp_subcktexpand() tells the Numparam
|
||||
library to clean up its mess, release its pointers to the original
|
||||
Spice circuit description text, and to get prepared for another run.
|
||||
Note: Numparam frees the storage related to the original lines
|
||||
whose pointers have been stolen in phase 1.
|
||||
|
||||
|
||||
In a future release, Numparam will be re-entrant, all its 'global' data being
|
||||
referenced via a handle which the client program should keep around.
|
||||
|
||||
|
||||
F. Files in this package
|
||||
|
||||
The following Ansi C code files belong to Numparam:
|
||||
|
||||
general.h header file with macros to disguise the C language.
|
||||
stuff for an 'overflow-safe' string library ( whose biggest bug
|
||||
is that it indexes strings from 1 like Pascal).
|
||||
|
||||
numparam.h header file for numparam-specific symbols and functions
|
||||
|
||||
mystring.c collection of 'safer' character string (and misc.) functions.
|
||||
beware of the nasty Turbo Pascal conventions.
|
||||
|
||||
xpressn.c the interpreter of arithmetic/logical expressions
|
||||
|
||||
spicenum.c the interface part, functions that are called by Spice.
|
||||
|
||||
nupatest.c a stand-alone subcircuit expander, for test purpose.
|
||||
|
||||
washprog.c a program that washes all the above C files, including itself,
|
||||
to recover the crude syntax of the True Language (see below).
|
||||
|
||||
Patched versions of spice's subckt.c file incorporate the library calls
|
||||
and maybe try to repair some memory leaks (in rhsubckt.c, not yet tested).
|
||||
|
||||
rhsubckt.c for spice3f5 1997 Red Hat (src/lib/fte/subckt.c)
|
||||
ngsubckt.c for ngspice version 14 (src/frontend/subckt.c)
|
||||
subckt.dif 'diff' between ngsubckt.c and ngspice frontend/subckt.c
|
||||
|
||||
The following text, data and script files are also included:
|
||||
|
||||
readme.txt this documentation file
|
||||
downgrad.txt the substitution rules required for washprog.c
|
||||
mknumpar.sh script to make the library binaries
|
||||
ngconfig.sh sample script to run ./configure for ngspice
|
||||
|
||||
configure.in crappy ?
|
||||
Makefile.am crappy ?
|
||||
|
||||
testfile.nup a test text (nonsense circuit) for Numparam ?
|
||||
|
||||
|
||||
So, if you are a Real Programmer, think that the Pascal amateurs confound
|
||||
programming with writing novels, and find those Basic greenhorns' style
|
||||
too childish, then execute the following two-liner first of all
|
||||
(should work on GNU/Linux, but it's not a speed monster) :
|
||||
|
||||
gcc -o washprog washprog.c mystring.c
|
||||
./washprog *.c
|
||||
|
||||
You get all the *.c files in a version where the first character becomes an
|
||||
underbar, and the interior resembles to _code_. (although it lacks such
|
||||
powerful features as continue, break, goto, ?:-expressions, gets(), ... )
|
||||
|
||||
|
||||
G. Known Bugs
|
||||
|
||||
First of all, lots of size limits - incompatible with the Spirit of the
|
||||
Gnu, who wants that everything may grow as much as malloc() can grab ...
|
||||
|
||||
- circuit source code line length: 80 chars
|
||||
- circuit '+' extended code lines: 250 chars
|
||||
- number of source lines: 1000
|
||||
- number of lines in expanded circuit: 5000
|
||||
- length of numparam identifiers: 20 chars
|
||||
- number of numparam identifiers: 200
|
||||
- length of file names: 80 chars
|
||||
- significant digits in param results: 5
|
||||
- nesting depth of parentheses 9
|
||||
- nesting of subckt calls 10
|
||||
|
||||
All these constants should be in the header file but aren't.
|
||||
|
||||
After each circuit expansion, numparam asks a silly question
|
||||
of the "Abort/Continue" type. A debugging feature, to be killed soon.
|
||||
|
||||
The Numparam symbol table accumulates the following sets of names:
|
||||
subcircuits, models, global parameters, subcircuit arguments.
|
||||
Node names, however, are completely ignored.
|
||||
|
||||
Call the following "bugs" or "features":
|
||||
- A model/subckt name cannot be defined twice, even if local to a subcircuit.
|
||||
- The same name cannot design a model here, and a parameter elsewhere.
|
||||
- A subcircuit argument masks any global parameter of same name,
|
||||
anytime the subckt is invoked. Inside a .subckt context, .param assignments
|
||||
also have local scope and override global identical names.
|
||||
|
||||
It is wise to always use unique names for everything.
|
||||
|
||||
|
||||
While Numparam is in 'early beta stage', I strongly suggest to use
|
||||
'nupatest' first, on any 'parameterized' Spice circuit file,
|
||||
before starting the enhanced circuit analyser.
|
||||
|
||||
The command
|
||||
nupatest foobar.cir
|
||||
produces an output file 'foobar.out' which is the expanded and
|
||||
parameter-reduced flat netlist.
|
||||
By the way, it produces error messages whenever it chokes on the source file.
|
||||
If nupatest succeeds, the spice+numparam combo should swallow it, too.
|
||||
Big bug: Nupatest does not yet prefix and infix things inside v() and i().
|
||||
|
||||
|
||||
Numparam comes with two very experimental files 'configure.in' and
|
||||
'Makefile.am' as an exercise of the automake/autoconf mechanisms.
|
||||
I certainly got a lot of things wrong and had to do _eight_ steps to
|
||||
have it kind of work:
|
||||
|
||||
1. edit/create configure.in
|
||||
2. edit/create Makefile.am
|
||||
3. run autoheader --> config.h.in
|
||||
4. run automake --foreign --add-missing --verbose --> Makefile.in
|
||||
5. run aclocal --> aclocal.m4
|
||||
6. run autoconf --> configure
|
||||
7. run ./configure --> Makefile config.h
|
||||
8. run make
|
||||
|
||||
Do we need all this, and -worse- do we need to repeat it whenever we touch
|
||||
'configure.in' and/or 'Makefile.am' ? Help!
|
||||
|
||||
|
||||
Please send your bug reports, improvements, flames etc. to the author:
|
||||
georg.post @ wanadoo.fr
|
||||
|
||||
|
|
@ -0,0 +1,550 @@
|
|||
/* spicenum.c Copyright (C) 2002 Georg Post
|
||||
*
|
||||
* This file is part of Numparam, see: readme.txt
|
||||
* Free software under the terms of the GNU Lesser General Public License
|
||||
*/
|
||||
|
||||
/* number parameter add-on for Spice.
|
||||
to link with mystring.o, xpressn.o (math formula interpreter),
|
||||
and with Spice frontend src/lib/fte.a .
|
||||
Interface function nupa_signal to tell us about automaton states.
|
||||
Buglist (some are 'features'):
|
||||
blank lines get category '*'
|
||||
inserts conditional blanks before or after braces
|
||||
between .control and .endc, flags all lines as 'category C', dont touch.
|
||||
there are reserved magic numbers (1e9 + n) as placeholders
|
||||
control lines must not contain {} .
|
||||
ignores the '.option numparam' line planned to trigger the actions
|
||||
operation of .include certainly doesnt work
|
||||
there are frozen maxima for source and expanded circuit size.
|
||||
Todo:
|
||||
add support for nested .if .elsif .else .endif controls.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef __TURBOC__
|
||||
#include <process.h> /* exit() */
|
||||
#endif
|
||||
|
||||
#include "general.h"
|
||||
#include "numparam.h"
|
||||
|
||||
/* the nupa_signal arguments sent from Spice:
|
||||
|
||||
sig=1: Start of the subckt expansion.
|
||||
sig=2: Stop of the subckt expansion.
|
||||
sig=3: Stop of the evaluation phase.
|
||||
sig=0: Start of a deck copy operation
|
||||
|
||||
After sig=1 until sig=2, nupa_copy does no transformations.
|
||||
At sig=2, we prepare for nupa_eval loop.
|
||||
After sig=3, we assume the initial state (clean).
|
||||
|
||||
In Clean state, a lot of deckcopy operations come in and we
|
||||
overwrite any line pointers, or we start a new set after each sig=0 ?
|
||||
Anyway, we neutralize all & and .param lines (category[] array!)
|
||||
and we substitute all {} &() and &id placeholders by dummy numbers.
|
||||
The placeholders are long integers 1000000000+n (10 digits, n small).
|
||||
|
||||
*/
|
||||
/********** string handling ***********/
|
||||
|
||||
#define PlaceHold 1000000000L
|
||||
Intern long placeholder= 0;
|
||||
|
||||
Intern
|
||||
Func short stripcomment( Pchar s)
|
||||
/* allow end-of-line comments in Spice, like C++ */
|
||||
Begin
|
||||
short i,ls;
|
||||
char c,d;
|
||||
Bool stop;
|
||||
ls=length(s);
|
||||
c=' '; i=0; stop=False;
|
||||
While (i<ls) And Not stop Do
|
||||
d=c;
|
||||
Inc(i); c=s[i-1];
|
||||
stop=(c==d) And ((c=='/')Or(c=='-')); /* comments after // or -- */
|
||||
Done
|
||||
If stop Then
|
||||
i=i-2; /*last valid character before Comment */
|
||||
While (i>0)And (s[i-1]<=' ') Do Dec(i) Done; /*strip blank space*/
|
||||
If i<=0 Then
|
||||
scopy(s,"")
|
||||
Else
|
||||
pscopy(s,s,1,i)
|
||||
EndIf
|
||||
Else
|
||||
i= -1
|
||||
EndIf
|
||||
return i /* i>=0 if comment stripped at that position */
|
||||
EndFunc
|
||||
|
||||
Intern
|
||||
Proc stripsomespace(Pchar s, Bool incontrol)
|
||||
Begin
|
||||
/* iff s starts with one of some markers, strip leading space */
|
||||
Str(12,markers);
|
||||
short i,ls;
|
||||
scopy(markers,"*.&+#$");
|
||||
If Not incontrol Then
|
||||
sadd(markers,"xX")
|
||||
EndIf
|
||||
ls=length(s); i=0;
|
||||
While (i<ls) And (s[i]<=' ') Do Inc(i) Done
|
||||
If (i>0) And (i<ls) And (cpos(s[i],markers) >0) Then
|
||||
pscopy(s,s,i+1,ls)
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
#if 0 /* unused? */
|
||||
Proc partition(Pchar t)
|
||||
/* t is a list val=expr val=expr .... Insert Lf-& before any val= */
|
||||
/* the Basic preprocessor doesnt understand multiple cmd/line */
|
||||
/* bug: strip trailing spaces */
|
||||
Begin
|
||||
Str(Llen,u);
|
||||
short i,lt,state;
|
||||
char c;
|
||||
cadd(u,Intro);
|
||||
state=0; /* a trivial 3-state machine */
|
||||
lt=length(t);
|
||||
While t[lt-1] <= ' ' Do Dec(lt) Done
|
||||
For i=0; i<lt; Inc(i) Do
|
||||
c=t[i];
|
||||
If c=='=' Then
|
||||
state=1
|
||||
ElsIf (state==1) And (c==' ') Then
|
||||
state=2
|
||||
EndIf
|
||||
If state==2 Then
|
||||
cadd(u,Lf); cadd(u,Intro);
|
||||
state=0
|
||||
EndIf
|
||||
cadd(u,c)
|
||||
Done
|
||||
scopy(t,u);
|
||||
For i=0; i<length(t); Inc(i) Do /* kill braces inside */
|
||||
If (t[i]=='{') Or (t[i]=='}') Then
|
||||
t[i]=' '
|
||||
EndIf
|
||||
Done
|
||||
EndProc
|
||||
#endif
|
||||
|
||||
Intern
|
||||
Func short stripbraces( Pchar s)
|
||||
/* puts the funny placeholders. returns the number of {...} substitutions */
|
||||
Begin
|
||||
short n,i,nest,ls,j;
|
||||
Str(Llen,t);
|
||||
n=0; ls=length(s);
|
||||
i=0;
|
||||
While i<ls Do
|
||||
If s[i]=='{' Then /* something to strip */
|
||||
j= i+1; nest=1;
|
||||
Inc(n);
|
||||
While (nest>0) And (j<ls) Do
|
||||
If s[j]=='{' Then
|
||||
Inc(nest)
|
||||
ElsIf s[j]=='}' Then
|
||||
Dec(nest)
|
||||
EndIf
|
||||
Inc(j)
|
||||
Done
|
||||
pscopy(t,s,1,i);
|
||||
Inc(placeholder);
|
||||
If t[i-1]>' ' Then cadd(t,' ') EndIf
|
||||
nadd(t, PlaceHold + placeholder);
|
||||
If s[j]>=' ' Then cadd(t,' ') EndIf
|
||||
i=length(t);
|
||||
pscopy(s,s, j+1, ls);
|
||||
sadd(t,s);
|
||||
scopy(s,t);
|
||||
Else
|
||||
Inc(i)
|
||||
EndIf
|
||||
ls=length(s)
|
||||
Done
|
||||
return n
|
||||
EndFunc
|
||||
|
||||
Intern
|
||||
Func short findsubname(tdico * dico, Pchar s)
|
||||
/* truncate the parameterized subckt call to regular old Spice */
|
||||
/* scan a string from the end, skipping non-idents and {expressions} */
|
||||
/* then truncate s after the last subckt(?) identifier */
|
||||
Begin
|
||||
Str(80, name);
|
||||
short h,j,k,nest,ls;
|
||||
Bool found;
|
||||
h=0;
|
||||
ls=length(s);
|
||||
k=ls; found=False;
|
||||
While (k>=0) And (Not found) Do /* skip space, then non-space */
|
||||
While (k>=0) And (s[k]<=' ') Do Dec(k) Done;
|
||||
h=k+1; /* at h: space */
|
||||
While (k>=0) And (s[k]>' ') Do
|
||||
If s[k]=='}' Then
|
||||
nest=1;
|
||||
Dec(k);
|
||||
While (nest>0) And (k>=0) Do
|
||||
If s[k]=='{' Then
|
||||
Dec(nest)
|
||||
ElsIf s[k]=='}' Then
|
||||
Inc(nest)
|
||||
EndIf
|
||||
Dec(k)
|
||||
Done
|
||||
h=k+1; /* h points to '{' */
|
||||
Else
|
||||
Dec(k)
|
||||
EndIf;
|
||||
Done
|
||||
found = (k>=0) And alfa(s[k+1]); /* suppose an identifier */
|
||||
If found Then /* check for known subckt name */
|
||||
scopy(name,""); j= k+1;
|
||||
While alfanum(s[j]) Do
|
||||
cadd(name, upcase(s[j])); Inc(j)
|
||||
Done
|
||||
found= (getidtype(dico, name) == 'U');
|
||||
EndIf
|
||||
Done
|
||||
If found And (h<ls) Then
|
||||
pscopy(s,s,1,h)
|
||||
EndIf
|
||||
return h;
|
||||
EndFunc
|
||||
|
||||
Intern
|
||||
Proc modernizeex( Pchar s)
|
||||
/* old style expressions &(..) and &id --> new style with braces. */
|
||||
Begin
|
||||
Str(250,t);
|
||||
short i,state, ls;
|
||||
char c,d;
|
||||
i=0; state=0;
|
||||
ls= length(s);
|
||||
While i<ls Do
|
||||
c= s[i]; d=s[i+1];
|
||||
If Zero(state) And (c==Intro) And (i>0) Then
|
||||
If d=='(' Then
|
||||
state=1; Inc(i); c='{'
|
||||
ElsIf alfa(d) Then
|
||||
cadd(t,'{'); Inc(i);
|
||||
While alfanum(s[i]) Do
|
||||
cadd(t,s[i]); Inc(i)
|
||||
Done
|
||||
c='}'; Dec(i);
|
||||
EndIf
|
||||
ElsIf NotZ(state) Then
|
||||
If c=='(' Then
|
||||
Inc(state)
|
||||
ElsIf c==')' Then
|
||||
Dec(state)
|
||||
EndIf
|
||||
If Zero(state) Then /* replace ) by terminator */
|
||||
c='}';
|
||||
EndIf
|
||||
EndIf
|
||||
cadd(t,c);
|
||||
Inc(i)
|
||||
Done
|
||||
scopy(s,t);
|
||||
EndProc
|
||||
|
||||
Intern
|
||||
Func char transform(tdico * dico, Pchar s, Bool nostripping, Pchar u)
|
||||
/* line s is categorized and crippled down to basic Spice
|
||||
* returns in u control word following dot, if any
|
||||
*
|
||||
* any + line is copied as-is.
|
||||
* any & or .param line is commented-out.
|
||||
* any .subckt line has params section stripped off
|
||||
* any X line loses its arguments after circuit name
|
||||
* any &id or &() or {} inside line gets a 10-digit substitute.
|
||||
*
|
||||
* strip the new syntax off the codeline s, and
|
||||
* return the line category as follows:
|
||||
* '*' comment line
|
||||
* '+' continuation line
|
||||
* ' ' other untouched netlist or command line
|
||||
* 'P' parameter line, commented-out; (name,linenr)-> symbol table.
|
||||
* 'S' subckt entry line, stripped; (name,linenr)-> symbol table.
|
||||
* 'U' subckt exit line
|
||||
* 'X' subckt call line, stripped
|
||||
* 'C' control entry line
|
||||
* 'E' control exit line
|
||||
* '.' any other dot line
|
||||
* 'B' netlist (or .model ?) line that had Braces killed
|
||||
*/
|
||||
Begin
|
||||
Str(Llen,t);
|
||||
char category;
|
||||
short i,k, a,n;
|
||||
i=stripcomment(s);
|
||||
stripsomespace(s, nostripping);
|
||||
modernizeex(s); /* required for stripbraces count */
|
||||
scopy(u,"");
|
||||
If s[0]=='.' Then /* check Pspice parameter format */
|
||||
scopy(t,s);
|
||||
stupcase(t);
|
||||
k=1;
|
||||
While t[k]>' ' Do
|
||||
cadd(u, t[k]); Inc(k)
|
||||
Done
|
||||
If spos(".PARAM",t) ==1 Then /* comment it out */
|
||||
s[0]='*';
|
||||
category='P';
|
||||
ElsIf spos(".SUBCKT",t) ==1 Then /* split off any "params" tail */
|
||||
a= spos("PARAMS:",t);
|
||||
If a>0 Then
|
||||
pscopy(s,s,1,a-1);
|
||||
EndIf
|
||||
category='S';
|
||||
ElsIf spos(".CONTROL",t) ==1 Then
|
||||
category='C'
|
||||
ElsIf spos(".ENDC",t) ==1 Then
|
||||
category='E'
|
||||
ElsIf spos(".ENDS",t) ==1 Then
|
||||
category='U'
|
||||
Else
|
||||
category='.';
|
||||
n= stripbraces(s);
|
||||
If n>0 Then category='B' EndIf /* priority category ! */
|
||||
EndIf
|
||||
ElsIf s[0]==Intro Then /* private style preprocessor line */
|
||||
s[0]='*';
|
||||
category='P';
|
||||
ElsIf upcase(s[0])=='X' Then /* strip actual parameters */
|
||||
i=findsubname(dico, s); /* i= index following last identifier in s */
|
||||
pscopy(s,s,1,i);
|
||||
category='X'
|
||||
ElsIf s[0]=='+' Then /* continuation line */
|
||||
category='+'
|
||||
ElsIf cpos(s[0],"*$#")<=0 Then /* not a comment line! */
|
||||
n= stripbraces(s);
|
||||
If n>0 Then
|
||||
category='B' /* line that uses braces */
|
||||
Else
|
||||
category=' '
|
||||
EndIf; /* ordinary code line*/
|
||||
Else
|
||||
category='*'
|
||||
EndIf
|
||||
return category
|
||||
EndFunc
|
||||
|
||||
/************ core of numparam **************/
|
||||
|
||||
/* some day, all these nasty globals will go into the tdico structure
|
||||
and everything will get hidden behind some "handle" ...
|
||||
*/
|
||||
|
||||
Intern int linecount= 0; /* global: number of lines received via nupa_copy */
|
||||
Intern int evalcount= 0; /* number of lines through nupa_eval() */
|
||||
Intern int nblog=0; /* serial number of (debug) logfile */
|
||||
Intern Bool inexpansion= False; /* flag subckt expansion phase */
|
||||
Intern Bool incontrol= False; /* flag control code sections */
|
||||
Intern Bool dologfile= True; /* for debugging */
|
||||
Intern Bool firstsignal=True;
|
||||
Intern Pfile logfile= Null;
|
||||
Intern tdico * dico=Null;
|
||||
|
||||
/* already part of dico : */
|
||||
/* Str(80, srcfile); source file */
|
||||
/* Darray(refptr, Pchar, Maxline) pointers to source code lines */
|
||||
/* Darray(category, char, Maxline) category of each line */
|
||||
|
||||
Intern
|
||||
Proc putlogfile(char c, int num, Pchar t)
|
||||
Begin
|
||||
Str(Llen, u);
|
||||
If dologfile And (logfile != Null) Then
|
||||
cadd(u,c); nadd(u,num);
|
||||
cadd(u,':'); cadd(u,' ');
|
||||
sadd(u,t); cadd(u,'\n');
|
||||
fputs(u,logfile);
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
Intern
|
||||
Proc nupa_init( Pchar srcfile)
|
||||
Begin
|
||||
short i;
|
||||
Str(20,fname);
|
||||
/* init the symbol table and so on, before the first nupa_copy. */
|
||||
evalcount=0;
|
||||
linecount= 0;
|
||||
incontrol=False;
|
||||
placeholder= 0;
|
||||
/* If logfile != Null Then fclose(logfile) EndIf */
|
||||
If dologfile And (logfile==Null) Then
|
||||
scopy(fname,"logfile.");
|
||||
Inc(nblog); nadd(fname,nblog);
|
||||
logfile=fopen(fname, "w");
|
||||
EndIf
|
||||
dico= New(tdico);
|
||||
initdico(dico);
|
||||
For i=0; i<Maxline; Inc(i) Do
|
||||
dico->refptr[i]= Null;
|
||||
dico->category[i]='?';
|
||||
Done
|
||||
Sini(dico->srcfile);
|
||||
If srcfile != Null Then scopy(dico->srcfile, srcfile) EndIf
|
||||
EndProc
|
||||
|
||||
Intern
|
||||
Proc nupa_done(void)
|
||||
Begin
|
||||
short i;
|
||||
Str(80,rep);
|
||||
short dictsize, nerrors;
|
||||
If logfile != Null Then
|
||||
fclose(logfile);
|
||||
logfile=Null;
|
||||
EndIf
|
||||
nerrors= dico->errcount;
|
||||
dictsize= donedico(dico);
|
||||
For i=Maxline-1; i>=0; Dec(i) Do
|
||||
Dispose( dico->refptr[i])
|
||||
Done
|
||||
Dispose(dico);
|
||||
dico= Null;
|
||||
/* debug: ask if spice run really wanted */
|
||||
scopy(rep," Copies="); nadd(rep,linecount);
|
||||
sadd(rep," Evals="); nadd(rep,evalcount);
|
||||
sadd(rep," Placeholders="); nadd(rep,placeholder);
|
||||
sadd(rep," Symbols="); nadd(rep,dictsize);
|
||||
sadd(rep," Errors="); nadd(rep,nerrors);
|
||||
cadd(rep,'\n'); ws(rep);
|
||||
ws("Expansion ");
|
||||
If Zero(nerrors) Then ws("done") Else ws("errors") EndIf
|
||||
ws(": Really run Spice y/n ? \n");
|
||||
rs(rep);
|
||||
If upcase(rep[0]) != 'Y' Then exit(-1) EndIf
|
||||
linecount= 0;
|
||||
evalcount= 0;
|
||||
placeholder= 0;
|
||||
/* release symbol table data */
|
||||
EndProc
|
||||
|
||||
Func Pchar nupa_copy(Pchar s, int linenum)
|
||||
/* returns a copy (not quite) of s in freshly allocated memory.
|
||||
linenum, for info only, is the source line number.
|
||||
origin pointer s is kept, memory is freed later in nupa_done.
|
||||
must abort all Spice if malloc() fails.
|
||||
Is called for the first time sequentially for all spice deck lines.
|
||||
Is then called again for all X invocation lines, top-down for
|
||||
subckts defined at the outer level, but bottom-up for local
|
||||
subcircuit expansion, but has no effect in that phase.
|
||||
we steal a copy of the source line pointer.
|
||||
- comment-out a .param or & line
|
||||
- substitute placeholders for all {..} --> 10-digit numeric values.
|
||||
*/
|
||||
Begin
|
||||
Str(250,u);
|
||||
Str(250,keywd);
|
||||
Pchar t;
|
||||
short i,ls;
|
||||
char c,d;
|
||||
ls= length(s);
|
||||
While (ls>0) And (s[ls-1]<=' ') Do Dec(ls) Done
|
||||
pscopy(u,s, 1,ls); /* strip trailing space, CrLf and so on */
|
||||
dico->srcline= linenum;
|
||||
If (Not inexpansion) And (linenum >=0) And (linenum<Maxline) Then
|
||||
Inc(linecount);
|
||||
dico->refptr[linenum]= s;
|
||||
c= transform(dico, u, incontrol, keywd);
|
||||
If c=='C' Then
|
||||
incontrol=True
|
||||
ElsIf c=='E' Then
|
||||
incontrol=False
|
||||
EndIf
|
||||
If incontrol Then c='C' EndIf /* force it */
|
||||
d= dico->category[linenum]; /* warning if already some strategic line! */
|
||||
If (d=='P') Or (d=='S') Or (d=='X') Then
|
||||
fputs(" Numparam warning: overwriting P,S or X line.\n",stderr);
|
||||
EndIf
|
||||
If c=='S' Then
|
||||
defsubckt( dico, s, linenum, 'U' )
|
||||
ElsIf steq(keywd,"MODEL") Then
|
||||
defsubckt( dico, s, linenum, 'O' )
|
||||
EndIf; /* feed symbol table */
|
||||
dico->category[linenum]= c;
|
||||
EndIf /* keep a local copy and mangle the string */
|
||||
ls=length(u);
|
||||
t= NewArr( char, ls+1); /* == (Pchar)malloc(ls+1); */
|
||||
If t==NULL Then
|
||||
fputs("Fatal: String malloc crash in nupa_copy()\n", stderr);
|
||||
exit(-1)
|
||||
Else
|
||||
For i=0;i<=ls; Inc(i) Do
|
||||
t[i]=u[i]
|
||||
Done
|
||||
If Not inexpansion Then
|
||||
putlogfile(dico->category[linenum],linenum,t)
|
||||
EndIf;
|
||||
EndIf
|
||||
return t
|
||||
EndFunc
|
||||
|
||||
Func int nupa_eval(Pchar s, int linenum)
|
||||
/* s points to a partially transformed line.
|
||||
compute variables if linenum points to a & or .param line.
|
||||
If the original is an X line, compute actual params.
|
||||
Else substitute any &(expr) with the current values.
|
||||
All the X lines are preserved (commented out) in the expanded circuit.
|
||||
*/
|
||||
Begin
|
||||
short idef; /* subckt definition line */
|
||||
char c;
|
||||
Str(80,subname);
|
||||
dico->srcline= linenum;
|
||||
c= dico->category[linenum];
|
||||
If c=='P' Then /* evaluate parameters */
|
||||
nupa_assignment( dico, dico->refptr[linenum] , 'N');
|
||||
ElsIf c=='B' Then /* substitute braces line */
|
||||
nupa_substitute( dico, dico->refptr[linenum], s, False);
|
||||
ElsIf c=='X' Then /* compute args of subcircuit, if required */
|
||||
idef = findsubckt( dico, s, subname);
|
||||
If idef>0 Then
|
||||
nupa_subcktcall( dico,
|
||||
dico->refptr[idef], dico->refptr[linenum], False);
|
||||
Else
|
||||
putlogfile('?',linenum, " illegal subckt call.");
|
||||
EndIf
|
||||
ElsIf c=='U' Then /* release local symbols = parameters */
|
||||
nupa_subcktexit( dico);
|
||||
EndIf
|
||||
putlogfile('e',linenum,s);
|
||||
Inc(evalcount);
|
||||
return 1
|
||||
EndFunc
|
||||
|
||||
Func int nupa_signal(int sig, Pchar info)
|
||||
/* warning: deckcopy may come inside a recursion ! substart no! */
|
||||
/* info is context-dependent string data */
|
||||
Begin
|
||||
putlogfile('!',sig, " Nupa Signal");
|
||||
If sig == NUPADECKCOPY Then
|
||||
If firstsignal Then
|
||||
nupa_init(info);
|
||||
firstsignal=False;
|
||||
EndIf
|
||||
ElsIf sig == NUPASUBSTART Then
|
||||
inexpansion=True
|
||||
ElsIf sig == NUPASUBDONE Then
|
||||
inexpansion=False
|
||||
ElsIf sig == NUPAEVALDONE Then
|
||||
nupa_done();
|
||||
firstsignal=True
|
||||
EndIf
|
||||
return 1
|
||||
EndFunc
|
||||
|
||||
Func tdico * nupa_fetchinstance(void)
|
||||
Begin
|
||||
return dico
|
||||
EndFunc
|
||||
|
||||
|
|
@ -0,0 +1,996 @@
|
|||
/* washprog.c Copyright (C) 2002 Georg Post
|
||||
*
|
||||
* This file is part of Numparam, see: readme.txt
|
||||
* Free software under the terms of the GNU Lesser General Public License
|
||||
*/
|
||||
|
||||
/**** washprog: trivial text substitution utility. ****/
|
||||
|
||||
/* history: this was an exercise to make an 'intersection' language
|
||||
of C and Java, that would look like Basic. A complete failure, of course.
|
||||
|
||||
Now only used to clean my Basic/Pascal-contaminated C code.
|
||||
With the rules file below, it destroys all those macros of mine for
|
||||
quiche eaters, which seem offensive to C aficionados.
|
||||
|
||||
Standard rules file needed : downgrad.txt
|
||||
|
||||
Typical command line: ./washprog -r downgrad washprog.c
|
||||
|
||||
There is no printf. Console Output/Input primitives are as follows:
|
||||
wc ws wr wn wi wln rln
|
||||
The bare-bones string(=Pchar) manipulation library is this:
|
||||
pscopy streq str length upcase scopy sadd saddn cadd pos
|
||||
|
||||
Format of substitution rules:
|
||||
|
||||
s <string> <string> substitute. use "" around string if spaces inside.
|
||||
w <string> <string> first string must be a whole word only
|
||||
m <macro1> <macro2> macro substitution with args 1 2 3 ...
|
||||
u <macro1> <macro2> macro with atomic args, no punctuation "(;,:)" inside.
|
||||
x <strng1> <strng2> exclude text section from strng1 to strng2.
|
||||
a <mac1> <mac2> dynamically add a new macro rule, if table space left.
|
||||
|
||||
string: may contain special chars: ^A ... ^Z \n \"
|
||||
macro1: string with "placeholders" 1 2 ... 9, in this order
|
||||
macro2: may contain the "arguments" anywhere
|
||||
non-arg digits in macro2 are prefixed 0
|
||||
|
||||
Heavy use of 3 string operations:
|
||||
- pscopy() substring extraction.
|
||||
- comparison: match().
|
||||
- spos() substring search
|
||||
|
||||
added : special postprocessing for C to place the ; and } :
|
||||
1. any ';' following a ';' or '}' is wiped out.
|
||||
2. any ';' preceding a '}' is wiped out.
|
||||
3. any remaining ';' on start of line is shifted to end of preceding one.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* NULL FILE fopen feof fgets fclose fputs fputc gets */
|
||||
#include "general.h"
|
||||
|
||||
Cconst(nsub, 100+1) /*max nbr of substitution rules */
|
||||
Cconst(nargs, 11) /*max number of macro args + 1*/
|
||||
Cconst(wild,'æ') /* wildcard character in patterns */
|
||||
Cconst(joker,1) /* one-character placeholder */
|
||||
Cconst( Recursion, True) /* 20 % slower, re-substitute inside macro args */
|
||||
|
||||
Tarray(macargs, string, nargs) /* 0..9 macro copy args, 10: a wildcard */
|
||||
|
||||
/* global vars */
|
||||
short isr; /* nb of substitution rules */
|
||||
Bool cMode; /* a scanning options: c language mode */
|
||||
short lookmax; /* input lookahead max size */
|
||||
Pfile fout; /* file filled by: echoOut macroOut translate traduire */
|
||||
|
||||
Tarray(str40, char, 44)
|
||||
Tarray(str80, char, 84)
|
||||
Darray(search, str40, nsub)
|
||||
Darray(replace, str80, nsub)
|
||||
Str(nsub, srule);
|
||||
Str(nsub, wildcard);
|
||||
|
||||
/********* trivial io ***/
|
||||
|
||||
Proc wsf( Pchar s, short fmt)
|
||||
Begin
|
||||
short k;
|
||||
For k=1; k<=fmt-length(s); Inc(k) Do
|
||||
wc(' ')
|
||||
Done
|
||||
ws(s)
|
||||
EndProc
|
||||
|
||||
Proc wcf(char c, short fmt)
|
||||
Begin
|
||||
short k;
|
||||
For k=1; k<=fmt-1; Inc(k) Do
|
||||
wc(' ')
|
||||
Done
|
||||
wc(c)
|
||||
EndProc
|
||||
|
||||
Proc wif(long i, short fmt)
|
||||
Begin /*default fmt=1*/
|
||||
Str(30, s);
|
||||
nadd(s,i);
|
||||
wsf(s,fmt)
|
||||
EndProc
|
||||
|
||||
Proc rln(Pchar s) /* 78 column limit */
|
||||
Begin
|
||||
short i; Bool done; char c;
|
||||
short max=maxlen(s);
|
||||
If max>78 Then max=78 EndIf
|
||||
i=0; done=False;
|
||||
scopy(s,"");
|
||||
While Not done Do
|
||||
c=fgetc(stdin);
|
||||
If (c>=' ') And (c<='~') And (i<max) Then
|
||||
cadd(s,c); Inc(i)
|
||||
EndIf
|
||||
done= (c=='\n') Or (c==Cr)
|
||||
Done
|
||||
EndProc
|
||||
|
||||
/*****************/
|
||||
|
||||
Proc saddn( Pchar s, Pchar t, short n)
|
||||
Begin
|
||||
Str(250,u);
|
||||
short lt= length(t);
|
||||
If lt<= n Then
|
||||
sadd(s,t)
|
||||
Else
|
||||
pscopy(u,t,1,n);
|
||||
sadd(s,u)
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
Proc allocdata(void)
|
||||
Begin /* prevent any string overflow */
|
||||
short i;
|
||||
For i=0; i<nsub; Inc(i) Do
|
||||
Sini(search[i]);
|
||||
Sini(replace[i])
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Proc setOptions(Pchar s)
|
||||
/* command-line options c-mode and/or lookahead buffer size */
|
||||
Begin
|
||||
short j,k;
|
||||
Bool num;
|
||||
short z;
|
||||
char c;
|
||||
/*-StartProc-*/
|
||||
ws("Options: ");
|
||||
For j=1; j<length(s); Inc(j) Do /*scan for option setting chars */
|
||||
If s[j]=='C' Then
|
||||
cMode=True; ws("cMode ")
|
||||
EndIf
|
||||
If s[j]=='L' Then /*redefine max lookahead length */
|
||||
z=0;
|
||||
k= (short)(j+1);
|
||||
Repeat
|
||||
Inc(k); c=s[k];
|
||||
num= (c>='0') And (c<='9');
|
||||
If num Then z= (short)( 10*z+ c - '0') EndIf
|
||||
Until Not num EndRep
|
||||
If (z>lookmax) And (z<255) Then
|
||||
lookmax= z
|
||||
EndIf
|
||||
ws("Lookahead="); wi(lookmax);
|
||||
EndIf
|
||||
Done
|
||||
wln();
|
||||
EndProc
|
||||
|
||||
/******** matching routines *******/
|
||||
|
||||
Proc copySpace(Pchar s, Pchar t, short a, short b) /* a,b>0 ! Pascal indexing */
|
||||
Begin
|
||||
/*echo any "nontrivial" whitespace t-->s */
|
||||
short lt,i,k, comment;
|
||||
Bool leader;
|
||||
char c;
|
||||
/*-StartProc-*/
|
||||
scopy(s,"");
|
||||
leader=False; /*leader space on new line...*/
|
||||
k=0;
|
||||
comment=0; /* for C type whitespaces 1 And 2*/
|
||||
lt= length(t);
|
||||
If b>lt Then b=lt EndIf
|
||||
For i=(short)(a-1); i<b; Inc(i) Do
|
||||
c=t[i];
|
||||
If (c>0) And (c<' ') Then leader=True EndIf
|
||||
If cMode And (c=='/') And (t[i+1]=='*') Then comment=1 EndIf
|
||||
If ((c>0) And (c<' ')) Or (leader And (c==' ')) Or (comment>0) Then
|
||||
cadd(s,c); Inc(k);
|
||||
EndIf
|
||||
If (comment==1) And (c=='/') And (t[i-1]=='*') Then comment=0 EndIf
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Func short skipCwhite(Pchar t, short j, short lt) /* assume C indexing */
|
||||
Begin
|
||||
/* skip any C And C++ type whitespace in t, from j to lt */
|
||||
/* returns j-1 If current char is no white at all! */
|
||||
char c;
|
||||
short comment; /*types 1 And 2! */
|
||||
/*t[j] may already be '/' ? */ comment=0;
|
||||
c=t[j]; /*If c>' ', we are done! */
|
||||
If (c>0) And (c<=' ') Then
|
||||
Repeat
|
||||
If (comment==0) And (c=='/') Then
|
||||
If t[j+1]=='*' Then
|
||||
comment=1
|
||||
ElsIf t[j+1]=='/' Then
|
||||
comment=2
|
||||
EndIf
|
||||
ElsIf (comment==1) And (c=='/') And (t[j-1]=='*') Then
|
||||
comment=0
|
||||
ElsIf (comment==2) And (c==Lf) Then
|
||||
comment=0
|
||||
EndIf
|
||||
Inc(j); c=t[j];
|
||||
Until (j>lt) Or ((comment==0) And (c>' ')) EndRep
|
||||
EndIf
|
||||
return (short)(j-1); /* return last white-matching char position */
|
||||
EndProc
|
||||
|
||||
Func Bool simple(Pchar s)
|
||||
Begin /* check if no strange punctuations inside s */
|
||||
char c;
|
||||
short i,ls;
|
||||
Bool found;
|
||||
/*-StartProc-*/
|
||||
ls=length(s);
|
||||
i=0;
|
||||
Repeat c=s[i];
|
||||
found=(c=='(') Or (c==')') Or (c==',') Or (c==';') Or (c==':');
|
||||
Inc(i);
|
||||
Until found Or (i>=ls) EndRep
|
||||
return Not found;
|
||||
EndFunc
|
||||
|
||||
Func Bool match(Pchar s, Pchar t, short n, short tstart)
|
||||
Begin
|
||||
/* test if t starts with substring s.
|
||||
returns 0 If tstart is out of range. But n may be 0 ?
|
||||
options: Singlechar wildcards "?"
|
||||
*/
|
||||
short i,j,lt;
|
||||
Bool ok;
|
||||
/*-StartProc-*/
|
||||
i=0; j=tstart;
|
||||
lt= length(t);
|
||||
ok=(tstart<lt);
|
||||
While ok And (i<n) Do
|
||||
ok= (j<lt) And ((s[i]==t[j]) Or (s[i]==joker));
|
||||
Inc(i); Inc(j);
|
||||
Done
|
||||
return ok
|
||||
EndFunc
|
||||
|
||||
Func short posi(Pchar sub, Pchar s)
|
||||
Begin /*re-defines Turbo Pos, result Pascal compatible */
|
||||
short a,b,k;
|
||||
Bool ok;
|
||||
/*-StartProc-*/
|
||||
ok=False;
|
||||
a=length(sub);
|
||||
b=(short)(length(s)-a);
|
||||
k=0;
|
||||
If a>0 Then /*Else return 0*/
|
||||
While (k<=b) And (Not ok) Do
|
||||
ok=match(sub,s, a,k); /*remark we must start at k=0 ! */
|
||||
Inc(k);
|
||||
Done
|
||||
EndIf
|
||||
If ok Then
|
||||
return k
|
||||
Else
|
||||
return 0
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func short matchwhite(Pchar s, Pchar t, short n, short tstart)
|
||||
Begin
|
||||
/* like match, but any whitespace in t matches space in s*/
|
||||
short i,j,lt; Bool ok;
|
||||
/*-StartProc-*/
|
||||
i=0; j=tstart;
|
||||
lt= length(t);
|
||||
ok=(tstart<lt);
|
||||
While ok And (i<n) Do
|
||||
If s[i]==' ' Then /* always Ok, skip space in t */
|
||||
If cMode Then
|
||||
j=skipCwhite(t,j,lt)
|
||||
Else
|
||||
While (j<=lt) And (t[j]<=' ') And (t[j]>0) Do Inc(j) Done
|
||||
Dec(j);
|
||||
EndIf
|
||||
Repeat
|
||||
Inc(j)
|
||||
Until (j>=lt) Or (t[j]>' ') EndRep /*skip space in t*/
|
||||
Dec(j);
|
||||
Else
|
||||
ok= (j<=lt) And ((s[i]==t[j]) Or (s[i]==joker));
|
||||
EndIf
|
||||
Inc(i); Inc(j);
|
||||
Done
|
||||
If ok Then
|
||||
return (short)(j-tstart)
|
||||
Else
|
||||
return (short)0
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func short posizero(Pchar sub, Pchar s)
|
||||
Begin /*another Pos */
|
||||
/* substring search. like posi, but reject quotes & bracketed stuff */
|
||||
short a,b,k;
|
||||
Bool ok;
|
||||
short blevel;
|
||||
char c;
|
||||
/*-StartProc-*/
|
||||
ok=False;
|
||||
a=length(sub);
|
||||
b=(short)(length(s)-a);
|
||||
k=0; blevel=0;
|
||||
If a>0 Then /*Else return 0*/
|
||||
While (k<=b) And (Not ok) Do
|
||||
ok= (matchwhite(sub,s, a,k)>0);
|
||||
If (k<=b) And (Not ok) Then
|
||||
c=s[k];
|
||||
If (c==')') Or (c==']') Or (c=='}') Then
|
||||
If c!=sub[0] Then Dec(blevel) EndIf /*negative level: fail!*/
|
||||
If blevel<0 Then k=b EndIf
|
||||
ElsIf (c=='\'') Or (c=='\"') Then /*skip quote */
|
||||
Repeat Inc(k)
|
||||
Until (k>=b) Or (s[k]==c) EndRep
|
||||
ElsIf (c=='(') Or (c=='[') Or (c=='{') Then /*skip block*/
|
||||
Inc(blevel); /*counts the bracketing level */
|
||||
Repeat
|
||||
Inc(k); c=s[k];
|
||||
If (c=='(') Or (c=='[') Or (c=='{') Then
|
||||
Inc(blevel)
|
||||
ElsIf (c==')') Or (c==']') Or (c=='}') Then
|
||||
Dec(blevel)
|
||||
EndIf
|
||||
Until (k>=b) Or (blevel==0) EndRep
|
||||
EndIf
|
||||
EndIf
|
||||
Inc(k);
|
||||
Done
|
||||
EndIf
|
||||
If ok Then
|
||||
return k
|
||||
Else
|
||||
return 0
|
||||
EndIf
|
||||
EndFunc
|
||||
|
||||
Func short isMacro(Pchar s, char option, Pchar t, short tstart,
|
||||
string maccopy[] )
|
||||
/* s= macro template, t=buffer, maccopy = arg Array
|
||||
return value: number of characters matched,
|
||||
restrictive option: 'u'
|
||||
macro substitution args 1 2 3 ...9.
|
||||
sample: bla1tra2gla3vla matches "bla ME tra YOU gla HIM vla"
|
||||
substitute 1 by maccopy[1] etc
|
||||
*/
|
||||
Begin
|
||||
Darray(ps, short, nargs+1)
|
||||
Word j,k,dk,ls, lst, lmt, jmax, pj;
|
||||
Bool ok;
|
||||
char arg;
|
||||
Str(250,u);
|
||||
Str(40,st);
|
||||
/* returns >0 If comparison Ok == length of compared Pchar */
|
||||
/*-StartProc-*/ k=0;
|
||||
ok= (s[0]==t[tstart]); /* shortcut: how much does it accelerate ? some % */
|
||||
If ok Then
|
||||
ps[0]=0;
|
||||
ps[nargs]=0; /*only 1..9 are valid data, 10 filler templates*/
|
||||
j=0;
|
||||
Repeat
|
||||
Inc(j); arg= (char)(j+'0');
|
||||
ps[j]= cpos(arg,s);
|
||||
Until (j>=nargs) Or (ps[j]==0) EndRep
|
||||
ls= length(s);
|
||||
ps[j]=(short)(ls+1); /*For last template chunk*/
|
||||
jmax=j; j=1;
|
||||
k=0; lmt=0;
|
||||
Repeat
|
||||
pscopy(st,s, (Word)(ps[j-1]+1), (Word)(ps[j]-ps[j-1]-1) );
|
||||
/*j-th template Pchar*/ lst=length(st);
|
||||
If j==1 Then
|
||||
If option=='u' Then
|
||||
lmt= matchwhite(st,t,lst,tstart);
|
||||
ok=(lmt>0) /*length of match in t*/
|
||||
Else
|
||||
ok= match(st,t,lst,tstart)
|
||||
EndIf
|
||||
If ok Then
|
||||
pscopy(u,t, (Word)(tstart+1), (Word)255);
|
||||
pj=1
|
||||
Else
|
||||
pj=0
|
||||
EndIf
|
||||
Else
|
||||
If option=='u' Then
|
||||
pj= posizero(st,u);
|
||||
If pj>0 Then lmt= matchwhite(st,u, lst, (short)(pj-1)) EndIf
|
||||
Else
|
||||
pj= posi(st,u)
|
||||
EndIf /* qs[j]= k+pj; is position in t*/
|
||||
ok=(pj>0);
|
||||
EndIf
|
||||
If ok Then
|
||||
If option=='u' Then
|
||||
If j==1 Then scopy(maccopy[0],"") EndIf
|
||||
saddn(maccopy[j-1],u, (Word)(pj-1));
|
||||
dk= (Word)(pj+lmt);
|
||||
copySpace(maccopy[j], t,
|
||||
(Word)(tstart+k+pj), (Word)(tstart+k+dk));
|
||||
/* space in t[k+pj...k+dk] goes into maccopy[j] as a prefix. */
|
||||
Else
|
||||
pscopy(maccopy[j-1],u, (Word)1, (Word)(pj-1));
|
||||
/*the stuff preceding the marker*/
|
||||
dk= (Word)(pj+lst); /* start of unexplored part */
|
||||
EndIf
|
||||
pscopy(u,u, (Word)dk, (Word)length(u)); /*shift in the rest*/
|
||||
k= (Word)(k+dk-1);
|
||||
EndIf
|
||||
Inc(j)
|
||||
Until (j>jmax) Or (Not ok) EndRep
|
||||
EndIf
|
||||
If Not ok Then k=0 EndIf
|
||||
return k
|
||||
EndFunc
|
||||
|
||||
Func short similar(Pchar s, char wilds, Pchar t,
|
||||
short tstart, string maccopy[] )
|
||||
/* try to match s with t, then save the wildcard parts ins maccopy[] */
|
||||
/* s=template, t=buffer, wilds= number of wildcards, maccopy=substitute */
|
||||
/* return value: number of characters matched */
|
||||
Begin
|
||||
Word j,k,ps,ls;
|
||||
Bool ok;
|
||||
char endc;
|
||||
Str(250,u);
|
||||
/* returns >0 if comparison Ok = length of compared string */
|
||||
/* char comparison, s may have wildcard regions with "æ" BUT 1 valid End */
|
||||
/*-StartProc-*/
|
||||
ls=length(s);
|
||||
k=0;
|
||||
If wilds==wild Then
|
||||
ps= cpos(wild,s)
|
||||
Else
|
||||
ps=0
|
||||
EndIf
|
||||
If ps==0 Then
|
||||
If match(s,t,ls,tstart) Then
|
||||
k=ls;
|
||||
ps= cpos(joker,s); /*save joker's substitute*/
|
||||
If ps>0 Then
|
||||
maccopy[nargs][0]=t[ps-1+tstart]
|
||||
EndIf
|
||||
Else
|
||||
k=0
|
||||
EndIf
|
||||
Else
|
||||
k= (Word)(ps-1);
|
||||
While s[k]==wild Do Inc(k) Done
|
||||
endc=s[k]; /*End char to detect, at length */
|
||||
ok= match(s,t, (short)(ps-1), tstart);
|
||||
If ok Then
|
||||
pscopy(u,t, (Word)(ps+tstart), (Word)255);
|
||||
j= cpos(endc, u);
|
||||
ok=(j>0);
|
||||
If ok Then
|
||||
k= (Word)(ps+j-1);
|
||||
pscopy(maccopy[nargs],t, (Word)(ps+tstart), (Word)(j-1));
|
||||
EndIf
|
||||
EndIf
|
||||
If Not ok Then k=0 EndIf
|
||||
EndIf
|
||||
return k
|
||||
EndProc
|
||||
|
||||
Func short addSubList(Pchar s, short isr)
|
||||
/* add the rule s to the Rule list at isr */
|
||||
Begin
|
||||
short j,ls;
|
||||
char c,d,endc;
|
||||
Bool start,stop;
|
||||
/*-StartProc-*/
|
||||
ls=length(s); /* must kill the Newline */
|
||||
endc=' ';
|
||||
While (ls>0) And (s[ls]<' ') Do Dec(ls) Done;
|
||||
s[ls+1]=' ';
|
||||
s[ls+2]=0; /* add a space */
|
||||
If s[0]=='o' Then
|
||||
setOptions(s)
|
||||
ElsIf (isr<nsub) And (cpos(s[0],"swmuxa") >0) Then
|
||||
j=1;
|
||||
Inc(isr);
|
||||
scopy(search[isr],""); scopy(replace[isr],"");
|
||||
srule[isr]=(s[0]);
|
||||
wildcard[isr]=0;
|
||||
/*init search*/
|
||||
start=True; stop=False;
|
||||
d=0;
|
||||
While Not stop Do
|
||||
Inc(j); c=s[j];
|
||||
If start Then
|
||||
If c !=' ' Then
|
||||
start=False;
|
||||
If c=='\"' Then endc=c Else endc=' ' EndIf
|
||||
EndIf
|
||||
Else
|
||||
stop=(c==endc)
|
||||
EndIf
|
||||
If Not (start Or (c==endc)) Then
|
||||
If c=='?' Then
|
||||
c=joker
|
||||
ElsIf (c=='^') And (s[j+1]>= ' ') Then
|
||||
Inc(j); c=s[j];
|
||||
If (c>='@') And (c<='_') Then
|
||||
c= (char)(c-'@')
|
||||
EndIf
|
||||
ElsIf (c=='\\') And (s[j+1]>= ' ') Then
|
||||
Inc(j); c=s[j];
|
||||
If c=='n' Then c= Cr; d=Lf EndIf
|
||||
EndIf
|
||||
cadd(search[isr],c);
|
||||
If (c==wild) Or (c==joker) Then
|
||||
wildcard[isr]=c
|
||||
EndIf
|
||||
If d!=0 Then
|
||||
cadd(search[isr],d);
|
||||
d=0
|
||||
EndIf
|
||||
EndIf
|
||||
Done
|
||||
If endc!=' ' Then Inc(j) EndIf
|
||||
/*init replace*/
|
||||
start=True; stop=False;
|
||||
d=0;
|
||||
While Not stop Do
|
||||
Inc(j); c=s[j];
|
||||
If start Then
|
||||
If c!=' ' Then
|
||||
start=False;
|
||||
If c=='\"' Then endc=c Else endc=' ' EndIf
|
||||
EndIf
|
||||
Else
|
||||
stop=(c==endc)
|
||||
EndIf
|
||||
If Not (start Or (c==endc)) Then
|
||||
If c=='?' Then
|
||||
c=joker
|
||||
ElsIf (c=='^') And (s[j+1]>= ' ') Then
|
||||
Inc(j); c=s[j];
|
||||
If (c>='@') And (c<='Z') Then c= (char)(c-'@') EndIf
|
||||
ElsIf (c=='\\') And (s[j+1]>= ' ') Then
|
||||
Inc(j); c=s[j]; /*echo next char */
|
||||
If c=='n' Then c=Cr; d=Lf EndIf
|
||||
EndIf
|
||||
cadd(replace[isr],c);
|
||||
If d!=0 Then
|
||||
cadd(replace[isr],d);
|
||||
d=0
|
||||
EndIf
|
||||
EndIf
|
||||
Done
|
||||
If endc !=' ' Then Inc(j) EndIf
|
||||
EndIf
|
||||
If isr>=nsub Then
|
||||
ws("No more room for rules."); wln()
|
||||
EndIf
|
||||
return isr
|
||||
EndFunc
|
||||
|
||||
Func Bool getSubList(Pchar slist)
|
||||
/* read the search and substitution rule list */
|
||||
Begin
|
||||
Str(250,s);
|
||||
Pfile f;
|
||||
Bool done, ok;
|
||||
/*-StartProc-*/
|
||||
cMode=False;
|
||||
lookmax= 80; /* or 250: handle 4 full lines maximum ? */
|
||||
If Zero(slist[0]) Then
|
||||
scopy(slist, "slist.txt")
|
||||
EndIf
|
||||
f=fopen(slist,"rb");
|
||||
isr=0;
|
||||
done= (f == Null);
|
||||
ok= Not done;
|
||||
While Not done Do
|
||||
fgets(s,(short)80,f);
|
||||
isr=addSubList(s,isr);
|
||||
done= feof(f)
|
||||
Done
|
||||
If f != Null Then fclose(f) EndIf
|
||||
ws("Number of rules: ");
|
||||
wi(isr); wln();
|
||||
return ok
|
||||
EndFunc
|
||||
|
||||
Func Bool nonAlfa(char c)
|
||||
Begin
|
||||
return ((c<'a') Or (c>'z')) And ((c<'A') Or (c>'Z'))
|
||||
EndFunc
|
||||
|
||||
/********** optional output postprocessor **************/
|
||||
|
||||
/* the main translator calls these:
|
||||
washinit to reset the postprocessor
|
||||
washchar to output a char
|
||||
washstring to output a string
|
||||
washflush to terminate
|
||||
*/
|
||||
|
||||
/* C reformatter, keeping an eye on the following (modulo whitespace):
|
||||
; } Lf.
|
||||
|
||||
This is just a state machine, handling 3 rules using an output buffer obf.
|
||||
<white> means space excluding \n, and <white2>, space including newlines.
|
||||
Wanted: regular-expression scripts or tricks to do the same or better...
|
||||
|
||||
Rule1: <white>Lf<white>; --> ;<white>Lf<white> states 2 3
|
||||
Rule2: ;<white2>; --> ;<white2> state 1
|
||||
Rule3: }<white2>; --> }<white2> state 1
|
||||
*/
|
||||
|
||||
Bool washmore= True; /* flag that activates the postprocessor */
|
||||
Str(250,obf); /* output buffer */
|
||||
short iobf=0; /* its index */
|
||||
short wstate=0; /* output state machine */
|
||||
|
||||
Proc washinit(void)
|
||||
Begin
|
||||
iobf=0;
|
||||
wstate=0
|
||||
EndProc
|
||||
|
||||
Proc washchar(char c)
|
||||
Begin /* state machine receives one character */
|
||||
short i;
|
||||
If Not washmore Then /* never leave state 0 */
|
||||
fputc(c, fout)
|
||||
ElsIf wstate==0 Then /* buffer empty */
|
||||
If (c==';') Or (c=='}') Then
|
||||
iobf=0; obf[iobf]=c;
|
||||
Inc(iobf); wstate=1
|
||||
ElsIf c<=' ' Then
|
||||
iobf=0; obf[iobf]=c;
|
||||
Inc(iobf);
|
||||
If c==Lf Then wstate=3 Else wstate=2 EndIf
|
||||
Else
|
||||
fputc(c, fout)
|
||||
EndIf
|
||||
ElsIf wstate==1 Then
|
||||
If c <= ' ' Then
|
||||
obf[iobf]=c; Inc(iobf)
|
||||
Else
|
||||
If c != ';' Then
|
||||
obf[iobf]=c; Inc(iobf)
|
||||
EndIf
|
||||
For i=0; i<iobf; Inc(i) Do
|
||||
fputc(obf[i], fout)
|
||||
Done
|
||||
iobf=0;
|
||||
wstate=0
|
||||
EndIf
|
||||
ElsIf wstate==2 Then
|
||||
obf[iobf]=c; Inc(iobf);
|
||||
If c==Lf Then
|
||||
wstate=3
|
||||
ElsIf c<=' ' Then /* keep state */
|
||||
Else
|
||||
For i=0; i<iobf; Inc(i) Do
|
||||
fputc(obf[i], fout)
|
||||
Done
|
||||
iobf=0;
|
||||
wstate=0
|
||||
EndIf
|
||||
ElsIf wstate==3 Then
|
||||
obf[iobf]=c; Inc(iobf);
|
||||
If c<=' ' Then /* keep state */
|
||||
Else
|
||||
If c==';' Then
|
||||
Dec(iobf); fputc(c, fout)
|
||||
EndIf
|
||||
For i=0; i<iobf; Inc(i) Do
|
||||
fputc(obf[i], fout)
|
||||
Done
|
||||
iobf=0;
|
||||
wstate=0
|
||||
EndIf
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
Proc washflush(void)
|
||||
Begin
|
||||
short i;
|
||||
If NotZ(wstate) Then
|
||||
For i=0; i<iobf; Inc(i) Do
|
||||
fputc(obf[i], fout)
|
||||
Done
|
||||
iobf=0;
|
||||
wstate=0
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
Proc washstring( Pchar s)
|
||||
Begin
|
||||
short i;
|
||||
For i=0; i<length(s); Inc(i) Do
|
||||
washchar(s[i])
|
||||
Done
|
||||
EndProc
|
||||
|
||||
/************* main part of translation filter ***********/
|
||||
|
||||
Proc translate(Pchar bf); /* recursion */
|
||||
|
||||
Proc echoOut(Pchar r, char isWild, string mac[] )
|
||||
Begin
|
||||
short u;
|
||||
Str(250,s);
|
||||
/*-StartProc-*/
|
||||
If isWild !=0 Then
|
||||
u= cpos(isWild,r)
|
||||
Else
|
||||
u=0
|
||||
EndIf
|
||||
If u==0 Then
|
||||
washstring(r)
|
||||
Else /*substitute with wildcard*/
|
||||
pscopy(s,r, (Word)1, (Word)(u-1)); washstring(s);
|
||||
If isWild==joker Then
|
||||
washchar(mac[nargs][0])
|
||||
ElsIf Recursion Then
|
||||
translate(mac[nargs])
|
||||
Else
|
||||
washstring(mac[nargs])
|
||||
EndIf
|
||||
scopy(mac[nargs], "");
|
||||
pscopy(s,r, (Word)(u+1), (Word)40);
|
||||
washstring(s);
|
||||
EndIf
|
||||
EndProc
|
||||
|
||||
Proc macroOut(Pchar r, string mac[] )
|
||||
Begin
|
||||
/* substitutes "1"..."9", uses "0" as escape character*/
|
||||
char c;
|
||||
short i,j;
|
||||
Bool escape;
|
||||
/*-StartProc-*/
|
||||
escape=False;
|
||||
For i=0; i<length(r); Inc(i) Do
|
||||
c=r[i];
|
||||
j= (short)(c-'0');
|
||||
If j==0 Then
|
||||
escape=True /*And skip*/
|
||||
ElsIf ((j>0) And (j<nargs)) And (Not escape) Then
|
||||
If Recursion Then
|
||||
translate(mac[j])
|
||||
Else
|
||||
washstring(mac[j])
|
||||
EndIf
|
||||
Else
|
||||
washchar(c);
|
||||
escape=False
|
||||
EndIf
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Proc makeNewRule(Pchar r, string mac[] )
|
||||
Begin
|
||||
/* substitutes "1"..."9", uses "0" as escape character*/
|
||||
char c;
|
||||
short i,j;
|
||||
Bool escape;
|
||||
Str(250,s);
|
||||
/*-StartProc-*/
|
||||
escape=False;
|
||||
For i=0; i<length(r); Inc(i) Do
|
||||
c=r[i];
|
||||
j= (short)(c-'0');
|
||||
If j==0 Then
|
||||
escape=True /*And skip*/
|
||||
ElsIf ((j>0) And (j<nargs)) And (Not escape) Then
|
||||
sadd(s,mac[j])
|
||||
Else
|
||||
cadd(s,c); escape=False
|
||||
EndIf
|
||||
Done
|
||||
isr= addSubList(s,isr)
|
||||
EndProc
|
||||
|
||||
Proc translate(Pchar bff)
|
||||
Begin /*light version, inside recursion only */
|
||||
Bool done;
|
||||
Str(250,bf);
|
||||
Darray(mac, string, nargs)
|
||||
Bool ok;
|
||||
short i,sm;
|
||||
char lastBf1;
|
||||
Word nbrep;
|
||||
/*-StartProc-*/
|
||||
For i=0; i<nargs; Inc(i) Do
|
||||
Sini(mac[i])
|
||||
Done
|
||||
nbrep=0;
|
||||
done= Zero(bff[0]);
|
||||
lastBf1=' ';
|
||||
If Not done Then scopy(bf,bff) EndIf
|
||||
While Not done Do
|
||||
i=1;
|
||||
ok=False; sm=0;
|
||||
While (i<=isr) And (Not ok) Do /*search For 1st match*/
|
||||
If (srule[i]=='m') Or (srule[i]=='u') Then
|
||||
If alfa(lastBf1) And (alfa(search[i][0])) Then
|
||||
sm=0 /*inside word*/
|
||||
Else
|
||||
sm= isMacro(search[i], srule[i], bf, (short)0,mac)
|
||||
EndIf
|
||||
Else
|
||||
sm=similar(search[i],wildcard[i],bf, (short)0, mac)
|
||||
EndIf
|
||||
ok=sm>0;
|
||||
If ok And (srule[i]=='w') Then
|
||||
ok=nonAlfa(lastBf1) And nonAlfa(bf[sm])
|
||||
EndIf
|
||||
If Not ok Then Inc(i) EndIf
|
||||
Done
|
||||
If ok Then
|
||||
If (srule[i]=='m') Or (srule[i]=='u') Then
|
||||
macroOut(replace[i], mac)
|
||||
Else
|
||||
echoOut(replace[i],wildcard[i], mac)
|
||||
EndIf
|
||||
lastBf1=bf[sm-1]; pscopy(bf,bf, (Word)(sm+1), (Word)255);
|
||||
Inc(nbrep);
|
||||
Else
|
||||
lastBf1=bf[0];
|
||||
washchar(lastBf1);
|
||||
pscopy(bf,bf, (Word)2, (Word)255);
|
||||
EndIf
|
||||
done= Zero(bf[0])
|
||||
Done
|
||||
EndProc
|
||||
|
||||
Proc translator( Pchar fname)
|
||||
/* checks list of possible substitution rules sequentially.
|
||||
Does the first that matches. Option: recursion.
|
||||
BUG: is very slow.
|
||||
*/
|
||||
Begin
|
||||
Str(250, outname); Str(250,bf);
|
||||
Bool done;
|
||||
Darray( mac, string, nargs)
|
||||
Pfile fin;
|
||||
Bool ok;
|
||||
short i,sm, exclusion, idot;
|
||||
char c,lastBf1;
|
||||
Word nbrep,nline;
|
||||
/*-StartProc-*/
|
||||
For i=0; i<nargs; Inc(i) Do
|
||||
Sini(mac[i])
|
||||
Done
|
||||
nbrep=0;
|
||||
nline=0;
|
||||
exclusion=0; /* will be >0 if an exclusion rule is active */
|
||||
fin=fopen( fname, "rb");
|
||||
scopy(outname, fname);
|
||||
idot= cpos('.',outname);
|
||||
If idot <= 8 Then /* room for underbar prefix, even in Ms-dos */
|
||||
cins(outname,'_')
|
||||
ElsIf NotZ(outname[0]) Then /* just erase first char */
|
||||
outname[0] = '_'
|
||||
Else
|
||||
scopy(outname,"washprog.out")
|
||||
EndIf
|
||||
fout=fopen( outname,"wb");
|
||||
washinit();
|
||||
done= (fin == Null) Or (fout == Null);
|
||||
scopy(bf,"");
|
||||
lastBf1=' ';
|
||||
/* lookmax=80; handle a line maximum ! */
|
||||
While Not done Do
|
||||
c=' ';
|
||||
While (c !=0) And (length(bf)<lookmax) Do /*refill buffer*/
|
||||
If Not feof(fin) Then
|
||||
c=fgetc(fin);
|
||||
If (c== Cr) Or (c== Lf) Then
|
||||
Inc(nline);
|
||||
If odd(nline) Then wc('.') EndIf
|
||||
If (nline Mod 150)==0 Then wln() EndIf
|
||||
EndIf
|
||||
If (c==0) Or feof(fin) Then c=' ' EndIf /*== space*/
|
||||
Else
|
||||
c=0
|
||||
EndIf
|
||||
If NotZ(c) Then cadd(bf,c) EndIf
|
||||
Done
|
||||
ok=False;
|
||||
sm=0; i=0;
|
||||
If exclusion>0 Then
|
||||
i=exclusion;
|
||||
sm=similar(replace[i], (char)0, bf, (short)0, mac);
|
||||
ok= sm>0
|
||||
EndIf
|
||||
If Zero(exclusion) Then
|
||||
i=1;
|
||||
While (i<=isr) And (Not ok) Do /*search for 1st match*/
|
||||
If (srule[i]=='m') Or (srule[i]=='u') Or (srule[i]=='a') Then
|
||||
If alfa(lastBf1) And (alfa(search[i][0])) Then
|
||||
sm=0 /*inside word*/
|
||||
Else
|
||||
sm= isMacro(search[i], srule[i], bf, (short)0,mac)
|
||||
EndIf
|
||||
Else
|
||||
sm=similar(search[i],wildcard[i],bf, (short)0, mac)
|
||||
EndIf
|
||||
ok=sm>0;
|
||||
If ok And (srule[i]=='w') Then
|
||||
ok=nonAlfa(lastBf1) And nonAlfa(bf[sm])
|
||||
EndIf
|
||||
If Not ok Then Inc(i) EndIf
|
||||
Done
|
||||
EndIf
|
||||
If ok Then
|
||||
If (srule[i]=='m') Or (srule[i]=='u') Then
|
||||
macroOut(replace[i], mac)
|
||||
ElsIf srule[i]=='x' Then
|
||||
If Zero(exclusion) Then
|
||||
exclusion=i
|
||||
Else
|
||||
exclusion=0
|
||||
EndIf
|
||||
ElsIf srule[i]=='a' Then
|
||||
makeNewRule(replace[i],mac)
|
||||
Else
|
||||
echoOut(replace[i],wildcard[i],mac)
|
||||
EndIf
|
||||
lastBf1=bf[sm-1]; pscopy(bf,bf, (Word)(sm+1), (Word)lookmax);
|
||||
Inc(nbrep);
|
||||
Else
|
||||
lastBf1=bf[0];
|
||||
If Zero(exclusion) Then washchar(lastBf1) EndIf;
|
||||
pscopy(bf,bf, (Word)2, (Word)lookmax);
|
||||
/*avoid this time-consuming buffer shuffling ?*/
|
||||
EndIf
|
||||
done= Zero(bf[0]);
|
||||
Done
|
||||
If fout !=Null Then
|
||||
washflush();
|
||||
fputc('\n', fout);
|
||||
fclose(fout)
|
||||
EndIf
|
||||
If fin !=Null Then fclose(fin) EndIf
|
||||
ws("Lines: "); wi(nline);
|
||||
ws(" Replacements: ");
|
||||
wi(nbrep); wln();
|
||||
EndProc
|
||||
|
||||
Func int main( int argc, Pchar argv[])
|
||||
Begin
|
||||
Str(80,dico);
|
||||
short istart= 1;
|
||||
Bool ok= True;
|
||||
/*-StartProc-*/
|
||||
allocdata();
|
||||
scopy(dico,"downgrad"); /* default rules file */
|
||||
ws(" washprog: A text substitution utility"); wln();
|
||||
If (argc>2) And steq(argv[1],"-r") Then
|
||||
scopy(dico,argv[2]);
|
||||
istart= 3;
|
||||
/*
|
||||
Else
|
||||
ws("Dictionary file (.TXT automatic): ");
|
||||
rln(dico);
|
||||
*/
|
||||
EndIf
|
||||
If spos(".txt",dico) <=0 Then
|
||||
sadd(dico,".txt")
|
||||
EndIf
|
||||
ok= getSubList(dico); /*list of substitution rules */
|
||||
While ok And (istart< argc) Do
|
||||
If argv[istart][0] != '_' Then /* leading underbar not accepted */
|
||||
translator( argv[istart])
|
||||
EndIf
|
||||
Inc(istart)
|
||||
Done
|
||||
return 0
|
||||
EndFunc
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue