Skip to content

Commit

Permalink
Adds lambda rule and lambda rule parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
erikvanhamme committed Jul 4, 2020
1 parent d2feb29 commit d95d1a2
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 13 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ SOURCES := \
wayfire/lexer/symbol.cpp \
wayfire/parser/action_parser.cpp \
wayfire/parser/condition_parser.cpp \
wayfire/parser/lambda_rule_parser.cpp \
wayfire/parser/rule_parser.cpp \
wayfire/rule/lambda_rule.cpp \
wayfire/rule/rule.cpp \
wayfire/variant.cpp \
main.cpp \
Expand Down
64 changes: 51 additions & 13 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "wayfire/action/action_interface.hpp"
#include "wayfire/condition/access_interface.hpp"
#include "wayfire/variant.hpp"
#include "wayfire/parser/lambda_rule_parser.hpp"
#include "wayfire/rule/lambda_rule.hpp"
#include <algorithm>
#include <iostream>
#include <memory>
Expand Down Expand Up @@ -135,6 +137,26 @@ test_action_interface_t::~test_action_interface_t()
{
}

bool if_function(void *arg)
{
int *a = reinterpret_cast<int *>(arg);
if (a == nullptr) {
return true;
}
std::cout << "IF_FUNCTION! a=" << *a << std::endl;
return false;
}

bool else_function(void *arg)
{
int *a = reinterpret_cast<int *>(arg);
if (a == nullptr) {
return true;
}
std::cout << "ELSE_FUNCTION! a=" << *a << std::endl;
return false;
}

int main()
{
/*
Expand Down Expand Up @@ -201,27 +223,43 @@ int main()
data->title = "Alacritty";

test_access_interface_t access_interface(data);
test_action_interface_t action_interface(data);
// test_action_interface_t action_interface(data);

std::vector<std::string> text = {
"property_a equals 4",
"all",
"none",
// std::vector<std::string> text = {
// "property_a equals 4",
// "all",
// "none",
// "then",
"title contains \" Alacritty \""
};
// "title contains \" Alacritty \""
// };

wf::lexer_t lexer;
wf::condition_parser_t parser;
// wf::lexer_t lexer;
// wf::condition_parser_t parser;

std::vector<std::shared_ptr<wf::condition_t>> conditions;
// std::vector<std::shared_ptr<wf::condition_t>> conditions;

for (const auto &t : text)
// for (const auto &t : text)
// {
// lexer.reset(t);
// conditions.push_back(parser.parse(lexer));
// }

std::string text = "on created if title contains \"Alacritty\" & property_a equals 4";
wf::lambda_rule_parser_t parser;

auto rule = parser.parse(text, &if_function, &else_function);

std::cout << "rule: " << rule->to_string() << std::endl;

int a = 14;

auto error = rule->apply("created", access_interface, &a);
if (error)
{
lexer.reset(t);
conditions.push_back(parser.parse(lexer));
std::cout << "Error!" << std::endl;
}


/*
lexer.reset(text_reverse);
Expand Down
3 changes: 3 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ sources = [
'wayfire/lexer/symbol.cpp',
'wayfire/parser/action_parser.cpp',
'wayfire/parser/condition_parser.cpp',
'wayfire/parser/lambda_rule_parser.cpp',
'wayfire/parser/rule_parser.cpp',
'wayfire/rule/lambda_rule.cpp',
'wayfire/rule/rule.cpp',
'wayfire/variant.cpp',
]
Expand Down Expand Up @@ -77,6 +79,7 @@ headers_parser = [
]

headers_rule = [
'wayfire/rule/lambda_rule.hpp',
'wayfire/rule/rule.hpp',
]

Expand Down
86 changes: 86 additions & 0 deletions wayfire/parser/lambda_rule_parser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include "wayfire/parser/lambda_rule_parser.hpp"

#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>

#include "wayfire/lexer/lexer.hpp"
#include "wayfire/parser/condition_parser.hpp"
#include "wayfire/rule/lambda_rule.hpp"

namespace wf
{

std::shared_ptr<lambda_rule_t> lambda_rule_parser_t::parse(const std::string &text, lambda_t if_lambda, lambda_t else_lambda)
{
lexer_t lexer(text);
return parse(lexer, if_lambda, else_lambda);
}

std::shared_ptr<lambda_rule_t> lambda_rule_parser_t::parse(lexer_t &lexer, lambda_t if_lambda, lambda_t else_lambda)
{
std::string signal;
std::shared_ptr<condition_t> condition;

try
{
// Expect the first symbol to be the 'on' keyword.
auto symbol = lexer.parse_symbol();
if ((symbol.type != symbol_t::type_t::KEYWORD) || (get_string(symbol.value) != "on"))
{
throw std::runtime_error("Lambda rule parser error. Expected 'on' keyword.");
}

// Expect a signal next.
symbol = lexer.parse_symbol();
if (symbol.type != symbol_t::type_t::SIGNAL)
{
throw std::runtime_error("Lambda rule parser error. Expected signal.");
}
signal = get_string(symbol.value);

// Expect the 'if' keyword next.
symbol = lexer.parse_symbol();
if ((symbol.type != symbol_t::type_t::KEYWORD) || (get_string(symbol.value) != "if"))
{
throw std::runtime_error("Lambda rule parser error. Expected 'if' keyword.");
}

// Delegate to logical condition parser.
condition = condition_parser_t().parse(lexer);

// Expect the lexer to be at the end - 1.
lexer.parse_symbol();
symbol = lexer.parse_symbol();
if (symbol.type != symbol_t::type_t::END)
{
std::string error = "Lambda rule parser error. Unexpected symbol: ";
error.append(to_string(symbol));
throw std::runtime_error(error);
}
}
catch (std::runtime_error &e)
{
std::cout << "Malformed input:" << std::endl;
std::cout << lexer.text() << std::endl;
std::string pad;
auto pos = lexer.current_symbol_position();
for (std::size_t i = 0; i < pos; ++i)
{
pad.append(" ");
}
pad.append("^ ");
std::cout << pad << e.what() << std::endl;
}

auto rule = std::make_shared<lambda_rule_t>(signal, condition);
if (rule != nullptr)
{
rule->setIfLambda(if_lambda);
rule->setElseLambda(else_lambda);
}
return rule;
}

} // End namespace wf.
53 changes: 53 additions & 0 deletions wayfire/parser/lambda_rule_parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#ifndef LAMBDA_RULE_PARSER_HPP
#define LAMBDA_RULE_PARSER_HPP

#include <memory>
#include <string>

#include "wayfire/rule/lambda_rule.hpp"

namespace wf
{

class lexer_t;

/**
* @brief The lambda_rule_parser_t class will parse a rule_t from a lexer_t instance.
*/
class lambda_rule_parser_t
{
public:
/**
* @brief parse Convenience method. Takes the text and makes a lexer_t instance which is then used to parse the lambda_rule_t.
*
* This method is convenient, but not recommended if you have multiple rules to parse. In the multiple rules case,
* it is smarter to make 1 lexer_t instance outside and reset it to a new text, thus reusing the lexer_t instance.
* Reuse is good for the environment!
*
* This method will throw std::runtime_error in case the text is malformed.
*
* @param[in] text The text to parse a lambda_rule_t from.
* @param[in] if_lambda The lambda method to execute if the condition holds.
* @param[in] else_lambda The lambda method to execute if the condition does not hold.
*
* @return The parsed rule.
*/
std::shared_ptr<lambda_rule_t> parse(const std::string &text, lambda_t if_lambda, lambda_t else_lambda);

/**
* @brief parse Takes the lexer_t instance and parses a lambda_rule_t from it.
*
* This method will throw std::runtime_error incase the lexer_t instance has malformed text in it.
*
* @param[in] lexer The lexer_t instance to parse the lambda_rule_t from.
* @param[in] if_lambda The lambda method to execute if the condition holds.
* @param[in] else_lambda The lambda method to execute if the condition does not hold.
*
* @return Shared pointer to the lambda_rule_t instance parsed from the text.
*/
std::shared_ptr<lambda_rule_t> parse(lexer_t &lexer, lambda_t if_lambda, lambda_t else_lambda);
};

} // End namespace wf,

