/*$Id: c_getckt.cc,v 26.20 2007/03/21 01:31:17 al Exp $ -*- C++ -*- * Copyright (C) 2001 Albert Davis * Author: Albert Davis * * This file is part of "Gnucap", the Gnu Circuit Analysis Package * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. *------------------------------------------------------------------ * build, get, merge, "<" commands * process circuit files, and keyboard entry */ //testing=script,sparse 2006.07.17 #include "e_model.h" #include "s__.h" #include "ap.h" #include "declare.h" /* getcmd */ #include "c_comand.h" #include "l_dispatcher.h" extern DISPATCHER command_dispatcher; extern DISPATCHER device_dispatcher; extern DISPATCHER model_dispatcher; extern std::string head; /* place to store title line */ /*--------------------------------------------------------------------------*/ namespace { /*--------------------------------------------------------------------------*/ enum Skip_Header {NO_HEADER, SKIP_HEADER}; /*--------------------------------------------------------------------------*/ // void CMD::build(CS&); // void CMD::get(CS&); // void CMD::merge(CS&); // void CMD::run(CS&); static void do_build(CARD* owner, CARD_LIST::fat_iterator putbefore); static void getmerge(CS&, Skip_Header); static void do_getmerge(CARD* owner, FILE* filen, bool, bool); static CARD* check_create_insert_parse(CS&,bool,CARD_LIST::fat_iterator&, CARD*); static void skip_pre_stuff(CS& cmd); static CARD* new_device(CS&); static CARD* do_model(CS&); /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ class DEV_DOT : public CARD { private: std::string _s; explicit DEV_DOT(const DEV_DOT& p) :CARD(p) {untested();set_constant(true);} public: explicit DEV_DOT() :CARD() {set_constant(true);} private: // override virtual char id_letter()const {untested();return '\0';} const char* dev_type()const {untested();return "dotcard";} CARD* clone()const {untested();return new DEV_DOT(*this);} void parse_spice(CS&); void print(OMSTREAM& o,LANGUAGE)const {o << _s << '\n';} }; /*--------------------------------------------------------------------------*/ void DEV_DOT::parse_spice(CS& cmd) { _s = cmd.fullstring(); switch (ENV::run_mode) { case rPRE_MAIN: unreachable(); break; case rBATCH: untested(); case rINTERACTIVE: untested(); case rPRESET: case rSCRIPT: cmd.skip1b("."); CMD::cmdproc(cmd.tail()); break; } } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ /* cmd_build: build command * get circuit description direct from keyboard (or stdin if redirected) * Command syntax: build * Bare command: add to end of list * If there is an arg: add before that element * null line exits this mode * preset, but do not execute "dot cards" */ class CMD_BUILD : public CMD { public: void do_it(CS& cmd) {untested(); SET_RUN_MODE xx(rPRESET); ::status.get.reset().start(); SIM::uninit(); do_build(NULL, findbranch(cmd, &CARD_LIST::card_list)); ::status.get.stop(); } } p1; DISPATCHER::INSTALL d1(&command_dispatcher, "build", &p1); /*--------------------------------------------------------------------------*/ /* cmd_get: get command * get circuit from a file, after clearing the old one * preset, but do not execute "dot cards" */ class CMD_GET : public CMD { public: void do_it(CS& cmd) { SET_RUN_MODE xx(rPRESET); ::status.get.reset().start(); command("clear"); getmerge(cmd, SKIP_HEADER); ::status.get.stop(); } } p2; DISPATCHER::INSTALL d2(&command_dispatcher, "get", &p2); /*--------------------------------------------------------------------------*/ /* cmd_include: include command * as get or run, but do not clear first, inherit the run-mode. */ class CMD_INCLUDE : public CMD { public: void do_it(CS& cmd) {itested(); ::status.get.reset().start(); getmerge(cmd, NO_HEADER); ::status.get.stop(); } } p3; DISPATCHER::INSTALL d3(&command_dispatcher, "include", &p3); /*--------------------------------------------------------------------------*/ /* cmd_merge: merge command * as get, but do not clear first */ class CMD_MERGE : public CMD { public: void do_it(CS& cmd) {untested(); SET_RUN_MODE xx(rPRESET); ::status.get.reset().start(); getmerge(cmd, NO_HEADER); ::status.get.stop(); } } p4; DISPATCHER::INSTALL d(&command_dispatcher, "merge", &p4); /*--------------------------------------------------------------------------*/ /* cmd_run: "<" and "<<" commands * run in batch mode. Spice format. * "<<" clears old circuit first, "<" does not * get circuit from file, execute dot cards in sequence */ class CMD_RUN : public CMD { public: void do_it(CS& cmd) { ::status.get.reset().start(); while (cmd.match1('<')) {untested(); command("clear"); cmd.skip(); cmd.skipbl(); } SET_RUN_MODE xx(rSCRIPT); getmerge(cmd, SKIP_HEADER); ::status.get.stop(); } } p5; DISPATCHER::INSTALL d5(&command_dispatcher, "<", &p5); /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ static void do_build(CARD* owner, CARD_LIST::fat_iterator putbefore) { itested(); for (;;) { itested(); // get a line char buffer[BIGBUFLEN]; getcmd(CKT_PROMPT, buffer, BIGBUFLEN); CS cmd(buffer); // exit if done if (cmd.is_end() || cmd.pmatch(".ENDs") || cmd.pmatch(".EOM")) { itested(); break; }else{ itested(); } // check for dups, create, insert, parse CARD* brh = check_create_insert_parse(cmd, true, putbefore, owner); // ^^dup_check // recursively process subcircuits if (exists(brh) && brh->recursive_parse()) { untested(); assert(brh->subckt()); do_build(brh, CARD_LIST::fat_iterator((brh->subckt()),brh->subckt()->end())); }else{ itested(); } } } /*--------------------------------------------------------------------------*/ /* getmerge: actually do the work for "get", "merge", etc. */ static void getmerge(CS& cmd, Skip_Header skip_header) { SIM::uninit(); FILE* filen = xopen(cmd,"","r"); if (!filen) { untested(); error(bERROR, ""); } bool echoon = false; /* echo on/off flag (echo as read from file) */ bool liston = false; /* list on/off flag (list actual values) */ bool quiet = false; /* don't echo title */ int here = cmd.cursor(); do{ ONE_OF || get(cmd, "Echo", &echoon) || get(cmd, "List", &liston) || get(cmd, "Quiet", &quiet) ; }while (cmd.more() && !cmd.stuck(&here)); cmd.check(bWARNING, "need echo, list, or quiet"); if (skip_header) { // get and store the header line std::string buffer(getlines(filen)); // title if (buffer.empty()) { untested(); error(bWARNING, "empty circuit file\n"); } head = buffer; if (!quiet) { IO::mstdout << head << '\n'; }else{ untested(); } }else{ itested(); } do_getmerge(NULL, filen, echoon, liston); xclose(&filen); } /*--------------------------------------------------------------------------*/ static void do_getmerge(CARD* owner, FILE* filen, bool echoon, bool liston) { CARD_LIST* scope = (owner) ? owner->subckt() : &CARD_LIST::card_list; assert(scope); CARD_LIST::fat_iterator putbefore(scope, scope->end()); for (;;) { // get a line, with extensions std::string buffer(getlines(filen)); CS cmd(buffer); // print as in file if (OPT::listing || echoon) { itested(); IO::mstdout << buffer << '\n'; } // exit if done if (buffer.empty() || cmd.pmatch(".ENDs") || cmd.pmatch(".EOM")) { break; } // check for dups, create, insert, parse CARD* brh = check_create_insert_parse(cmd,OPT::dupcheck,putbefore,owner); // print as interpreted if (OPT::listing || liston) {itested(); if (exists(brh)) {itested(); brh->print(IO::mstdout, OPT::language); }else{itested(); IO::mstdout << "**\n"; } }else{ } // recursively process subcircuits if (exists(brh) && brh->recursive_parse()) { do_getmerge(brh, filen, echoon, liston); } } } /*--------------------------------------------------------------------------*/ /* check_create_insert_parse: parse an input line, process it, store it. */ static CARD *check_create_insert_parse(CS& cmd, bool dup_check, CARD_LIST::fat_iterator& putbefore, CARD* owner) { CARD* brh = NULL; // check for dups if (dup_check) { itested(); skip_pre_stuff(cmd); CARD_LIST::fat_iterator ci = findbranch(cmd, putbefore.list()); cmd.reset(); brh = *ci; } // create if (!exists(brh)) { brh = new_device(cmd); if (exists(brh)) { brh->set_owner(owner); skip_pre_stuff(cmd); brh->parse_spice(cmd); // parse must be before insert, so get or clear doesn't clear self putbefore.insert(brh); }else{ } }else{ untested(); assert(brh->owner() == owner); skip_pre_stuff(cmd); brh->parse_spice(cmd); // no insert. it's already there } return brh; } /*--------------------------------------------------------------------------*/ static void skip_pre_stuff(CS& cmd) { cmd.skipbl(); while (cmd.ematch(CKT_PROMPT)) { itested(); /* skip any number of copies of the prompt */ } cmd.ematch(ANTI_COMMENT); /* skip mark so spice ignores but gnucap reads */ } /*--------------------------------------------------------------------------*/ /* new_device: create a new device of specified type */ static CARD *new_device(CS& cmd) { skip_pre_stuff(cmd); bool dot(false); std::string s; char id_letter = toupper(cmd.peek()); int here = cmd.cursor(); switch (id_letter) { case '\0': { return NULL; } break; case '.': { cmd.skip(); cmd >> s; notstd::to_lower(&s); dot = true; } break; case 'G': { int here = cmd.cursor(); if (cmd.pscan("VCCAP")){untested(); s = "vccap";} else if (cmd.pscan("VCG")) {untested(); s = "vcg";} else if (cmd.pscan("VCR")) { s = "vcr";} else if (cmd.pscan("VCCS")) {untested(); s = "vccs";} else { s = id_letter;} cmd.reset(here); } break; default: { s = id_letter; } break; } const CARD* p = device_dispatcher[s]; if (p) { return p->clone(); }else if (s == "model") { return do_model(cmd); }else if (dot){ cmd.reset(here); return new DEV_DOT; }else if (s != "") {itested(); cmd.reset(here); cmd.warn(bDANGER, "what's this?"); return NULL; }else{ cmd.reset(here); return NULL; } } /*--------------------------------------------------------------------------*/ static CARD* do_model(CS& cmd) { cmd.skiparg(); // skip name int here = cmd.cursor(); std::string s; cmd >> s; notstd::to_upper(&s); // "level" kluge .... // if there is a "level" keyword, with integer argument, // tack that onto the given modelname and look for that cmd.skip1b('('); int level = 0; { int here = cmd.cursor(); scan_get(cmd, "LEVEL", &level); if (!cmd.stuck(&here)) { char buf[20]; sprintf(buf, "%u", level); s += buf; }else{ } } const MODEL_CARD* p = model_dispatcher[s]; if (p) { return p->clone(); }else{itested(); cmd.warn(bDANGER, here, "no match"); return NULL; } } /*--------------------------------------------------------------------------*/ } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/