2005/5/9

     
 

ScriptEval.cpp

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.
// 


#include <acdk/io/StreamTokenizer.h>
#include <acdk/io/FileReader.h>
#include <acdk/io/StringReader.h>
#include <acdk/io/ByteToCharReader.h>
#include <acdk/locale/Encoding.h>
#include <acdk/lang/System.h>
#include <acdk/lang/dmi/AcdkStdWeakTypeDmiClient.h>
#include <acdk/lang/sys/core_vector.h>
#include <acdk/lang/dmi/DmiObject.h>
#include <acdk/lang/dmi/DmiDelegate.h>
#include <acdk/lang/dmi/MetaInfoChildsArray.h>
#include <acdk/lang/reflect/Method.h>

#include "ScriptObject.h"
#include "Script.h"
#include "ScriptException.h"
#include "ScriptEval.h"
#include "ScriptExpr.h"


namespace acdk {
namespace cfgscript {

GLOBAL_ASCLITERAL(this);
GLOBAL_ASCLITERAL(super);

using acdk::io::StreamTokenizer;

int 
scanEndBlock(PEStack& stack)
{
  int tk = 0;
  int blockCount = 0;
  while ((tk = stack.nextToken()) != StreamTokenizer::TT_EOF)
  {
    if (tk == '}')
    {
      if (blockCount > 0)
        --blockCount;
      else
        return tk;
    }
    else if (tk == '{')
      ++blockCount;
  }
  return tk;
}
  
bool 
splitClassToVar(PEStack& stack, INP(RString) label, OUTP(RString) classOrObjName, OUTP(RString) memberName)
{
  RString tstr = label;
  StringBuffer rpart;
  int idx = 0;
  while (idx != -1)
  {
    idx = tstr->indexOf('.');
    if (idx == -1)
    {
    }
    RString rs = tstr->substr(0, idx);
    RString ls = tstr->substr(idx + 1);
    if (rpart.length() > 0)
      rpart << ".";
    rpart << rs;
    // check if can be resolved
    if (rpart.toString()->indexOf('.') == -1 && stack.props->hasValue(rpart.toString()) == true)
    {
      classOrObjName = rpart.toString();
      memberName = ls;
      return true;
    }
    const ClazzInfo* ci = ClazzInfo::findClazzInfo(rpart.toString());
    if (ci != 0)
    {
      classOrObjName = rpart.toString();
      memberName = ls;
      return true;
    }
    tstr = ls;
  }
  return false;
}

acdk::lang::Object getObjectWrapper(PEStack& stack, const ScriptVar& sv);

bool
resolveInUsings(PEStack& stack, IN(RString) label, int flags)
{
  const MetaInfo* mi = 0;
  RUsingEntry ue = stack.findUsingChild(label, 0, &mi);
  if (ue != Nil)
  {
    if (mi->isFieldInfo() == true)
    {
      /*
      if (mi->isStatic() == true)
      {
        if (ue->isVar == true)
          continue;

      }
      else // not static
      {
        if (ue->isVar == false)
          continue;
        //PEVal dotop(TT_DOTOP, PEVal(TT_VALUE, inOf(ue->usingName)), PEVal(TT_IDENT, inOf(label)));
        //stack.push(dotop);
      }
      */
      PEVal dotop(TT_DOTOP, PEVal(TT_IDENT, inOf(ue->usingName)), PEVal(TT_IDENT, inOf(label)));
      stack.push(dotop);
      if (stack.resolveDopOp(ResolveNotInUsings | ResolveNothing) == true)
      //if (stack.resolveVarOnTop(ResolveNotInUsings) == true)
      {
        return true;
        /*
        acdk::lang::Object obj = getObjectWrapper(stack, stack.pop().val);
        PEVal dotop(TT_DOTOP, PEVal(TT_VALUE, inOf(obj)), PEVal(TT_IDENT, inOf(label)));
        stack.push(dotop);
        return stack.resolveDopOp(flags);
        */
      }
      //return stack.resolveDopOp(flags);
    }
  }
  return false;
  /* old code
  for (int i = usings->length() - 1; i >= 0; --i)
  {
    RString t = usings[i];
    stack.push(TT_IDENT, inOf(t));
    if (stack.resolveVarOnTop(ResolveNotInUsings) == true)
    {
      acdk::lang::Object obj = getObjectWrapper(stack, stack.pop().val);
      const MetaInfo* mi = obj->getClazzInfo()->findMetaInfo(label, 0);
      if (mi != 0 && mi->isFieldInfo() == true)
      {
        PEVal dotop(TT_DOTOP, PEVal(TT_VALUE, inOf(obj)), PEVal(TT_IDENT, inOf(label)));
        stack.push(dotop);
        return stack.resolveDopOp(flags);
      }
    }
    else
    {
      stack.pop();
    }
  }
  return false;
  */
}

void 
PEStack::_init()
{
}

bool 
PEStack::resolveVarOnTop(int flags/* = ResolveFail*/)
{
  PEVal val = top();
  if (val.tk == TT_DOTOP)
    return resolveDopOp(flags);
  if (val.tk == TT_METHOD || val.tk == TT_CLASS || val.tk == TT_UNIT)
    return false;

  if (val.tk != TT_IDENT)
    return true;
  
  RString label = val.val.getStringVar();
  if (props->hasValue(label) == true)
  {
    pop();
    if (evalAsRef == true || (flags & ResolveToRef))
      push(TT_REFVALUE, outOf(*props->get(label)));
    else
      push(TT_VALUE, inOf(*props->get(label)));
    return true;
  }
  
  if ((flags & ResolveNotInUsings) == 0)
  {
    pop();
    if (resolveInUsings(*this, label, flags) == true)
      return true;
    push(val);
  }
  int subscridx; 
  const MetaInfo* mi = findElement(label);
  if ((mi == 0 || mi->isUnitInfo() == true)
    && 
    ((ResolveNoCompIdent & flags) != ResolveNoCompIdent) && 
    ((subscridx = label->lastIndexOf('.')) != -1))
  {
    PEVal val = pop();
    RString classOrObject;
    RString member;
    if (splitClassToVar(*this, label, classOrObject, member) == true)
    {
      push(TT_IDENT, inOf(member));
      push(TT_IDENT, inOf(classOrObject));
      PARSEEXECUTE_INSTACK(ExecPeek().);
      return true;
    }
    else
      push(val);
  }
  if (mi != 0 && mi->isUnitInfo() == false)
  {
    if (mi->isEnumValInfo() == true) 
    {
      const ClazzEnumValueInfo* evi = reinterpret_cast<const ClazzEnumValueInfo*>(mi);
      pop();
      push(TT_VALUE, inOf(evi->value));
      return true;
    }
  }
  if (flags & ResolveFail)
  {
    ELOG_INSTACK("Cannot resolve label: " + label);
  }
  if (flags & ResolvePushNil)
  {
    pop();
    push(TT_VALUE, ScriptVar(Nil));
    return true;
  }
  return false;
}

bool
PEStack::resolveDopOp(int flags)
{
  
  PEVal op = pop();
  PEVal lv = op.args->left;
  push(op.args->left);
  PEVal rv = op.args->right;
  if (resolveVarOnTop(flags & ~ResolveFail)  == false)
  {
    // has to be class or namespace
    PEVal lv = pop();
    if (ClazzInfo::findClazzInfo(lv.val.getStringVar()) != 0)
    {
      push(rv);
      push(lv);
      PARSEEXECUTE_INSTACK(ExecStaticPeek().);
    }
    else
    {
      RString ns = SBSTR(lv.val.getStringVar() << "." << rv.val.getStringVar());
      const ClazzEnumValueInfo* enunval = ClazzEnumInfo::findEnumValue(ns);
      if (enunval != 0)
      {
        push(TT_VALUE, inOf(enunval->value));
        return true;
      }
      if ((flags & ResolveFail) == ResolveFail)
        ELOG_INSTACK("Cannot resolve label: " + ns);
      push(TT_IDENT, inOf(ns));
      return false;
    }
  }
  else
  {
    PEVal lv = pop();
    push(rv);
    push(lv);
    
    PARSEEXECUTE_INSTACK(ExecPeek().);
  }
  return true;
}



bool 
PEStack::skipStatement()
{
  int tk = nextToken();
  if (tk == -1)
    ELOG_INSTACK("EOF while parsing statement");
  if (tk == ';')
    return true;
  if (tk == '{')
  {
    tk = scanEndBlock(*this);
    return true;
  }
  if (tk == StreamTokenizer::TT_WORD)
  {
    RString ctstr = curTokenAsString();
    ASCLITERAL(if);
    ASCLITERAL(for);
    if (ctstr->equals(lit_if) == true)
    {
      skipExpression();
      skipStatement();
      tk = nextToken();
      ctstr = curTokenAsString();
      if (tk == StreamTokenizer::TT_WORD && ctstr->equals("else") == true)
        skipStatement();
      else
        pushBack();
      return true;
    }
    else if (ctstr->equals(lit_for) == true)
    {
      skipExpression();
      return skipStatement();
    }
    // while, do, switch as standard format
    /*
    else if (cstr->equals("switch") == true)
    {
    }*/
  }
  while (tk != -1)
  {
    tk = nextToken();
    if (tk == ';')
      return true;
  }
  ELOG_INSTACK("EOF while parsing statement");
  return false;
}

int 
PEStack::getBlockEndPos()
{
  int sp = getCurTokenIndex();
  if (skipStatement() == false)
    return -1;
  int erg = getCurTokenIndex();
  setCurTokenIndex(sp);
  return erg;
}

bool 
PEStack::skipLine()
{
  int cl = curSourceToken().sourcePos.linePos;

   do {
    int tk = nextToken();
    if (tk == -1)
      return false;
    if (curSourceToken().sourcePos.linePos > cl)
    {
      pushBack();
      return true;
    }
  } while(true);
  return false;
}

bool 
PEStack::skipExpression(int stopTk)
{
  
  int parcount = 0;
  do {
    int tk = nextToken();
    if (tk == -1)
      return false;
    if (tk == '(')
      ++parcount;
    else if (tk == ';' && parcount == 0)
    {
      pushBack();
      return true;
    }
    else if (tk == ')')
    {
      --parcount;
      if (parcount == -1)
      {
        pushBack();
        return true;
      }
      if (parcount == 0)
        return true;
    }
    else if (stopTk == tk && parcount <= 0)
    {
      pushBack();
      return true;
    }

  } while (true);
  return false;
}

bool 
PEStack::skipExpressionUntilOpLowerOrEqual(int opPrio)
{
  
  int parcount = 0;
  do {
    int tk = nextToken();
    if (tk == -1)
      return false;
    if (parcount <= 0 && isOperator(curSourceToken()) == true)
    {
      int prec = getOperatorPredence(curTokenAsString());
      if (opPrio <= prec)
      {
        pushBack();
        return true;
      }
    }
    if (tk == '(')
      ++parcount;
    else if (tk == ';')
    {
      pushBack();
      return true;
    }
    else if (tk == ')')
    {
      --parcount;
      if (parcount == -1)
      {
        pushBack();
        return true;
      }
    }
  } while (true);
  return false;
}

RString
PEStack::parseIdentifier(IN(RString) begin)
{
  int tk = 0;
  int blockCount = 0;
  bool expectDot = true;
  StringBuffer sb(begin);
  if (begin->length() == 0)
    expectDot = false;
  while ((tk = nextToken()) != StreamTokenizer::TT_EOF)
  {
    RString sval = curTokenAsCode();
    if (sval->equals(".") == true)
    {
      if (expectDot == true)
      {
        sb.append('.');
        expectDot = false;
        continue;
      } 
      
    } 
    else if (tk == StreamTokenizer::TT_WORD)
    {
      if (expectDot == false)
      {
        sb.append(sval);
        expectDot = true;
        continue;
      }
    }
    pushBack();
    break;
  }
  return sb.toString();
}



RString 
PEStack_getTypeAlias(IN(RProps) props, IN(RString) alias)
{
  if (props->hasValue("._cfgscript_typealias", PropsNoParentRead) == true)
  {
    RStringArray sa = props->getStringArrayVal("._cfgscript_typealias", PropsNoParentRead | PropsNoWarnRead);
    for (int i = 0; i + 1 < sa->length(); i += 2)
    {
      if (sa[i]->equals(alias) == true)
        return sa[i + 1];
    }
  }
  RPropsArray parr = props->getParentsProps();
  for (int i = 0; i < parr->length(); ++i)
  {
    RString ret = PEStack_getTypeAlias(parr[i], alias);
    if (ret != Nil)
      return ret;
  }
  return Nil;
}

RString 
PEStack::getTypeAlias(IN(RString) alias)
{
  return PEStack_getTypeAlias(props, alias);
}

void
splitNamespaceClassName(IN(RString) name, OUT(RString) ns, OUT(RString) cn)
{
  int idx;
  if ((idx = name->lastIndexOf('.')) == -1)
  {
    ns = Nil;
    cn = name;
    return;
  }
  ns = name->substr(0, idx);
  cn = name->substr(idx + 1);
}

const NamedScopedParentMetaInfo* 
PEStack::resolveNewUsing(IN(RString) use, OUT(bool) isVariable)
{
  if (props->hasValue(use) == true)
  {
    acdk::lang::Object o = props->getObjectVal(use);
    if (o == Nil)
      THROW1(Exception, "Cannot 'using' Nil object");
    isVariable = true;
    return (const NamedScopedParentMetaInfo*)o->getClazzInfo()->getMetaInfo();
  }
  isVariable = false;
  const MetaInfo* mi = MetaInfo::findMetaInfo(use, 0, true);
  if (mi == 0)
    THROW1(Exception, "Cannot resolve using '" + use + "'");
  if (mi->isClazzInfo() == false && mi->isUnitInfo() == false)
    THROW1(Exception, "Cannot use this type of such a type: " + use);
  return (const NamedScopedParentMetaInfo*)mi;
}

void 
PEStack::_appendUsing(IN(RUsingEntry) ue)
{
  RUsingEntryArray usings = (RUsingEntryArray)props->getObjectVal("._cfgscript_using", PropsNoParentRead | PropsNoWarnRead);
  if (usings == Nil)
  {
    usings = new UsingEntryArray(0);
    props->setObjectVal("._cfgscript_using", &usings, PropsNoParentWrite);
  }
  usings->append(ue);
}

void 
PEStack::addUsingVar(IN(RString) s, IN(acdk::lang::Object) var)
{
  if (var == Nil)
    THROW1(Exception, "Cannot 'using' Nil object");
  const NamedScopedParentMetaInfo* mi = (const NamedScopedParentMetaInfo*)var->getClazzInfo()->getMetaInfo();
  _appendUsing(new UsingEntry(s, mi, true));
}

void 
PEStack::addUsing(IN(RString) s)
{
  bool isVar = false;
  const NamedScopedParentMetaInfo* mi = resolveNewUsing(s, isVar);
  _appendUsing(new UsingEntry(s, mi, isVar));
}

void 
PEStack::addUsingType(const acdk::lang::dmi::ClazzInfo* metaInfo)
{
  bool isVar = false;
  RString tpName = metaInfo->toTypeString(TpFtJavaType);
  _appendUsing(new UsingEntry(tpName, metaInfo->getMetaInfo(), false));
}

RString 
PEStack::getAlias(IN(RString) typname)
{
  int tpHash = typname->hashCode();
  switch (tpHash)
  {
  case StringHash<'A','n', 'y'>::hash:
    return "acdk.lang.dmi.DmiObject";
  case StringHash<'R','e', 's', 't'>::hash:
    return "acdk.lang.dmi.DmiObjectArray";
  case StringHash<'N', 'a', 'm', 'e', 'd', 'R','e', 's', 't'>::hash:
     return "acdk.lang.dmi.DmiNamedArgArray";
  case StringHash<'D', 'e', 'l', 'e', 'g', 'a','t', 'e'>::hash:
     return "acdk.lang.dmi.DmiDelegate";
  case StringHash<'D', 'e', 'l', 'e', 'g', 'a','t', 'e', 'A', 'r', 'r', 'a', 'y'>::hash:
     return "acdk.lang.dmi.DmiDelegateArray";
  default:
    break;
  }
  return getTypeAlias(typname);
}

OUT(RUsingEntryArray)
getGlobalUsings()
{
  static RUsingEntryArray globalUsings = Nil;
  if (globalUsings != Nil)
    return globalUsings;
  globalUsings = new UsingEntryArray(0);
  System::registerStaticReference(globalUsings);
  globalUsings->append(new UsingEntry("acdk/lang", UnitInfo::findCreateUnit("acdk/lang")->getMetaInfo(), false));
  return globalUsings;
}

RUsingEntry 
_findUsingChild2(IN(RProps) p, IN(RString) tpName, int dmiFlags, const MetaInfo** foundPtr = 0)
{
  RUsingEntryArray usings = (RUsingEntryArray)p->getObjectVal("._cfgscript_using", PropsNoParentRead | PropsNoWarnRead);
  if (usings != Nil)
  {
    for (int i = 0; i < usings->length(); ++i)
    {
      RUsingEntry ue = usings[i];
      const MetaInfo* mi = ue->_metaInfo->findMetaInfo(tpName, dmiFlags); //??
      if (mi != 0)
      {
        if (mi->isFieldInfo() == true || mi->isMethodInfo() == true)
        {
          if (ue->isVar == false && mi->isStatic() == false)
            continue;
        }
        if (foundPtr != 0)
          *foundPtr = mi;
        return ue;
      }        
    }
  }
  RPropsArray parents  = p->getParentsProps();
  if (parents == Nil)
    return Nil;
  for (int i = 0; i < parents->length(); ++i)
  {
    RUsingEntry ue = _findUsingChild2(parents[i], tpName, dmiFlags, foundPtr);
    if (ue != Nil)
      return ue;
  }
  return Nil;
}

RUsingEntry 
_findUsingChild(IN(RProps) p, IN(RString) tpName, int dmiFlags, const MetaInfo** foundPtr = 0)
{
  RUsingEntry ue = _findUsingChild2(p, tpName, dmiFlags, foundPtr);
  if (ue != Nil)
    return ue;
  OUT(RUsingEntryArray) globals = getGlobalUsings();
  for (int i = 0; i < globals->length(); ++i)
  {
    RUsingEntry ue = globals[i];
    const MetaInfo* mi = ue->_metaInfo->findMetaInfo(tpName, dmiFlags); //??
    if (mi != 0)
    {
      if (ue->isVar == false && mi->isStatic() == false)
          continue;
      if (foundPtr != 0)
        *foundPtr = mi;
      return ue;
    }        
  }
  return Nil;
}

RUsingEntry 
PEStack::findUsingChild(IN(RString) tpName, int dmiFlags, const MetaInfo** foundPtr)
{
  return _findUsingChild(props, tpName, dmiFlags, foundPtr);
}


RString 
PEStack::getAliasOrUsingTypeName(IN(RString) typname, bool tryLoad)
{
  RString orgType = getAlias(typname);
  if (orgType != Nil)
    return orgType;
  const MetaInfo* mi = 0;
  if (_findUsingChild(props, typname, 0, &mi) != Nil)
  {
    if (mi->isEnumInfo() == true)
      return "int";
    if (mi->isClazzInfo() == true)
    {
      const ClazzInfo* ci = (const ClazzInfo*)mi;
      return ci->toTypeString(TpFtLoadableClass);
    }
  }
  return typname;
}




const ClazzInfo* 
PEStack::findType(IN(RString) str, bool throwOnFail)
{
  RString ns;
  RString cn;
  RString typname = str;
  splitNamespaceClassName(typname, ns, cn);
  if (cn->startsWith("R") && cn->length() > 1 && Character::isUpperCase(cn->charAt(1)) == true)
    cn = cn->substr(1);
  RString clsname;
  if (ns != Nil)
    typname = ns + "." + cn;
  else
    typname = cn;

  RString alias = getAlias(typname);
  if (alias != Nil)
    typname = alias;

  const MetaInfo* mi = MetaInfo::findMetaInfo(typname, false);
  if (mi == 0)
    _findUsingChild(props, typname, 0, &mi);

  if (mi != 0)
  {
    if (mi->isClazzInfo() == true)
      return (const ClazzInfo*)mi;
    if (mi->isEnumInfo() == true)
      return Class::forName("int")->objectClazzInfo(); 
  }
  if (throwOnFail == true)
    return Class::forName(typname)->objectClazzInfo(); // throws Ex
  
  try {
    return Class::forName(typname)->objectClazzInfo(); // throws Ex
  } catch(RClassNotFoundException ex) {
    return 0;
  }
}


const MetaInfo*
PEStack::getAliasOrUsingInfo(IN(RString) typeName, int dmiFlags, bool tryLoad)
{
  RString orgType = getAlias(typeName);
  if (orgType != Nil)
    return MetaInfo::findMetaInfo(orgType, dmiFlags, tryLoad);
  
  const MetaInfo* mi = 0;
  _findUsingChild(props, typeName, dmiFlags, &mi);
  if (mi != 0)
    return mi;
  return MetaInfo::findMetaInfo(typeName, dmiFlags, tryLoad);
}

const MetaInfo* 
PEStack::findElement(IN(RString) str, int dmiFlags)
{
  RString ns;
  RString cn;
  RString typname = str;
  splitNamespaceClassName(typname, ns, cn);
  if (cn->startsWith("R") && cn->length() > 1 && Character::isUpperCase(cn->charAt(1)) == true)
    cn = cn->substr(1);
  RString clsname;
  if (ns != Nil)
    typname = ns + "." + cn;
  else
    typname = cn;

  const MetaInfo* mi = getAliasOrUsingInfo(typname, dmiFlags, false);
  if (mi != 0)
    return mi;
  mi = getAliasOrUsingInfo(typname, dmiFlags, true);
  return mi;
}

} // namespace cfgscript
} // namespace acdk