#endif // LAMBDA_RULE_PARSER_HPP
98 changes: 98 additions & 0 deletions wayfire/rule/lambda_rule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include "wayfire/rule/lambda_rule.hpp"
#include "wayfire/condition/condition.hpp"
#include <cstddef>
#include <functional>
#include <memory>
#include <sstream>
#include <string>

namespace wf
{

lambda_rule_t::lambda_rule_t(const std::string &signal, std::shared_ptr<condition_t> condition) :
_signal(signal), _condition(condition)
{
}

void lambda_rule_t::setIfLambda(lambda_t if_lambda)
{
_if_lambda = if_lambda;
}

void lambda_rule_t::setElseLambda(lambda_t else_lambda)
{
_else_lambda = else_lambda;
}

bool lambda_rule_t::apply(const std::string &signal, access_interface_t &access, void *argument)
{
if ((signal.empty()) || (_condition == nullptr) || (_if_lambda == nullptr))
{
return true;
}

bool error = false;
if (signal == _signal)
{
auto check_result = _condition->evaluate(access, error);
if (!error)
{
if (check_result)
{
error = _if_lambda(argument);
}
else
{
if (_else_lambda != nullptr)
{
error = _else_lambda(argument);
}
}

}
}

return error;
}

std::string lambda_rule_t::to_string() const
{
std::string out = "lambda rule: [signal: ";
out.append(_signal).append(", condition: ");
if (_condition)
{
out.append(_condition->to_string());
}
else
{
out.append("nullptr");
}
out.append(", if_lambda: ");
if (_if_lambda)
{
auto uint_ptr = reinterpret_cast<std::uintptr_t>(&_if_lambda);
std::stringstream ss;
ss << "0x" << std::hex << uint_ptr;
out.append(ss.str());
}
else
{
out.append("nullptr");
}
out.append(", else_lambda: ");
if (_else_lambda)
{
auto uint_ptr = reinterpret_cast<std::uintptr_t>(&_else_lambda);
std::stringstream ss;
ss << "0x" << std::hex << uint_ptr;
out.append(ss.str());
}
else
{
out.append("nullptr");
}
out.append("]");
return out;
}

} // End namespace wf.
Loading

0 comments on commit d95d1a2

Please sign in to comment.