InputCommon: Add a new ExpressionParser to replace the old hack language
This contains a new, hand-written expression parser to replace the old hack language based on string munging. The new approach is a simple AST-based evaluation approach, instead of the "list of operations" infix-based hack that there was before. The new language for configuration has support for parentheses, and counts "!" as a unary operator instead of the binary "NOT OR" operator it was before. A simple example: (X & Y) | !B Explicit device references, and complex device names ("Right Y+") are handled with backticks and colons: (`SDL/0/6 axis joystick:Right X+` & `DInput/0/Keyboard Mouse:A`) The basic editor UI that inserts tokens has not been updated to reflect the new language.
This commit is contained in:
parent
877106b027
commit
6246f6e815
|
@ -3,7 +3,8 @@ set(SRCS Src/ControllerEmu.cpp
|
||||||
Src/UDPWiimote.cpp
|
Src/UDPWiimote.cpp
|
||||||
Src/UDPWrapper.cpp
|
Src/UDPWrapper.cpp
|
||||||
Src/ControllerInterface/ControllerInterface.cpp
|
Src/ControllerInterface/ControllerInterface.cpp
|
||||||
Src/ControllerInterface/Device.cpp)
|
Src/ControllerInterface/Device.cpp
|
||||||
|
Src/ControllerInterface/ExpressionParser.cpp)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(SRCS ${SRCS}
|
set(SRCS ${SRCS}
|
||||||
|
|
|
@ -167,6 +167,7 @@
|
||||||
<ClCompile Include="Src\ControllerEmu.cpp" />
|
<ClCompile Include="Src\ControllerEmu.cpp" />
|
||||||
<ClCompile Include="Src\ControllerInterface\ControllerInterface.cpp" />
|
<ClCompile Include="Src\ControllerInterface\ControllerInterface.cpp" />
|
||||||
<ClCompile Include="Src\ControllerInterface\Device.cpp" />
|
<ClCompile Include="Src\ControllerInterface\Device.cpp" />
|
||||||
|
<ClCompile Include="Src\ControllerInterface\ExpressionParser.cpp" />
|
||||||
<ClCompile Include="Src\ControllerInterface\DInput\DInput.cpp" />
|
<ClCompile Include="Src\ControllerInterface\DInput\DInput.cpp" />
|
||||||
<ClCompile Include="Src\ControllerInterface\DInput\DInputJoystick.cpp" />
|
<ClCompile Include="Src\ControllerInterface\DInput\DInputJoystick.cpp" />
|
||||||
<ClCompile Include="Src\ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
|
<ClCompile Include="Src\ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
|
||||||
|
@ -180,6 +181,7 @@
|
||||||
<ClInclude Include="Src\ControllerEmu.h" />
|
<ClInclude Include="Src\ControllerEmu.h" />
|
||||||
<ClInclude Include="Src\ControllerInterface\ControllerInterface.h" />
|
<ClInclude Include="Src\ControllerInterface\ControllerInterface.h" />
|
||||||
<ClInclude Include="Src\ControllerInterface\Device.h" />
|
<ClInclude Include="Src\ControllerInterface\Device.h" />
|
||||||
|
<ClInclude Include="Src\ControllerInterface\ExpressionParser.h" />
|
||||||
<ClInclude Include="Src\ControllerInterface\DInput\DInput.h" />
|
<ClInclude Include="Src\ControllerInterface\DInput\DInput.h" />
|
||||||
<ClInclude Include="Src\ControllerInterface\DInput\DInputJoystick.h" />
|
<ClInclude Include="Src\ControllerInterface\DInput\DInputJoystick.h" />
|
||||||
<ClInclude Include="Src\ControllerInterface\DInput\DInputKeyboardMouse.h" />
|
<ClInclude Include="Src\ControllerInterface\DInput\DInputKeyboardMouse.h" />
|
||||||
|
|
|
@ -23,6 +23,12 @@
|
||||||
<ClCompile Include="Src\ControllerInterface\DInput\DInput.cpp">
|
<ClCompile Include="Src\ControllerInterface\DInput\DInput.cpp">
|
||||||
<Filter>ControllerInterface\DInput</Filter>
|
<Filter>ControllerInterface\DInput</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Src\ControllerInterface\ExpressionParser.cpp">
|
||||||
|
<Filter>ControllerInterface</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Src\ControllerInterface\Device.cpp">
|
||||||
|
<Filter>ControllerInterface</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Src\ControllerEmu.h" />
|
<ClInclude Include="Src\ControllerEmu.h" />
|
||||||
|
@ -42,6 +48,9 @@
|
||||||
<ClInclude Include="Src\ControllerInterface\Device.h">
|
<ClInclude Include="Src\ControllerInterface\Device.h">
|
||||||
<Filter>ControllerInterface\Device</Filter>
|
<Filter>ControllerInterface\Device</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Src\ControllerInterface\ExpressionParser.h">
|
||||||
|
<Filter>ControllerInterface</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="Src\ControllerInterface\DInput\NamedKeys.h">
|
<ClInclude Include="Src\ControllerInterface\DInput\NamedKeys.h">
|
||||||
<Filter>ControllerInterface\DInput</Filter>
|
<Filter>ControllerInterface\DInput</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -72,4 +81,4 @@
|
||||||
<UniqueIdentifier>{5a9c1b94-2eab-4357-b44f-87d5db50da3d}</UniqueIdentifier>
|
<UniqueIdentifier>{5a9c1b94-2eab-4357-b44f-87d5db50da3d}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
|
||||||
|
using namespace ciface::ExpressionParser;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const float INPUT_DETECT_THRESHOLD = 0.55f;
|
const float INPUT_DETECT_THRESHOLD = 0.55f;
|
||||||
|
@ -187,48 +189,10 @@ bool ControllerInterface::UpdateOutput(const bool force)
|
||||||
//
|
//
|
||||||
ControlState ControllerInterface::InputReference::State( const ControlState ignore )
|
ControlState ControllerInterface::InputReference::State( const ControlState ignore )
|
||||||
{
|
{
|
||||||
//if (NULL == device)
|
if (parsed_expression)
|
||||||
//return 0;
|
return parsed_expression->GetValue();
|
||||||
|
else
|
||||||
ControlState state = 0;
|
return 0.0f;
|
||||||
|
|
||||||
std::vector<DeviceControl>::const_iterator
|
|
||||||
ci = m_controls.begin(),
|
|
||||||
ce = m_controls.end();
|
|
||||||
|
|
||||||
// bit of hax for "NOT" to work at start of expression
|
|
||||||
if (ci != ce)
|
|
||||||
{
|
|
||||||
if (ci->mode == 2)
|
|
||||||
state = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; ci!=ce; ++ci)
|
|
||||||
{
|
|
||||||
const ControlState istate = ci->control->ToInput()->GetState();
|
|
||||||
|
|
||||||
switch (ci->mode)
|
|
||||||
{
|
|
||||||
// OR
|
|
||||||
case 0 :
|
|
||||||
state = std::max(state, istate);
|
|
||||||
break;
|
|
||||||
// AND
|
|
||||||
case 1 :
|
|
||||||
state = std::min(state, istate);
|
|
||||||
break;
|
|
||||||
// NOT
|
|
||||||
case 2 :
|
|
||||||
state = std::max(std::min(state, 1.0f - istate), 0.0f);
|
|
||||||
break;
|
|
||||||
// ADD
|
|
||||||
case 3 :
|
|
||||||
state += istate;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::min(1.0f, state * range);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -240,17 +204,9 @@ ControlState ControllerInterface::InputReference::State( const ControlState igno
|
||||||
//
|
//
|
||||||
ControlState ControllerInterface::OutputReference::State(const ControlState state)
|
ControlState ControllerInterface::OutputReference::State(const ControlState state)
|
||||||
{
|
{
|
||||||
const ControlState tmp_state = std::min(1.0f, state * range);
|
if (parsed_expression)
|
||||||
|
parsed_expression->SetValue(state);
|
||||||
// output ref just ignores the modes ( |&!... )
|
return 0.0f;
|
||||||
|
|
||||||
std::vector<DeviceControl>::iterator
|
|
||||||
ci = m_controls.begin(),
|
|
||||||
ce = m_controls.end();
|
|
||||||
for (; ci != ce; ++ci)
|
|
||||||
ci->control->ToOutput()->SetState(tmp_state);
|
|
||||||
|
|
||||||
return state; // just return the output, watever
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -262,65 +218,13 @@ ControlState ControllerInterface::OutputReference::State(const ControlState stat
|
||||||
void ControllerInterface::UpdateReference(ControllerInterface::ControlReference* ref
|
void ControllerInterface::UpdateReference(ControllerInterface::ControlReference* ref
|
||||||
, const DeviceQualifier& default_device) const
|
, const DeviceQualifier& default_device) const
|
||||||
{
|
{
|
||||||
ref->m_controls.clear();
|
delete ref->parsed_expression;
|
||||||
|
ref->parsed_expression = NULL;
|
||||||
|
|
||||||
// adding | to parse the last item, silly
|
ControlFinder finder(*this, default_device, ref->is_input);
|
||||||
std::istringstream ss(ref->expression + '|');
|
ExpressionParseStatus status;
|
||||||
|
status = ParseExpression(ref->expression, finder, &ref->parsed_expression);
|
||||||
const std::string mode_chars("|&!^");
|
// XXX: do something with status?
|
||||||
|
|
||||||
ControlReference::DeviceControl devc;
|
|
||||||
|
|
||||||
std::string dev_str;
|
|
||||||
std::string ctrl_str;
|
|
||||||
|
|
||||||
char c = 0;
|
|
||||||
while (ss.read(&c, 1))
|
|
||||||
{
|
|
||||||
const size_t f = mode_chars.find(c);
|
|
||||||
|
|
||||||
if (mode_chars.npos != f)
|
|
||||||
{
|
|
||||||
// add ctrl
|
|
||||||
if (ctrl_str.size())
|
|
||||||
{
|
|
||||||
DeviceQualifier devq;
|
|
||||||
|
|
||||||
// using default device or alterate device inside `backticks`
|
|
||||||
if (dev_str.empty())
|
|
||||||
devq = default_device;
|
|
||||||
else
|
|
||||||
devq.FromString(dev_str);
|
|
||||||
|
|
||||||
// find device
|
|
||||||
Device* const def_device = FindDevice(devq);
|
|
||||||
|
|
||||||
if (def_device)
|
|
||||||
{
|
|
||||||
if (ref->is_input)
|
|
||||||
devc.control = FindInput(ctrl_str, def_device);
|
|
||||||
else
|
|
||||||
devc.control = FindOutput(ctrl_str, def_device);
|
|
||||||
|
|
||||||
if (devc.control)
|
|
||||||
ref->m_controls.push_back(devc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// reset stuff for next ctrl
|
|
||||||
devc.mode = (int)f;
|
|
||||||
ctrl_str.clear();
|
|
||||||
}
|
|
||||||
else if ('`' == c)
|
|
||||||
{
|
|
||||||
// different device
|
|
||||||
if (std::getline(ss, dev_str, '`').eof())
|
|
||||||
break; // no terminating '`' character
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ctrl_str += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -388,7 +292,7 @@ Device::Control* ControllerInterface::OutputReference::Detect(const unsigned int
|
||||||
// ignore device
|
// ignore device
|
||||||
|
|
||||||
// don't hang if we don't even have any controls mapped
|
// don't hang if we don't even have any controls mapped
|
||||||
if (m_controls.size())
|
if (BoundCount() > 0)
|
||||||
{
|
{
|
||||||
State(1);
|
State(1);
|
||||||
unsigned int slept = 0;
|
unsigned int slept = 0;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
#include "ExpressionParser.h"
|
||||||
#include "Device.h"
|
#include "Device.h"
|
||||||
|
|
||||||
// enable disable sources
|
// enable disable sources
|
||||||
|
@ -53,30 +54,28 @@ public:
|
||||||
class ControlReference
|
class ControlReference
|
||||||
{
|
{
|
||||||
friend class ControllerInterface;
|
friend class ControllerInterface;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~ControlReference() {}
|
|
||||||
|
|
||||||
virtual ControlState State(const ControlState state = 0) = 0;
|
virtual ControlState State(const ControlState state = 0) = 0;
|
||||||
virtual Device::Control* Detect(const unsigned int ms, Device* const device) = 0;
|
virtual Device::Control* Detect(const unsigned int ms, Device* const device) = 0;
|
||||||
size_t BoundCount() const { return m_controls.size(); }
|
|
||||||
|
|
||||||
ControlState range;
|
ControlState range;
|
||||||
std::string expression;
|
std::string expression;
|
||||||
const bool is_input;
|
const bool is_input;
|
||||||
|
|
||||||
|
virtual ~ControlReference() {
|
||||||
|
delete parsed_expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BoundCount() {
|
||||||
|
if (parsed_expression)
|
||||||
|
return parsed_expression->num_controls;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ControlReference(const bool _is_input) : range(1), is_input(_is_input) {}
|
ControlReference(const bool _is_input) : range(1), is_input(_is_input), parsed_expression(NULL) {}
|
||||||
|
ciface::ExpressionParser::Expression *parsed_expression;
|
||||||
struct DeviceControl
|
|
||||||
{
|
|
||||||
DeviceControl() : control(NULL), mode(0) {}
|
|
||||||
|
|
||||||
Device::Control* control;
|
|
||||||
int mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<DeviceControl> m_controls;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -0,0 +1,516 @@
|
||||||
|
|
||||||
|
#include "ExpressionParser.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace ciface::Core;
|
||||||
|
|
||||||
|
namespace ciface
|
||||||
|
{
|
||||||
|
namespace ExpressionParser
|
||||||
|
{
|
||||||
|
|
||||||
|
enum TokenType
|
||||||
|
{
|
||||||
|
TOK_DISCARD,
|
||||||
|
TOK_INVALID,
|
||||||
|
TOK_EOF,
|
||||||
|
TOK_LPAREN,
|
||||||
|
TOK_RPAREN,
|
||||||
|
TOK_AND,
|
||||||
|
TOK_OR,
|
||||||
|
TOK_NOT,
|
||||||
|
TOK_CONTROL,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::string OpName(TokenType op)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case TOK_AND:
|
||||||
|
return "And";
|
||||||
|
case TOK_OR:
|
||||||
|
return "Or";
|
||||||
|
case TOK_NOT:
|
||||||
|
return "Not";
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Token
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TokenType type;
|
||||||
|
ControlQualifier qualifier;
|
||||||
|
|
||||||
|
Token(TokenType type_) : type(type_) {}
|
||||||
|
Token(TokenType type_, ControlQualifier qualifier_) : type(type_), qualifier(qualifier_) {}
|
||||||
|
|
||||||
|
operator std::string()
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TOK_INVALID:
|
||||||
|
return "Invalid";
|
||||||
|
case TOK_DISCARD:
|
||||||
|
return "Discard";
|
||||||
|
case TOK_EOF:
|
||||||
|
return "EOF";
|
||||||
|
case TOK_LPAREN:
|
||||||
|
return "(";
|
||||||
|
case TOK_RPAREN:
|
||||||
|
return ")";
|
||||||
|
case TOK_AND:
|
||||||
|
return "&";
|
||||||
|
case TOK_OR:
|
||||||
|
return "|";
|
||||||
|
case TOK_NOT:
|
||||||
|
return "!";
|
||||||
|
case TOK_CONTROL:
|
||||||
|
return "Device(" + (std::string)qualifier + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Lexer {
|
||||||
|
public:
|
||||||
|
std::string expr;
|
||||||
|
std::string::iterator it;
|
||||||
|
|
||||||
|
Lexer(std::string expr_) : expr(expr_)
|
||||||
|
{
|
||||||
|
it = expr.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FetchBacktickString(std::string &value, char otherDelim = 0)
|
||||||
|
{
|
||||||
|
while (it != expr.end())
|
||||||
|
{
|
||||||
|
char c = *it;
|
||||||
|
if (c == '`')
|
||||||
|
return false;
|
||||||
|
if (c > 0 && c == otherDelim)
|
||||||
|
return true;
|
||||||
|
value += c;
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token GetControlQualifier()
|
||||||
|
{
|
||||||
|
ControlQualifier qualifier;
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
if (FetchBacktickString(value, ':'))
|
||||||
|
{
|
||||||
|
// Found colon, this is the device name
|
||||||
|
qualifier.has_device = true;
|
||||||
|
qualifier.device_qualifier.FromString(value);
|
||||||
|
FetchBacktickString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
qualifier.control_name = value;
|
||||||
|
|
||||||
|
return Token(TOK_CONTROL, qualifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token NextToken()
|
||||||
|
{
|
||||||
|
if (it == expr.end())
|
||||||
|
return Token(TOK_EOF);
|
||||||
|
|
||||||
|
char c = *it++;
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
return Token(TOK_DISCARD);
|
||||||
|
case '(':
|
||||||
|
return Token(TOK_LPAREN);
|
||||||
|
case ')':
|
||||||
|
return Token(TOK_RPAREN);
|
||||||
|
case '&':
|
||||||
|
return Token(TOK_AND);
|
||||||
|
case '|':
|
||||||
|
return Token(TOK_OR);
|
||||||
|
case '!':
|
||||||
|
return Token(TOK_NOT);
|
||||||
|
case '`':
|
||||||
|
return GetControlQualifier();
|
||||||
|
default:
|
||||||
|
return Token(TOK_INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionParseStatus Tokenize(std::vector<Token> &tokens)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Token tok = NextToken();
|
||||||
|
|
||||||
|
if (tok.type == TOK_DISCARD)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tok.type == TOK_INVALID)
|
||||||
|
{
|
||||||
|
tokens.empty();
|
||||||
|
return EXPRESSION_PARSE_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.push_back(tok);
|
||||||
|
|
||||||
|
if (tok.type == TOK_EOF)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return EXPRESSION_PARSE_SUCCESS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExpressionNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ExpressionNode() {}
|
||||||
|
virtual ControlState GetValue() { return 0; }
|
||||||
|
virtual void SetValue(ControlState state) {}
|
||||||
|
virtual operator std::string() { return ""; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ControlExpression : public ExpressionNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ControlQualifier qualifier;
|
||||||
|
Device::Control *control;
|
||||||
|
|
||||||
|
ControlExpression(ControlQualifier qualifier_, Device::Control *control_) : qualifier(qualifier_), control(control_) {}
|
||||||
|
|
||||||
|
virtual ControlState GetValue()
|
||||||
|
{
|
||||||
|
return control->ToInput()->GetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetValue(ControlState value)
|
||||||
|
{
|
||||||
|
control->ToOutput()->SetState(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual operator std::string()
|
||||||
|
{
|
||||||
|
return "`" + (std::string)qualifier + "`";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BinaryExpression : public ExpressionNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TokenType op;
|
||||||
|
ExpressionNode *lhs;
|
||||||
|
ExpressionNode *rhs;
|
||||||
|
|
||||||
|
BinaryExpression(TokenType op_, ExpressionNode *lhs_, ExpressionNode *rhs_) : op(op_), lhs(lhs_), rhs(rhs_) {}
|
||||||
|
virtual ~BinaryExpression()
|
||||||
|
{
|
||||||
|
delete lhs;
|
||||||
|
delete rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ControlState GetValue()
|
||||||
|
{
|
||||||
|
ControlState lhsValue = lhs->GetValue();
|
||||||
|
ControlState rhsValue = rhs->GetValue();
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case TOK_AND:
|
||||||
|
return std::min(lhsValue, rhsValue);
|
||||||
|
case TOK_OR:
|
||||||
|
return std::max(lhsValue, rhsValue);
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetValue(ControlState value)
|
||||||
|
{
|
||||||
|
// Don't do anything special with the op we have.
|
||||||
|
// Treat "A & B" the same as "A | B".
|
||||||
|
lhs->SetValue(value);
|
||||||
|
rhs->SetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual operator std::string()
|
||||||
|
{
|
||||||
|
return OpName(op) + "(" + (std::string)(*lhs) + ", " + (std::string)(*rhs) + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnaryExpression : public ExpressionNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TokenType op;
|
||||||
|
ExpressionNode *inner;
|
||||||
|
|
||||||
|
UnaryExpression(TokenType op_, ExpressionNode *inner_) : op(op_), inner(inner_) {}
|
||||||
|
virtual ~UnaryExpression()
|
||||||
|
{
|
||||||
|
delete inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ControlState GetValue()
|
||||||
|
{
|
||||||
|
ControlState value = inner->GetValue();
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case TOK_NOT:
|
||||||
|
return 1.0f - value;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetValue(ControlState value)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case TOK_NOT:
|
||||||
|
inner->SetValue(1.0f - value);
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual operator std::string()
|
||||||
|
{
|
||||||
|
return OpName(op) + "(" + (std::string)(*inner) + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Device *ControlFinder::FindDevice(ControlQualifier qualifier)
|
||||||
|
{
|
||||||
|
if (qualifier.has_device)
|
||||||
|
return container.FindDevice(qualifier.device_qualifier);
|
||||||
|
else
|
||||||
|
return container.FindDevice(default_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::Control *ControlFinder::FindControl(ControlQualifier qualifier)
|
||||||
|
{
|
||||||
|
Device *device = FindDevice(qualifier);
|
||||||
|
if (is_input)
|
||||||
|
return device->FindInput(qualifier.control_name);
|
||||||
|
else
|
||||||
|
return device->FindOutput(qualifier.control_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Parser(std::vector<Token> tokens_, ControlFinder &finder_) : tokens(tokens_), finder(finder_)
|
||||||
|
{
|
||||||
|
m_it = tokens.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionParseStatus Parse(Expression **expr_out)
|
||||||
|
{
|
||||||
|
Expression *expr;
|
||||||
|
ExpressionNode *expr_node;
|
||||||
|
ExpressionParseStatus status = Toplevel(&expr_node);
|
||||||
|
if (status != EXPRESSION_PARSE_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
expr = new Expression();
|
||||||
|
expr->expr = expr_node;
|
||||||
|
expr->num_controls = CountNumControls();
|
||||||
|
*expr_out = expr;
|
||||||
|
|
||||||
|
return EXPRESSION_PARSE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Token> tokens;
|
||||||
|
std::vector<Token>::iterator m_it;
|
||||||
|
ControlFinder &finder;
|
||||||
|
|
||||||
|
Token Chew()
|
||||||
|
{
|
||||||
|
return *m_it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token Peek()
|
||||||
|
{
|
||||||
|
return *m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Expects(TokenType type)
|
||||||
|
{
|
||||||
|
Token tok = Chew();
|
||||||
|
return tok.type == type;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionParseStatus Atom(ExpressionNode **expr_out)
|
||||||
|
{
|
||||||
|
Token tok = Chew();
|
||||||
|
switch (tok.type)
|
||||||
|
{
|
||||||
|
case TOK_CONTROL:
|
||||||
|
{
|
||||||
|
Device::Control *control = finder.FindControl(tok.qualifier);
|
||||||
|
if (control == NULL)
|
||||||
|
return EXPRESSION_PARSE_NO_DEVICE;
|
||||||
|
|
||||||
|
*expr_out = new ControlExpression(tok.qualifier, control);
|
||||||
|
return EXPRESSION_PARSE_SUCCESS;
|
||||||
|
}
|
||||||
|
case TOK_LPAREN:
|
||||||
|
return Paren(expr_out);
|
||||||
|
default:
|
||||||
|
return EXPRESSION_PARSE_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsUnaryExpression(TokenType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TOK_NOT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionParseStatus Unary(ExpressionNode **expr_out)
|
||||||
|
{
|
||||||
|
ExpressionParseStatus status;
|
||||||
|
|
||||||
|
if (IsUnaryExpression(Peek().type))
|
||||||
|
{
|
||||||
|
Token tok = Chew();
|
||||||
|
ExpressionNode *atom_expr;
|
||||||
|
if ((status = Atom(&atom_expr)) != EXPRESSION_PARSE_SUCCESS)
|
||||||
|
return status;
|
||||||
|
*expr_out = new UnaryExpression(tok.type, atom_expr);
|
||||||
|
return EXPRESSION_PARSE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Atom(expr_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBinaryToken(TokenType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TOK_AND:
|
||||||
|
case TOK_OR:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionParseStatus Binary(ExpressionNode **expr_out)
|
||||||
|
{
|
||||||
|
ExpressionParseStatus status;
|
||||||
|
|
||||||
|
if ((status = Unary(expr_out)) != EXPRESSION_PARSE_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
while (IsBinaryToken(Peek().type))
|
||||||
|
{
|
||||||
|
Token tok = Chew();
|
||||||
|
ExpressionNode *unary_expr;
|
||||||
|
if ((status = Unary(&unary_expr)) != EXPRESSION_PARSE_SUCCESS)
|
||||||
|
{
|
||||||
|
delete *expr_out;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
*expr_out = new BinaryExpression(tok.type, *expr_out, unary_expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXPRESSION_PARSE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionParseStatus Paren(ExpressionNode **expr_out)
|
||||||
|
{
|
||||||
|
ExpressionParseStatus status;
|
||||||
|
|
||||||
|
// lparen already chewed
|
||||||
|
if ((status = Toplevel(expr_out)) != EXPRESSION_PARSE_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
if (!Expects(TOK_RPAREN))
|
||||||
|
{
|
||||||
|
delete *expr_out;
|
||||||
|
return EXPRESSION_PARSE_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXPRESSION_PARSE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionParseStatus Toplevel(ExpressionNode **expr_out)
|
||||||
|
{
|
||||||
|
return Binary(expr_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CountNumControls()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (std::vector<Token>::iterator it = tokens.begin(); it != tokens.end(); ++it)
|
||||||
|
if (it->type == TOK_CONTROL)
|
||||||
|
count++;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ControlState Expression::GetValue()
|
||||||
|
{
|
||||||
|
return expr->GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Expression::SetValue(ControlState value)
|
||||||
|
{
|
||||||
|
expr->SetValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::~Expression()
|
||||||
|
{
|
||||||
|
delete expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionParseStatus ParseExpression(std::string str, ControlFinder &finder, Expression **expr_out)
|
||||||
|
{
|
||||||
|
ExpressionParseStatus status;
|
||||||
|
Expression *expr;
|
||||||
|
*expr_out = NULL;
|
||||||
|
|
||||||
|
if (str == "")
|
||||||
|
return EXPRESSION_PARSE_SUCCESS;
|
||||||
|
|
||||||
|
Lexer l(str);
|
||||||
|
std::vector<Token> tokens;
|
||||||
|
status = l.Tokenize(tokens);
|
||||||
|
if (status != EXPRESSION_PARSE_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
Parser p(tokens, finder);
|
||||||
|
status = p.Parse(&expr);
|
||||||
|
if (status != EXPRESSION_PARSE_SUCCESS)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
*expr_out = expr;
|
||||||
|
return EXPRESSION_PARSE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
|
||||||
|
#ifndef _EXPRESSIONPARSER_H_
|
||||||
|
#define _EXPRESSIONPARSER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "Device.h"
|
||||||
|
|
||||||
|
namespace ciface
|
||||||
|
{
|
||||||
|
namespace ExpressionParser
|
||||||
|
{
|
||||||
|
|
||||||
|
class ControlQualifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool has_device;
|
||||||
|
Core::DeviceQualifier device_qualifier;
|
||||||
|
std::string control_name;
|
||||||
|
|
||||||
|
ControlQualifier() : has_device(false) {}
|
||||||
|
|
||||||
|
operator std::string()
|
||||||
|
{
|
||||||
|
if (has_device)
|
||||||
|
return device_qualifier.ToString() + ":" + control_name;
|
||||||
|
else
|
||||||
|
return control_name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ControlFinder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ControlFinder(const Core::DeviceContainer &container_, const Core::DeviceQualifier &default_, const bool is_input_) : container(container_), default_device(default_), is_input(is_input_) {}
|
||||||
|
Core::Device::Control *FindControl(ControlQualifier qualifier);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Core::Device *FindDevice(ControlQualifier qualifier);
|
||||||
|
const Core::DeviceContainer &container;
|
||||||
|
const Core::DeviceQualifier &default_device;
|
||||||
|
bool is_input;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Parser;
|
||||||
|
class ExpressionNode;
|
||||||
|
class Expression
|
||||||
|
{
|
||||||
|
friend class Parser;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Expression() : expr(NULL) {}
|
||||||
|
~Expression();
|
||||||
|
ControlState GetValue();
|
||||||
|
void SetValue (ControlState state);
|
||||||
|
int num_controls;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ExpressionNode *expr;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ExpressionParseStatus
|
||||||
|
{
|
||||||
|
EXPRESSION_PARSE_SUCCESS = 0,
|
||||||
|
EXPRESSION_PARSE_SYNTAX_ERROR,
|
||||||
|
EXPRESSION_PARSE_NO_DEVICE,
|
||||||
|
};
|
||||||
|
|
||||||
|
ExpressionParseStatus ParseExpression(std::string expr, ControlFinder &finder, Expression **expr_out);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue