Commit c74c2caa authored by Axel Auweter's avatar Axel Auweter
Browse files

Added simple parser for virtual sensor expressions.

parent 67d39235
...@@ -14,6 +14,7 @@ OBJS = src/connection.o \ ...@@ -14,6 +14,7 @@ OBJS = src/connection.o \
src/sensorconfig.o \ src/sensorconfig.o \
src/sensorid.o \ src/sensorid.o \
src/unitconv.o \ src/unitconv.o \
src/virtualsensor.o \
src/c_api.o src/c_api.o
# List of public header files necessary to use this libray # List of public header files necessary to use this libray
...@@ -42,7 +43,7 @@ P = $(shell cd $(DCDBDEPLOYPATH)/lib/ && pwd) ...@@ -42,7 +43,7 @@ P = $(shell cd $(DCDBDEPLOYPATH)/lib/ && pwd)
.PHONY : check-target-env all clean install .PHONY : check-target-env all clean install
# Main Library Target # Main Library Target
$(TARGET): $(OBJS) $(TARGET): $(OBJS) .header-check
@if [ "$(OS)" = "Darwin" ]; then \ @if [ "$(OS)" = "Darwin" ]; then \
echo "Linking library in Mac OS style: $(TARGET)"; \ echo "Linking library in Mac OS style: $(TARGET)"; \
$(CXX) $(CXXFLAGS) $(DLFLAGS) -o $(TARGET) $(OBJS) $(LIBS); \ $(CXX) $(CXXFLAGS) $(DLFLAGS) -o $(TARGET) $(OBJS) $(LIBS); \
...@@ -68,6 +69,11 @@ check-target-env: ...@@ -68,6 +69,11 @@ check-target-env:
fi; \ fi; \
fi fi
# Header check
.header-check: $(PUBHEADERS) $(PRIVHEADERS)
@touch .header-check
@touch $(OBJS:.o=.cpp)
# Build the documentation # Build the documentation
docs: $(PUBHEADERS) $(PRIVHEADERS) $(SRC) docs: $(PUBHEADERS) $(PRIVHEADERS) $(SRC)
@echo "Creating documentation..." @echo "Creating documentation..."
...@@ -75,6 +81,7 @@ docs: $(PUBHEADERS) $(PRIVHEADERS) $(SRC) ...@@ -75,6 +81,7 @@ docs: $(PUBHEADERS) $(PRIVHEADERS) $(SRC)
# Clean everything # Clean everything
clean: clean-docs clean: clean-docs
rm -f .header-check
rm -f $(OBJS) $(TARGET) rm -f $(OBJS) $(TARGET)
# Clean the documentation # Clean the documentation
......
/*
* virtualsensor.h
*
* Created on: Jan 15, 2016
* Author: Axel Auweter
*/
/**
* @file
* @brief This file contains classes for handling virtual sensors.
*/
#include <stdexcept>
#include <string>
#ifndef DCDB_VIRTUAL_SENSOR_H
#define DCDB_VIRTUAL_SENSOR_H
namespace DCDB {
/**
* @brief Exception class for parsing Virtual Sensor Expressions
*
* Exceptions of this type are thrown whenever the parser for virtual
* sensor expressions encounters a syntax error. What() tries to
* provide the user with a hint on the location where the error was
* encountered while processing the expression string.
*/
class VSExpressionParserException : public std::runtime_error
{
public:
VSExpressionParserException(const std::string& where) : runtime_error("Error parsing expression at: "), where_(where) {}
virtual const char* what() const throw();
private:
static std::string msg_;
std::string where_;
};
/* Forward declare VirtualSensor::VSensorImpl */
namespace VirtualSensor{
class VSensorImpl;
}
/**
* @brief Class for evaluating Virtual Sensors
*
* TODO: Implement this :)
*/
class VSensor
{
protected:
VirtualSensor::VSensorImpl *impl;
public:
VSensor(std::string expr);
virtual ~VSensor();
};
} /* End of namespace DCDB */
#endif /* DCDB_VIRTUAL_SENSOR_H */
/*
* virtualsensor_internal.h
*
* Created on: Jan 18, 2016
* Author: Axel Auweter
*/
/**
* @file
* @brief This file contains internal classes for handling virtual sensors.
*/
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#ifndef DCDB_VIRTUAL_SENSOR_INTERNAL_H
#define DCDB_VIRTUAL_SENSOR_INTERNAL_H
namespace DCDB {
namespace VirtualSensor {
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
/**
* @brief Definitions for objects in the Virtual Sensor expression's AST.
*/
namespace AST {
/**
* @brief The NIL object.
*
* The NIL object represents a no-op or empty operand in the AST.
*/
struct Nil {};
/**
* @brief The SIGNED object.
*
* The SIGNED object represents a positive or negative sign preceding an operand in the AST.
*/
struct Signd;
/**
* @brief The OPSEQ object.
*
* The OPSEQ object represents a sequence of operations in the AST.
*/
struct Opseq;
/**
* @brief The OP object.
*
* The OP object represents a simple operation in the AST.
*/
struct Op;
typedef boost::variant<
Nil,
unsigned int,
boost::recursive_wrapper<Signd>,
boost::recursive_wrapper<Opseq>
> Operand;
struct Signd {
char sgn;
Operand oprnd;
};
struct Opseq {
Operand frst;
std::list<Op> rst;
};
struct Op {
char oprtr;
Operand oprnd;
};
} /* End of namespace AST */
} /* End of namespace VirtualSensor */
} /* End of namespace DCDB */
/*
* Create Random Access Sequence for AST Objects.
* These must be created at the top of namespaces but obviously after defining the structs.
*/
BOOST_FUSION_ADAPT_STRUCT (
DCDB::VirtualSensor::AST::Signd,
(char, sgn)
(DCDB::VirtualSensor::AST::Operand, oprnd)
)
BOOST_FUSION_ADAPT_STRUCT (
DCDB::VirtualSensor::AST::Opseq,
(DCDB::VirtualSensor::AST::Operand, frst)
(std::list<DCDB::VirtualSensor::AST::Op>, rst)
)
BOOST_FUSION_ADAPT_STRUCT (
DCDB::VirtualSensor::AST::Op,
(char, oprtr)
(DCDB::VirtualSensor::AST::Operand, oprnd)
)
namespace DCDB {
namespace VirtualSensor {
/**
* @brief The ExpressionGrammar struct holds the grammar definition for
* the arithmetic expressions that describe virtual sensors.
*
* Since we're using the Boost Spirit framework's Qi parser generator,
* we define the grammar using the qi syntax and built-in qi parsers
* (e.g. qi::uint_ or qi:hex).
*/
template <typename Iterator>
struct ExpressionGrammar : qi::grammar<Iterator, AST::Opseq(), ascii::space_type>
{
ExpressionGrammar() : ExpressionGrammar::base_type(expression)
{
expression =
term
>> *( (qi::char_('+') >> term)
| (qi::char_('-') >> term)
)
;
term =
factor
>> *( (qi::char_('*') >> factor)
| (qi::char_('/') >> factor)
)
;
factor =
("0x" >> qi::hex)
| qi::uint_
| '(' >> expression >> ')'
| (qi::char_('-') >> factor)
| (qi::char_('+') >> factor)
;
}
qi::rule<Iterator, AST::Opseq(), ascii::space_type> expression;
qi::rule<Iterator, AST::Opseq(), ascii::space_type> term;
qi::rule<Iterator, AST::Operand(), ascii::space_type> factor;
};
class VSensorImpl
{
protected:
VirtualSensor::AST::Opseq opseq;
void validateExpression(std::string expr);
void dumpAST();
public:
VSensorImpl(std::string expr);
virtual ~VSensorImpl();
};
} /* End of namesapce VirtualSensor */
} /* End of namespace DCDB */
#endif /* DCDB_VIRTUAL_SENSOR_INTERNAL_H */
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "sensorconfig_internal.h" #include "sensorconfig_internal.h"
#include "dcdbglobals.h" #include "dcdbglobals.h"
#include "dcdbendian.h" #include "dcdbendian.h"
#include "virtualsensor.h"
using namespace DCDB; using namespace DCDB;
...@@ -311,7 +312,14 @@ SCError SensorConfigImpl::publishVirtualSensor(std::string publicName, std::stri ...@@ -311,7 +312,14 @@ SCError SensorConfigImpl::publishVirtualSensor(std::string publicName, std::stri
return SC_INVALIDSESSION; return SC_INVALIDSESSION;
} }
/* TODO: Validate vSensorExpression */ /* Validate vSensorExpression */
try {
VSensor vsensor(vSensorExpression);
}
catch (std::exception& e) {
std::cout << e.what();
return SC_INVALIDEXPRESSION;
}
/* Check if the vSensorId is valid */ /* Check if the vSensorId is valid */
SensorId vSensor; SensorId vSensor;
......
/*
* virtualsensor.cpp
*
* Created on: Jan 15, 2016
* Author: Axel Auweter
*/
#include "virtualsensor.h"
#include "virtualsensor_internal.h"
namespace DCDB {
/*
* Implementations for VSExpressionParserException class.
*/
std::string VSExpressionParserException::msg_;
const char* VSExpressionParserException::what() const throw() {
msg_ = runtime_error::what();
msg_ += where_;
msg_ += "\n";
return msg_.c_str();
}
/*
* Implementations for VSensor class.
*/
VSensor::VSensor(std::string expr) {
impl = new VirtualSensor::VSensorImpl(expr);
}
VSensor::~VSensor() {
if (impl) {
delete impl;
}
}
namespace VirtualSensor {
/*
* Implementations for VSensorImpl class.
*/
void VSensorImpl::validateExpression(std::string expr)
{
/* Try to generate AST */
typedef std::string::const_iterator StringIterator;
typedef ExpressionGrammar<StringIterator> Grammar;
ascii::space_type space;
Grammar grammar;
StringIterator it = expr.begin();
StringIterator end = expr.end();
bool success = phrase_parse(it, end, grammar, space, opseq);
if ((!success) || (it != end)) {
std::string rest(it, end);
throw VSExpressionParserException(rest);
}
/* Success - opseq now represents the top level of our AST */
}
void VSensorImpl::dumpAST()
{
/* Declare a struct describing the action for each object in the AST when it comes to printing */
struct ASTPrinter {
typedef void result_type;
void operator()(AST::Nil) const {}
void operator()(unsigned int n) const { std::cout << n; }
void operator()(AST::Op const& x) const {
boost::apply_visitor(*this, x.oprnd);
switch (x.oprtr) {
case '+': std::cout << " add"; break;
case '-': std::cout << " sub"; break;
case '*': std::cout << " mul"; break;
case '/': std::cout << " div"; break;
}
}
void operator()(AST::Signd const& x) const {
boost::apply_visitor(*this, x.oprnd);
switch (x.sgn) {
case '-': std::cout << " neg"; break;
case '+': std::cout << " pos"; break;
}
}
void operator()(AST::Opseq const& x) const {
boost::apply_visitor(*this, x.frst);
BOOST_FOREACH(AST::Op const& o, x.rst) {
std::cout << ' ';
(*this)(o);
}
}
};
ASTPrinter printer;
printer(opseq);
std::cout << std::endl;
}
VSensorImpl::VSensorImpl(std::string expr)
{
/* Check if the expression is valid */
validateExpression(expr);
/* Dump AST */
dumpAST();
}
VSensorImpl::~VSensorImpl()
{
}
} /* End of namespace VirtualSensor */
} /* End of namespace DCDB */
...@@ -216,7 +216,8 @@ void SensorAction::doVCreateSensor(const char* publicName, const char* expressio ...@@ -216,7 +216,8 @@ void SensorAction::doVCreateSensor(const char* publicName, const char* expressio
switch(err) { switch(err) {
case DCDB::SC_INVALIDEXPRESSION: case DCDB::SC_INVALIDEXPRESSION:
std::cout << "Invalid expression: " << expression << std::endl; // We should get a proper error message in the exception handler.
//std::cout << "Invalid expression: " << expression << std::endl;
break; break;
case DCDB::SC_INVALIDVSENSORID: case DCDB::SC_INVALIDVSENSORID:
std::cout << "Invalid vsensorid: " << vSensorId << std::endl; std::cout << "Invalid vsensorid: " << vSensorId << std::endl;
......
Supports Markdown
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