aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/rfnoc/nocscript/parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/rfnoc/nocscript/parser.cpp')
-rw-r--r--host/lib/rfnoc/nocscript/parser.cpp357
1 files changed, 0 insertions, 357 deletions
diff --git a/host/lib/rfnoc/nocscript/parser.cpp b/host/lib/rfnoc/nocscript/parser.cpp
deleted file mode 100644
index 8ef1b7f44..000000000
--- a/host/lib/rfnoc/nocscript/parser.cpp
+++ /dev/null
@@ -1,357 +0,0 @@
-//
-// Copyright 2015 Ettus Research LLC
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "parser.hpp"
-#include <uhd/utils/cast.hpp>
-#include <boost/assign.hpp>
-#include <boost/bind.hpp>
-#include <boost/format.hpp>
-#include <boost/make_shared.hpp>
-#include <boost/spirit/include/lex_lexertl.hpp>
-#include <sstream>
-#include <stack>
-
-using namespace uhd::rfnoc::nocscript;
-namespace lex = boost::spirit::lex;
-
-class parser_impl : public parser
-{
-public:
- /******************************************************************
- * Structors TODO make them protected
- *****************************************************************/
- parser_impl(function_table::sptr ftable,
- expression_variable::type_getter_type var_type_getter,
- expression_variable::value_getter_type var_value_getter)
- : _ftable(ftable)
- , _var_type_getter(var_type_getter)
- , _var_value_getter(var_value_getter)
- {
- // nop
- }
-
- virtual ~parser_impl() {}
-
-
- /******************************************************************
- * Parsing
- *****************************************************************/
- //! List of parser tokens
- enum token_ids {
- ID_WHITESPACE = lex::min_token_id + 42,
- ID_KEYWORD,
- ID_ARG_SEP,
- ID_PARENS_OPEN,
- ID_PARENS_CLOSE,
- ID_VARIABLE,
- ID_LITERAL_DOUBLE,
- ID_LITERAL_INT,
- ID_LITERAL_HEX,
- ID_LITERAL_STR,
- ID_LITERAL_VECTOR_INT
- };
-
- //! The Lexer object used for NocScript
- template <typename Lexer> struct ns_lexer : lex::lexer<Lexer>
- {
- ns_lexer()
- {
- this->self.add("\\s+", ID_WHITESPACE)(",", ID_ARG_SEP)(
- "[A-Z][A-Z0-9_]*", ID_KEYWORD)("\\(", ID_PARENS_OPEN)(
- "\\)", ID_PARENS_CLOSE)("\\$[a-z][a-z0-9_]*", ID_VARIABLE)(
- "-?\\d+\\.\\d+", ID_LITERAL_DOUBLE)("-?\\d+", ID_LITERAL_INT)(
- "0x[0-9A-F]+", ID_LITERAL_HEX)("\\\"[^\\\"]*\\\"", ID_LITERAL_STR)(
- "'[^']*'", ID_LITERAL_STR) // both work
- ("\\[[0-9]\\]", ID_LITERAL_VECTOR_INT);
- }
- };
-
-private:
- struct grammar_props
- {
- function_table::sptr ftable;
- expression_variable::type_getter_type var_type_getter;
- expression_variable::value_getter_type var_value_getter;
-
- //! Store the last keyword
- std::string function_name;
- std::string error;
- std::stack<expression_container::sptr> expr_stack;
-
- grammar_props(function_table::sptr ftable_,
- expression_variable::type_getter_type var_type_getter_,
- expression_variable::value_getter_type var_value_getter_)
- : ftable(ftable_)
- , var_type_getter(var_type_getter_)
- , var_value_getter(var_value_getter_)
- , function_name("")
- {
- UHD_ASSERT_THROW(expr_stack.empty());
- // Push an empty container to the stack to hold the result
- expr_stack.push(expression_container::make());
- }
-
- expression::sptr get_result()
- {
- UHD_ASSERT_THROW(expr_stack.size() == 1);
- return expr_stack.top();
- }
- };
-
- //! This isn't strictly a grammar, as it also includes semantic
- // actions etc. I'm not going to spend ages thinking of a better
- // name at this point.
- struct grammar
- {
- // Implementation detail specific to boost::bind (see Boost::Spirit
- // examples)
- typedef bool result_type;
-
- static const int VALID_COMMA = 0x1;
- static const int VALID_PARENS_OPEN = 0x2;
- static const int VALID_PARENS_CLOSE = 0x4;
- static const int VALID_EXPRESSION = 0x8 + 0x02;
- static const int VALID_OPERATOR = 0x10;
-
- // !This function operator gets called for each of the matched tokens.
- template <typename Token>
- bool operator()(Token const& t, grammar_props& P, int& next_valid_state) const
- {
- //! This is totally not how Boost::Spirit is meant to be used,
- // as there's token types etc. But for now let's just convert
- // every token to a string, and then handle it as such.
- std::stringstream sstr;
- sstr << t.value();
- std::string val = sstr.str();
- // std::cout << "VAL: " << val << std::endl;
- // std::cout << "Next valid states:\n"
- //<< boost::format("VALID_COMMA [%s]\n") % ((next_valid_state & 0x1) ?
- //"x" : " ")
- //<< boost::format("VALID_PARENS_OPEN [%s]\n") % ((next_valid_state & 0x2) ?
- //"x" : " ")
- //<< boost::format("VALID_PARENS_CLOSE [%s]\n") % ((next_valid_state & 0x4) ?
- //"x" : " ")
- //<< boost::format("VALID_EXPRESSION [%s]\n") % ((next_valid_state & (0x8 +
- //0x02)) ? "x" : " ")
- //<< boost::format("VALID_OPERATOR [%s]\n") % ((next_valid_state & 0x10)
- //? "x" : " ")
- //<< std::endl;
-
- switch (t.id()) {
- case ID_WHITESPACE:
- // Ignore
- break;
-
- case ID_KEYWORD:
- // Ambiguous, could be an operator (AND, OR) or a function name (ADD,
- // MULT...). So first, check which it is:
- if (val == "AND" or val == "OR") {
- if (not(next_valid_state & VALID_OPERATOR)) {
- P.error = str(boost::format("Unexpected operator: %s") % val);
- return false;
- }
- next_valid_state = VALID_EXPRESSION;
- try {
- if (val == "AND") {
- P.expr_stack.top()->set_combiner_safe(
- expression_container::COMBINE_AND);
- } else if (val == "OR") {
- P.expr_stack.top()->set_combiner_safe(
- expression_container::COMBINE_OR);
- }
- } catch (const uhd::syntax_error&) {
- P.error = str(boost::format("Operator %s is mixing operator "
- "types within this container.")
- % val);
- }
- // Right now, we can't have multiple operator types within a
- // container. We might be able to change that, if there's enough
- // demand. Either we keep track of multiple operators, or we open
- // a new container. In the latter case, we'd need a way of keeping
- // track of those containers, so it's a bit tricky.
- break;
- }
- // If it's not a keyword, it has to be a function, so check the
- // function table:
- if (not(next_valid_state & VALID_EXPRESSION)) {
- P.error = str(boost::format("Unexpected expression: %s") % val);
- return false;
- }
- if (not P.ftable->function_exists(val)) {
- P.error = str(boost::format("Unknown function: %s") % val);
- return false;
- }
- P.function_name = val;
- next_valid_state = VALID_PARENS_OPEN;
- break;
-
- // Every () creates a new container, either a raw container or
- // a function.
- case ID_PARENS_OPEN:
- if (not(next_valid_state & VALID_PARENS_OPEN)) {
- P.error = str(boost::format("Unexpected parentheses."));
- return false;
- }
- if (not P.function_name.empty()) {
- // We've already checked the function name exists
- P.expr_stack.push(
- expression_function::make(P.function_name, P.ftable));
- P.function_name.clear();
- } else {
- P.expr_stack.push(expression_container::make());
- }
- // Push another empty container to hold the first element/argument
- // in this container:
- P.expr_stack.push(expression_container::make());
- next_valid_state = VALID_EXPRESSION | VALID_PARENS_CLOSE;
- break;
-
- case ID_PARENS_CLOSE: {
- if (not(next_valid_state & VALID_PARENS_CLOSE)) {
- P.error = str(boost::format("Unexpected parentheses."));
- return false;
- }
- if (P.expr_stack.size() < 2) {
- P.error = str(boost::format("Unbalanced closing parentheses."));
- return false;
- }
- // First pop the last expression inside the parentheses,
- // if it's not empty, add it to the top container (this also avoids
- // adding arguments to functions if none were provided):
- expression_container::sptr c = P.expr_stack.top();
- P.expr_stack.pop();
- if (not c->empty()) {
- P.expr_stack.top()->add(c);
- }
- // At the end of (), either a function or container is complete,
- // so pop that and add it to its top container:
- expression_container::sptr c2 = P.expr_stack.top();
- P.expr_stack.pop();
- P.expr_stack.top()->add(c2);
- next_valid_state = VALID_OPERATOR | VALID_COMMA | VALID_PARENS_CLOSE;
- } break;
-
- case ID_ARG_SEP: {
- if (not(next_valid_state & VALID_COMMA)) {
- P.error = str(boost::format("Unexpected comma."));
- return false;
- }
- next_valid_state = VALID_EXPRESSION;
- // If stack size is 1, we're on the base container, which means we
- // simply string stuff.
- if (P.expr_stack.size() == 1) {
- break;
- }
- // Otherwise, a ',' always means we add the previous expression to
- // the current container:
- expression_container::sptr c = P.expr_stack.top();
- P.expr_stack.pop();
- P.expr_stack.top()->add(c);
- // It also means another expression is following, so create another
- // empty container for that:
- P.expr_stack.push(expression_container::make());
- } break;
-
- // All the atomic expressions just get added to the current container:
-
- case ID_VARIABLE: {
- if (not(next_valid_state & VALID_EXPRESSION)) {
- P.error = str(boost::format("Unexpected expression."));
- return false;
- }
- expression_variable::sptr v = expression_variable::make(
- val, P.var_type_getter, P.var_value_getter);
- P.expr_stack.top()->add(v);
- next_valid_state = VALID_OPERATOR | VALID_COMMA | VALID_PARENS_CLOSE;
- } break;
-
- default:
- // If we get here, we assume it's a literal expression
- {
- if (not(next_valid_state & VALID_EXPRESSION)) {
- P.error = str(boost::format("Unexpected expression."));
- return false;
- }
- expression::type_t token_type;
- switch (t.id()) { // A map lookup would be more elegant, but we'd
- // need a nicer C++ for that
- case ID_LITERAL_DOUBLE:
- token_type = expression::TYPE_DOUBLE;
- break;
- case ID_LITERAL_INT:
- token_type = expression::TYPE_INT;
- break;
- case ID_LITERAL_HEX:
- token_type = expression::TYPE_INT;
- break;
- case ID_LITERAL_STR:
- token_type = expression::TYPE_STRING;
- break;
- case ID_LITERAL_VECTOR_INT:
- token_type = expression::TYPE_INT_VECTOR;
- break;
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- P.expr_stack.top()->add(
- boost::make_shared<expression_literal>(val, token_type));
- next_valid_state = VALID_OPERATOR | VALID_COMMA
- | VALID_PARENS_CLOSE;
- break;
- }
-
- } // end switch
- return true;
- }
- };
-
-public:
- expression::sptr create_expr_tree(const std::string& code)
- {
- // Create empty stack and keyword states
- grammar_props P(_ftable, _var_type_getter, _var_value_getter);
- int next_valid_state = grammar::VALID_EXPRESSION;
-
- // Create a lexer instance
- ns_lexer<lex::lexertl::lexer<>> lexer_functor;
-
- // Tokenize the string
- char const* first = code.c_str();
- char const* last = &first[code.size()];
- bool r = lex::tokenize(first,
- last, // Iterators
- lexer_functor, // Lexer
- boost::bind(grammar(),
- _1,
- boost::ref(P),
- boost::ref(next_valid_state)) // Function object
- );
-
- // Check the parsing worked:
- if (not r or P.expr_stack.size() != 1) {
- std::string rest(first, last);
- throw uhd::syntax_error(
- str(boost::format("Parsing stopped at: %s\nError message: %s") % rest
- % P.error));
- }
-
- // Clear stack and return result
- return P.get_result();
- }
-
-private:
- function_table::sptr _ftable;
- expression_variable::type_getter_type _var_type_getter;
- expression_variable::value_getter_type _var_value_getter;
-};
-
-parser::sptr parser::make(function_table::sptr ftable,
- expression_variable::type_getter_type var_type_getter,
- expression_variable::value_getter_type var_value_getter)
-{
- return sptr(new parser_impl(ftable, var_type_getter, var_value_getter));
-}