// --------------------------------------------------------------------------- // - Scanner.cpp - // - afnix:txt module - scanner object class implementation - // --------------------------------------------------------------------------- // - This program is free software; you can redistribute it and/or modify - // - it provided that this copyright notice is kept intact. - // - - // - 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. In no event shall - // - the copyright holder be liable for any direct, indirect, incidental or - // - special damages arising in any way out of the use of this software. - // --------------------------------------------------------------------------- // - copyright (c) 1999-2007 amaury darsch - // --------------------------------------------------------------------------- #include "Vector.hpp" #include "Scanner.hpp" #include "Integer.hpp" #include "Runnable.hpp" #include "QuarkZone.hpp" #include "Exception.hpp" #include "OutputTerm.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // scanner context struct s_sctx { // the status flag bool d_status; // the scanned string String d_lexeme; // simple constructor s_sctx (void) { reset (); } // reset this context void reset (void) { d_status = false; d_lexeme = ""; } // return true if the context is valid bool isvalid (void) const { return d_status; } // return the lexeme value String getlval (void) const { return d_status ? d_lexeme : ""; } // scan an input stream void scan (Input& is, const String& ps, const Pattern& pat) { reset (); d_lexeme = pat.match (&is, ps); if (d_lexeme.length () > 0) d_status = true; } }; // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create a default scanner Scanner::Scanner (void) { d_mmin = false; } // return the class name String Scanner::repr (void) const { return "Scanner"; } // return the scanner length long Scanner::length (void) const { rdlock (); long result = d_vpat.length (); unlock (); return result; } // add a pattern to the scanner void Scanner::add (Pattern* pat) { wrlock (); try { // get the append index long index = d_vpat.length (); pat->settag (index); d_vpat.append (pat); unlock (); } catch (...) { unlock (); throw; } } // return a pattern by index Pattern* Scanner::get (const long index) const { rdlock (); Pattern* pat = nilp; try { Object* obj = d_vpat.get (index); pat = dynamic_cast (obj); unlock (); } catch (...) { unlock (); throw; } return pat; } // check a string with the scanner Lexeme* Scanner::check (const String& s) const { rdlock (); try { // iterate with the patterns long len = length (); for (long i = 0; i < len; i++) { // get the pattern Pattern* pat = get (i); if (pat == nilp) continue; // check the regex against the string if (pat->check (s) == false) continue; // build a lexeme since we have a match Lexeme* lexm = new Lexeme (s, pat->gettag ()); unlock (); return lexm; } } catch (...) { unlock (); throw; } unlock (); return nilp; } // scan an input stream and return a lexeme Lexeme* Scanner::scan (Input& is) const { wrlock (); // check for patterns long slen = length (); if (slen == 0) { unlock (); return nilp; } // create a scanner context s_sctx* sctx = new s_sctx[slen]; try { // the lexeme index long lidx = -1; for (long i = 0; i < slen; i++) { // get the pattern Pattern* pat = get (i); if (pat == nilp) continue; // get the initial value String init = (lidx == -1) ? "" : sctx[lidx].getlval (); // scan for good value sctx[i].scan (is, init, *pat); if (sctx[i].isvalid () == true) { lidx = i; if (d_mmin == true) break; } } // if a lexeme was found, the value is the index Lexeme* lexm = nilp; if (lidx != -1) { String lval = sctx[lidx].getlval (); long ltag = get(lidx)->gettag (); lexm = new Lexeme (lval, ltag); } // no match found delete [] sctx; unlock (); return lexm; } catch (...) { delete [] sctx; unlock (); throw; } } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // the quark zone static const long QUARK_ZONE_LENGTH = 5; static QuarkZone zone (QUARK_ZONE_LENGTH); // the scanner supported quarks static const long QUARK_ADD = zone.intern ("add"); static const long QUARK_GET = zone.intern ("get"); static const long QUARK_SCAN = zone.intern ("scan"); static const long QUARK_CHECK = zone.intern ("check"); static const long QUARK_LENGTH = zone.intern ("length"); // create a new object in a generic way Object* Scanner::mknew (Vector* argv) { long argc = (argv == nilp) ? 0 : argv->length (); // check for 0 argument if (argc == 0) return new Scanner; throw Exception ("argument-error", "too many arguments with scanner"); } // return true if the given quark is defined bool Scanner::isquark (const long quark, const bool hflg) const { rdlock (); if (zone.exists (quark) == true) { unlock (); return true; } bool result = hflg ? Object::isquark (quark, hflg) : false; unlock (); return result; } // apply this object with a set of arguments and a quark Object* Scanner::apply (Runnable* robj, Nameset* nset, const long quark, Vector* argv) { // get the number of arguments long argc = (argv == nilp) ? 0 : argv->length (); // process generic quark if (quark == QUARK_ADD) { for (long i = 0; i < argc; i++) { Object* obj = argv->get (i); Pattern* pat = dynamic_cast (obj); if (pat == nilp) { throw Exception ("type-error", "invalid object to add ", Object::repr (obj)); } add (pat); } return nilp; } // dispatch 0 argument if (argc == 0) { if (quark == QUARK_LENGTH) return new Integer (length ()); } // dispatch 1 argument if (argc == 1) { if (quark == QUARK_GET) { rdlock (); try { long index = argv->getint (0); Pattern* pat = get (index); robj->post (pat); unlock (); return pat; } catch (...) { unlock (); throw; } } if (quark == QUARK_CHECK) { rdlock (); try { String sval = argv->getstring (0); Lexeme* lexm = check (sval); robj->post (lexm); unlock (); return lexm; } catch (...) { unlock (); throw; } } if (quark == QUARK_SCAN) { Object* obj = argv->get (0); Input* is = dynamic_cast (obj); if (is == nilp) { throw Exception ("type-error", "invalid object to scan ", Object::repr (obj)); } rdlock (); try { Lexeme* lexm = scan (*is); robj->post (lexm); unlock (); return lexm; } catch (...) { unlock (); throw; } } } // call the object method return Object::apply (robj, nset, quark, argv); } }