Commit f756fd53 authored by Cyril Deguet's avatar Cyril Deguet

* parser/expr_evaluator: expression evaluator using Reverse Polish Notation

    (a bit "C-style-coded" at the moment ;)
  * utils/var_bool.*: added true and false boolean variables, and the "OR"
    composite variable
  * src/var_manager.*: support for anonymous variables
  * parser/interpreter.cpp: use the RPN evaluator to resolve boolean
    expressions in the XML. Any well-parenthesized expression using "not",
    "or", "and", "true", "false" and boolean variables should be understood
    (not much tested yet).
parent 385f1979
......@@ -70,6 +70,8 @@ SOURCES_skins2 = \
parser/builder.cpp \
parser/builder.hpp \
parser/builder_data.hpp \
parser/expr_evaluator.cpp \
parser/expr_evaluator.hpp \
parser/interpreter.cpp \
parser/interpreter.hpp \
parser/skin_parser.cpp \
......
/*****************************************************************************
* expr_evaluator.cpp
*****************************************************************************
* Copyright (C) 2004 VideoLAN
* $Id$
*
* Authors: Cyril Deguet <asmax@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include "expr_evaluator.hpp"
void ExprEvaluator::parse( const string &rExpr )
{
m_stack.clear();
const char *pString = rExpr.c_str();
list<string> opStack; // operator stack
string token;
// Tokenize the expression
int begin = 0, end = 0;
while( pString[begin] )
{
// Find the next significant char in the string
while( pString[begin] == ' ' )
{
begin++;
}
if( pString[begin] == '(' )
{
// '(' found: push it on the stack and continue
opStack.push_back( "(" );
begin++;
}
else if( pString[begin] == ')' )
{
// ')' found: pop the stack until a '(' is found
while( !opStack.empty() )
{
// Pop the stack
string lastOp = opStack.back();
opStack.pop_back();
if( lastOp == "(" )
{
break;
}
// Push the operator on the RPN stack
m_stack.push_back( lastOp );
}
begin++;
}
else
{
// Skip white spaces
end = begin;
while( pString[end] && pString[end] != ' ' )
{
end++;
}
// Get the next token
token = rExpr.substr( begin, end - begin );
begin = end;
// TODO compare to a set of operators
if( token == "not" || token == "or" || token == "and" )
{
// Pop the operator stock while the operator has a higher
// precedence than the top of the stack
while( !opStack.empty() &&
hasPrecedency( token, opStack.back() ) )
{
// Pop the stack
string lastOp = opStack.back();
opStack.pop_back();
m_stack.push_back( lastOp );
}
opStack.push_back( token );
}
else
{
m_stack.push_back( token );
}
}
}
// Finish popping the operator stack
while( !opStack.empty() )
{
string lastOp = opStack.back();
opStack.pop_back();
m_stack.push_back( lastOp );
}
}
string ExprEvaluator::getToken()
{
if( !m_stack.empty() )
{
string token = m_stack.front();
m_stack.pop_front();
return token;
}
return "";
}
bool ExprEvaluator::hasPrecedency( const string &op1, const string &op2 ) const
{
// FIXME
if( op1 == "(" )
{
return true;
}
if( op1 == "and" )
{
return (op2 == "or") || (op2 == "not" );
}
if( op1 == "or" )
{
return (op2 == "not" );
}
return false;
}
/*****************************************************************************
* expr_evaluator.hpp
*****************************************************************************
* Copyright (C) 2004 VideoLAN
* $Id$
*
* Authors: Cyril Deguet <asmax@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#ifndef EXPR_EVALUATOR_HPP
#define EXPR_EVALUATOR_HPP
#include "../src/skin_common.hpp"
#include <list>
#include <string>
/// Expression evaluator using Reverse Polish Notation
class ExprEvaluator: public SkinObject
{
public:
/// Constructor
ExprEvaluator( intf_thread_t *pIntf ): SkinObject( pIntf ) {}
/// Destructor
~ExprEvaluator() {}
/// Clear the RPN stack and parse an expression
void parse( const string &rExpr );
/// Pop the first token from the RPN stack.
/// Return NULL when the stack is empty.
string getToken();
private:
/// RPN stack
list<string> m_stack;
/// Returns true if op1 has precedency over op2
bool hasPrecedency( const string &op1, const string &op2 ) const;
};
#endif
......@@ -23,6 +23,7 @@
*****************************************************************************/
#include "interpreter.hpp"
#include "expr_evaluator.hpp"
#include "../commands/cmd_playlist.hpp"
#include "../commands/cmd_dialogs.hpp"
#include "../commands/cmd_dummy.hpp"
......@@ -74,6 +75,13 @@ Interpreter::Interpreter( intf_thread_t *pIntf ): SkinObject( pIntf )
REGISTER_CMD( "vlc.faster()", CmdFaster )
REGISTER_CMD( "vlc.slower()", CmdSlower )
REGISTER_CMD( "vlc.stop()", CmdStop )
// Register the constant bool variables in the var manager
VarManager *pVarManager = VarManager::instance( getIntf() );
VarBool *pVarTrue = new VarBoolTrue( getIntf() );
pVarManager->registerVar( VariablePtr( pVarTrue ), "true" );
VarBool *pVarFalse = new VarBoolFalse( getIntf() );
pVarManager->registerVar( VariablePtr( pVarFalse ), "false" );
}
......@@ -165,74 +173,101 @@ CmdGeneric *Interpreter::parseAction( const string &rAction, Theme *pTheme )
VarBool *Interpreter::getVarBool( const string &rName, Theme *pTheme )
{
// Try to get the variable from the variable manager
VarManager *pVarManager = VarManager::instance( getIntf() );
VarBool *pVar = (VarBool*)pVarManager->getVar( rName, "bool" );
if( pVar )
{
return pVar;
}
else if( rName.find( " and " ) != string::npos )
// Convert the expression into Reverse Polish Notation
ExprEvaluator *pEvaluator = new ExprEvaluator( getIntf() );
pEvaluator->parse( rName );
list<VarBool*> varStack;
// Get the first token from the RPN stack
string token = pEvaluator->getToken();
while( !token.empty() )
{
int leftPos = rName.find( " and " );
string name1 = rName.substr( 0, leftPos );
int rightPos = leftPos + 5; // 5 is the size of " and "
string name2 = rName.substr( rightPos, rName.size() - rightPos );
// Retrive the two boolean variables
VarBool *pVar1 = getVarBool( name1, pTheme );
VarBool *pVar2 = getVarBool( name2, pTheme );
// Create a composite boolean variable
if( pVar1 && pVar2 )
if( token == "and" )
{
// Get the 2 last variables on the stack
if( varStack.empty() )
{
msg_Err( getIntf(), "Invalid boolean expression: %s",
rName.c_str());
return NULL;
}
VarBool *pVar1 = varStack.back();
varStack.pop_back();
if( varStack.empty() )
{
msg_Err( getIntf(), "Invalid boolean expression: %s",
rName.c_str());
return NULL;
}
VarBool *pVar2 = varStack.back();
varStack.pop_back();
// Create a composite boolean variable
VarBool *pNewVar = new VarBoolAndBool( getIntf(), *pVar1, *pVar2 );
varStack.push_back( pNewVar );
// Register this variable in the manager
pVarManager->registerVar( VariablePtr( pNewVar ), rName );
return pNewVar;
pVarManager->registerVar( VariablePtr( pNewVar ) );
}
else
{
return NULL;
}
}
else if( rName.find( "not " ) != string::npos )
{
int rightPos = rName.find( "not " ) + 4;
string name = rName.substr( rightPos, rName.size() - rightPos );
// Retrive the boolean variable
VarBool *pVar = getVarBool( name, pTheme );
// Create a composite boolean variable
if( pVar )
else if( token == "not" )
{
// Get the last variable on the stack
if( varStack.empty() )
{
msg_Err( getIntf(), "Invalid boolean expression: %s",
rName.c_str());
return NULL;
}
VarBool *pVar = varStack.back();
varStack.pop_back();
// Create a composite boolean variable
VarBool *pNewVar = new VarNotBool( getIntf(), *pVar );
varStack.push_back( pNewVar );
// Register this variable in the manager
pVarManager->registerVar( VariablePtr( pNewVar ), rName );
return pNewVar;
pVarManager->registerVar( VariablePtr( pNewVar ) );
}
else
else if( token.find( ".isVisible" ) != string::npos )
{
return NULL;
}
}
else if( rName.find( ".isVisible" ) != string::npos )
{
int leftPos = rName.find( ".isVisible" );
string windowId = rName.substr( 0, leftPos );
TopWindow *pWin = pTheme->getWindowById( windowId );
if( pWin )
{
return &pWin->getVisibleVar();
int leftPos = token.find( ".isVisible" );
string windowId = token.substr( 0, leftPos );
TopWindow *pWin = pTheme->getWindowById( windowId );
if( pWin )
{
// Push the visibility variable on the stack
varStack.push_back( &pWin->getVisibleVar() );
}
else
{
msg_Err( getIntf(), "Unknown window (%s)", windowId.c_str() );
return NULL;
}
}
else
{
msg_Err( getIntf(), "Unknown window (%s)", windowId.c_str() );
return NULL;
// Try to get the variable from the variable manager
VarBool *pVar = (VarBool*)pVarManager->getVar( token, "bool" );
if( !pVar )
{
msg_Err( getIntf(), "Cannot resolve boolean variable: %s",
token.c_str());
return NULL;
}
varStack.push_back( pVar );
}
// Get the first token from the RPN stack
token = pEvaluator->getToken();
}
else
// The stack should contain a single variable
if( varStack.size() != 1 )
{
msg_Err( getIntf(), "Invalid boolean expression: %s", rName.c_str() );
return NULL;
}
return varStack.back();
}
......
......@@ -2,7 +2,7 @@
* var_manager.cpp
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id: var_manager.cpp,v 1.3 2004/01/24 13:08:12 asmax Exp $
* $Id$
*
* Authors: Cyril Deguet <asmax@via.ecp.fr>
* Olivier Teulire <ipkiss@via.ecp.fr>
......@@ -34,10 +34,16 @@ VarManager::VarManager( intf_thread_t *pIntf ): SkinObject( pIntf ),
VarManager::~VarManager()
{
// Delete the variables in the reverse order they were added
list<string>::const_iterator it;
for( it = m_varList.begin(); it != m_varList.end(); it++ )
list<string>::const_iterator it1;
for( it1 = m_varList.begin(); it1 != m_varList.end(); it1++ )
{
m_varMap.erase(*it);
m_varMap.erase(*it1);
}
// Delete the anonymous variables
while( !m_anonVarList.empty() )
{
m_anonVarList.pop_back();
}
}
......@@ -70,7 +76,13 @@ void VarManager::destroy( intf_thread_t *pIntf )
void VarManager::registerVar( const VariablePtr &rcVar, const string &rName )
{
m_varMap[rName] = rcVar;
m_varList.push_front(rName);
m_varList.push_front( rName );
}
void VarManager::registerVar( const VariablePtr &rcVar )
{
m_anonVarList.push_back( rcVar );
}
......
......@@ -2,7 +2,7 @@
* var_manager.hpp
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id: var_manager.hpp,v 1.3 2004/01/24 13:08:12 asmax Exp $
* $Id$
*
* Authors: Cyril Deguet <asmax@via.ecp.fr>
* Olivier Teulière <ipkiss@via.ecp.fr>
......@@ -39,9 +39,12 @@ class VarManager: public SkinObject
/// Delete the instance of VarManager
static void destroy( intf_thread_t *pIntf );
/// Register a variable in the manager
/// Register a named variable in the manager
void registerVar( const VariablePtr &rcVar, const string &rName );
/// Register an anonymous variable in the manager
void registerVar( const VariablePtr &rcVar );
/// Get a variable by its name (NULL if not found)
Variable *getVar( const string &rName );
......@@ -59,10 +62,12 @@ class VarManager: public SkinObject
VarText m_tooltipText;
/// Help text
VarText m_helpText;
/// Map of registerd variables
/// Map of named registered variables
map<string, VariablePtr> m_varMap;
/// List of registed variables
/// List of named registed variables
list<string> m_varList;
/// List of anonymous registed variables
list<VariablePtr> m_anonVarList;
/// Private because it is a singleton
VarManager( intf_thread_t *pIntf );
......
......@@ -44,7 +44,7 @@ void VarBoolImpl::set( bool value )
}
VarBoolAndBool::VarBoolAndBool( intf_thread_t *pIntf, VarBool &rVar1,
VarBoolAndBool::VarBoolAndBool( intf_thread_t *pIntf, VarBool &rVar1,
VarBool &rVar2 ):
VarBool( pIntf ), m_rVar1( rVar1 ), m_rVar2( rVar2 )
{
......@@ -66,6 +66,28 @@ void VarBoolAndBool::onUpdate( Subject<VarBool> &rVariable )
}
VarBoolOrBool::VarBoolOrBool( intf_thread_t *pIntf, VarBool &rVar1,
VarBool &rVar2 ):
VarBool( pIntf ), m_rVar1( rVar1 ), m_rVar2( rVar2 )
{
m_rVar1.addObserver( this );
m_rVar2.addObserver( this );
}
VarBoolOrBool::~VarBoolOrBool()
{
m_rVar1.delObserver( this );
m_rVar2.delObserver( this );
}
void VarBoolOrBool::onUpdate( Subject<VarBool> &rVariable )
{
notify();
}
VarNotBool::VarNotBool( intf_thread_t *pIntf, VarBool &rVar ):
VarBool( pIntf ), m_rVar( rVar )
{
......
......@@ -49,6 +49,26 @@ class VarBool: public Variable, public Subject<VarBool>
};
/// Constant true VarBool
class VarBoolTrue: public VarBool
{
public:
VarBoolTrue( intf_thread_t *pIntf ): VarBool( pIntf ) {}
virtual ~VarBoolTrue() {}
virtual bool get() const { return true; }
};
/// Constant false VarBool
class VarBoolFalse: public VarBool
{
public:
VarBoolFalse( intf_thread_t *pIntf ): VarBool( pIntf ) {}
virtual ~VarBoolFalse() {}
virtual bool get() const { return false; }
};
/// Boolean variable implementation (read/write)
class VarBoolImpl: public VarBool
{
......@@ -87,6 +107,25 @@ class VarBoolAndBool: public VarBool, public Observer<VarBool>
};
/// Disjunction of two boolean variables (OR)
class VarBoolOrBool: public VarBool, public Observer<VarBool>
{
public:
VarBoolOrBool( intf_thread_t *pIntf, VarBool &rVar1, VarBool &rVar2 );
virtual ~VarBoolOrBool();
// Get the boolean value
virtual bool get() const { return m_rVar1.get() || m_rVar2.get(); }
// Called when one of the observed variables is changed
void onUpdate( Subject<VarBool> &rVariable );
private:
/// Boolean variables
VarBool &m_rVar1, &m_rVar2;
};
/// Negation of a boolean variable (NOT)
class VarNotBool: public VarBool, public Observer<VarBool>
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment