diff --git a/applications/test/dictionary/Make/files b/applications/test/dictionary/Make/files index 759804fcfa6cf2737cd9599f8aeba508687cf353..148f5fb1c94c8ff332eed2edec36e2771eb65e02 100644 --- a/applications/test/dictionary/Make/files +++ b/applications/test/dictionary/Make/files @@ -1,4 +1,7 @@ -calcEntry/calcEntry.C dictionaryTest.C +calcEntry/calcEntry.C +calcEntry/calcEntryParser.cpp +calcEntry/calcEntryScanner.cpp + EXE = $(FOAM_USER_APPBIN)/dictionaryTest diff --git a/applications/test/dictionary/calcEntry/CocoParserErrors.H b/applications/test/dictionary/calcEntry/CocoParserErrors.H new file mode 100644 index 0000000000000000000000000000000000000000..e4c819e302448b508c62b22a1176fda353acc03d --- /dev/null +++ b/applications/test/dictionary/calcEntry/CocoParserErrors.H @@ -0,0 +1,145 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2009-2009 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM 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 of the License, or (at your + option) any later version. + + OpenFOAM 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 OpenFOAM; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Class + Foam::CocoParserErrors + +Description + Templated class to shadow the error handling for Coco/R parsers + +\*---------------------------------------------------------------------------*/ + +#ifndef CocoParserErrors_H +#define CocoParserErrors_H + +#include "error.H" +#include "wchar.H" + +// avoid confusion about which Warning is meant +#undef WarningIn +#define WarningIn(fn) Foam::Warning(fn, __FILE__, __LINE__) + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class CocoParserErrors Declaration +\*---------------------------------------------------------------------------*/ + +template<class BaseClass> +class CocoParserErrors +: + public BaseClass +{ + // Private data + + //- The name issued in warnings and errors + word name_; + +public: + + // Constructors + + //- Construct with given name + CocoParserErrors(const word& name) + : + BaseClass(), + name_(name) + {} + + + //- Destructor + virtual ~CocoParserErrors() + {} + + + // Member functions + + //- Return the name issued for warnings + virtual const word& name() const + { + return name_; + } + + //- Return the name issued for warnings + virtual word& name() + { + return name_; + } + + + // Error Handling + + //- Handle a general warning 'msg' + virtual void Warning(const wchar_t* msg) + { + WarningIn(name_) + << msg << endl; + } + + //- Handle a general warning 'msg' + virtual void Warning(int line, int col, const wchar_t* msg) + { + WarningIn(name_) + <<"line " << line << " col " << col << ": " + << msg << endl; + } + + //- Handle general error 'msg' (eg, a semantic error) + virtual void Error(int line, int col, const wchar_t* msg) + { + FatalErrorIn(name_) + << "line " << line << " col " << col <<": " << msg << endl + << exit(FatalError); + } + + //- Handle general error 'msg' (eg, a semantic error) + virtual void Error(const wchar_t* msg) + { + FatalErrorIn(name_) + << msg << endl + << exit(FatalError); + } + + //- Handle a general exception 'msg' + virtual void Exception(const wchar_t* msg) + { + this->Error(msg); + } + +}; + + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/applications/test/dictionary/calcEntry/SimpleCalc.atg b/applications/test/dictionary/calcEntry/SimpleCalc.atg new file mode 100644 index 0000000000000000000000000000000000000000..cdc84cd0c9009a47a6425f6cf5a11ea225e74517 --- /dev/null +++ b/applications/test/dictionary/calcEntry/SimpleCalc.atg @@ -0,0 +1,225 @@ +/*------------------------------------------------------------------------- + compile with: + Coco \ + -frames $WM_THIRD_PARTY_DIR/coco-r \ + -prefix calcEntry \ + -namespace Foam::functionEntries::calcEntryInternal \ + SimpleCalc.atg +-------------------------------------------------------------------------*/ + +#include "dictionary.H" +#include "scalar.H" +#include "error.H" +#include "wchar.H" + + +COMPILER SimpleCalc + // Simple four function calculator for OpenFOAM dictionaries + + //! with debug + static const int debug = 0; + + //! The parent dictionary + mutable dictionary* dict_; + + //! The calculation result + scalar val; + + //! token -> scalar + scalar getScalar() const + { + return coco_string_toDouble(t->val); + } + + //! token -> string + std::string getString() const + { + char* str = coco_string_create_char(t->val); + std::string s(str); + coco_string_delete(str); + return s; + } + + + //! attach a dictionary + void dict(const dictionary& dict) const + { + dict_ = const_cast<dictionary*>(&dict); + } + + + //! lookup dictionary entry + scalar getDictLookup() const + { + scalar dictValue = 0; + + if (!dict_) + { + FatalErrorIn + ( + "SimpleCalc::getDictEntry() const" + ) << "No dictionary attached!" + << exit(FatalError); + + return 0; + } + + char* chars = coco_string_create_char + ( + t->val, + 1, + (coco_string_length(t->val) - 1) + ); + word keyword(chars); + coco_string_delete(chars); + + if (debug) + { + Info<<"lookup: " << keyword << nl; + } + + entry* entryPtr = dict_->lookupEntryPtr(keyword, true, false); + if (entryPtr && !entryPtr->isDict()) + { + entryPtr->stream() >> dictValue; + } + else + { + FatalErrorIn + ( + "SimpleCalc::getDictEntry() const" + ) << "keyword " << keyword << " is undefined in dictionary " + << exit(FatalError); + } + + + return dictValue; + } + + scalar Result() const + { + return val; + } + + +// * * * * * * * * * * * * * * * CHARACTERS * * * * * * * * * * * * * * * * // + +CHARACTERS + letter = 'A'..'Z' + 'a'..'z'. + qualifier = '_' + ':'. + dollar = '$'. + digit = "0123456789". + sign = '+' + '-'. + cr = '\r'. + lf = '\n'. + tab = '\t'. + stringCh = ANY - '"' - '\\' - cr - lf. + printable = '\u0020' .. '\u007e'. + + +// * * * * * * * * * * * * * * * * TOKENS * * * * * * * * * * * * * * * * * // + +TOKENS + +// identifier +ident = + letter { letter | digit | qualifier }. + +// string +string = + '"' { stringCh | '\\' printable } '"'. + +// dictionary lookup identifier +// starts with '$' and otherwise limited to a normal indentifier +variable = + dollar letter { letter | digit | qualifier }. + +// floating point and integer numbers +number = + [sign] ('.' digit { digit } ) | ( digit { digit } [ '.' { digit } ]) + [ ('E' | 'e') [sign] digit { digit } ]. + + +// * * * * * * * * * * * PRAGMAS / COMMENTS / IGNORE * * * * * * * * * * * // + +COMMENTS FROM "/*" TO "*/" NESTED +COMMENTS FROM "//" TO lf + +// ignore unprintables +IGNORE ANY - printable + + +// * * * * * * * * * * * * * * * PRODUCTIONS * * * * * * * * * * * * * * * // + +PRODUCTIONS + +SimpleCalc (. val = 0; + if (debug){Info<<"start val"<< nl;} + .) += + ( '{' Expr<val> '}' | Expr<val> ) + EOF +. + + +/*---------------------------------------------------------------------------*/ + +Expr<scalar& val> (. scalar val2 = 0; + if (debug) {Info<<"Expr:"<< val<< nl;} + .) += + Term<val> + { + "+" Term<val2> (. if (debug) {Info<<"+Term:"<<val2 <<nl;} + val += val2; + if (debug) {Info<<"="<< val << nl;} + .) + | "-" Term<val2> (. if (debug) {Info<<"-Term:"<<val2 <<nl;} + val -= val2; + if (debug) {Info<<"="<< val << nl;} + .) + } +. + + +/*---------------------------------------------------------------------------*/ + +Term<scalar& val> (. scalar val2 = 0; + if (debug) {Info<<"Term:"<< val<< nl;} + .) += + Factor<val> + { + "*" Factor<val2> (. if (debug) {Info<<"*Factor:"<<val2 << nl;} + val *= val2; + if (debug) {Info<<"="<< val << nl; } + .) + | "/" Factor<val2> (. if (debug) {Info<<"/Factor:"<<val2 << nl;} + val /= val2; + if (debug) {Info<<"="<< val << nl; } + .) + } +. + + +/*---------------------------------------------------------------------------*/ +Factor<scalar& val> += + variable (. val = getDictLookup(); + if (debug) {Info<<"lookup:"<<val<<nl;} + .) + | number (. val = getScalar(); + if (debug) {Info<<"got num:"<<val<<nl;} + .) + | '-' '(' Expr<val> ')' (. val = -val; + if (debug) {Info<<"inv:"<<val<<nl;} + .) + | '(' Expr<val> ')' (. if (debug){Info<<"got Expr:"<<val<<nl;} .) +. + + +/*---------------------------------------------------------------------------*/ + +END SimpleCalc. + +// ************************************************************************* // diff --git a/applications/test/dictionary/calcEntry/build.sh b/applications/test/dictionary/calcEntry/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..4bbaf0eeaf25462e491e09641e6a297427e1698a --- /dev/null +++ b/applications/test/dictionary/calcEntry/build.sh @@ -0,0 +1,11 @@ +#/bin/sh +cd ${0%/*} || exit 1 # run from this directory + +# this will have to do until we make a makefile rule + +Coco \ + -frames $WM_THIRD_PARTY_DIR/coco-r \ + -prefix calcEntry \ + -namespace Foam::functionEntries::calcEntryInternal \ + SimpleCalc.atg + diff --git a/applications/test/dictionary/calcEntry/calcEntry.C b/applications/test/dictionary/calcEntry/calcEntry.C index 76738dd9097bca706d99910afbb8d920537a6534..ccfc68b7027ea5e6f79251784fbc122a18984019 100644 --- a/applications/test/dictionary/calcEntry/calcEntry.C +++ b/applications/test/dictionary/calcEntry/calcEntry.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2009-2009 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -30,6 +30,10 @@ License #include "OStringStream.H" #include "addToMemberFunctionSelectionTable.H" +#include "ISstream.H" +#include "CocoParserErrors.H" +#include "calcEntryParser.h" + // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam @@ -56,14 +60,121 @@ bool Foam::functionEntries::calcEntry::execute ( const dictionary& parentDict, primitiveEntry& entry, - Istream& is + Istream& istr ) { - dictionary args(parentDict, is); - OStringStream resultStream; - resultStream - << (args.lookup("x")[0].number() + args.lookup("y")[0].number()); - entry.read(parentDict, IStringStream(resultStream.str())()); + static const int maxLen = 1024; + static const int errLen = 80; // truncate error message for readability + static char buf[maxLen]; + + ISstream& is = dynamicCast<ISstream>(istr); + + // get the { ... } argument without the enclosing brace brackets + // + // THIS NEEDS REWORKING (LATER) ... + + char c = 0; + + if (!is.read(c).good() || c != token::BEGIN_BLOCK) + { + is.setBad(); + FatalIOErrorIn("functionEntries::calcEntry::execute()", is) + << "Expected a '" << token::BEGIN_BLOCK + << "', found '" << c << "'" << exit(FatalIOError); + + return false; + } + + register int nChar = 0; + buf[nChar++] = token::BEGIN_BLOCK; + int listDepth = 1; // already saw the first '{' + + while (is.get(c).good()) + { + buf[nChar++] = c; + if (nChar == maxLen) + { + buf[errLen] = '\0'; + + FatalIOErrorIn("functionEntries::calcEntry::execute()", is) + << "argument \"" << buf << "...\"\n" + << " is too long (max. " << maxLen << " characters)" + << exit(FatalIOError); + + return false; + } + + // handle nested blocks, even if we don't know what they'd + // be useful for + if (c == token::BEGIN_BLOCK) + { + ++listDepth; + } + else if (c == token::END_BLOCK) + { + if (--listDepth == 0) + { + // done reading - overwrite the final '}' + // --nChar; + break; + } + } + } + + buf[nChar] = '\0'; + + + // emit some info + Info<< "grabbed " << nChar << " characters:" << nl + << "----------\n" + << buf << nl + << "----------\n" + << nl; + + + // define parser error handler + CocoParserErrors<calcEntryInternal::Errors> + myErrorHandler("calcEntry::Parser--"); + + calcEntryInternal::Scanner scanner(buf, nChar); + calcEntryInternal::Parser parser(&scanner, &myErrorHandler); + + // Attach dictionary context + parser.dict(parentDict); + + parser.Parse(); + +// Info<<"got: " << parser.Result() << endl; + + tokenList tokens(2); + tokens[0] = parser.Result(); + tokens[1] = token::END_STATEMENT; + +// Info<<"tokens[0] = " << tokens[0].info() <<nl; +// Info<<"tokens[1] = " << tokens[1].info() <<nl; +// +// { +// const tokenList& toks = entry; +// +// forAll(toks, tokI) +// { +// Info<< tokI <<":= " << toks[tokI].info() << endl; +// } +// } + + ITstream its("ParserResult", tokens); + entry.read(parentDict, its); +// Info<< "size: " << entry.size() << endl; +// entry = newente; +// entry.print(Info); + +/// const tokenList& toks = entry; +/// +/// forAll(toks, tokI) +/// { +/// Info<< tokI <<":= " << toks[tokI].info() << endl; +/// } +/// return true; } diff --git a/applications/test/dictionary/calcEntry/calcEntry.H b/applications/test/dictionary/calcEntry/calcEntry.H index 119f15564375fa3bb2c31ab1b1c79014745328a9..4f579bd4f29614015c308f0ca64f20e25daf3bf3 100644 --- a/applications/test/dictionary/calcEntry/calcEntry.H +++ b/applications/test/dictionary/calcEntry/calcEntry.H @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2009-2009 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -26,6 +26,8 @@ Class Foam::functionEntries::calcEntry Description + This dictionary function entry may or may not do anything particularly + useful - depending upon what is currently being used to test. SourceFiles calcEntry.C @@ -45,7 +47,7 @@ namespace functionEntries { /*---------------------------------------------------------------------------*\ - Class calcEntry Declaration + Class calcEntry Declaration \*---------------------------------------------------------------------------*/ class calcEntry @@ -64,7 +66,7 @@ class calcEntry public: //- Runtime type information - ClassName("calc"); + ClassName("test"); // Member Functions diff --git a/applications/test/dictionary/calcEntry/calcEntryParser.cpp b/applications/test/dictionary/calcEntry/calcEntryParser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..412a00c52f5f97cdaa2ed5b2b3b4d8e0d00a8448 --- /dev/null +++ b/applications/test/dictionary/calcEntry/calcEntryParser.cpp @@ -0,0 +1,324 @@ + + +#include <wchar.h> +#include "calcEntryParser.h" + + +namespace Foam { +namespace functionEntries { +namespace calcEntryInternal { + + +// ---------------------------------------------------------------------------- +// Parser Implementation +// ---------------------------------------------------------------------------- + +void Parser::SynErr(int n) { + if (errDist >= minErrDist) errors->SynErr(la->line, la->col, n); + errDist = 0; +} + + +void Parser::SemErr(const wchar_t* msg) { + if (errDist >= minErrDist) errors->Error(t->line, t->col, msg); + errDist = 0; +} + + +void Parser::Get() { + for (;;) { + t = la; + la = scanner->Scan(); + if (la->kind <= maxT) { + ++errDist; + break; + } + + if (dummyToken != t) { + dummyToken->kind = t->kind; + dummyToken->pos = t->pos; + dummyToken->col = t->col; + dummyToken->line = t->line; + dummyToken->next = NULL; + coco_string_delete(dummyToken->val); + dummyToken->val = coco_string_create(t->val); + t = dummyToken; + } + la = t; + } +} + + +void Parser::Expect(int n) { + if (la->kind == n) { + Get(); + } + else { + SynErr(n); + } +} + + +void Parser::ExpectWeak(int n, int follow) { + if (la->kind == n) { + Get(); + } + else { + SynErr(n); + while (!StartOf(follow)) { + Get(); + } + } +} + + +bool Parser::WeakSeparator(int n, int syFol, int repFol) { + if (la->kind == n) { + Get(); + return true; + } + else if (StartOf(repFol)) { + return false; + } + else { + SynErr(n); + while (!(StartOf(syFol) || StartOf(repFol) || StartOf(0))) { + Get(); + } + return StartOf(syFol); + } +} + + +void Parser::SimpleCalc() { + val = 0; + if (debug){Info<<"start val"<< nl;} + + if (la->kind == 5) { + Get(); + Expr(val); + Expect(6); + } else if (StartOf(1)) { + Expr(val); + } else SynErr(14); + Expect(0); +} + +void Parser::Expr(scalar& val) { + scalar val2 = 0; + if (debug) {Info<<"Expr:"<< val<< nl;} + + Term(val); + while (la->kind == 7 || la->kind == 8) { + if (la->kind == 7) { + Get(); + Term(val2); + if (debug) {Info<<"+Term:"<<val2 <<nl;} + val += val2; + if (debug) {Info<<"="<< val << nl;} + + } else { + Get(); + Term(val2); + if (debug) {Info<<"-Term:"<<val2 <<nl;} + val -= val2; + if (debug) {Info<<"="<< val << nl;} + + } + } +} + +void Parser::Term(scalar& val) { + scalar val2 = 0; + if (debug) {Info<<"Term:"<< val<< nl;} + + Factor(val); + while (la->kind == 9 || la->kind == 10) { + if (la->kind == 9) { + Get(); + Factor(val2); + if (debug) {Info<<"*Factor:"<<val2 << nl;} + val *= val2; + if (debug) {Info<<"="<< val << nl; } + + } else { + Get(); + Factor(val2); + if (debug) {Info<<"/Factor:"<<val2 << nl;} + val /= val2; + if (debug) {Info<<"="<< val << nl; } + + } + } +} + +void Parser::Factor(scalar& val) { + if (la->kind == 3) { + Get(); + val = getDictLookup(); + if (debug) {Info<<"lookup:"<<val<<nl;} + + } else if (la->kind == 4) { + Get(); + val = getScalar(); + if (debug) {Info<<"got num:"<<val<<nl;} + + } else if (la->kind == 8) { + Get(); + Expect(11); + Expr(val); + Expect(12); + val = -val; + if (debug) {Info<<"inv:"<<val<<nl;} + + } else if (la->kind == 11) { + Get(); + Expr(val); + Expect(12); + if (debug){Info<<"got Expr:"<<val<<nl;} + } else SynErr(15); +} + + + +void Parser::Parse() { + t = NULL; + if (dummyToken) { // safety: someone might call Parse() twice + delete dummyToken; + } + la = dummyToken = new Token(); + la->val = coco_string_create(L"Dummy Token"); + Get(); + SimpleCalc(); + + Expect(0); +} + + +Parser::Parser(Scanner* scan, Errors* err) +: + dummyToken(NULL), + deleteErrorsDestruct_(!err), + minErrDist(2), + errDist(minErrDist), + scanner(scan), + errors(err), + t(NULL), + la(NULL) +{ + maxT = 13; + + if (!errors) { // add in default error handling + errors = new Errors(); + } +} + + +bool Parser::StartOf(int s) { + const bool T = true; + const bool x = false; + + static bool set[2][15] = { + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x}, + {x,x,x,T, T,x,x,x, T,x,x,T, x,x,x} + }; + + + + return set[s][la->kind]; +} + + +Parser::~Parser() { + if (deleteErrorsDestruct_) { // delete default error handling + delete errors; + } + delete dummyToken; +} + + +// ---------------------------------------------------------------------------- +// Errors Implementation +// ---------------------------------------------------------------------------- + +Errors::Errors() +: + count(0) +{} + + +Errors::~Errors() +{} + + +void Errors::clear() { + count = 0; +} + + +wchar_t* Errors::strerror(int n) +{ + wchar_t* s; + switch (n) { + case 0: s = coco_string_create(L"EOF expected"); break; + case 1: s = coco_string_create(L"ident expected"); break; + case 2: s = coco_string_create(L"string expected"); break; + case 3: s = coco_string_create(L"variable expected"); break; + case 4: s = coco_string_create(L"number expected"); break; + case 5: s = coco_string_create(L"\"{\" expected"); break; + case 6: s = coco_string_create(L"\"}\" expected"); break; + case 7: s = coco_string_create(L"\"+\" expected"); break; + case 8: s = coco_string_create(L"\"-\" expected"); break; + case 9: s = coco_string_create(L"\"*\" expected"); break; + case 10: s = coco_string_create(L"\"/\" expected"); break; + case 11: s = coco_string_create(L"\"(\" expected"); break; + case 12: s = coco_string_create(L"\")\" expected"); break; + case 13: s = coco_string_create(L"??? expected"); break; + case 14: s = coco_string_create(L"invalid SimpleCalc"); break; + case 15: s = coco_string_create(L"invalid Factor"); break; + + default: + { + wchar_t format[20]; + coco_swprintf(format, 20, L"error %d", n); + s = coco_string_create(format); + } + break; + } + return s; +} + + +void Errors::Warning(const wchar_t* msg) { + wprintf(L"%ls\n", msg); +} + + +void Errors::Warning(int line, int col, const wchar_t* msg) { + wprintf(L"-- line %d col %d: %ls\n", line, col, msg); +} + + +void Errors::Error(int line, int col, const wchar_t* msg) { + wprintf(L"-- line %d col %d: %ls\n", line, col, msg); + count++; +} + + +void Errors::SynErr(int line, int col, int n) { + wchar_t* msg = this->strerror(n); + this->Error(line, col, msg); + coco_string_delete(msg); +} + + +void Errors::Exception(const wchar_t* msg) { + wprintf(L"%ls", msg); + ::exit(1); +} + +} // namespace +} // namespace +} // namespace + + diff --git a/applications/test/dictionary/calcEntry/calcEntryParser.h b/applications/test/dictionary/calcEntry/calcEntryParser.h new file mode 100644 index 0000000000000000000000000000000000000000..d3da4d05fc487a09e18e9b80a4196796737d5130 --- /dev/null +++ b/applications/test/dictionary/calcEntry/calcEntryParser.h @@ -0,0 +1,192 @@ + + +#ifndef COCO_calcEntryPARSER_H__ +#define COCO_calcEntryPARSER_H__ + +#include "dictionary.H" +#include "scalar.H" +#include "error.H" +#include "wchar.H" + + +#include "calcEntryScanner.h" + +namespace Foam { +namespace functionEntries { +namespace calcEntryInternal { + + +//! Parser error handing +class Errors { +public: + int count; //!< The number of errors detected + + //! Allocate and return a string describing the given error code. + /** It is the responsibility of the caller to free this string, + * eg, with coco_string_delete() + */ + static wchar_t* strerror(int n); + + Errors(); //!< Construct null - start with no errors + virtual ~Errors(); //!< Destructor + virtual void clear(); //!< Clear the error count + + //! Handle a general warning 'msg' + virtual void Warning(const wchar_t* msg); + //! Handle a general warning 'msg' + virtual void Warning(int line, int col, const wchar_t* msg); + //! Handle general error 'msg' (eg, a semantic error) + virtual void Error(int line, int col, const wchar_t* msg); + //! Handle syntax error 'n', uses strerror for the message, calls Error() + virtual void SynErr(int line, int col, int n); + //! Handle a general exception 'msg' + virtual void Exception(const wchar_t* msg); + +}; // Errors + + +//! A Coco/R Parser +class Parser { +private: + enum { + _EOF=0, + _ident=1, + _string=2, + _variable=3, + _number=4, + }; + int maxT; + + Token *dummyToken; + bool deleteErrorsDestruct_; //!< delete the 'errors' member in destructor + int minErrDist; + int errDist; + + void SynErr(int n); //!< Handle syntax error 'n' + void Get(); + void Expect(int n); + bool StartOf(int s); + void ExpectWeak(int n, int follow); + bool WeakSeparator(int n, int syFol, int repFol); + +public: + Scanner *scanner; + Errors *errors; + + Token *t; //!< last recognized token + Token *la; //!< lookahead token + +static const int debug = 0; + + //! The parent dictionary + mutable dictionary* dict_; + + //! The calculation result + scalar val; + + //! token -> scalar + scalar getScalar() const + { + return coco_string_toDouble(t->val); + } + + //! token -> string + std::string getString() const + { + char* str = coco_string_create_char(t->val); + std::string s(str); + coco_string_delete(str); + return s; + } + + + //! attach a dictionary + void dict(const dictionary& dict) const + { + dict_ = const_cast<dictionary*>(&dict); + } + + + //! lookup dictionary entry + scalar getDictLookup() const + { + scalar dictValue = 0; + + if (!dict_) + { + FatalErrorIn + ( + "SimpleCalc::getDictEntry() const" + ) << "No dictionary attached!" + << exit(FatalError); + + return 0; + } + + char* chars = coco_string_create_char + ( + t->val, + 1, + (coco_string_length(t->val) - 1) + ); + word keyword(chars); + coco_string_delete(chars); + + if (debug) + { + Info<<"lookup: " << keyword << nl; + } + + entry* entryPtr = dict_->lookupEntryPtr(keyword, true, false); + if (entryPtr && !entryPtr->isDict()) + { + entryPtr->stream() >> dictValue; + } + else + { + FatalErrorIn + ( + "SimpleCalc::getDictEntry() const" + ) << "keyword " << keyword << " is undefined in dictionary " + << exit(FatalError); + } + + + return dictValue; + } + + scalar Result() const + { + return val; + } + + +// * * * * * * * * * * * * * * * CHARACTERS * * * * * * * * * * * * * * * * // + + + + //! Construct for the specified scanner + /** + * Use the default error handling, or optionally provide an error + * handler, which will not be deleted upon destruction. + */ + Parser(Scanner* scan, Errors* err = 0); + ~Parser(); //!< Destructor - cleanup errors and dummyToken + void SemErr(const wchar_t* msg); //!< Handle semantic error + + void SimpleCalc(); + void Expr(scalar& val); + void Term(scalar& val); + void Factor(scalar& val); + + void Parse(); //!< Execute the parse operation + +}; // end Parser + +} // namespace +} // namespace +} // namespace + + +#endif // COCO_calcEntryPARSER_H__ + diff --git a/applications/test/dictionary/calcEntry/calcEntryScanner.cpp b/applications/test/dictionary/calcEntry/calcEntryScanner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..86a92d32333c0b39ff53f707cb1f0a0026a1e5ab --- /dev/null +++ b/applications/test/dictionary/calcEntry/calcEntryScanner.cpp @@ -0,0 +1,825 @@ + + +#include <memory.h> +#include <string.h> +#include "calcEntryScanner.h" + +namespace Foam { +namespace functionEntries { +namespace calcEntryInternal { + + +// * * * * * * * * * * Wide Character String Routines * * * * * * * * * * * // + +// string handling, wide character + +wchar_t* coco_string_create(const wchar_t* str) { + int len = coco_string_length(str); + wchar_t* dest = new wchar_t[len + 1]; + if (len) { + wcsncpy(dest, str, len); + } + dest[len] = 0; + return dest; +} + +wchar_t* coco_string_create(const wchar_t* str, int index, int length) { + int len = coco_string_length(str); + if (len) { + len = length; + } + wchar_t* dest = new wchar_t[len + 1]; + if (len) { + wcsncpy(dest, &(str[index]), len); + } + dest[len] = 0; + return dest; +} + +wchar_t* coco_string_create_upper(const wchar_t* str) { + if (!str) { return NULL; } + return coco_string_create_upper(str, 0, wcslen(str)); +} + + +wchar_t* coco_string_create_upper(const wchar_t* str, int index, int len) { + if (!str) { return NULL; } + wchar_t* dest = new wchar_t[len + 1]; + + for (int i = 0; i < len; i++) { + const wchar_t ch = str[index + i]; + if ((L'a' <= ch) && (ch <= L'z')) { + dest[i] = ch + (L'A' - L'a'); + } + else { + dest[i] = ch; + } + } + dest[len] = L'\0'; + return dest; +} + + +wchar_t* coco_string_create_lower(const wchar_t* str) { + if (!str) { return NULL; } + return coco_string_create_lower(str, 0, wcslen(str)); +} + + +wchar_t* coco_string_create_lower(const wchar_t* str, int index, int len) { + if (!str) { return NULL; } + wchar_t* dest = new wchar_t[len + 1]; + + for (int i = 0; i < len; i++) { + const wchar_t ch = str[index + i]; + if ((L'A' <= ch) && (ch <= L'Z')) { + dest[i] = ch - (L'A' - L'a'); + } + else { + dest[i] = ch; + } + } + dest[len] = L'\0'; + return dest; +} + + +wchar_t* coco_string_create_append(const wchar_t* str1, const wchar_t* str2) { + int str1Len = coco_string_length(str1); + int str2Len = coco_string_length(str2); + + wchar_t* dest = new wchar_t[str1Len + str2Len + 1]; + + if (str1Len) { wcscpy(dest, str1); } + if (str2Len) { wcscpy(dest + str1Len, str2); } + + dest[str1Len + str2Len] = 0; + return dest; +} + +wchar_t* coco_string_create_append(const wchar_t* str1, const wchar_t ch) { + int len = coco_string_length(str1); + wchar_t* dest = new wchar_t[len + 2]; + wcsncpy(dest, str1, len); // or use if (len) { wcscpy(dest, str1); } + dest[len] = ch; + dest[len + 1] = 0; + return dest; +} + +void coco_string_delete(wchar_t* &str) { + delete [] str; + str = NULL; +} + +int coco_string_length(const wchar_t* str) { + return str ? wcslen(str) : 0; +} + +bool coco_string_endswith(const wchar_t* str, const wchar_t* endstr) { + int strLen = wcslen(str); + int endLen = wcslen(endstr); + return (endLen <= strLen) && (wcscmp(str + strLen - endLen, endstr) == 0); +} + +int coco_string_indexof(const wchar_t* str, const wchar_t ch) { + const wchar_t* fnd = wcschr(str, ch); + return fnd ? (fnd - str) : -1; +} + +int coco_string_lastindexof(const wchar_t* str, const wchar_t ch) { + const wchar_t* fnd = wcsrchr(str, ch); + return fnd ? (fnd - str) : -1; +} + +void coco_string_merge(wchar_t* &dest, const wchar_t* str) { + if (!str) { return; } + wchar_t* newstr = coco_string_create_append(dest, str); + delete [] dest; + dest = newstr; +} + +bool coco_string_equal(const wchar_t* str1, const wchar_t* str2) { + return wcscmp(str1, str2) == 0; +} + +int coco_string_compareto(const wchar_t* str1, const wchar_t* str2) { + return wcscmp(str1, str2); +} + +int coco_string_hash(const wchar_t* str) { + int h = 0; + if (!str) { return 0; } + while (*str != 0) { + h = (h * 7) ^ *str; + ++str; + } + if (h < 0) { h = -h; } + return h; +} + + +double coco_string_toDouble(const wchar_t* str) +{ + return str ? wcstod(str, NULL) : 0; +} + +float coco_string_toFloat(const wchar_t* str) +{ + return str ? wcstof(str, NULL) : 0; +} + + + +// +// string handling, byte character +// + +wchar_t* coco_string_create(const char* str) { + int len = str ? strlen(str) : 0; + wchar_t* dest = new wchar_t[len + 1]; + for (int i = 0; i < len; ++i) { + dest[i] = (wchar_t) str[i]; + } + dest[len] = 0; + return dest; +} + +wchar_t* coco_string_create(const char* str, int index, int length) { + int len = str ? length : 0; + wchar_t* dest = new wchar_t[len + 1]; + for (int i = 0; i < len; ++i) { + dest[i] = (wchar_t) str[index + i]; + } + dest[len] = 0; + return dest; +} + + +char* coco_string_create_char(const wchar_t* str) { + int len = coco_string_length(str); + char *dest = new char[len + 1]; + for (int i = 0; i < len; ++i) + { + dest[i] = (char) str[i]; + } + dest[len] = 0; + return dest; +} + +char* coco_string_create_char(const wchar_t* str, int index, int length) { + int len = coco_string_length(str); + if (len) { + len = length; + } + char *dest = new char[len + 1]; + for (int i = 0; i < len; ++i) { + dest[i] = (char) str[index + i]; + } + dest[len] = 0; + return dest; +} + + +void coco_string_delete(char* &str) { + delete [] str; + str = NULL; +} + + +double coco_string_toDouble(const char* str) +{ + return str ? strtod(str, NULL) : 0; +} + +float coco_string_toFloat(const char* str) +{ + return str ? strtof(str, NULL) : 0; +} + + +// * * * * * * * * * End of Wide Character String Routines * * * * * * * * * // + + +Token::Token() +: + kind(0), + pos(0), + col(0), + line(0), + val(NULL), + next(NULL) +{} + + +Token::~Token() { + coco_string_delete(val); +} + + +Buffer::Buffer(FILE* s, bool isUserStream) { +// ensure binary read on windows +#if _MSC_VER >= 1300 + _setmode(_fileno(s), _O_BINARY); +#endif + stream = s; this->isUserStream = isUserStream; + if (CanSeek()) { + fseek(s, 0, SEEK_END); + fileLen = ftell(s); + fseek(s, 0, SEEK_SET); + bufLen = (fileLen < MAX_BUFFER_LENGTH) ? fileLen : MAX_BUFFER_LENGTH; + bufStart = INT_MAX; // nothing in the buffer so far + } + else { + fileLen = bufLen = bufStart = 0; + } + bufCapacity = (bufLen > 0) ? bufLen : MIN_BUFFER_LENGTH; + buf = new unsigned char[bufCapacity]; + if (fileLen > 0) SetPos(0); // setup buffer to position 0 (start) + else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid + if (bufLen == fileLen && CanSeek()) Close(); +} + + +Buffer::Buffer(Buffer* b) { + buf = b->buf; + bufCapacity = b->bufCapacity; + b->buf = NULL; + bufStart = b->bufStart; + bufLen = b->bufLen; + fileLen = b->fileLen; + bufPos = b->bufPos; + stream = b->stream; + b->stream = NULL; + isUserStream = b->isUserStream; +} + + +Buffer::Buffer(const unsigned char* buf, int len) { + this->buf = new unsigned char[len]; + memcpy(this->buf, buf, len*sizeof(unsigned char)); + bufStart = 0; + bufCapacity = bufLen = len; + fileLen = len; + bufPos = 0; + stream = NULL; +} + + +Buffer::Buffer(const char* buf, int len) { + this->buf = new unsigned char[len]; + memcpy(this->buf, buf, len*sizeof(unsigned char)); + bufStart = 0; + bufCapacity = bufLen = len; + fileLen = len; + bufPos = 0; + stream = NULL; +} + + +Buffer::~Buffer() { + Close(); + if (buf != NULL) { + delete [] buf; + buf = NULL; + } +} + + +void Buffer::Close() { + if (!isUserStream && stream != NULL) { + fclose(stream); + stream = NULL; + } +} + + +int Buffer::Read() { + if (bufPos < bufLen) { + return buf[bufPos++]; + } else if (GetPos() < fileLen) { + SetPos(GetPos()); // shift buffer start to Pos + return buf[bufPos++]; + } else if ((stream != NULL) && !CanSeek() && (ReadNextStreamChunk() > 0)) { + return buf[bufPos++]; + } else { + return EoF; + } +} + + +int Buffer::Peek() { + int curPos = GetPos(); + int ch = Read(); + SetPos(curPos); + return ch; +} + + +wchar_t* Buffer::GetString(int beg, int end) { + int len = 0; + wchar_t *buf = new wchar_t[end - beg]; + int oldPos = GetPos(); + SetPos(beg); + while (GetPos() < end) buf[len++] = (wchar_t) Read(); + SetPos(oldPos); + wchar_t *res = coco_string_create(buf, 0, len); + coco_string_delete(buf); + return res; +} + + +int Buffer::GetPos() { + return bufPos + bufStart; +} + + +void Buffer::SetPos(int value) { + if ((value >= fileLen) && (stream != NULL) && !CanSeek()) { + // Wanted position is after buffer and the stream + // is not seek-able e.g. network or console, + // thus we have to read the stream manually till + // the wanted position is in sight. + while ((value >= fileLen) && (ReadNextStreamChunk() > 0)) + {} + } + + if ((value < 0) || (value > fileLen)) { + wprintf(L"--- buffer out of bounds access, position: %d\n", value); + ::exit(1); + } + + if ((value >= bufStart) && (value < (bufStart + bufLen))) { // already in buffer + bufPos = value - bufStart; + } else if (stream != NULL) { // must be swapped in + fseek(stream, value, SEEK_SET); + bufLen = fread(buf, sizeof(unsigned char), bufCapacity, stream); + bufStart = value; bufPos = 0; + } else { + bufPos = fileLen - bufStart; // make Pos return fileLen + } +} + + +// Read the next chunk of bytes from the stream, increases the buffer +// if needed and updates the fields fileLen and bufLen. +// Returns the number of bytes read. +int Buffer::ReadNextStreamChunk() { + int freeLen = bufCapacity - bufLen; + if (freeLen == 0) { + // in the case of a growing input stream + // we can neither seek in the stream, nor can we + // foresee the maximum length, thus we must adapt + // the buffer size on demand. + bufCapacity = bufLen * 2; + unsigned char *newBuf = new unsigned char[bufCapacity]; + memcpy(newBuf, buf, bufLen*sizeof(unsigned char)); + delete [] buf; + buf = newBuf; + freeLen = bufLen; + } + int read = fread(buf + bufLen, sizeof(unsigned char), freeLen, stream); + if (read > 0) { + fileLen = bufLen = (bufLen + read); + return read; + } + // end of stream reached + return 0; +} + + +bool Buffer::CanSeek() { + return (stream != NULL) && (ftell(stream) != -1); +} + + +int UTF8Buffer::Read() { + int ch; + do { + ch = Buffer::Read(); + // until we find a utf8 start (0xxxxxxx or 11xxxxxx) + } while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EoF)); + if (ch < 128 || ch == EoF) { + // nothing to do, first 127 chars are the same in ascii and utf8 + // 0xxxxxxx or end of file character + } else if ((ch & 0xF0) == 0xF0) { + // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + int c1 = ch & 0x07; ch = Buffer::Read(); + int c2 = ch & 0x3F; ch = Buffer::Read(); + int c3 = ch & 0x3F; ch = Buffer::Read(); + int c4 = ch & 0x3F; + ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4; + } else if ((ch & 0xE0) == 0xE0) { + // 1110xxxx 10xxxxxx 10xxxxxx + int c1 = ch & 0x0F; ch = Buffer::Read(); + int c2 = ch & 0x3F; ch = Buffer::Read(); + int c3 = ch & 0x3F; + ch = (((c1 << 6) | c2) << 6) | c3; + } else if ((ch & 0xC0) == 0xC0) { + // 110xxxxx 10xxxxxx + int c1 = ch & 0x1F; ch = Buffer::Read(); + int c2 = ch & 0x3F; + ch = (c1 << 6) | c2; + } + return ch; +} + + +Scanner::Scanner(const unsigned char* buf, int len) { + buffer = new Buffer(buf, len); + Init(); +} + + +Scanner::Scanner(const char* buf, int len) { + buffer = new Buffer(buf, len); + Init(); +} + + +Scanner::Scanner(const wchar_t* fileName) { + FILE* stream; + char *chFileName = coco_string_create_char(fileName); + if ((stream = fopen(chFileName, "rb")) == NULL) { + wprintf(L"--- Cannot open file %ls\n", fileName); + ::exit(1); + } + coco_string_delete(chFileName); + buffer = new Buffer(stream, false); + Init(); +} + + +Scanner::Scanner(FILE* s) { + buffer = new Buffer(s, true); + Init(); +} + + +Scanner::~Scanner() { + char* cur = (char*) firstHeap; + + while (cur != NULL) { + cur = *(char**) (cur + HEAP_BLOCK_SIZE); + free(firstHeap); + firstHeap = cur; + } + delete [] tval; + delete buffer; +} + + +void Scanner::Init() { + maxT = 13; + noSym = 13; + int i; + for (i = 65; i <= 90; ++i) start.set(i, 1); + for (i = 97; i <= 122; ++i) start.set(i, 1); + for (i = 36; i <= 36; ++i) start.set(i, 5); + start.set(45, 20); + for (i = 48; i <= 57; ++i) start.set(i, 9); + start.set(34, 2); + start.set(46, 7); + start.set(123, 14); + start.set(125, 15); + start.set(43, 21); + start.set(42, 16); + start.set(47, 17); + start.set(40, 18); + start.set(41, 19); + start.set(Buffer::EoF, -1); + + + tvalLength = 128; + tval = new wchar_t[tvalLength]; // text of current token + + // HEAP_BLOCK_SIZE byte heap + pointer to next heap block + heap = malloc(HEAP_BLOCK_SIZE + sizeof(void*)); + firstHeap = heap; + heapEnd = (void**) (((char*) heap) + HEAP_BLOCK_SIZE); + *heapEnd = 0; + heapTop = heap; + if (sizeof(Token) > HEAP_BLOCK_SIZE) { + wprintf(L"--- Too small HEAP_BLOCK_SIZE\n"); + ::exit(1); + } + + pos = -1; line = 1; col = 0; + oldEols = 0; + NextCh(); + if (ch == 0xEF) { // check optional byte order mark for UTF-8 + NextCh(); int ch1 = ch; + NextCh(); int ch2 = ch; + if (ch1 != 0xBB || ch2 != 0xBF) { + wprintf(L"Illegal byte order mark at start of file"); + ::exit(1); + } + Buffer *oldBuf = buffer; + buffer = new UTF8Buffer(buffer); col = 0; + delete oldBuf; oldBuf = NULL; + NextCh(); + } + + + pt = tokens = CreateToken(); // first token is a dummy +} + + +void Scanner::NextCh() { + if (oldEols > 0) { + ch = EOL; + oldEols--; + } + else { + pos = buffer->GetPos(); + ch = buffer->Read(); col++; + // replace isolated '\r' by '\n' in order to make + // eol handling uniform across Windows, Unix and Mac + if (ch == L'\r' && buffer->Peek() != L'\n') ch = EOL; + if (ch == EOL) { line++; col = 0; } + } + +} + + +void Scanner::AddCh() { + if (tlen >= tvalLength) { + tvalLength *= 2; + wchar_t *newBuf = new wchar_t[tvalLength]; + memcpy(newBuf, tval, tlen*sizeof(wchar_t)); + delete [] tval; + tval = newBuf; + } + if (ch != Buffer::EoF) { + tval[tlen++] = ch; + NextCh(); + } +} + + + +bool Scanner::Comment0() { + int level = 1, pos0 = pos, line0 = line, col0 = col; + NextCh(); + if (ch == L'/') { + NextCh(); + for(;;) { + if (ch == 10) { + level--; + if (level == 0) { oldEols = line - line0; NextCh(); return true; } + NextCh(); + } else if (ch == buffer->EoF) return false; + else NextCh(); + } + } else { + buffer->SetPos(pos0); NextCh(); line = line0; col = col0; + } + return false; +} + +bool Scanner::Comment1() { + int level = 1, pos0 = pos, line0 = line, col0 = col; + NextCh(); + if (ch == L'*') { + NextCh(); + for(;;) { + if (ch == L'*') { + NextCh(); + if (ch == L'/') { + level--; + if (level == 0) { oldEols = line - line0; NextCh(); return true; } + NextCh(); + } + } else if (ch == L'/') { + NextCh(); + if (ch == L'*') { + level++; NextCh(); + } + } else if (ch == buffer->EoF) return false; + else NextCh(); + } + } else { + buffer->SetPos(pos0); NextCh(); line = line0; col = col0; + } + return false; +} + + +void Scanner::CreateHeapBlock() { + void* newHeap; + char* cur = (char*) firstHeap; + + while (((char*) tokens < cur) || ((char*) tokens > (cur + HEAP_BLOCK_SIZE))) { + cur = *((char**) (cur + HEAP_BLOCK_SIZE)); + free(firstHeap); + firstHeap = cur; + } + + // HEAP_BLOCK_SIZE byte heap + pointer to next heap block + newHeap = malloc(HEAP_BLOCK_SIZE + sizeof(void*)); + *heapEnd = newHeap; + heapEnd = (void**) (((char*) newHeap) + HEAP_BLOCK_SIZE); + *heapEnd = 0; + heap = newHeap; + heapTop = heap; +} + + +Token* Scanner::CreateToken() { + Token *t; + if (((char*) heapTop + (int) sizeof(Token)) >= (char*) heapEnd) { + CreateHeapBlock(); + } + t = (Token*) heapTop; + heapTop = (void*) ((char*) heapTop + sizeof(Token)); + t->val = NULL; + t->next = NULL; + return t; +} + + +void Scanner::AppendVal(Token *t) { + int reqMem = (tlen + 1) * sizeof(wchar_t); + if (((char*) heapTop + reqMem) >= (char*) heapEnd) { + if (reqMem > HEAP_BLOCK_SIZE) { + wprintf(L"--- Too long token value\n"); + ::exit(1); + } + CreateHeapBlock(); + } + t->val = (wchar_t*) heapTop; + heapTop = (void*) ((char*) heapTop + reqMem); + + wcsncpy(t->val, tval, tlen); + t->val[tlen] = L'\0'; +} + + +Token* Scanner::NextToken() { + while (ch == ' ' || + ch <= 31 || (ch >= 127 && ch <= 65535) + ) NextCh(); + if ((ch == L'/' && Comment0()) || (ch == L'/' && Comment1())) return NextToken(); + t = CreateToken(); + t->pos = pos; t->col = col; t->line = line; + int state = start.state(ch); + tlen = 0; AddCh(); + + switch (state) { + case -1: { t->kind = eofSym; break; } // NextCh already done + case 0: { t->kind = noSym; break; } // NextCh already done + case 1: + case_1: + if ((ch >= L'0' && ch <= L':') || (ch >= L'A' && ch <= L'Z') || ch == L'_' || (ch >= L'a' && ch <= L'z')) {AddCh(); goto case_1;} + else {t->kind = 1; break;} + case 2: + case_2: + if (ch <= 9 || (ch >= 11 && ch <= 12) || (ch >= 14 && ch <= L'!') || (ch >= L'#' && ch <= L'[') || (ch >= L']' && ch <= 65535)) {AddCh(); goto case_2;} + else if (ch == L'"') {AddCh(); goto case_4;} + else if (ch == 92) {AddCh(); goto case_3;} + else {t->kind = noSym; break;} + case 3: + case_3: + if ((ch >= L' ' && ch <= L'~')) {AddCh(); goto case_2;} + else {t->kind = noSym; break;} + case 4: + case_4: + {t->kind = 2; break;} + case 5: + if ((ch >= L'A' && ch <= L'Z') || (ch >= L'a' && ch <= L'z')) {AddCh(); goto case_6;} + else {t->kind = noSym; break;} + case 6: + case_6: + if ((ch >= L'0' && ch <= L':') || (ch >= L'A' && ch <= L'Z') || ch == L'_' || (ch >= L'a' && ch <= L'z')) {AddCh(); goto case_6;} + else {t->kind = 3; break;} + case 7: + case_7: + if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_8;} + else {t->kind = noSym; break;} + case 8: + case_8: + if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_8;} + else {t->kind = 4; break;} + case 9: + case_9: + if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_9;} + else if (ch == L'E' || ch == L'e') {AddCh(); goto case_10;} + else if (ch == L'.') {AddCh(); goto case_13;} + else {t->kind = 4; break;} + case 10: + case_10: + if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_12;} + else if (ch == L'+' || ch == L'-') {AddCh(); goto case_11;} + else {t->kind = noSym; break;} + case 11: + case_11: + if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_12;} + else {t->kind = noSym; break;} + case 12: + case_12: + if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_12;} + else {t->kind = 4; break;} + case 13: + case_13: + if ((ch >= L'0' && ch <= L'9')) {AddCh(); goto case_13;} + else if (ch == L'E' || ch == L'e') {AddCh(); goto case_10;} + else {t->kind = 4; break;} + case 14: + {t->kind = 5; break;} + case 15: + {t->kind = 6; break;} + case 16: + {t->kind = 9; break;} + case 17: + {t->kind = 10; break;} + case 18: + {t->kind = 11; break;} + case 19: + {t->kind = 12; break;} + case 20: + if (ch == L'.') {AddCh(); goto case_7;} + else {t->kind = 8; break;} + case 21: + if (ch == L'.') {AddCh(); goto case_7;} + else {t->kind = 7; break;} + + } + AppendVal(t); + return t; +} + + +// get the next token (possibly a token already seen during peeking) +Token* Scanner::Scan() { + if (tokens->next == NULL) { + return pt = tokens = NextToken(); + } else { + pt = tokens = tokens->next; + return tokens; + } +} + + +// peek for the next token, ignore pragmas +Token* Scanner::Peek() { + do { + if (pt->next == NULL) { + pt->next = NextToken(); + } + pt = pt->next; + } while (pt->kind > maxT); // skip pragmas + + return pt; +} + + +// make sure that peeking starts at the current scan position +void Scanner::ResetPeek() { + pt = tokens; +} + + +} // namespace +} // namespace +} // namespace + + diff --git a/applications/test/dictionary/calcEntry/calcEntryScanner.h b/applications/test/dictionary/calcEntry/calcEntryScanner.h new file mode 100644 index 0000000000000000000000000000000000000000..8847c9fd2e2e7ab24b714dc3fb3cedf2ab65364c --- /dev/null +++ b/applications/test/dictionary/calcEntry/calcEntryScanner.h @@ -0,0 +1,379 @@ + + +#ifndef COCO_calcEntrySCANNER_H__ +#define COCO_calcEntrySCANNER_H__ + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +// io.h and fcntl are used to ensure binary read from streams on windows +#if _MSC_VER >= 1300 +#include <io.h> +#include <fcntl.h> +#endif + +#if _MSC_VER >= 1400 +#define coco_swprintf swprintf_s +#elif _MSC_VER >= 1300 +#define coco_swprintf _snwprintf +#else +// assume every other compiler knows swprintf +#define coco_swprintf swprintf +#endif + +#define COCO_WCHAR_MAX 65535 +#define MIN_BUFFER_LENGTH 1024 +#define MAX_BUFFER_LENGTH (64*MIN_BUFFER_LENGTH) +#define HEAP_BLOCK_SIZE (64*1024) + + +namespace Foam { +namespace functionEntries { +namespace calcEntryInternal { + + + +// * * * * * * * * * * Wide Character String Routines * * * * * * * * * * * // + +// +// string handling, wide character +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//! Create by copying str +wchar_t* coco_string_create(const wchar_t* str); + +//! Create a substring of str starting at index and length characters long +wchar_t* coco_string_create(const wchar_t* str, int index, int length); + +//! Create an uppercase string from str +wchar_t* coco_string_create_upper(const wchar_t* str); + +//! Create an uppercase substring from str starting at index and length characters long +wchar_t* coco_string_create_upper(const wchar_t* str, int index, int length); + +//! Create a lowercase string from str +wchar_t* coco_string_create_lower(const wchar_t* str); + +//! Create a lowercase substring from str starting at index and length characters long +wchar_t* coco_string_create_lower(const wchar_t* str, int index, int length); + +//! Create a string by concatenating str1 and str2 +wchar_t* coco_string_create_append(const wchar_t* str1, const wchar_t* str2); + +//! Create a string by concatenating a character to the end of str +wchar_t* coco_string_create_append(const wchar_t* str, const wchar_t ch); + +//! Free storage and nullify the argument +void coco_string_delete(wchar_t* &str); + +//! The length of the str, or 0 if the str is NULL +int coco_string_length(const wchar_t* str); + +//! Return true if the str ends with the endstr +bool coco_string_endswith(const wchar_t* str, const wchar_t* endstr); + +//! Return the index of the first occurrence of ch. +// Return -1 if nothing is found. +int coco_string_indexof(const wchar_t* str, const wchar_t ch); + +//! Return the index of the last occurrence of ch. +// Return -1 if nothing is found. +int coco_string_lastindexof(const wchar_t* str, const wchar_t ch); + +//! Append str to dest +void coco_string_merge(wchar_t* &dest, const wchar_t* str); + +//! Compare strings, return true if they are equal +bool coco_string_equal(const wchar_t* str1, const wchar_t* str2); + +//! Compare strings, return 0 if they are equal +int coco_string_compareto(const wchar_t* str1, const wchar_t* str2); + +//! Simple string hashing function +int coco_string_hash(const wchar_t* str); + +// +// String conversions +// ~~~~~~~~~~~~~~~~~~ + +//! Convert wide string to double +double coco_string_toDouble(const wchar_t* str); + +//! Convert wide string to float +float coco_string_toFloat(const wchar_t* str); + +// +// String handling, byte character +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//! Create by copying byte str +wchar_t* coco_string_create(const char* str); + +//! Create a substring of byte str starting at index and length characters long +wchar_t* coco_string_create(const char* str, int index, int length); + +//! Create a byte string by copying str +char* coco_string_create_char(const wchar_t* str); + +//! Create a byte substring of str starting at index and length characters long +char* coco_string_create_char(const wchar_t* str, int index, int length); + +//! Free storage and nullify the argument +void coco_string_delete(char* &str); + + +// +// String conversions, byte character +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//! Convert byte string to double +double coco_string_toDouble(const char* str); + +//! Convert byte string to float +float coco_string_toFloat(const char* str); + +// * * * * * * * * * End of Wide Character String Routines * * * * * * * * * // + + + +//! Scanner Token +class Token +{ +public: + int kind; //!< token kind + int pos; //!< token position in the source text (starting at 0) + int col; //!< token column (starting at 1) + int line; //!< token line (starting at 1) + wchar_t* val; //!< token value + Token *next; //!< Peek tokens are kept in linked list + + Token(); //!< Construct null + ~Token(); //!< Destructor - cleanup allocated val +}; + + +//! Scanner Buffer +// +//! This Buffer supports the following cases: +//! -# seekable stream (file) +//! -# whole stream in buffer +//! -# part of stream in buffer +//! -# non seekable stream (network, console) +class Buffer { +private: + unsigned char *buf; //!< input buffer + int bufCapacity; //!< capacity of buf + int bufStart; //!< position of first byte in buffer relative to input stream + int bufLen; //!< length of buffer + int fileLen; //!< length of input stream (may change if the stream is no file) + int bufPos; //!< current position in buffer + FILE* stream; //!< input stream (seekable) + bool isUserStream; //!< was the stream opened by the user? + + int ReadNextStreamChunk(); + bool CanSeek(); //!< true if stream can be seeked otherwise false + +public: + static const int EoF = COCO_WCHAR_MAX + 1; + + Buffer(FILE*, bool isUserStream); + Buffer(const unsigned char* buf, int len); + Buffer(const char* buf, int len); + Buffer(Buffer*); + virtual ~Buffer(); + + virtual void Close(); + virtual int Read(); + virtual int Peek(); + virtual wchar_t* GetString(int beg, int end); + virtual int GetPos(); + virtual void SetPos(int value); +}; + + +//! A Scanner buffer that handles UTF-8 characters +class UTF8Buffer : public Buffer { +public: + UTF8Buffer(Buffer* b) : Buffer(b) {} + virtual int Read(); +}; + + +//------------------------------------------------------------------------------ +// StartStates +//------------------------------------------------------------------------------ +//! maps characters to start states of tokens +class StartStates { +private: + class Elem { + public: + int key, val; + Elem *next; + Elem(int key, int val) { + this->key = key; + this->val = val; + next = NULL; + } + }; + + Elem **tab; + +public: + StartStates() { + tab = new Elem*[128]; + memset(tab, 0, 128 * sizeof(Elem*)); + } + virtual ~StartStates() { + for (int i = 0; i < 128; ++i) { + Elem *e = tab[i]; + while (e != NULL) { + Elem *next = e->next; + delete e; + e = next; + } + } + delete [] tab; + } + + void set(int key, int val) { + Elem *e = new Elem(key, val); + int k = ((unsigned int) key) % 128; + e->next = tab[k]; + tab[k] = e; + } + + int state(int key) { + Elem *e = tab[((unsigned int) key) % 128]; + while (e != NULL && e->key != key) e = e->next; + return e == NULL ? 0 : e->val; + } +}; + + +//------------------------------------------------------------------------------ +// KeywordMap +//------------------------------------------------------------------------------ +//! maps strings to integers (identifiers to keyword kinds) +class KeywordMap { +private: + class Elem { + public: + wchar_t *key; + int val; + Elem *next; + Elem(const wchar_t *key, int val) { + this->key = coco_string_create(key); + this->val = val; + next = NULL; + } + virtual ~Elem() { + coco_string_delete(key); + } + }; + + Elem **tab; + +public: + KeywordMap() { + tab = new Elem*[128]; + memset(tab, 0, 128 * sizeof(Elem*)); + } + virtual ~KeywordMap() { + for (int i = 0; i < 128; ++i) { + Elem *e = tab[i]; + while (e != NULL) { + Elem *next = e->next; + delete e; + e = next; + } + } + delete [] tab; + } + + void set(const wchar_t *key, int val) { + Elem *e = new Elem(key, val); + int k = coco_string_hash(key) % 128; + e->next = tab[k]; tab[k] = e; + } + + int get(const wchar_t *key, int defaultVal) { + Elem *e = tab[coco_string_hash(key) % 128]; + while (e != NULL && !coco_string_equal(e->key, key)) e = e->next; + return e == NULL ? defaultVal : e->val; + } +}; + + +//! A Coco/R Scanner +class Scanner { +private: + static const unsigned char EOL = '\n'; // end-of-line character + static const int eofSym = 0; // end-of-file token id + + void *firstHeap; + void *heap; + void *heapTop; + void **heapEnd; + + int noSym; //!< noSym gets highest number, set in Parser + int maxT; + int charSetSize; //!< unused? + StartStates start; + KeywordMap keywords; + + Token *t; //!< current token + wchar_t *tval; //!< text of current token + int tvalLength; //!< length of text of current token + int tlen; //!< length of current token + + Token *tokens; //!< list of tokens already peeked (first token is a dummy) + Token *pt; //!< current peek token + + int ch; //!< current input character + + int pos; //!< byte position of current character + int line; //!< line number of current character + int col; //!< column number of current character + int oldEols; //!< EOLs that appeared in a comment; + + void CreateHeapBlock(); + Token* CreateToken(); + void AppendVal(Token*); + + void Init(); + void NextCh(); + void AddCh(); + bool Comment0(); + bool Comment1(); + + Token* NextToken(); + +public: + //! scanner buffer + Buffer *buffer; + + //! Attach scanner to an existing character buffer + Scanner(const unsigned char* buf, int len); + //! Attach scanner to an existing character buffer + Scanner(const char* buf, int len); + //! Open a file for reading and attach scanner + Scanner(const wchar_t* fileName); + //! Using an existing open file handle for the scanner + Scanner(FILE* s); + ~Scanner(); + Token* Scan(); + Token* Peek(); + void ResetPeek(); + +}; // end Scanner + +} // namespace +} // namespace +} // namespace + + +#endif // COCO_calcEntrySCANNER_H__ + diff --git a/applications/test/dictionary/dictionaryTest.C b/applications/test/dictionary/dictionaryTest.C index 2c2395e9fb956b769db5a48bec0798fd945ca2d5..ff2add8169b156cd75213095cfad581c9d286daa 100644 --- a/applications/test/dictionary/dictionaryTest.C +++ b/applications/test/dictionary/dictionaryTest.C @@ -43,70 +43,86 @@ using namespace Foam; int main(int argc, char *argv[]) { argList::noParallel(); - argList args(argc, argv); + argList::validArgs.insert("dict .. dictN"); + argList args(argc, argv, false, true); Info<< nl << "FOAM_CASE=" << getEnv("FOAM_CASE") << nl << "FOAM_CASENAME=" << getEnv("FOAM_CASENAME") << nl << endl; - + if (args.additionalArgs().empty()) { - dictionary dict1(IFstream("testDict")()); - Info<< "dict1: " << dict1 << nl - << "toc: " << dict1.toc() << nl - << "keys: " << dict1.keys() << nl - << "patterns: " << dict1.keys(true) << endl; - - dictionary dict2(dict1.xfer()); - - Info<< "dict1.toc(): " << dict1.name() << " " << dict1.toc() << nl - << "dict2.toc(): " << dict2.name() << " " << dict2.toc() << endl; - - // copy back - dict1 = dict2; - Info<< "dict1.toc(): " << dict1.name() << " " << dict1.toc() << endl; - - dictionary dict3(dict2.subDictPtr("boundaryField")); - dictionary dict4(dict2.subDictPtr("NONEXISTENT")); - - Info<< "dictionary construct from pointer" << nl - << "ok = " << dict3.name() << " " << dict3.toc() << nl - << "no = " << dict4.name() << " " << dict4.toc() << endl; + { + dictionary dict1(IFstream("testDict")()); + Info<< "dict1: " << dict1 << nl + << "toc: " << dict1.toc() << nl + << "keys: " << dict1.keys() << nl + << "patterns: " << dict1.keys(true) << endl; + + dictionary dict2(dict1.xfer()); + + Info<< "dict1.toc(): " << dict1.name() << " " << dict1.toc() << nl + << "dict2.toc(): " << dict2.name() << " " << dict2.toc() << endl; + + // copy back + dict1 = dict2; + Info<< "dict1.toc(): " << dict1.name() << " " << dict1.toc() << endl; + + dictionary dict3(dict2.subDictPtr("boundaryField")); + dictionary dict4(dict2.subDictPtr("NONEXISTENT")); + + Info<< "dictionary construct from pointer" << nl + << "ok = " << dict3.name() << " " << dict3.toc() << nl + << "no = " << dict4.name() << " " << dict4.toc() << endl; + } + + + IOobject::writeDivider(Info); + + { + dictionary dict(IFstream("testDictRegex")()); + dict.add(keyType("fooba[rz]", true), "anything"); + + Info<< "dict:" << dict << nl + << "toc: " << dict.toc() << nl + << "keys: " << dict.keys() << nl + << "patterns: " << dict.keys(true) << endl; + + Info<< "Pattern find \"abc\" in top directory : " + << dict.lookup("abc") << endl; + Info<< "Pattern find \"abc\" in sub directory : " + << dict.subDict("someDict").lookup("abc") + << endl; + Info<< "Recursive pattern find \"def\" in sub directory : " + << dict.subDict("someDict").lookup("def", true) + << endl; + Info<< "Recursive pattern find \"foo\" in sub directory : " + << dict.subDict("someDict").lookup("foo", true) + << endl; + Info<< "Recursive pattern find \"fooz\" in sub directory : " + << dict.subDict("someDict").lookup("fooz", true) + << endl; + Info<< "Recursive pattern find \"bar\" in sub directory : " + << dict.subDict("someDict").lookup("bar", true) + << endl; + Info<< "Recursive pattern find \"xxx\" in sub directory : " + << dict.subDict("someDict").lookup("xxx", true) + << endl; + } } + else + { + IOobject::writeDivider(Info); + forAll(args.additionalArgs(), argI) + { + const string& dictFile = args.additionalArgs()[argI]; + IFstream is(dictFile); + dictionary dict(is); - IOobject::writeDivider(Info); - - { - dictionary dict(IFstream("testDictRegex")()); - dict.add(keyType("fooba[rz]", true), "anything"); - - Info<< "dict:" << dict << nl - << "toc: " << dict.toc() << nl - << "keys: " << dict.keys() << nl - << "patterns: " << dict.keys(true) << endl; - - Info<< "Pattern find \"abc\" in top directory : " - << dict.lookup("abc") << endl; - Info<< "Pattern find \"abc\" in sub directory : " - << dict.subDict("someDict").lookup("abc") - << endl; - Info<< "Recursive pattern find \"def\" in sub directory : " - << dict.subDict("someDict").lookup("def", true) - << endl; - Info<< "Recursive pattern find \"foo\" in sub directory : " - << dict.subDict("someDict").lookup("foo", true) - << endl; - Info<< "Recursive pattern find \"fooz\" in sub directory : " - << dict.subDict("someDict").lookup("fooz", true) - << endl; - Info<< "Recursive pattern find \"bar\" in sub directory : " - << dict.subDict("someDict").lookup("bar", true) - << endl; - Info<< "Recursive pattern find \"xxx\" in sub directory : " - << dict.subDict("someDict").lookup("xxx", true) - << endl; + Info<< dict << endl; + } } return 0; diff --git a/applications/test/dictionary/testDictCalc b/applications/test/dictionary/testDictCalc new file mode 100644 index 0000000000000000000000000000000000000000..cfce0e0b525dd0bb0f7f2d09ba2a5e4a52633d30 --- /dev/null +++ b/applications/test/dictionary/testDictCalc @@ -0,0 +1,33 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: Any | +| \\ / A nd | Web: www.OpenFOAM.org | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object testDictTest; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +x 10 /* what ever */ 20; +y 20; +// z #test{ // this +// 123 - 456 +// // comments // are +// /* stripped +// * 10 +// * {} +// */ +// + 1 /*100 */ 10 +// }; + +p #test{ 1 + 2 + 10 * 15 + $x - $y }; + +foo 30; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //