2014-02-10 18:54:46 +00:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
2015-05-17 23:08:10 +00:00
|
|
|
// Licensed under GPLv2+
|
2014-02-10 18:54:46 +00:00
|
|
|
// Refer to the license.txt file included.
|
2013-06-14 03:09:55 +00:00
|
|
|
|
2014-02-17 10:18:15 +00:00
|
|
|
#include <algorithm>
|
2013-06-14 03:09:55 +00:00
|
|
|
#include <cassert>
|
2018-12-30 19:16:28 +00:00
|
|
|
#include <chrono>
|
|
|
|
#include <cmath>
|
2013-06-14 03:09:55 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <map>
|
2016-06-26 03:34:09 +00:00
|
|
|
#include <memory>
|
2018-12-30 18:38:02 +00:00
|
|
|
#include <regex>
|
2014-02-17 10:18:15 +00:00
|
|
|
#include <string>
|
2013-06-14 03:09:55 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2018-12-30 19:16:28 +00:00
|
|
|
#include "Common/MathUtil.h"
|
2017-06-07 23:03:36 +00:00
|
|
|
#include "Common/StringUtil.h"
|
2016-10-12 00:48:38 +00:00
|
|
|
#include "InputCommon/ControlReference/ExpressionParser.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
|
2019-06-17 20:39:24 +00:00
|
|
|
namespace ciface::ExpressionParser
|
|
|
|
{
|
2013-06-14 03:09:55 +00:00
|
|
|
using namespace ciface::Core;
|
|
|
|
|
|
|
|
enum TokenType
|
|
|
|
{
|
|
|
|
TOK_DISCARD,
|
|
|
|
TOK_INVALID,
|
|
|
|
TOK_EOF,
|
|
|
|
TOK_LPAREN,
|
|
|
|
TOK_RPAREN,
|
|
|
|
TOK_AND,
|
|
|
|
TOK_OR,
|
2018-12-30 18:38:02 +00:00
|
|
|
TOK_UNARY,
|
2013-06-14 06:52:07 +00:00
|
|
|
TOK_ADD,
|
2018-12-30 17:51:12 +00:00
|
|
|
TOK_MUL,
|
|
|
|
TOK_DIV,
|
2018-12-30 19:16:28 +00:00
|
|
|
TOK_MOD,
|
2018-12-30 22:06:29 +00:00
|
|
|
TOK_ASSIGN,
|
2013-06-14 03:09:55 +00:00
|
|
|
TOK_CONTROL,
|
2018-12-30 16:37:23 +00:00
|
|
|
TOK_LITERAL,
|
2018-12-30 22:06:29 +00:00
|
|
|
TOK_VARIABLE,
|
2013-06-14 03:09:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
inline std::string OpName(TokenType op)
|
|
|
|
{
|
|
|
|
switch (op)
|
|
|
|
{
|
|
|
|
case TOK_AND:
|
|
|
|
return "And";
|
|
|
|
case TOK_OR:
|
|
|
|
return "Or";
|
2018-12-30 18:38:02 +00:00
|
|
|
case TOK_UNARY:
|
|
|
|
return "Unary";
|
2013-06-14 06:52:07 +00:00
|
|
|
case TOK_ADD:
|
|
|
|
return "Add";
|
2018-12-30 17:51:12 +00:00
|
|
|
case TOK_MUL:
|
|
|
|
return "Mul";
|
|
|
|
case TOK_DIV:
|
|
|
|
return "Div";
|
2018-12-30 19:16:28 +00:00
|
|
|
case TOK_MOD:
|
|
|
|
return "Mod";
|
2018-12-30 22:06:29 +00:00
|
|
|
case TOK_ASSIGN:
|
|
|
|
return "Assign";
|
|
|
|
case TOK_VARIABLE:
|
|
|
|
return "Var";
|
2013-06-14 03:09:55 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Token
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TokenType type;
|
2018-12-30 16:37:23 +00:00
|
|
|
std::string data;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
Token(TokenType type_) : type(type_) {}
|
2018-12-30 16:37:23 +00:00
|
|
|
Token(TokenType type_, std::string data_) : type(type_), data(std::move(data_)) {}
|
2017-02-26 07:04:16 +00:00
|
|
|
operator std::string() const
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
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 "|";
|
2018-12-30 18:38:02 +00:00
|
|
|
case TOK_UNARY:
|
|
|
|
return "!" + data;
|
2013-06-14 06:52:07 +00:00
|
|
|
case TOK_ADD:
|
|
|
|
return "+";
|
2018-12-30 17:51:12 +00:00
|
|
|
case TOK_MUL:
|
|
|
|
return "*";
|
|
|
|
case TOK_DIV:
|
|
|
|
return "/";
|
2018-12-30 19:16:28 +00:00
|
|
|
case TOK_MOD:
|
|
|
|
return "%";
|
2018-12-30 22:06:29 +00:00
|
|
|
case TOK_ASSIGN:
|
|
|
|
return "=";
|
2013-06-14 03:09:55 +00:00
|
|
|
case TOK_CONTROL:
|
2018-12-30 16:37:23 +00:00
|
|
|
return "Device(" + data + ")";
|
|
|
|
case TOK_LITERAL:
|
|
|
|
return '\'' + data + '\'';
|
2018-12-30 22:06:29 +00:00
|
|
|
case TOK_VARIABLE:
|
|
|
|
return '$' + data;
|
2015-09-10 02:41:47 +00:00
|
|
|
case TOK_INVALID:
|
|
|
|
break;
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2015-09-10 02:41:47 +00:00
|
|
|
return "Invalid";
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class Lexer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
std::string expr;
|
|
|
|
std::string::iterator it;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2015-05-29 00:28:48 +00:00
|
|
|
Lexer(const std::string& expr_) : expr(expr_) { it = expr.begin(); }
|
2018-12-30 16:37:23 +00:00
|
|
|
|
|
|
|
bool FetchDelimString(std::string& value, char delim)
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
2013-07-12 17:26:09 +00:00
|
|
|
value = "";
|
2013-06-14 03:09:55 +00:00
|
|
|
while (it != expr.end())
|
|
|
|
{
|
|
|
|
char c = *it;
|
2014-02-12 15:00:34 +00:00
|
|
|
++it;
|
2018-12-30 16:37:23 +00:00
|
|
|
if (c == delim)
|
2013-06-14 03:09:55 +00:00
|
|
|
return true;
|
|
|
|
value += c;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
std::string FetchWordChars()
|
2018-12-30 18:38:02 +00:00
|
|
|
{
|
2018-12-30 22:06:29 +00:00
|
|
|
std::string word;
|
2018-12-30 18:38:02 +00:00
|
|
|
|
|
|
|
std::regex valid_name_char("[a-z0-9_]", std::regex_constants::icase);
|
|
|
|
|
|
|
|
while (it != expr.end() && std::regex_match(std::string(1, *it), valid_name_char))
|
|
|
|
{
|
2018-12-30 22:06:29 +00:00
|
|
|
word += *it;
|
2018-12-30 18:38:02 +00:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
return word;
|
2018-12-30 18:38:02 +00:00
|
|
|
}
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
Token GetUnaryFunction() { return Token(TOK_UNARY, FetchWordChars()); }
|
|
|
|
|
2018-12-30 16:37:23 +00:00
|
|
|
Token GetLiteral()
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
std::string value;
|
2018-12-30 16:37:23 +00:00
|
|
|
FetchDelimString(value, '\'');
|
|
|
|
return Token(TOK_LITERAL, value);
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
Token GetVariable() { return Token(TOK_VARIABLE, FetchWordChars()); }
|
|
|
|
|
2018-12-30 16:37:23 +00:00
|
|
|
Token GetFullyQualifiedControl()
|
|
|
|
{
|
|
|
|
std::string value;
|
|
|
|
FetchDelimString(value, '`');
|
|
|
|
return Token(TOK_CONTROL, value);
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 07:10:27 +00:00
|
|
|
Token GetBarewordsControl(char c)
|
|
|
|
{
|
|
|
|
std::string name;
|
|
|
|
name += c;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2014-08-30 20:44:28 +00:00
|
|
|
while (it != expr.end())
|
|
|
|
{
|
2013-06-14 07:10:27 +00:00
|
|
|
c = *it;
|
|
|
|
if (!isalpha(c))
|
|
|
|
break;
|
|
|
|
name += c;
|
2014-02-12 15:00:34 +00:00
|
|
|
++it;
|
2013-06-14 07:10:27 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 07:10:27 +00:00
|
|
|
ControlQualifier qualifier;
|
|
|
|
qualifier.control_name = name;
|
|
|
|
return Token(TOK_CONTROL, qualifier);
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
Token NextToken()
|
|
|
|
{
|
|
|
|
if (it == expr.end())
|
|
|
|
return Token(TOK_EOF);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
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 '!':
|
2018-12-30 18:38:02 +00:00
|
|
|
return GetUnaryFunction();
|
2013-06-14 06:52:07 +00:00
|
|
|
case '+':
|
|
|
|
return Token(TOK_ADD);
|
2018-12-30 17:51:12 +00:00
|
|
|
case '*':
|
|
|
|
return Token(TOK_MUL);
|
|
|
|
case '/':
|
|
|
|
return Token(TOK_DIV);
|
2018-12-30 19:16:28 +00:00
|
|
|
case '%':
|
|
|
|
return Token(TOK_MOD);
|
2018-12-30 22:06:29 +00:00
|
|
|
case '=':
|
|
|
|
return Token(TOK_ASSIGN);
|
2018-12-30 16:37:23 +00:00
|
|
|
case '\'':
|
|
|
|
return GetLiteral();
|
2018-12-30 22:06:29 +00:00
|
|
|
case '$':
|
|
|
|
return GetVariable();
|
2013-06-14 03:09:55 +00:00
|
|
|
case '`':
|
2013-06-14 07:10:27 +00:00
|
|
|
return GetFullyQualifiedControl();
|
2013-06-14 03:09:55 +00:00
|
|
|
default:
|
2013-06-14 07:10:27 +00:00
|
|
|
if (isalpha(c))
|
|
|
|
return GetBarewordsControl(c);
|
|
|
|
else
|
|
|
|
return Token(TOK_INVALID);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-02-28 09:58:03 +00:00
|
|
|
ParseStatus Tokenize(std::vector<Token>& tokens)
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
Token tok = NextToken();
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
if (tok.type == TOK_DISCARD)
|
|
|
|
continue;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
if (tok.type == TOK_INVALID)
|
|
|
|
{
|
2013-07-03 01:42:44 +00:00
|
|
|
tokens.clear();
|
2017-02-28 09:58:03 +00:00
|
|
|
return ParseStatus::SyntaxError;
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
tokens.push_back(tok);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
if (tok.type == TOK_EOF)
|
|
|
|
break;
|
|
|
|
}
|
2017-04-02 10:13:12 +00:00
|
|
|
return ParseStatus::Successful;
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-06-08 00:30:07 +00:00
|
|
|
class ControlExpression : public Expression
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
public:
|
2017-06-07 22:54:59 +00:00
|
|
|
// Keep a shared_ptr to the device so the control pointer doesn't become invalid
|
2018-12-30 22:06:29 +00:00
|
|
|
// TODO: This is causing devices to be destructed after backends are shutdown:
|
2017-06-07 22:54:59 +00:00
|
|
|
std::shared_ptr<Device> m_device;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-08 02:02:16 +00:00
|
|
|
explicit ControlExpression(ControlQualifier qualifier_) : qualifier(qualifier_) {}
|
2019-02-27 01:46:21 +00:00
|
|
|
ControlState GetValue() const override
|
|
|
|
{
|
2018-12-30 22:06:29 +00:00
|
|
|
if (!input)
|
2019-02-27 01:46:21 +00:00
|
|
|
return 0.0;
|
|
|
|
|
|
|
|
// Note: Inputs may return negative values in situations where opposing directions are
|
|
|
|
// activated. We clamp off the negative values here.
|
|
|
|
|
|
|
|
// FYI: Clamping values greater than 1.0 is purposely not done to support unbounded values in
|
|
|
|
// the future. (e.g. raw accelerometer/gyro data)
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
return std::max(0.0, input->GetState());
|
2019-02-27 01:46:21 +00:00
|
|
|
}
|
2017-06-07 22:56:49 +00:00
|
|
|
void SetValue(ControlState value) override
|
|
|
|
{
|
2018-12-30 22:06:29 +00:00
|
|
|
if (output)
|
|
|
|
output->SetState(value);
|
2017-06-07 22:56:49 +00:00
|
|
|
}
|
2018-12-30 22:06:29 +00:00
|
|
|
int CountNumControls() const override { return (input || output) ? 1 : 0; }
|
|
|
|
void UpdateReferences(ControlEnvironment& env) override
|
2017-06-08 01:48:17 +00:00
|
|
|
{
|
2018-12-30 22:06:29 +00:00
|
|
|
m_device = env.FindDevice(qualifier);
|
|
|
|
input = env.FindInput(qualifier);
|
|
|
|
output = env.FindOutput(qualifier);
|
2017-06-08 01:48:17 +00:00
|
|
|
}
|
2017-06-07 22:54:59 +00:00
|
|
|
operator std::string() const override { return "`" + static_cast<std::string>(qualifier) + "`"; }
|
2018-12-30 22:06:29 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
ControlQualifier qualifier;
|
|
|
|
Device::Input* input = nullptr;
|
|
|
|
Device::Output* output = nullptr;
|
2013-06-14 03:09:55 +00:00
|
|
|
};
|
|
|
|
|
2017-06-08 00:30:07 +00:00
|
|
|
class BinaryExpression : public Expression
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
TokenType op;
|
2017-06-08 00:30:07 +00:00
|
|
|
std::unique_ptr<Expression> lhs;
|
|
|
|
std::unique_ptr<Expression> rhs;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-08 00:30:07 +00:00
|
|
|
BinaryExpression(TokenType op_, std::unique_ptr<Expression>&& lhs_,
|
|
|
|
std::unique_ptr<Expression>&& rhs_)
|
2017-06-07 22:29:00 +00:00
|
|
|
: op(op_), lhs(std::move(lhs_)), rhs(std::move(rhs_))
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-02-26 07:04:16 +00:00
|
|
|
ControlState GetValue() const override
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
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);
|
2013-06-14 06:52:07 +00:00
|
|
|
case TOK_ADD:
|
2018-12-30 17:51:12 +00:00
|
|
|
return lhsValue + rhsValue;
|
|
|
|
case TOK_MUL:
|
|
|
|
return lhsValue * rhsValue;
|
|
|
|
case TOK_DIV:
|
|
|
|
{
|
|
|
|
const ControlState result = lhsValue / rhsValue;
|
|
|
|
return std::isinf(result) ? 0.0 : result;
|
|
|
|
}
|
2018-12-30 19:16:28 +00:00
|
|
|
case TOK_MOD:
|
|
|
|
{
|
|
|
|
const ControlState result = std::fmod(lhsValue, rhsValue);
|
|
|
|
return std::isnan(result) ? 0.0 : result;
|
|
|
|
}
|
2018-12-30 22:06:29 +00:00
|
|
|
case TOK_ASSIGN:
|
|
|
|
{
|
|
|
|
lhs->SetValue(rhsValue);
|
|
|
|
// TODO: Should this instead GetValue(lhs) ?
|
|
|
|
return rhsValue;
|
|
|
|
}
|
2013-06-14 03:09:55 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
return 0;
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
2015-07-30 10:47:02 +00:00
|
|
|
void SetValue(ControlState value) override
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
// Don't do anything special with the op we have.
|
|
|
|
// Treat "A & B" the same as "A | B".
|
|
|
|
lhs->SetValue(value);
|
|
|
|
rhs->SetValue(value);
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-02-26 07:04:16 +00:00
|
|
|
int CountNumControls() const override
|
|
|
|
{
|
|
|
|
return lhs->CountNumControls() + rhs->CountNumControls();
|
|
|
|
}
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
void UpdateReferences(ControlEnvironment& env) override
|
2017-06-08 01:48:17 +00:00
|
|
|
{
|
2018-12-30 22:06:29 +00:00
|
|
|
lhs->UpdateReferences(env);
|
|
|
|
rhs->UpdateReferences(env);
|
2017-06-08 01:48:17 +00:00
|
|
|
}
|
|
|
|
|
2017-02-26 07:04:16 +00:00
|
|
|
operator std::string() const override
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
return OpName(op) + "(" + (std::string)(*lhs) + ", " + (std::string)(*rhs) + ")";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-06-08 00:30:07 +00:00
|
|
|
class UnaryExpression : public Expression
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
public:
|
2018-12-30 18:38:02 +00:00
|
|
|
UnaryExpression(std::unique_ptr<Expression>&& inner_) : inner(std::move(inner_)) {}
|
|
|
|
|
|
|
|
int CountNumControls() const override { return inner->CountNumControls(); }
|
2018-12-30 22:06:29 +00:00
|
|
|
void UpdateReferences(ControlEnvironment& env) override { inner->UpdateReferences(env); }
|
2018-12-30 18:38:02 +00:00
|
|
|
|
|
|
|
operator std::string() const override
|
|
|
|
{
|
|
|
|
return "!" + GetFuncName() + "(" + (std::string)(*inner) + ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual std::string GetFuncName() const = 0;
|
|
|
|
|
2017-06-08 00:30:07 +00:00
|
|
|
std::unique_ptr<Expression> inner;
|
2018-12-30 18:38:02 +00:00
|
|
|
};
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2018-12-30 18:38:02 +00:00
|
|
|
// TODO: Return an oscillating value to make it apparent something was spelled wrong?
|
|
|
|
class UnaryUnknownExpression : public UnaryExpression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
UnaryUnknownExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_))
|
2017-06-07 22:29:00 +00:00
|
|
|
{
|
|
|
|
}
|
2018-12-30 18:38:02 +00:00
|
|
|
|
|
|
|
ControlState GetValue() const override { return 0.0; }
|
|
|
|
void SetValue(ControlState value) override {}
|
|
|
|
std::string GetFuncName() const override { return "Unknown"; }
|
|
|
|
};
|
|
|
|
|
|
|
|
class UnaryToggleExpression : public UnaryExpression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
UnaryToggleExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_))
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
2018-12-30 18:38:02 +00:00
|
|
|
ControlState GetValue() const override
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
2018-12-30 18:38:02 +00:00
|
|
|
const ControlState inner_value = inner->GetValue();
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2018-12-30 18:38:02 +00:00
|
|
|
if (inner_value < THRESHOLD)
|
|
|
|
{
|
|
|
|
m_released = true;
|
|
|
|
}
|
|
|
|
else if (m_released && inner_value > THRESHOLD)
|
|
|
|
{
|
|
|
|
m_released = false;
|
|
|
|
m_state ^= true;
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2018-12-30 18:38:02 +00:00
|
|
|
|
|
|
|
return m_state;
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
2018-12-30 18:38:02 +00:00
|
|
|
void SetValue(ControlState value) override {}
|
|
|
|
std::string GetFuncName() const override { return "Toggle"; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
static constexpr ControlState THRESHOLD = 0.5;
|
|
|
|
// eww:
|
|
|
|
mutable bool m_released{};
|
|
|
|
mutable bool m_state{};
|
2013-06-14 03:09:55 +00:00
|
|
|
};
|
|
|
|
|
2018-12-30 18:38:02 +00:00
|
|
|
class UnaryNotExpression : public UnaryExpression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
UnaryNotExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_)) {}
|
|
|
|
|
|
|
|
ControlState GetValue() const override { return 1.0 - inner->GetValue(); }
|
|
|
|
void SetValue(ControlState value) override { inner->SetValue(1.0 - value); }
|
|
|
|
std::string GetFuncName() const override { return ""; }
|
|
|
|
};
|
|
|
|
|
2018-12-30 19:16:28 +00:00
|
|
|
class UnarySinExpression : public UnaryExpression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
UnarySinExpression(std::unique_ptr<Expression>&& inner_) : UnaryExpression(std::move(inner_)) {}
|
|
|
|
|
|
|
|
ControlState GetValue() const override { return std::cos(inner->GetValue()); }
|
|
|
|
void SetValue(ControlState value) override {}
|
|
|
|
std::string GetFuncName() const override { return "Sin"; }
|
|
|
|
};
|
|
|
|
|
2018-12-30 18:38:02 +00:00
|
|
|
std::unique_ptr<UnaryExpression> MakeUnaryExpression(std::string name,
|
|
|
|
std::unique_ptr<Expression>&& inner_)
|
|
|
|
{
|
|
|
|
// Case insensitive matching.
|
|
|
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
|
|
|
|
|
|
|
if ("" == name)
|
|
|
|
return std::make_unique<UnaryNotExpression>(std::move(inner_));
|
|
|
|
else if ("toggle" == name)
|
|
|
|
return std::make_unique<UnaryToggleExpression>(std::move(inner_));
|
2018-12-30 19:16:28 +00:00
|
|
|
else if ("sin" == name)
|
|
|
|
return std::make_unique<UnarySinExpression>(std::move(inner_));
|
2018-12-30 18:38:02 +00:00
|
|
|
else
|
|
|
|
return std::make_unique<UnaryUnknownExpression>(std::move(inner_));
|
|
|
|
}
|
|
|
|
|
2018-12-30 16:37:23 +00:00
|
|
|
class LiteralExpression : public Expression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void SetValue(ControlState value) override
|
|
|
|
{
|
|
|
|
// Do nothing.
|
|
|
|
}
|
|
|
|
|
|
|
|
int CountNumControls() const override { return 1; }
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
void UpdateReferences(ControlEnvironment&) override
|
2018-12-30 16:37:23 +00:00
|
|
|
{
|
|
|
|
// Nothing needed.
|
|
|
|
}
|
|
|
|
|
2018-12-30 19:16:28 +00:00
|
|
|
operator std::string() const override { return '\'' + GetName() + '\''; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual std::string GetName() const = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
class LiteralReal : public LiteralExpression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
LiteralReal(ControlState value) : m_value(value) {}
|
|
|
|
|
|
|
|
ControlState GetValue() const override { return m_value; }
|
|
|
|
|
|
|
|
std::string GetName() const override { return ValueToString(m_value); }
|
2018-12-30 16:37:23 +00:00
|
|
|
|
|
|
|
private:
|
2018-12-30 19:16:28 +00:00
|
|
|
const ControlState m_value{};
|
2018-12-30 16:37:23 +00:00
|
|
|
};
|
|
|
|
|
2018-12-30 19:16:28 +00:00
|
|
|
// A +1.0 per second incrementing timer:
|
|
|
|
class LiteralTimer : public LiteralExpression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ControlState GetValue() const override
|
|
|
|
{
|
|
|
|
const auto ms =
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now().time_since_epoch());
|
|
|
|
// TODO: Will this roll over nicely?
|
|
|
|
return ms.count() / 1000.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetName() const override { return "Timer"; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
using Clock = std::chrono::steady_clock;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<LiteralExpression> MakeLiteralExpression(std::string name)
|
|
|
|
{
|
|
|
|
// Case insensitive matching.
|
|
|
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
|
|
|
|
|
|
|
// Check for named literals:
|
|
|
|
if ("timer" == name)
|
|
|
|
{
|
|
|
|
return std::make_unique<LiteralTimer>();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Assume it's a Real. If TryParse fails we'll just get a Zero.
|
|
|
|
ControlState val{};
|
|
|
|
TryParse(name, &val);
|
|
|
|
return std::make_unique<LiteralReal>(val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
class VariableExpression : public Expression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
VariableExpression(std::string name) : m_name(name) {}
|
|
|
|
|
|
|
|
ControlState GetValue() const override { return *m_value_ptr; }
|
|
|
|
|
|
|
|
void SetValue(ControlState value) override { *m_value_ptr = value; }
|
|
|
|
|
|
|
|
int CountNumControls() const override { return 1; }
|
|
|
|
|
|
|
|
void UpdateReferences(ControlEnvironment& env) override
|
|
|
|
{
|
|
|
|
m_value_ptr = env.GetVariablePtr(m_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
operator std::string() const override { return '$' + m_name; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
const std::string m_name;
|
|
|
|
ControlState* m_value_ptr{};
|
|
|
|
};
|
|
|
|
|
2017-06-08 01:29:02 +00:00
|
|
|
// This class proxies all methods to its either left-hand child if it has bound controls, or its
|
|
|
|
// right-hand child. Its intended use is for supporting old-style barewords expressions.
|
|
|
|
class CoalesceExpression : public Expression
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CoalesceExpression(std::unique_ptr<Expression>&& lhs, std::unique_ptr<Expression>&& rhs)
|
|
|
|
: m_lhs(std::move(lhs)), m_rhs(std::move(rhs))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ControlState GetValue() const override { return GetActiveChild()->GetValue(); }
|
2018-12-22 17:17:05 +00:00
|
|
|
void SetValue(ControlState value) override { GetActiveChild()->SetValue(value); }
|
2017-06-08 01:29:02 +00:00
|
|
|
|
|
|
|
int CountNumControls() const override { return GetActiveChild()->CountNumControls(); }
|
|
|
|
operator std::string() const override
|
|
|
|
{
|
|
|
|
return "Coalesce(" + static_cast<std::string>(*m_lhs) + ", " +
|
|
|
|
static_cast<std::string>(*m_rhs) + ')';
|
|
|
|
}
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
void UpdateReferences(ControlEnvironment& env) override
|
2017-06-08 01:48:17 +00:00
|
|
|
{
|
2018-12-30 22:06:29 +00:00
|
|
|
m_lhs->UpdateReferences(env);
|
|
|
|
m_rhs->UpdateReferences(env);
|
2017-06-08 01:48:17 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 01:29:02 +00:00
|
|
|
private:
|
|
|
|
const std::unique_ptr<Expression>& GetActiveChild() const
|
|
|
|
{
|
|
|
|
return m_lhs->CountNumControls() > 0 ? m_lhs : m_rhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<Expression> m_lhs;
|
|
|
|
std::unique_ptr<Expression> m_rhs;
|
|
|
|
};
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
std::shared_ptr<Device> ControlEnvironment::FindDevice(ControlQualifier qualifier) const
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
if (qualifier.has_device)
|
|
|
|
return container.FindDevice(qualifier.device_qualifier);
|
|
|
|
else
|
|
|
|
return container.FindDevice(default_device);
|
|
|
|
}
|
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
Device::Input* ControlEnvironment::FindInput(ControlQualifier qualifier) const
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
2016-06-25 19:46:39 +00:00
|
|
|
const std::shared_ptr<Device> device = FindDevice(qualifier);
|
2013-06-26 20:54:48 +00:00
|
|
|
if (!device)
|
2014-03-09 20:14:26 +00:00
|
|
|
return nullptr;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2018-12-30 22:06:29 +00:00
|
|
|
return device->FindInput(qualifier.control_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
Device::Output* ControlEnvironment::FindOutput(ControlQualifier qualifier) const
|
|
|
|
{
|
|
|
|
const std::shared_ptr<Device> device = FindDevice(qualifier);
|
|
|
|
if (!device)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return device->FindOutput(qualifier.control_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
ControlState* ControlEnvironment::GetVariablePtr(const std::string& name)
|
|
|
|
{
|
|
|
|
return &m_variables[name];
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
struct ParseResult
|
|
|
|
{
|
2017-06-08 00:30:07 +00:00
|
|
|
ParseResult(ParseStatus status_, std::unique_ptr<Expression>&& expr_ = {})
|
2017-06-07 22:29:00 +00:00
|
|
|
: status(status_), expr(std::move(expr_))
|
2017-06-07 22:08:55 +00:00
|
|
|
{
|
|
|
|
}
|
2017-06-07 22:29:00 +00:00
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseStatus status;
|
2017-06-08 00:30:07 +00:00
|
|
|
std::unique_ptr<Expression> expr;
|
2017-06-07 22:08:55 +00:00
|
|
|
};
|
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
class Parser
|
|
|
|
{
|
|
|
|
public:
|
2017-06-08 01:48:17 +00:00
|
|
|
explicit Parser(std::vector<Token> tokens_) : tokens(tokens_) { m_it = tokens.begin(); }
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult Parse() { return Toplevel(); }
|
2018-04-12 12:18:04 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
private:
|
|
|
|
std::vector<Token> tokens;
|
|
|
|
std::vector<Token>::iterator m_it;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
Token Chew() { return *m_it++; }
|
|
|
|
Token Peek() { return *m_it; }
|
|
|
|
bool Expects(TokenType type)
|
|
|
|
{
|
|
|
|
Token tok = Chew();
|
|
|
|
return tok.type == type;
|
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult Atom()
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
Token tok = Chew();
|
|
|
|
switch (tok.type)
|
|
|
|
{
|
|
|
|
case TOK_CONTROL:
|
2018-12-30 16:37:23 +00:00
|
|
|
{
|
|
|
|
ControlQualifier cq;
|
|
|
|
cq.FromString(tok.data);
|
|
|
|
return {ParseStatus::Successful, std::make_unique<ControlExpression>(cq)};
|
|
|
|
}
|
|
|
|
case TOK_LITERAL:
|
|
|
|
{
|
2018-12-30 19:16:28 +00:00
|
|
|
return {ParseStatus::Successful, MakeLiteralExpression(tok.data)};
|
2018-12-30 16:37:23 +00:00
|
|
|
}
|
2018-12-30 22:06:29 +00:00
|
|
|
case TOK_VARIABLE:
|
|
|
|
{
|
|
|
|
return {ParseStatus::Successful, std::make_unique<VariableExpression>(tok.data)};
|
|
|
|
}
|
2013-06-14 03:09:55 +00:00
|
|
|
case TOK_LPAREN:
|
2017-06-07 22:08:55 +00:00
|
|
|
return Paren();
|
2013-06-14 03:09:55 +00:00
|
|
|
default:
|
2017-06-07 22:08:55 +00:00
|
|
|
return {ParseStatus::SyntaxError};
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
bool IsUnaryExpression(TokenType type)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
2018-12-30 18:38:02 +00:00
|
|
|
case TOK_UNARY:
|
2013-06-14 03:09:55 +00:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult Unary()
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
if (IsUnaryExpression(Peek().type))
|
|
|
|
{
|
|
|
|
Token tok = Chew();
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult result = Atom();
|
|
|
|
if (result.status == ParseStatus::SyntaxError)
|
|
|
|
return result;
|
2018-12-30 18:38:02 +00:00
|
|
|
return {ParseStatus::Successful, MakeUnaryExpression(tok.data, std::move(result.expr))};
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
return Atom();
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
bool IsBinaryToken(TokenType type)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case TOK_AND:
|
|
|
|
case TOK_OR:
|
2013-06-14 06:52:07 +00:00
|
|
|
case TOK_ADD:
|
2018-12-30 17:51:12 +00:00
|
|
|
case TOK_MUL:
|
|
|
|
case TOK_DIV:
|
2018-12-30 19:16:28 +00:00
|
|
|
case TOK_MOD:
|
2018-12-30 22:06:29 +00:00
|
|
|
case TOK_ASSIGN:
|
2013-06-14 03:09:55 +00:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult Binary()
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult result = Unary();
|
|
|
|
if (result.status == ParseStatus::SyntaxError)
|
|
|
|
return result;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-08 00:30:07 +00:00
|
|
|
std::unique_ptr<Expression> expr = std::move(result.expr);
|
2013-06-14 03:09:55 +00:00
|
|
|
while (IsBinaryToken(Peek().type))
|
|
|
|
{
|
|
|
|
Token tok = Chew();
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult unary_result = Unary();
|
|
|
|
if (unary_result.status == ParseStatus::SyntaxError)
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
2017-06-07 22:08:55 +00:00
|
|
|
return unary_result;
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:29:00 +00:00
|
|
|
expr = std::make_unique<BinaryExpression>(tok.type, std::move(expr),
|
|
|
|
std::move(unary_result.expr));
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:29:00 +00:00
|
|
|
return {ParseStatus::Successful, std::move(expr)};
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult Paren()
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
// lparen already chewed
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult result = Toplevel();
|
|
|
|
if (result.status != ParseStatus::Successful)
|
|
|
|
return result;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-06-14 03:09:55 +00:00
|
|
|
if (!Expects(TOK_RPAREN))
|
|
|
|
{
|
2017-06-07 22:08:55 +00:00
|
|
|
return {ParseStatus::SyntaxError};
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
return result;
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-07 22:08:55 +00:00
|
|
|
ParseResult Toplevel() { return Binary(); }
|
2013-06-14 03:09:55 +00:00
|
|
|
};
|
|
|
|
|
2017-06-08 01:48:17 +00:00
|
|
|
static ParseResult ParseComplexExpression(const std::string& str)
|
2013-06-14 03:09:55 +00:00
|
|
|
{
|
|
|
|
Lexer l(str);
|
|
|
|
std::vector<Token> tokens;
|
2017-06-07 21:53:41 +00:00
|
|
|
ParseStatus tokenize_status = l.Tokenize(tokens);
|
|
|
|
if (tokenize_status != ParseStatus::Successful)
|
2017-06-08 00:30:07 +00:00
|
|
|
return {tokenize_status};
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-06-08 07:23:38 +00:00
|
|
|
return Parser(std::move(tokens)).Parse();
|
2013-06-14 03:09:55 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 01:48:17 +00:00
|
|
|
static std::unique_ptr<Expression> ParseBarewordExpression(const std::string& str)
|
2013-06-27 00:19:23 +00:00
|
|
|
{
|
|
|
|
ControlQualifier qualifier;
|
|
|
|
qualifier.control_name = str;
|
|
|
|
qualifier.has_device = false;
|
|
|
|
|
2017-06-08 01:48:17 +00:00
|
|
|
return std::make_unique<ControlExpression>(qualifier);
|
2017-06-08 01:29:02 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 01:48:17 +00:00
|
|
|
std::pair<ParseStatus, std::unique_ptr<Expression>> ParseExpression(const std::string& str)
|
2017-06-08 01:29:02 +00:00
|
|
|
{
|
|
|
|
if (StripSpaces(str).empty())
|
|
|
|
return std::make_pair(ParseStatus::EmptyExpression, nullptr);
|
|
|
|
|
2017-06-08 01:48:17 +00:00
|
|
|
auto bareword_expr = ParseBarewordExpression(str);
|
|
|
|
ParseResult complex_result = ParseComplexExpression(str);
|
2017-06-08 01:29:02 +00:00
|
|
|
|
|
|
|
if (complex_result.status != ParseStatus::Successful)
|
2014-08-30 20:44:28 +00:00
|
|
|
{
|
2017-06-08 01:29:02 +00:00
|
|
|
return std::make_pair(complex_result.status, std::move(bareword_expr));
|
2013-06-27 00:19:23 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 01:29:02 +00:00
|
|
|
auto combined_expr = std::make_unique<CoalesceExpression>(std::move(bareword_expr),
|
|
|
|
std::move(complex_result.expr));
|
|
|
|
return std::make_pair(complex_result.status, std::move(combined_expr));
|
2013-06-27 00:19:23 +00:00
|
|
|
}
|
2019-06-17 20:39:24 +00:00
|
|
|
} // namespace ciface::ExpressionParser
|