2005/5/9

     
 

ScriptEval.h

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
#ifndef acdk_cfgscript_ScriptEval_h
#define acdk_cfgscript_ScriptEval_h
#if !defined(DOXYGENONLY)

#include "ScriptSource.h"
#include "ScriptException.h"
#include "Script.h"

#include "SourceTokenizer.h"
#include <acdk/lang/System.h>
#include <acdk/lang/Thread.h>

namespace acdk {
namespace cfgscript {

using namespace acdk::lang::dmi;


//#define LOCAL_DEBUG
#if defined(LOCAL_DEBUG)

RString spaces(int count);

#define DOUT(strexpr) \
do { \
  StringBuffer sb; \
  sb << ThreadID::getCurrentThreadID().getId() << ": "; \
  sb << strexpr; \
  System::out->println(sb.toString()); \
} while (false)
#else
/FONT>
#define DOUT(strexpr) do { } while(false)
#endif
/FONT>

#define ELOG(msg) \
do { \
  ACDK_NLOG("acdk.cfgscript", Error, stack.getPositionAsString() + ": " + msg); \
  if (ExecutionStack::throwOnFail() == true) \
    THROW1(ScriptException, stack.getPositionAsString() + ": " + msg); \
  return false; \
} while(false)


#define ELOG_INSTACK(msg) \
do { \
  ACDK_NLOG("acdk.cfgscript", Error, getPositionAsString() + ": " + msg); \
  if (ExecutionStack::throwOnFail() == true) \
    THROW1(ScriptException, getPositionAsString() + ": " + msg); \
  return false; \
} while(false)

#define PARSEEXECUTE(object) \
do { \
  DOUT("enter: " #object); \
  bool erg = object parseExecute(stack); \
  DOUT("leave: " #object); \
  if (erg == false) return false; \
  if (stack.executionFlags & EFActiveReturn) \
    return true; \
} while (false)


#define PARSEEXECUTE_INSTACK(object) \
do { \
  DOUT("enter: " #object); \
  bool erg = object parseExecute(*this); \
  DOUT("leave: " #object); \
  if (erg == false) return false; \
  if (executionFlags & EFActiveReturn) \
    return true; \
} while (false)

#define PARSEEXECUTE_EX(object) \
do { \
try { \
  if (object parseExecute(stack) == false) \
    return false; \
  if (stack.executionFlags & EFActiveReturn) \
    return true; \
} catch (RScriptException ex) { \
  throw; \
} catch (RThrowable ex) { \
  stack.activeException = ex; \
  stack.executionFlags |= EFActiveException; \
  return true; \
} \
} while (false)


/* old
#define CFGSCRIPT_WRAPP_EXT_INVOKE( Code ) \
  stack.script->pushExecutionStack(ExecutionStack(stack.script, stack.curCharStreamPos(), stack.script->_currentClazzMethod)); \
  Code \
  stack.script->popExecutionStack();
*/
#define CFGSCRIPT_WRAPP_EXT_INVOKE( Code ) \
  Code

foreign
enum Token
{
  TT_VALUE    = 1000,
  TT_REFVALUE = 1001,
  TT_IDENT  =   1002,
  TT_ARGLABEL = 1003,
  TT_OPERATOR = 1004,
  TT_METHOD   = 1005,
  TT_CLASS    = 1006,
  TT_UNIT     = 1007,
  TT_FIELD    = 1008,
  TT_DOTOP    = 1009,

};

foreign
enum ResolveFlags
{
  ResolveNothing = 0x0000,
  /** 
    throws ex if cannot be resolved
  */
  ResolveFail  = 0x0001,
  /**
    Push Nil if cannot be resolveld
  */
  ResolvePushNil      = 0x0002,
  /**
    Pushes a reference to stack
  */
  ResolveToRef      = 0x0004,

  ResolveNoCompIdent = 0x0008,
  ResolveNotInUsings = 0x0010
};


ACDK_DECL_CLASS(LeftRightArgs);


struct PEVal
{
  Token tk;
  ScriptVar val;
  RLeftRightArgs args;
  PEVal(Token t, const ScriptVar& v) : tk(t), val(v) {}
  PEVal(Token t, IN(PEVal) left, IN(PEVal) right);
  static RString tokenToString(int tk);
  RString toCode();
  RString toString();
};

class LeftRightArgs
: extends acdk::lang::Object
{
public:
  PEVal left;
  PEVal right;
  LeftRightArgs(IN(PEVal) l, IN(PEVal) r) : left(l), right(r) {}
};



ACDK_DECL_CLASS(ScriptMethodInfo);

foreign
class ScriptMethodInfo
: extends acdk::lang::Object
{
public:
  int begintokenIndex;
  int endtokenIndex;
  RScript script;
  ScriptMethodInfo(IN(RScript) scrpt, int idxBegin, int idxEnd)
  : begintokenIndex(idxBegin)
  , endtokenIndex(idxEnd)
  , script(scrpt)
  {
  }
  virtual void getCollectableFields(FieldReferences& fields)
  {
    fields.push_back((acdk::lang::Object*)script._ref_this());
  }
};

ACDK_DECL_CLASS(UsingEntry);
foreign 
class UsingEntry
: extends acdk::lang::Object
, virtual public MetaInfoChangeListener
{
public:
  RString usingName;
  const acdk::lang::dmi::NamedScopedParentMetaInfo* _metaInfo;
  bool isVar;
  UsingEntry(IN(RString) use, const acdk::lang::dmi::NamedScopedParentMetaInfo* mi, bool isvar)
  : usingName(use)
  , _metaInfo(mi)
  , isVar(isvar)
  {
    MetaInfo::registerMetaInfoListener(this);
  }
  ~UsingEntry()
  {
    MetaInfo::unRegisterMetaInfoListener(this);
  }
  virtual void onReplaceMetaInfo(MetaInfo* oldMetaInfo, MetaInfo* newMetaInfo) 
  {
    if (_metaInfo == (const acdk::lang::dmi::NamedScopedParentMetaInfo*)oldMetaInfo)
      _metaInfo = (const acdk::lang::dmi::NamedScopedParentMetaInfo*)newMetaInfo;
  }
  virtual void getCollectableFields(FieldReferences& fields)
  {
    fields.push_back((acdk::lang::Object*)usingName._ref_this());
  }
};

struct PEStack
{
  typedef acdk::lang::sys::core_vector<PEVal> ValStack;
  RScript script;
  
  RProps props;
  ValStack vs;
  /**
    @see ExecutionFlags
  */
  int executionFlags;
  RThrowable activeException;
  SourceTokenizer tokenizer;
  bool evalAsRef;
  /// execute until higher than:
  RString leftOp;
  const ClazzInfo* curLeftDeclType;
  PEStack(IN(RScript) scr, IN(RProps) p)  
  : script(scr)
  , props(p)
  , vs()
  , executionFlags(0) 
  , tokenizer(scr->getTokenized())
  , evalAsRef(false)
  , curLeftDeclType(0)
  {
    _init();
  }
  PEStack(IN(RScript) scr, IN(RProps) p, int beginTkIdx, int endTkIdx)  
  : script(scr)
  , props(p)
  , vs()
  , executionFlags(0) 
  , tokenizer(scr->getTokenized())
  , evalAsRef(false)
  , curLeftDeclType(0)
  {
    tokenizer._tokenIdx = beginTkIdx;
    tokenizer._endTokenIdx = endTkIdx;
    _init();
  }
  void _init();
  void push(Token tk, const ScriptVar& sv) 
  { 
    push(PEVal(tk, sv)); 
  }
  void push(const PEVal& v)  
  { 
    vs.push_back(v); 
    DOUT("Push: " << getStackAsString());
  }
  void pushValOrIdent(const SourceToken& stk)
  {
    if (stk.tk == acdk::io::StreamTokenizer::TT_WORD)
      push(TT_IDENT, stk.value);
    else 
      push(TT_VALUE, stk.value);
  }
  PEVal& top() 
  {
    if (vs.empty() == true)
      THROW1(ScriptException, "Eval Stack underflow");
    return vs.back();
  }
  bool isStackEmtpy() { return vs.empty(); }
  PEVal pop() 
  {
    PEVal ret = top();
    DOUT("Pop: " << getStackAsString());
    vs.pop_back();
    return ret;
  }
  /**
    idx == 0 is top idx = 1 top -1 
    */
  PEVal& peek(int idx)
  {
    return vs[vs.size() - idx - 1];
  }
  int nextToken()
  {
    int tk = tokenizer.nextToken();
    ExecutionStack::setCurrentTokenIndex(tokenizer.getCurrentTokenIndex());
    //script->setCurrentTokenIndex(tokenizer.getCurrentTokenIndex());
    DOUT(">> [" << tokenizer.getCurrentTokenIndex() << "], " << String::valueOf(tk) << ": " << (tk != -1 ? tokenizer.getCurTokenAsCode() : String::emptyString()));
    return tk;
  }
  void pushBack() 
  {
    DOUT("<< [" << tokenizer.getCurrentTokenIndex() << "], " << tokenizer.getCurTokenAsCode());
    tokenizer.pushBack();
  }
  RString curTokenAsCode() 
  {
    SourceToken& stk = tokenizer.curSourceToken();
    return acdk::io::StreamTokenizer::toCode(stk.tk, stk.value);
  }
  RString curTokenAsString() 
  {
    return curTokenAsCode();
  }
  acdk::lang::dmi::ScriptVar& curTokenValue() { return tokenizer.curSourceToken().value; }
  int curToken() { return tokenizer.curSourceToken().tk; }
  INOUT(SourceToken) curSourceToken() { return tokenizer.curSourceToken(); }
  int getCurTokenIndex() { return tokenizer.getCurrentTokenIndex(); }
  void setCurTokenIndex(int tokenIndex) { tokenizer.setCurrentTokenIndex(tokenIndex); }
  
  RString getStackAsString()
  {
    StringBuffer sb;
    for (ValStack::iterator it = vs.begin(); it != vs.end(); ++it)
    {
      sb.append(" | ");
      sb.append(it->toString());
    }
    return sb.toString();
  }
  bool hasActiveEx() { return (executionFlags & EFActiveException) == EFActiveException; }
  RString getPositionAsString()
  {
    acdk::io::CharStreamPos csp = tokenizer.curSourceToken().sourcePos;
    return SBSTR(script->getFileName() << "(" << csp.linePos + 1 << "," << csp.columnPos << "): ");
  }
  acdk::io::CharStreamPos curCharStreamPos() { return tokenizer.curSourceToken().sourcePos; }
  //INOUT(RSourceTokenizer) getTokenizer() { return script->getTokenizer(); }
  /**
    @see ResolveFlags
  */
  bool resolveVarOnTop(int flags = ResolveFail);
  /**
    read until '}' or ';' end statement
  */
  bool skipStatement();
  /** skip all token until next line */
  bool skipLine();
  /**
    return end of block token position
  */
  int getBlockEndPos();

  /**
    skip expression
    optional token where also to stop (only if expression is not wrapped with ()
  */
  bool skipExpression(int stopTk = 0);
  /**
    skip expression until an operator with equal or lower prio than given
  */
  bool  skipExpressionUntilOpLowerOrEqual(int opPrio);

  /**
    read in a identifier a.b.c
  */
  RString parseIdentifier(IN(RString) begin);
  /**
    resolve TT_DOTOP 
    @see ResolveFlags
  */
  bool resolveDopOp(int flags);

  void addUsing(IN(RString) s);
  void addUsingVar(IN(RString) s, IN(acdk::lang::Object) var);
  void addUsingType(const acdk::lang::dmi::ClazzInfo* metaInfo);
  void _appendUsing(IN(RUsingEntry) ue);
  //RStringArray getUsings() { return props->getAllStringArrayVal("._cfgscript_using", PropsParentRead | PropsNoWarnRead); }
  
  void addTypeAlias(IN(RString) aliasType, IN(RString) realType)
  {
    props->appendStringArrayVal("._cfgscript_typealias", aliasType, PropsNoParentWrite | PropsNoParentRead);
    props->appendStringArrayVal("._cfgscript_typealias", realType, PropsNoParentWrite | PropsNoParentRead);
  }
  /**
    return Nil if no alias
  */
  RString getTypeAlias(IN(RString) alias);
  /**
    return the alias of param or orignal name
  */
  RString getAlias(IN(RString) alias);
  /**
    search for type name in aliases and usings
    if no alias or using is found, return orginal typname
  */
  RString getAliasOrUsingTypeName(IN(RString) typname, bool tryLoad = true);
  void makeTopInOf()
  {
    PEVal& val = top();
    if (val.val.flags & MiAiIn && ((val.val.flags & MiAiOut) == 0))
      return;
    val.val = val.val.inOf();
  }
  /**
    find a type with given name
    @throw if throwOnFail = true (default) ClassNotFoundException if type cannot be found
  */
  const ClazzInfo* findType(IN(RString) str, bool throwOnFail = true);
  /**
    search for element
    @param dmiFlags requested type acdk::lang::dmi::MetaInfoFlags
  */

  const MetaInfo* findElement(IN(RString) str, int dmiFlags = 0);
  const MetaInfo* getAliasOrUsingInfo(IN(RString) typname, int dmiFlags, bool tryLoad);
  /**
    @return Nil if tpName cannot be found
  */
  RUsingEntry findUsingChild(IN(RString) tpName, int dmiFlags = 0, const MetaInfo** foundPtr = 0);
  const NamedScopedParentMetaInfo* resolveNewUsing(IN(RString) use, OUT(bool) isVariable);
  
};

foreign
class PropsScope
{
public:
  RProps& _props;
  int _oldPropsFlags;
  PropsScope(RProps& props, int propsflags = PropsParentRead | PropsParentWrite) 
  : _props(props) 
  , _oldPropsFlags(0)
  {
    _props = new Props("block", propsflags, props);
    
    _props->set("__props", new DmiObject((acdk::lang::Object)props), PropsNoParentWrite);
    ExecutionStack::getTop()->setScopeProps(_props);
  }
   PropsScope(RProps& props, RProps& newRoot, int propsflags = PropsParentRead | PropsParentWrite) 
  : _props(props) 
  {
    _props = newRoot;
    _oldPropsFlags = newRoot->getDefaultFlags();
    newRoot->setDefaultFlags(propsflags);
    ExecutionStack::getTop()->setScopeProps(_props);

  }
  ~PropsScope() 
  { 
    _props->unset("__props", PropsNoParentWrite);
    _props->setDefaultFlags(_oldPropsFlags);
    _props = _props->getParentProps(); 
    ExecutionStack::getTop()->setScopeProps(_props);
  }
  
};



foreign
class ParseNode
: extends acdk::lang::Object
{
public:
  virtual bool parseExecute(PEStack& stack) = 0;
};

#define STD_PARSENODE_DECL(Name) \
ACDK_DECL_CLASS(Name); \
class Name \
: extends ParseNode \
{ \
public: \
  virtual bool parseExecute(PEStack& stack); \
}

#define STD_SKIPABLE_PARSENODE_DECL(Name) \
ACDK_DECL_CLASS(Name); \
class Name \
: extends ParseNode \
{ \
public: \
  virtual bool parseExecute(PEStack& stack); \
}

#define EXTERN_ASCLITERAL(strlit) extern acdk::lang::StaticAsciiLiteral lit_##strlit
#define GLOBAL_ASCLITERAL(strlit) acdk::lang::StaticAsciiLiteral lit_##strlit(#strlit)

EXTERN_ASCLITERAL(this);
EXTERN_ASCLITERAL(super);


} // namespace cfgscript
} // namespace acdk 
#endif //!defined(DOXYGENONLY)

#endif //acdk_cfgscript_ScriptEval_h