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:
Jasper St. Pierre
2013-06-25 00:58:30 -04:00
parent 877106b027
commit 6246f6e815
7 changed files with 634 additions and 130 deletions
+2 -1
View File
@@ -3,7 +3,8 @@ set(SRCS Src/ControllerEmu.cpp
Src/UDPWiimote.cpp
Src/UDPWrapper.cpp
Src/ControllerInterface/ControllerInterface.cpp
Src/ControllerInterface/Device.cpp)
Src/ControllerInterface/Device.cpp
Src/ControllerInterface/ExpressionParser.cpp)
if(WIN32)
set(SRCS ${SRCS}
@@ -167,6 +167,7 @@
<ClCompile Include="Src\ControllerEmu.cpp" />
<ClCompile Include="Src\ControllerInterface\ControllerInterface.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\DInputJoystick.cpp" />
<ClCompile Include="Src\ControllerInterface\DInput\DInputKeyboardMouse.cpp" />
@@ -180,6 +181,7 @@
<ClInclude Include="Src\ControllerEmu.h" />
<ClInclude Include="Src\ControllerInterface\ControllerInterface.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\DInputJoystick.h" />
<ClInclude Include="Src\ControllerInterface\DInput\DInputKeyboardMouse.h" />
@@ -23,6 +23,12 @@
<ClCompile Include="Src\ControllerInterface\DInput\DInput.cpp">
<Filter>ControllerInterface\DInput</Filter>
</ClCompile>
<ClCompile Include="Src\ControllerInterface\ExpressionParser.cpp">
<Filter>ControllerInterface</Filter>
</ClCompile>
<ClCompile Include="Src\ControllerInterface\Device.cpp">
<Filter>ControllerInterface</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Src\ControllerEmu.h" />
@@ -42,6 +48,9 @@
<ClInclude Include="Src\ControllerInterface\Device.h">
<Filter>ControllerInterface\Device</Filter>
</ClInclude>
<ClInclude Include="Src\ControllerInterface\ExpressionParser.h">
<Filter>ControllerInterface</Filter>
</ClInclude>
<ClInclude Include="Src\ControllerInterface\DInput\NamedKeys.h">
<Filter>ControllerInterface\DInput</Filter>
</ClInclude>
@@ -72,4 +81,4 @@
<UniqueIdentifier>{5a9c1b94-2eab-4357-b44f-87d5db50da3d}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>
@@ -21,6 +21,8 @@
#include "Thread.h"
using namespace ciface::ExpressionParser;
namespace
{
const float INPUT_DETECT_THRESHOLD = 0.55f;
@@ -187,48 +189,10 @@ bool ControllerInterface::UpdateOutput(const bool force)
//
ControlState ControllerInterface::InputReference::State( const ControlState ignore )
{
//if (NULL == device)
//return 0;
ControlState state = 0;
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);
if (parsed_expression)
return parsed_expression->GetValue();
else
return 0.0f;
}
//
@@ -240,17 +204,9 @@ ControlState ControllerInterface::InputReference::State( const ControlState igno
//
ControlState ControllerInterface::OutputReference::State(const ControlState state)
{
const ControlState tmp_state = std::min(1.0f, state * range);
// output ref just ignores the modes ( |&!... )
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
if (parsed_expression)
parsed_expression->SetValue(state);
return 0.0f;
}
//
@@ -262,65 +218,13 @@ ControlState ControllerInterface::OutputReference::State(const ControlState stat
void ControllerInterface::UpdateReference(ControllerInterface::ControlReference* ref
, const DeviceQualifier& default_device) const
{
ref->m_controls.clear();
delete ref->parsed_expression;
ref->parsed_expression = NULL;
// adding | to parse the last item, silly
std::istringstream ss(ref->expression + '|');
const std::string mode_chars("|&!^");
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;
}
}
ControlFinder finder(*this, default_device, ref->is_input);
ExpressionParseStatus status;
status = ParseExpression(ref->expression, finder, &ref->parsed_expression);
// XXX: do something with status?
}
//
@@ -388,7 +292,7 @@ Device::Control* ControllerInterface::OutputReference::Detect(const unsigned int
// ignore device
// don't hang if we don't even have any controls mapped
if (m_controls.size())
if (BoundCount() > 0)
{
State(1);
unsigned int slept = 0;
@@ -9,6 +9,7 @@
#include "Common.h"
#include "Thread.h"
#include "ExpressionParser.h"
#include "Device.h"
// enable disable sources
@@ -53,30 +54,28 @@ public:
class ControlReference
{
friend class ControllerInterface;
public:
virtual ~ControlReference() {}
virtual ControlState State(const ControlState state = 0) = 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;
const bool is_input;
virtual ~ControlReference() {
delete parsed_expression;
}
int BoundCount() {
if (parsed_expression)
return parsed_expression->num_controls;
else
return 0;
}
protected:
ControlReference(const bool _is_input) : range(1), is_input(_is_input) {}
struct DeviceControl
{
DeviceControl() : control(NULL), mode(0) {}
Device::Control* control;
int mode;
};
std::vector<DeviceControl> m_controls;
ControlReference(const bool _is_input) : range(1), is_input(_is_input), parsed_expression(NULL) {}
ciface::ExpressionParser::Expression *parsed_expression;
};
//
File diff suppressed because it is too large Load Diff
@@ -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