Corrected a long-standing error in which ending text with a literal
[xcircuit.git] / spiceparser / scanner.c
blob87ff6ff5bf5630a236fccbedd49975c1f34b97f1
1 /********************
2 This file is part of the software library CADLIB written by Conrad Ziesler
3 Copyright 2003, Conrad Ziesler, all rights reserved.
5 *************************
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 ******************/
21 /* scanner.c, support for scanning text, tokenifies input, uses file_readline
22 Conrad Ziesler
26 /* goal: support cad tool file reading
28 cad tool requirements: large file size, regular file handling
29 needs to: minimize memory allocations, prevent memory leaks
30 should provide: allow higher up layers to never need to call strdup
31 unless they really want to. that is we alloc in place
35 #include <ctype.h>
36 #include <stdio.h>
37 #include "debug.h"
39 #include "scanner.h"
46 /**** standard error and warning reporting ************/
48 static void parse_warn_error(int which, scanner_t *scan, char *fmt, va_list va)
51 static char buf[4096];
52 card_t *c=scan->errcard;
53 deck_t *d=scan->errdeck;
54 char *bp=buf;
55 char *warnerr="WARNING";
56 int line=-1;
57 int file=-1;
59 if(d==NULL)
61 if (scan->inputp!=NULL)
63 line=scan->inputp->line;
64 file=scan->inputp->index;
67 else { line=d->line.line; file=d->line.fileindex; }
69 if(which) warnerr="ERROR";
70 bp[0]=0;
72 if(c!=NULL)
73 sprintf(bp,"line %i: %s: near %s : ",line,warnerr,c->str);
74 else sprintf(bp,"line %i: %s: :",line,warnerr);
76 if(d!=NULL)
77 for(c=d->card;c!=NULL;c=c->next)
79 bp+=strlen(bp);
80 if(bp>(buf+sizeof(buf)-256)) assert(0);
81 if(c->val!=NULL)sprintf(bp,"%s=%s ",c->str,c->val);
82 else sprintf(bp,"%s ",c->str);
84 bp+=strlen(bp);
85 sprintf(bp,"\n --> %s",fmt);
86 bp+=strlen(bp);
87 sprintf(bp,"\n");
88 bp+=strlen(bp);
89 if(which==0)
90 if(scan->warnfunc!=NULL)
91 { scan->warnfunc(scan,buf,va); return; }
93 if(scan->errfunc!=NULL)
94 scan->errfunc(scan,buf,va);
97 void parse_error (scanner_t *scan, char *fmt, ...)
99 va_list va;
100 va_start(va,fmt);
101 parse_warn_error(1,scan,fmt,va);
102 va_end(va);
105 void parse_warn (scanner_t *scan, char *fmt, ...)
107 va_list va;
108 va_start(va,fmt);
109 parse_warn_error(0,scan,fmt,va);
110 va_end(va);
113 void scanner_def_err(scanner_t *scan, char *fmt, va_list vp)
115 vfprintf(stderr,fmt,vp);
120 /***** private helper functions *********/
122 #ifdef NO_MEMORY
123 static void free_cards(scanner_t *scan, card_t *c)
125 card_t *tofree;
126 while(c!=NULL)
128 tofree=c;
129 c=c->next;
130 free(tofree);
135 static void deck_free(scanner_t *scan, deck_t *deck)
137 deck_t *tofree;
138 while(deck!=NULL)
140 tofree=deck;
141 deck=deck->next;
142 free_cards(tofree->card);
143 free(tofree);
146 #endif
149 static deck_t *new_deck(scanner_t *scan)
151 deck_t *d;
152 d=memory_alloc(&(scan->strmem),sizeof(deck_t));
153 assert(d!=NULL);
154 d->next=NULL;
155 d->card=NULL;
156 d->line=scanner_current_file_line(scan);
157 return d;
161 void scanner_add_tokens(scanner_t *scan, tokenmap_t *map)
163 int i;
164 if(scan->sectp==NULL)assert(0);
165 else
167 for(i=0;map[i].str!=NULL;i++)
168 names_add(scan->sectp->tokenizer,map[i].token,map[i].str);
172 void scanner_reset_tokens(scanner_t *scan)
174 if(scan->sectp!=NULL)
176 names_free(scan->sectp->tokenizer);
177 scan->sectp->tokenizer=names_new();
179 else assert(0);
183 #ifdef OLD_WAY
184 void scanner_input_new(scanner_t *scan, file_spec_p file)
186 scanner_input_t *p;
187 p=malloc(sizeof(scanner_input_t));
188 if(p!=NULL)memset(p,0,sizeof(scanner_input_t));
189 else assert(0);
190 p->file=file;
191 file_openread(file);
192 p->line=0;
193 if(scan->allinputs==NULL)
194 p->index=0;
195 else
196 p->index=scan->allinputs->index+1;
197 p->next=scan->allinputs;
198 scan->allinputs=p;
199 p->back=scan->inputp;
200 scan->inputp=p;
202 #endif
206 void scanner_input_newfp(scanner_t *scan, void *fp)
208 scanner_input_t *p;
209 p=malloc(sizeof(scanner_input_t));
210 if(p!=NULL)memset(p,0,sizeof(scanner_input_t));
211 else assert(0);
212 p->file_fp=fp;
213 p->line=0;
214 if(scan->allinputs==NULL)
215 p->index=0;
216 else
217 p->index=scan->allinputs->index+1;
218 p->next=scan->allinputs;
219 scan->allinputs=p;
220 p->back=scan->inputp;
221 scan->inputp=p;
225 void scanner_sect_new(scanner_t *scan, scanner_def_t *defs, tokenmap_t *map)
227 scanner_sect_t *sect;
229 sect=malloc(sizeof(scanner_sect_t));
230 if(sect!=NULL)memset(sect,0,sizeof(scanner_sect_t));
231 else assert(0);
233 sect->tokenizer=names_new();
234 sect->def=defs;
235 sect->cp=NULL;
236 sect->dp=NULL;
237 sect->dhead=NULL;
238 sect->eolstring=0;
239 sect->line_cont=0;
240 sect->lbuf[0]=0;
241 sect->eoline[0]=0;
242 sect->back=scan->sectp;
243 scan->sectp=sect;
244 scanner_add_tokens(scan,map);
248 void scanner_input_release(scanner_t *scan) /* release top of stack */
250 scanner_input_t *p;
251 if((p=scan->inputp)!=NULL)
253 scan->inputp=scan->inputp->back;
254 /* we don't free, we keep on list,
255 for error reporting
260 void scanner_sect_release(scanner_t *scan) /* release top of stack */
262 scanner_sect_t *p;
264 if((p=scan->sectp)!=NULL)
266 scan->sectp=scan->sectp->back;
267 names_free(p->tokenizer);
268 free(p);
272 void scanner_init(scanner_t *scan)
274 memory_init(&(scan->strmem));
275 scan->sectp=NULL;
276 scan->allinputs=NULL;
277 scan->inputp=NULL;
278 scan->errdeck=NULL;
279 scan->errcard=NULL;
280 scan->errfunc=scanner_def_err;
281 scan->warnfunc=scanner_def_err;
285 void scanner_free_all(scanner_t *scan)
287 memory_freeall(&(scan->strmem));
290 void scanner_release(scanner_t *scan)
292 scanner_input_t *p,*np;
294 while(scan->sectp!=NULL)
295 scanner_sect_release(scan);
297 while(scan->inputp!=NULL)
298 scanner_input_release(scan);
300 for(p=scan->allinputs;p!=NULL;p=np)
302 np=p->next;
303 if(p->file_fp!=NULL) fclose(p->file_fp);
304 free(p);
307 scanner_free_all(scan);
313 int scanner_checkvalid(deck_t *d, int qty)
315 int i;
316 card_t *c;
317 if(d==NULL)return 0;
318 for(i=0,c=d->card;c!=NULL;c=c->next,i++)
319 { if(i>=qty)break; }
321 if(i>=qty)return 1;
322 return 0;
332 /***** the actual scanner routines *****/
335 static char *skip_stuff(char *toskip, char *str, int sense)
337 char *cp;
338 char *ws;
339 for(cp=str;cp[0]!=0;cp++)
341 for(ws=toskip;ws[0]!=0;ws++)
342 if(ws[0]==cp[0]) break;
343 if( (ws[0]==0) && (sense==0) ) break; /* no toskip char found */
344 if( (ws[0]!=0) && (sense!=0) ) break; /* toskip char found */
346 return cp;
349 static void skip_space(scanner_t *scan, char **p)
350 { *p=skip_stuff(scan->sectp->def->whitespace,*p,0); }
352 #if 0
353 static void skip_nonspace(scanner_t *scan, char **p)
354 { *p=skip_stuff(scan->sectp->def->whitespace,*p,1); }
355 #endif
357 static void skip_nonspace_quote(scanner_t *scan, char **p)
359 scanner_def_t *def=scan->sectp->def;
360 int quote=0;
361 char *cp;
362 char *ws;
363 cp=*p;
364 /* this is set to 2 origninally since first iteration hits same character */
365 if(cp[0]==def->quote_char)quote=2;
366 for(;cp[0]!=0;cp++)
368 if(cp[0]==def->quote_char){ quote--; if(quote==0){ if(cp[0]!=0)cp++; break; } }
369 if(quote<=0)
371 for(ws=def->whitespace;ws[0]!=0;ws++)
372 if(ws[0]==cp[0]) break;
373 if( (ws[0]!=0) ) break; /* whitespace char found */
377 *p=cp;
381 static int astreq(char *s, char *lng)
383 if(s[0]==0)return 2;
384 while(s[0]!=0)
386 if(lng[0]==0)return -1;
387 if(s[0]!=lng[0])return 1;
388 s++;
389 lng++;
391 return 0;
395 static card_t *make_card(scanner_t *scan, char *p1, char *p2, char *p3, char *p4)
397 scanner_def_t *def=scan->sectp->def;
398 card_t *c;
399 int l1=0,l2=0,lc;
400 int i;
402 if( (p1!=NULL) && (p2!=NULL)) l1=p2-p1;
403 if( (p3!=NULL) && (p4!=NULL)) l2=p4-p3;
405 assert(l1>=0);
406 assert(l2>=0);
407 lc=strlen(def->eol_continue);
409 if(p1[0]==0) return NULL; /* nothing here */
411 if(lc <= l1)
412 if(!astreq(def->eol_continue,p2-lc))
414 if(lc < l1 )
416 memcpy(scan->sectp->eoline,p1,l1-lc);
417 scan->sectp->eoline[l1]=def->assignment;
418 scan->sectp->eoline[l1+1]=0;
419 scan->sectp->eolstring=1;
421 return NULL;
424 if(p3!=NULL)
426 if((p3[0]==0)||(!astreq(def->eol_continue,p3)))
428 memcpy(scan->sectp->eoline,p1,l1);
429 scan->sectp->eoline[l1]=def->assignment;
430 scan->sectp->eoline[l1+1]=0;
431 scan->sectp->eolstring=1;
432 return NULL;
436 c=memory_alloc(&(scan->strmem),sizeof(card_t)+l1+l2+2);
437 assert(c!=NULL);
438 c->next=NULL;
439 c->token=0;
440 c->str[0]=0;
441 c->val=NULL;
443 memcpy(c->str,p1,l1);
444 c->str[l1]=0;
445 if(l2!=0)
447 memcpy(c->str+l1+1,p3,l2);
448 c->str[l1+1+l2]=0;
449 c->val=c->str+l1+1;
452 if(def->convert_case==PARSE_CASE_TOLOWER)
453 for(i=0;i<(l1+l2+2);i++)
454 c->str[i]=tolower(c->str[i]);
455 else if(def->convert_case==PARSE_CASE_TOUPPER)
456 for(i=0;i<(l1+l2+2);i++)
457 c->str[i]=toupper(c->str[i]);
460 if((def->tokenize[0]==0) || (!astreq(def->tokenize,c->str)))
461 c->token=names_check(scan->sectp->tokenizer,c->str);
463 if(c->val!=NULL)
464 if(c->val[0]==scan->sectp->def->quote_char)c->val++;
466 return c;
473 int scanner_parse_line(scanner_t *scan)
475 scanner_def_t *def=scan->sectp->def;
476 char *line=scan->sectp->lbuf;
477 char *lp, *tmp;
478 char *p1=NULL,*p2,*p3,*p4,*p5,*p6;
479 card_t *ncp=NULL;
480 int dbg=0;
482 if(scan->inputp==NULL)return 0;
483 if(scan->inputp->file_fp!=NULL) { if(feof(((FILE *)scan->inputp->file_fp)))return 0; }
484 /* return 0; else { if(file_eof(scan->inputp->file)) return 0; } */
486 line[0]=0;
487 line[1]=0;
488 line[sizeof(scan->sectp->lbuf)-1]=0;
489 if(scan->inputp->file_fp!=NULL)
490 fgets(line,sizeof(scan->sectp->lbuf)-2,scan->inputp->file_fp);
491 else
492 ; /* file_readline(scan->inputp->file,sizeof(scan->sectp->lbuf)-2,line); */
494 scan->inputp->line++;
495 if(line[0]==0)return 3;
497 /* read one line */
498 if(dbg)fprintf(stderr,"line: %s",line);
499 lp=line;
501 skip_space(scan,&lp);
503 if(lp[0]==0) return 4;
505 if(!astreq(def->commentstart, lp))return 5;
506 if(!astreq(def->line_stop, lp)) return 0; /* stop scanning */
507 if(!astreq(def->bol_continue,lp)) /* continue line */
508 { scan->sectp->line_cont=1; lp+=strlen(def->bol_continue); }
510 if(dbg)fprintf(stderr,"lp: %s",lp);
511 if(dbg)
513 int l=strlen(lp);
514 if(lp[l-1]=='\n')lp[l-1]=0;
517 if(! (scan->sectp->line_cont||scan->sectp->eolstring)) /* make a new deck unless we are continuing */
519 deck_t *dp;
520 dp=new_deck(scan);
521 if(scan->sectp->dhead==NULL)
522 { scan->sectp->dhead=scan->sectp->dp=dp; }
523 else
524 { scan->sectp->dp->next=dp; scan->sectp->dp=dp; }
526 scan->sectp->line_cont=0;
528 /* continue last string if necessary */
529 if(scan->sectp->eolstring)
531 p1=scan->sectp->eoline;
532 skip_space(scan,&p1);
533 p2=p1;
534 skip_nonspace_quote(scan,&p2);
535 p3=p2;
536 skip_space(scan,&p3);
537 p4=p3;
538 skip_nonspace_quote(scan,&p4);
539 p5=p4;
540 skip_space(scan,&p5);
541 p6=p5;
542 skip_nonspace_quote(scan,&p6);
544 if(p5[0]==0)p5=NULL;
545 if(p3[0]==0)p3=NULL;
546 if(p1[0]==0)p1=NULL;
547 scan->sectp->eolstring=0;
550 while((lp[0]!=0)||(p1!=NULL))
552 int arg=0;
554 if(p1==NULL)
556 p1=lp;
557 skip_space(scan,&p1);
558 p2=p1;
559 skip_nonspace_quote(scan,&p2);
560 p3=p2;
561 skip_space(scan,&p3);
562 p4=p3;
563 skip_nonspace_quote(scan,&p4);
564 p5=p4;
565 skip_space(scan,&p5);
566 p6=p5;
567 skip_nonspace_quote(scan,&p6);
569 else if(p3==NULL)
571 p3=lp;
572 skip_space(scan,&p3);
573 p4=p3;
574 skip_nonspace_quote(scan,&p4);
575 p5=p4;
576 skip_space(scan,&p5);
577 p6=p5;
578 skip_nonspace_quote(scan,&p6);
581 else if(p5==NULL)
583 p5=lp;
584 skip_space(scan,&p5);
585 p6=p5;
586 skip_nonspace_quote(scan,&p6);
589 if(dbg)
590 fprintf(stderr,"doing line: line is [%s]\nlp=[%s]\np1=[%s]\np2=[%s]\np3=[%s]\np4=[%s]\np5=[%s]\np6=[%s]\n\n",
591 line,lp,p1,p2,p3,p4,p5,p6);
595 /* cases:
596 {p1}{p2}{p3}[ ]{p4}{p5}{p6} -> end of line
597 {p1}text{p2}[ ]{p3}{p4}{p5}{p6} -> word end of line
598 {p1}text=moretext{p2}... -> one word embedded assignment
599 {p1}text={p2}{p3}{p4}{p5}{p6} -> word assignment spills over line
600 {p1}={p2} .. -> continue assignment from prev line if possible
601 {p1}text={p2} {p3}text{p4} -> word assign 1
602 {p1}text{p2} {p3}=text{p4} -> word assign 2
603 {p1}text{p2} {p3}={p4} {p5}text{p6} -> word assign 3
604 {p1}text{p2} {p3}text={p4} -> word
605 text = EOL {p1}text{p2} .. -> eol continue
608 if(p1==p2){ p1=NULL; break; } /* end of line */
610 if(p2>p1)
611 { if(p2[-1]==def->assignment)arg=1; }
613 if((arg==0)&&(p2>p1))
616 if( (tmp=memchr(p1,def->assignment,p2-p1))!=NULL)
618 arg=5;
619 p2=tmp+1;
620 p3=tmp+1;
621 p4=p3;
622 skip_nonspace_quote(scan,&p4);
626 if(arg==0)
628 if(p3[0]==def->assignment)
629 arg=2;
634 switch(arg)
636 case 0: /* no arg */
637 ncp=make_card(scan,p1,p2,NULL,NULL);
638 p1=p3; p2=p4; p3=p5; p4=p6; p5=NULL; lp=p6;
639 break;
640 case 1: /* arg in p3 */
641 ncp=make_card(scan,p1,p2-1,p3,p4);
642 p1=p5; p2=p6; p3=NULL; p5=NULL; lp=p6;
643 break;
644 case 2: /* arg in p3 or p4 */
645 if((p3+1)<p4)
647 ncp=make_card(scan,p1,p2,p3+1,p4);
648 p1=p5; p2=p6; p3=NULL; p5=NULL; lp=p6;
650 else
652 ncp=make_card(scan,p1,p2,p5,p6);
653 p1=NULL; p3=NULL; p5=NULL; lp=p6;
655 break;
656 case 5: /* embedded arg, we've fixed up p's */
657 ncp=make_card(scan,p1,p2-1,p3,p4);
658 p1=NULL; p3=NULL; p5=NULL; lp=p4;
659 break;
660 default: assert(0);
663 if(ncp!=NULL)
665 if(dbg)fprintf(stderr,"Adding card %s %i %s\n",ncp->str,ncp->token,ncp->val);
666 if(scan->sectp->dp==NULL)assert(0);
668 if(scan->sectp->dp->card==NULL)
669 scan->sectp->dp->card=scan->sectp->cp=ncp;
670 else
672 if(scan->sectp->cp!=NULL)
674 scan->sectp->cp->next=ncp;
675 scan->sectp->cp=scan->sectp->cp->next;
677 else assert(0);
680 else
682 if(dbg)fprintf(stderr,"card is null, line is [%s]\nlp=[%s]\np1=[%s]\np2=[%s]\np3=[%s]\np4=[%s]\np5=[%s]\np6=[%s]\n\n",
683 line,lp,p1,p2,p3,p4,p5,p6);
689 return 6;
696 int scanner_parse_all(scanner_t *scan)
698 scan->sectp->eolstring=0;
699 scan->sectp->line_cont=0;
700 while(scanner_parse_line(scan));
701 return 0;
705 scanner_def_t *scanner_def_spice(void)
707 static scanner_def_t spicedef;
708 strcpy(spicedef.line_stop,".end_spice");
709 strcpy(spicedef.eol_continue,"\\");
710 strcpy(spicedef.bol_continue,"+");
711 spicedef.convert_case=PARSE_CASE_TOLOWER;
712 spicedef.quote_char='\'';
713 spicedef.newline='\n';
714 spicedef.assignment='=';
715 strcpy(spicedef.tokenize,".");
716 strcpy(spicedef.whitespace," \t\n\r");
717 strcpy(spicedef.commentstart,"*");
718 return &spicedef;
721 char *scanner_token(scanner_t *scan, int token)
723 return names_lookup(scan->sectp->tokenizer,token);
726 file_line_t scanner_current_file_line(scanner_t *scan)
728 file_line_t l;
729 l.fileindex=scan->inputp->index;
730 l.line=scan->inputp->line;
731 return l;
734 void scanner_debug_all(scanner_t *scan, void *dbg)
736 FILE *fp=dbg;
737 int q;
738 deck_t *d;
739 card_t *c;
741 fprintf(fp,"\n\n** begin \n\n");
742 for(d=scan->sectp->dhead;d!=NULL;d=d->next)
744 q=0;
745 fprintf(fp,"\nline %i: ",d->line.line);
746 for(c=d->card;c!=NULL;c=c->next)
748 if(c->token>0)
749 fprintf(fp," **%i=%s** ",c->token,scanner_token(scan,c->token));
751 if(c->val==NULL)
752 fprintf(fp," [%s] ",c->str);
753 else fprintf(fp," [%s]=[%s] ",c->str,c->val);
754 q++;
755 if(q>=6){ fprintf(fp,"\n "); q=1; }
759 fprintf(fp,"\n\n** .end\n\n");
762 /* parses 0101.0101.0101011.01 */
763 unsigned parse_binary(char **str)
765 unsigned result=0;
766 char *p;
767 for(p=*str;p[0]!=0;p++)
769 if(p[0]=='0')
770 result<<=1;
772 else if(p[0]=='1')
773 { result<<=1; result|=1; }
775 else if(p[0]=='.');
776 else break;
778 *str=p;
779 return result;