aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/rfnoc/nocscript
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/rfnoc/nocscript')
-rw-r--r--host/lib/rfnoc/nocscript/CMakeLists.txt37
-rw-r--r--host/lib/rfnoc/nocscript/block_iface.cpp255
-rw-r--r--host/lib/rfnoc/nocscript/block_iface.hpp94
-rw-r--r--host/lib/rfnoc/nocscript/expression.cpp413
-rw-r--r--host/lib/rfnoc/nocscript/expression.hpp380
-rw-r--r--host/lib/rfnoc/nocscript/function_table.cpp113
-rw-r--r--host/lib/rfnoc/nocscript/function_table.hpp93
-rwxr-xr-xhost/lib/rfnoc/nocscript/gen_basic_funcs.py474
-rw-r--r--host/lib/rfnoc/nocscript/parser.cpp363
-rw-r--r--host/lib/rfnoc/nocscript/parser.hpp50
10 files changed, 2272 insertions, 0 deletions
diff --git a/host/lib/rfnoc/nocscript/CMakeLists.txt b/host/lib/rfnoc/nocscript/CMakeLists.txt
new file mode 100644
index 000000000..77fcc101c
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/CMakeLists.txt
@@ -0,0 +1,37 @@
+#
+# Copyright 2015 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_basic_funcs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/basic_functions.hpp
+)
+
+IF(ENABLE_MANUAL)
+ LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_basic_funcs.py
+ ${CMAKE_BINARY_DIR}/docs/nocscript_functions.dox
+ )
+ENDIF(ENABLE_MANUAL)
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/expression.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/function_table.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/parser.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/block_iface.cpp
+)
diff --git a/host/lib/rfnoc/nocscript/block_iface.cpp b/host/lib/rfnoc/nocscript/block_iface.cpp
new file mode 100644
index 000000000..2034d3438
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/block_iface.cpp
@@ -0,0 +1,255 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "block_iface.hpp"
+#include "function_table.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/utils/msg.hpp>
+#include <boost/assign.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#define UHD_NOCSCRIPT_LOG() UHD_LOGV(never)
+
+using namespace uhd::rfnoc;
+using namespace uhd::rfnoc::nocscript;
+
+block_iface::block_iface(block_ctrl_base *block_ptr)
+ : _block_ptr(block_ptr)
+{
+ function_table::sptr ft = function_table::make();
+
+ // Add the SR_WRITE() function
+ expression_function::argtype_list_type sr_write_args = boost::assign::list_of
+ (expression::TYPE_STRING)
+ (expression::TYPE_INT)
+ ;
+ ft->register_function(
+ "SR_WRITE",
+ boost::bind(&block_iface::_nocscript__sr_write, this, _1),
+ expression::TYPE_BOOL,
+ sr_write_args
+ );
+
+ // Add read access to arguments ($foo)
+ expression_function::argtype_list_type arg_set_args_wo_port = boost::assign::list_of
+ (expression::TYPE_STRING)
+ (expression::TYPE_INT)
+ ;
+ expression_function::argtype_list_type arg_set_args_w_port = boost::assign::list_of
+ (expression::TYPE_STRING)
+ (expression::TYPE_INT)
+ (expression::TYPE_INT)
+ ;
+#define REGISTER_ARG_SETTER(noctype, setter_func) \
+ arg_set_args_wo_port[1] = expression::noctype; \
+ arg_set_args_w_port[1] = expression::noctype; \
+ ft->register_function( \
+ "SET_ARG", \
+ boost::bind(&block_iface::setter_func, this, _1), \
+ expression::TYPE_BOOL, \
+ arg_set_args_wo_port \
+ ); \
+ ft->register_function( \
+ "SET_ARG", \
+ boost::bind(&block_iface::setter_func, this, _1), \
+ expression::TYPE_BOOL, \
+ arg_set_args_w_port \
+ );
+ REGISTER_ARG_SETTER(TYPE_INT, _nocscript__arg_set_int);
+ REGISTER_ARG_SETTER(TYPE_STRING, _nocscript__arg_set_string);
+ REGISTER_ARG_SETTER(TYPE_DOUBLE, _nocscript__arg_set_double);
+ REGISTER_ARG_SETTER(TYPE_INT_VECTOR, _nocscript__arg_set_intvec);
+
+
+ // Add read/write access to local variables
+ expression_function::argtype_list_type set_var_args = boost::assign::list_of
+ (expression::TYPE_STRING)
+ (expression::TYPE_INT)
+ ;
+ const expression_function::argtype_list_type get_var_args = boost::assign::list_of
+ (expression::TYPE_STRING)
+ ;
+#define REGISTER_VAR_ACCESS(noctype, typestr) \
+ set_var_args[1] = expression::noctype; \
+ ft->register_function( \
+ "SET_VAR", \
+ boost::bind(&block_iface::_nocscript__var_set, this, _1), \
+ expression::TYPE_BOOL, \
+ set_var_args \
+ ); \
+ ft->register_function( \
+ "GET_"#typestr, \
+ boost::bind(&block_iface::_nocscript__var_get, this, _1), \
+ expression::noctype, \
+ get_var_args \
+ );
+ REGISTER_VAR_ACCESS(TYPE_INT, INT);
+ REGISTER_VAR_ACCESS(TYPE_STRING, STRING);
+ REGISTER_VAR_ACCESS(TYPE_DOUBLE, DOUBLE);
+ REGISTER_VAR_ACCESS(TYPE_INT_VECTOR, INT_VECTOR);
+
+ // Create the parser
+ _parser = parser::make(
+ ft,
+ boost::bind(&block_iface::_nocscript__arg_get_type, this, _1),
+ boost::bind(&block_iface::_nocscript__arg_get_val, this, _1)
+ );
+}
+
+
+void block_iface::run_and_check(const std::string &code, const std::string &error_message)
+{
+ boost::mutex::scoped_lock local_interpreter_lock(_lil_mutex);
+
+ UHD_NOCSCRIPT_LOG() << "[NocScript] Executing and asserting code: " << code << std::endl;
+ expression::sptr e = _parser->create_expr_tree(code);
+ expression_literal result = e->eval();
+ if (not result.to_bool()) {
+ if (error_message.empty()) {
+ throw uhd::runtime_error(str(
+ boost::format("[NocScript] Code returned false: %s")
+ % code
+ ));
+ } else {
+ throw uhd::runtime_error(str(
+ boost::format("[NocScript] Error: %s")
+ % error_message
+ ));
+ }
+ }
+
+ _vars.clear(); // We go out of scope, and so do NocScript variables
+}
+
+
+expression_literal block_iface::_nocscript__sr_write(expression_container::expr_list_type args)
+{
+ const std::string reg_name = args[0]->eval().get_string();
+ const boost::uint32_t reg_val = boost::uint32_t(args[1]->eval().get_int());
+ bool result = true;
+ try {
+ UHD_NOCSCRIPT_LOG() << "[NocScript] Executing SR_WRITE() " << std::endl;
+ _block_ptr->sr_write(reg_name, reg_val);
+ } catch (const uhd::exception &e) {
+ UHD_MSG(error) << boost::format("[NocScript] Error while executing SR_WRITE(%s, 0x%X):\n%s")
+ % reg_name % reg_val % e.what()
+ << std::endl;
+ result = false;
+ }
+
+ return expression_literal(result);
+}
+
+expression::type_t block_iface::_nocscript__arg_get_type(const std::string &varname)
+{
+ const std::string var_type = _block_ptr->get_arg_type(varname);
+ if (var_type == "int") {
+ return expression::TYPE_INT;
+ } else if (var_type == "string") {
+ return expression::TYPE_STRING;
+ } else if (var_type == "double") {
+ return expression::TYPE_DOUBLE;
+ } else if (var_type == "int_vector") {
+ UHD_THROW_INVALID_CODE_PATH(); // TODO
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+expression_literal block_iface::_nocscript__arg_get_val(const std::string &varname)
+{
+ const std::string var_type = _block_ptr->get_arg_type(varname);
+ if (var_type == "int") {
+ return expression_literal(_block_ptr->get_arg<int>(varname));
+ } else if (var_type == "string") {
+ return expression_literal(_block_ptr->get_arg<std::string>(varname));
+ } else if (var_type == "double") {
+ return expression_literal(_block_ptr->get_arg<double>(varname));
+ } else if (var_type == "int_vector") {
+ UHD_THROW_INVALID_CODE_PATH(); // TODO
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+expression_literal block_iface::_nocscript__arg_set_int(const expression_container::expr_list_type &args)
+{
+ const std::string var_name = args[0]->eval().get_string();
+ const int val = args[1]->eval().get_int();
+ size_t port = 0;
+ if (args.size() == 3) {
+ port = size_t(args[2]->eval().get_int());
+ }
+ UHD_NOCSCRIPT_LOG() << "[NocScript] Setting $" << var_name << std::endl;
+ _block_ptr->set_arg<int>(var_name, val, port);
+ return expression_literal(true);
+}
+
+expression_literal block_iface::_nocscript__arg_set_string(const expression_container::expr_list_type &args)
+{
+ const std::string var_name = args[0]->eval().get_string();
+ const std::string val = args[1]->eval().get_string();
+ size_t port = 0;
+ if (args.size() == 3) {
+ port = size_t(args[2]->eval().get_int());
+ }
+ UHD_NOCSCRIPT_LOG() << "[NocScript] Setting $" << var_name << std::endl;
+ _block_ptr->set_arg<std::string>(var_name, val, port);
+ return expression_literal(true);
+}
+
+expression_literal block_iface::_nocscript__arg_set_double(const expression_container::expr_list_type &args)
+{
+ const std::string var_name = args[0]->eval().get_string();
+ const double val = args[1]->eval().get_double();
+ size_t port = 0;
+ if (args.size() == 3) {
+ port = size_t(args[2]->eval().get_int());
+ }
+ UHD_NOCSCRIPT_LOG() << "[NocScript] Setting $" << var_name << std::endl;
+ _block_ptr->set_arg<double>(var_name, val, port);
+ return expression_literal(true);
+}
+
+expression_literal block_iface::_nocscript__arg_set_intvec(const expression_container::expr_list_type &)
+{
+ UHD_THROW_INVALID_CODE_PATH();
+}
+
+block_iface::sptr block_iface::make(uhd::rfnoc::block_ctrl_base* block_ptr)
+{
+ return sptr(new block_iface(block_ptr));
+}
+
+expression_literal block_iface::_nocscript__var_get(const expression_container::expr_list_type &args)
+{
+ expression_literal expr = _vars[args[0]->eval().get_string()];
+ //std::cout << "[NocScript] Getting var " << args[0]->eval().get_string() << " == " << expr << std::endl;
+ //std::cout << "[NocScript] Type " << expr.infer_type() << std::endl;
+ //return _vars[args[0]->eval().get_string()];
+ return expr;
+}
+
+expression_literal block_iface::_nocscript__var_set(const expression_container::expr_list_type &args)
+{
+ _vars[args[0]->eval().get_string()] = args[1]->eval();
+ //std::cout << "[NocScript] Set var " << args[0]->eval().get_string() << " to " << _vars[args[0]->eval().get_string()] << std::endl;
+ //std::cout << "[NocScript] Type " << _vars[args[0]->eval().get_string()].infer_type() << std::endl;
+ return expression_literal(true);
+}
+
diff --git a/host/lib/rfnoc/nocscript/block_iface.hpp b/host/lib/rfnoc/nocscript/block_iface.hpp
new file mode 100644
index 000000000..6354409f2
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/block_iface.hpp
@@ -0,0 +1,94 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "expression.hpp"
+#include "parser.hpp"
+#include <uhd/rfnoc/block_ctrl_base.hpp>
+#include <boost/thread/mutex.hpp>
+
+#ifndef INCLUDED_LIBUHD_NOCSCRIPT_BLOCK_IFACE_HPP
+#define INCLUDED_LIBUHD_NOCSCRIPT_BLOCK_IFACE_HPP
+
+namespace uhd { namespace rfnoc { namespace nocscript {
+
+/*! NocScript / Block interface class.
+ *
+ * This class only exists as a member of an rfnoc::block_ctrl_base class.
+ * It should never be instantiated anywhere else. It is used to execute
+ * NocScript function calls that require access to the original block
+ * controller class.
+ */
+class block_iface {
+
+ public:
+ typedef boost::shared_ptr<block_iface> sptr;
+
+ static sptr make(uhd::rfnoc::block_ctrl_base* block_ptr);
+
+ block_iface(uhd::rfnoc::block_ctrl_base* block_ptr);
+
+ /*! Execute \p code and make sure it returns 'true'.
+ *
+ * \param code Must be a valid NocScript expression that returns a boolean value.
+ * If it returns false, this is interpreted as failure.
+ * \param error_message If the expression fails, this error message is printed.
+ * \throws uhd::runtime_error if the expression returns false.
+ * \throws uhd::syntax_error if the expression is invalid.
+ */
+ void run_and_check(const std::string &code, const std::string &error_message="");
+
+ private:
+ //! For the local interpreter lock (lil)
+ boost::mutex _lil_mutex;
+
+ //! Wrapper for block_ctrl_base::sr_write, so we can call it from within NocScript
+ expression_literal _nocscript__sr_write(expression_container::expr_list_type);
+
+ //! Argument type getter that can be used within NocScript
+ expression::type_t _nocscript__arg_get_type(const std::string &argname);
+
+ //! Argument value getter that can be used within NocScript
+ expression_literal _nocscript__arg_get_val(const std::string &argname);
+
+ //! Argument value setters:
+ expression_literal _nocscript__arg_set_int(const expression_container::expr_list_type &);
+ expression_literal _nocscript__arg_set_string(const expression_container::expr_list_type &);
+ expression_literal _nocscript__arg_set_double(const expression_container::expr_list_type &);
+ expression_literal _nocscript__arg_set_intvec(const expression_container::expr_list_type &);
+
+ //! Variable value getter
+ expression_literal _nocscript__var_get(const expression_container::expr_list_type &);
+
+ //! Variable value setter
+ expression_literal _nocscript__var_set(const expression_container::expr_list_type &);
+
+ //! Raw pointer to the block class. Note that since block_iface may
+ // only live as a member of a block_ctrl_base, we don't really need
+ // the reference counting.
+ uhd::rfnoc::block_ctrl_base* _block_ptr;
+
+ //! Pointer to the parser object
+ parser::sptr _parser;
+
+ //! Container for scoped variables
+ std::map<std::string, expression_literal> _vars;
+};
+
+}}} /* namespace uhd::rfnoc::nocscript */
+
+#endif /* INCLUDED_LIBUHD_NOCSCRIPT_BLOCK_IFACE_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/rfnoc/nocscript/expression.cpp b/host/lib/rfnoc/nocscript/expression.cpp
new file mode 100644
index 000000000..38d6e2128
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/expression.cpp
@@ -0,0 +1,413 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "expression.hpp"
+#include "function_table.hpp"
+#include <uhd/utils/cast.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/assign.hpp>
+#include <boost/algorithm/string.hpp>
+
+using namespace uhd::rfnoc::nocscript;
+
+std::map<expression::type_t, std::string> expression::type_repr = boost::assign::map_list_of
+ (TYPE_INT, "INT")
+ (TYPE_DOUBLE, "DOUBLE")
+ (TYPE_STRING, "STRING")
+ (TYPE_BOOL, "BOOL")
+ (TYPE_INT_VECTOR, "INT_VECTOR")
+;
+
+/********************************************************************
+ * Literal expressions (constants)
+ *******************************************************************/
+expression_literal::expression_literal(
+ const std::string token_val,
+ expression::type_t type
+) : _bool_val(false)
+ , _int_val(0)
+ , _double_val(0.0)
+ , _val(token_val)
+ , _type(type)
+{
+ switch (_type) {
+ case expression::TYPE_STRING:
+ // Remove the leading and trailing quotes:
+ _val = _val.substr(1, _val.size()-2);
+ break;
+
+ case expression::TYPE_INT:
+ if (_val.substr(0, 2) == "0x") {
+ _int_val = uhd::cast::hexstr_cast<int>(_val);
+ } else {
+ _int_val = boost::lexical_cast<int>(_val);
+ }
+ break;
+
+ case expression::TYPE_DOUBLE:
+ _double_val = boost::lexical_cast<double>(_val);
+ break;
+
+ case expression::TYPE_BOOL:
+ if (boost::to_upper_copy(_val) == "TRUE") {
+ _bool_val = true;
+ } else {
+ // lexical cast to bool is too picky
+ _bool_val = bool(boost::lexical_cast<int>(_val));
+ }
+ break;
+
+ case expression::TYPE_INT_VECTOR:
+ {
+ std::string str_vec = _val.substr(1, _val.size()-2);
+ std::vector<std::string> subtoken_list;
+ boost::split(subtoken_list, str_vec, boost::is_any_of(", "), boost::token_compress_on);
+ BOOST_FOREACH(const std::string &t, subtoken_list) {
+ _int_vector_val.push_back(boost::lexical_cast<int>(t));
+ }
+ break;
+ }
+
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+expression_literal::expression_literal(bool b)
+ : _bool_val(b)
+ , _int_val(0)
+ , _double_val(0.0)
+ , _val("")
+ , _type(expression::TYPE_BOOL)
+{
+ // nop
+}
+
+expression_literal::expression_literal(int i)
+ : _bool_val(false)
+ , _int_val(i)
+ , _double_val(0.0)
+ , _val("")
+ , _type(expression::TYPE_INT)
+{
+ // nop
+}
+
+expression_literal::expression_literal(double d)
+ : _bool_val(false)
+ , _int_val(0)
+ , _double_val(d)
+ , _val("")
+ , _type(expression::TYPE_DOUBLE)
+{
+ // nop
+}
+
+expression_literal::expression_literal(const std::string &s)
+ : _bool_val(false)
+ , _int_val(0)
+ , _double_val(0.0)
+ , _val(s)
+ , _type(expression::TYPE_STRING)
+{
+ // nop
+}
+
+expression_literal::expression_literal(const std::vector<int> v)
+ : _bool_val(false)
+ , _int_val(0)
+ , _double_val(0.0)
+ , _int_vector_val(v)
+ , _val("")
+ , _type(expression::TYPE_INT_VECTOR)
+{
+ // nop
+}
+
+bool expression_literal::to_bool() const
+{
+ switch (_type) {
+ case TYPE_INT:
+ return bool(boost::lexical_cast<int>(_val));
+ case TYPE_STRING:
+ return not _val.empty();
+ case TYPE_DOUBLE:
+ return bool(boost::lexical_cast<double>(_val));
+ case TYPE_BOOL:
+ return _bool_val;
+ case TYPE_INT_VECTOR:
+ return not _int_vector_val.empty();
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+int expression_literal::get_int() const
+{
+ if (_type != TYPE_INT) {
+ throw uhd::type_error("Cannot call get_int() on non-int value.");
+ }
+
+ return _int_val;
+}
+
+double expression_literal::get_double() const
+{
+ if (_type != TYPE_DOUBLE) {
+ throw uhd::type_error("Cannot call get_double() on non-double value.");
+ }
+
+ return _double_val;
+}
+
+std::string expression_literal::get_string() const
+{
+ if (_type != TYPE_STRING) {
+ throw uhd::type_error("Cannot call get_string() on non-string value.");
+ }
+
+ return _val;
+}
+
+bool expression_literal::get_bool() const
+{
+ if (_type != TYPE_BOOL) {
+ throw uhd::type_error("Cannot call get_bool() on non-boolean value.");
+ }
+
+ return _bool_val;
+}
+
+std::vector<int> expression_literal::get_int_vector() const
+{
+ if (_type != TYPE_INT_VECTOR) {
+ throw uhd::type_error("Cannot call get_bool() on non-boolean value.");
+ }
+
+ return _int_vector_val;
+}
+
+std::string expression_literal::repr() const
+{
+ switch (_type) {
+ case TYPE_INT:
+ return boost::lexical_cast<std::string>(_int_val);
+ case TYPE_STRING:
+ return _val;
+ case TYPE_DOUBLE:
+ return boost::lexical_cast<std::string>(_double_val);
+ case TYPE_BOOL:
+ return _bool_val ? "TRUE" : "FALSE";
+ case TYPE_INT_VECTOR:
+ {
+ std::stringstream sstr;
+ sstr << "[";
+ for (size_t i = 0; i < _int_vector_val.size(); i++) {
+ if (i > 0) {
+ sstr << ", ";
+ }
+ sstr << _int_vector_val[i];
+ }
+ sstr << "]";
+ return sstr.str();
+ }
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+bool expression_literal::operator==(const expression_literal &rhs) const
+{
+ if (rhs.infer_type() != _type) {
+ return false;
+ }
+
+ switch (_type) {
+ case TYPE_INT:
+ return get_int() == rhs.get_int();
+ case TYPE_STRING:
+ return get_string() == rhs.get_string();
+ case TYPE_DOUBLE:
+ return get_double() == rhs.get_double();
+ case TYPE_BOOL:
+ return get_bool() == rhs.get_bool();
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+/********************************************************************
+ * Containers
+ *******************************************************************/
+expression_container::sptr expression_container::make()
+{
+ return sptr(new expression_container);
+}
+
+expression::type_t expression_container::infer_type() const
+{
+ if (_combiner == COMBINE_OR or _combiner == COMBINE_AND) {
+ return TYPE_BOOL;
+ }
+
+ if (_sub_exprs.empty()) {
+ return TYPE_BOOL;
+ }
+
+ return _sub_exprs.back()->infer_type();
+}
+
+void expression_container::add(expression::sptr new_expr)
+{
+ _sub_exprs.push_back(new_expr);
+}
+
+bool expression_container::empty() const
+{
+ return _sub_exprs.empty();
+}
+
+void expression_container::set_combiner_safe(const combiner_type c)
+{
+ if (_combiner == COMBINE_NOTSET) {
+ _combiner = c;
+ return;
+ }
+
+ throw uhd::syntax_error("Attempting to override combiner type");
+}
+
+expression_literal expression_container::eval()
+{
+ if (_sub_exprs.empty()) {
+ return expression_literal(true);
+ }
+
+ expression_literal ret_val;
+ BOOST_FOREACH(const expression::sptr &sub_expr, _sub_exprs) {
+ ret_val = sub_expr->eval();
+ if (_combiner == COMBINE_AND and ret_val.to_bool() == false) {
+ return ret_val;
+ }
+ if (_combiner == COMBINE_OR and ret_val.to_bool() == true) {
+ return ret_val;
+ }
+ // For ALL, we return the last one, so just overwrite it
+ }
+ return ret_val;
+}
+
+/********************************************************************
+ * Functions
+ *******************************************************************/
+std::string expression_function::to_string(const std::string &name, const argtype_list_type &types)
+{
+ std::string s = name;
+ int arg_count = 0;
+ BOOST_FOREACH(const expression::type_t type, types) {
+ if (arg_count == 0) {
+ s += "(";
+ } else {
+ s += ", ";
+ }
+ s += type_repr[type];
+ arg_count++;
+ }
+ s += ")";
+
+ return s;
+}
+
+expression_function::expression_function(
+ const std::string &name,
+ const function_table::sptr func_table
+) : _name(name)
+ , _func_table(func_table)
+{
+ _combiner = COMBINE_ALL;
+ if (not _func_table->function_exists(_name)) {
+ throw uhd::syntax_error(str(
+ boost::format("Unknown function: %s")
+ % _name
+ ));
+ }
+}
+
+void expression_function::add(expression::sptr new_expr)
+{
+ expression_container::add(new_expr);
+ _arg_types.push_back(new_expr->infer_type());
+}
+
+expression::type_t expression_function::infer_type() const
+{
+ return _func_table->get_type(_name, _arg_types);
+}
+
+expression_literal expression_function::eval()
+{
+ return _func_table->eval(_name, _arg_types, _sub_exprs);
+}
+
+
+std::string expression_function::repr() const
+{
+ return to_string(_name, _arg_types);
+}
+
+expression_function::sptr expression_function::make(
+ const std::string &name,
+ const function_table::sptr func_table
+) {
+ return sptr(new expression_function(name, func_table));
+}
+
+/********************************************************************
+ * Variables
+ *******************************************************************/
+expression_variable::expression_variable(
+ const std::string &token_val,
+ type_getter_type type_getter,
+ value_getter_type value_getter
+) : _type_getter(type_getter)
+ , _value_getter(value_getter)
+{
+ // We can assume this is true because otherwise, it's not a valid token:
+ UHD_ASSERT_THROW(not token_val.empty() and token_val[0] == '$');
+
+ _varname = token_val.substr(1);
+}
+
+expression::type_t expression_variable::infer_type() const
+{
+ return _type_getter(_varname);
+}
+
+expression_literal expression_variable::eval()
+{
+ return _value_getter(_varname);
+}
+
+expression_variable::sptr expression_variable::make(
+ const std::string &token_val,
+ type_getter_type type_getter,
+ value_getter_type value_getter
+) {
+ return sptr(new expression_variable(token_val, type_getter, value_getter));
+}
+
diff --git a/host/lib/rfnoc/nocscript/expression.hpp b/host/lib/rfnoc/nocscript/expression.hpp
new file mode 100644
index 000000000..83fc5bcbc
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/expression.hpp
@@ -0,0 +1,380 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/exception.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/function.hpp>
+#include <boost/make_shared.hpp>
+#include <vector>
+#include <map>
+
+#ifndef INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_EXPR_HPP
+#define INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_EXPR_HPP
+
+namespace uhd { namespace rfnoc { namespace nocscript {
+
+// Forward declaration for expression::eval()
+class expression_literal;
+
+/*! Virtual base class for Noc-Script expressions.
+ */
+class expression
+{
+ public:
+ typedef boost::shared_ptr<expression> sptr;
+
+ //! All the possible return types for expressions within Noc-Script
+ enum type_t {
+ TYPE_INT,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ TYPE_BOOL,
+ TYPE_INT_VECTOR
+ };
+
+ // TODO make this a const and fix the [] usage
+ static std::map<type_t, std::string> type_repr;
+
+ //! Returns the type of this expression without evaluating it
+ virtual type_t infer_type() const = 0;
+
+ //! Evaluate current expression and return its return value
+ virtual expression_literal eval() = 0;
+};
+
+/*! Literal (constant) expression class
+ *
+ * A literal is any value that is literally given in the NoC-Script
+ * source code, such as '5', '"FOO"', or '2.3'.
+ */
+class expression_literal : public expression
+{
+ public:
+ typedef boost::shared_ptr<expression_literal> sptr;
+
+ template <typename expr_type>
+ static sptr make(expr_type x) { return boost::make_shared<expression_literal>(x); };
+
+ /*! Generate the literal expression from its token string representation.
+ * This includes markup, e.g. a string would still have the quotes, and
+ * a hex value would still have leading 0x.
+ */
+ expression_literal(
+ const std::string token_val,
+ expression::type_t type
+ );
+
+ //! Create a boolean literal expression from a C++ bool.
+ expression_literal(bool b=false);
+ //! Create an integer literal expression from a C++ int.
+ expression_literal(int i);
+ //! Create a double literal expression from a C++ double.
+ expression_literal(double d);
+ //! Create a string literal expression from a C++ string.
+ expression_literal(const std::string &s);
+ //! Create an int vector literal expression from a C++ vector<int>.
+ expression_literal(std::vector<int> v);
+
+ expression::type_t infer_type() const
+ {
+ return _type;
+ }
+
+ //! Literals aren't evaluated as such, so the evaluation
+ // simply returns a copy of itself.
+ expression_literal eval()
+ {
+ return *this; // TODO make sure this is copy
+ }
+
+ /*! A 'type cast' to bool. Cast rules are similar to most
+ * scripting languages:
+ * - Integers and doubles are false if zero, true otherwise
+ * - Strings are false if empty, true otherwise
+ * - Vectors are false if empty, true otherwise
+ */
+ bool to_bool() const;
+
+ /*! Convenience function to typecast to C++ int
+ *
+ * Note that the current type must be TYPE_INT.
+ *
+ * \return C++ int representation of current literal
+ * \throws uhd::type_error if type didn't match
+ */
+ int get_int() const;
+
+ /*! Convenience function to typecast to C++ double
+ *
+ * Note that the current type must be TYPE_DOUBLE.
+ *
+ * \return C++ double representation of current literal
+ * \throws uhd::type_error if type didn't match
+ */
+ double get_double() const;
+
+ /*! Convenience function to typecast to C++ std::string.
+ *
+ * Note that the current type must be TYPE_STRING.
+ *
+ * \return String representation of current literal.
+ * \throws uhd::type_error if type didn't match.
+ */
+ std::string get_string() const;
+
+ /*! Convenience function to typecast to C++ int vector.
+ *
+ * Note that the current type must be TYPE_INT_VECTOR.
+ *
+ * \return String representation of current literal.
+ * \throws uhd::type_error if type didn't match.
+ */
+ std::vector<int> get_int_vector() const;
+
+ /*! Convenience function to typecast to C++ bool.
+ *
+ * Note that the current type must be TYPE_BOOL.
+ * See also expression_literal::to_bool() for a type-cast
+ * style function.
+ *
+ * \return bool representation of current literal.
+ * \throws uhd::type_error if type didn't match.
+ */
+ bool get_bool() const;
+
+ //! String representation
+ std::string repr() const;
+
+ bool operator==(const expression_literal &rhs) const;
+
+ private:
+ //! For TYPE_BOOL
+ bool _bool_val;
+
+ //! For TYPE_INT
+ int _int_val;
+
+ //! For TYPE_DOUBLE
+ double _double_val;
+
+ //! For TYPE_INT_VECTOR
+ std::vector<int> _int_vector_val;
+
+ //! Store the token value
+ std::string _val;
+
+ //! Current expression type
+ expression::type_t _type;
+};
+
+UHD_INLINE std::ostream& operator<< (std::ostream& out, const expression_literal &l)
+{
+ out << l.repr();
+ return out;
+}
+
+UHD_INLINE std::ostream& operator<< (std::ostream& out, const expression_literal::sptr &l)
+{
+ out << l->repr();
+ return out;
+}
+
+/*! Contains multiple (sub-)expressions.
+ */
+class expression_container : public expression
+{
+ public:
+ typedef boost::shared_ptr<expression_container> sptr;
+ typedef std::vector<expression::sptr> expr_list_type;
+
+ //! Return an sptr to an empty container
+ static sptr make();
+
+ //! List of valid combination types (see expression_container::eval()).
+ enum combiner_type {
+ COMBINE_ALL,
+ COMBINE_AND,
+ COMBINE_OR,
+ COMBINE_NOTSET
+ };
+
+ //! Create an empty container
+ expression_container() : _combiner(COMBINE_NOTSET) {};
+
+ /*! Type-deduction rules for containers are:
+ * - If the combination type is COMBINE_ALL or COMBINE_AND,
+ * return value must be TYPE_BOOL
+ * - In all other cases, we return the last expression return
+ * value, and hence its type is relevant
+ */
+ expression::type_t infer_type() const;
+
+ /*! Add another expression container to this container.
+ */
+ virtual void add(expression::sptr new_expr);
+
+ virtual bool empty() const;
+
+ void set_combiner_safe(const combiner_type c);
+
+ void set_combiner(const combiner_type c) { _combiner = c; };
+
+ combiner_type get_combiner() const { return _combiner; };
+
+ /*! Evaluate a container by evaluating its sub-expressions.
+ *
+ * If a container contains multiple sub-expressions, the rules
+ * for evaluating them depend on the combiner_type:
+ * - COMBINE_ALL: Run all the sub-expressions and return the last
+ * expression's return value
+ * - COMBINE_AND: Run sub-expressions, in order, until one of them
+ * returns false. Following expressions are not evaluated (like
+ * most C++ compilers).
+ * - COMBINE_OR: Run sub-expressions, in order, until one of them
+ * returns true. Following expressions are not evaluated.
+ *
+ * In the special case where no sub-expressions are contained, always
+ * returns true.
+ */
+ virtual expression_literal eval();
+
+ protected:
+ //! Store all the sub-expressions, in order
+ expr_list_type _sub_exprs;
+ combiner_type _combiner;
+};
+
+// Forward declaration:
+class function_table;
+/*! A function call is a special type of container.
+ *
+ * All arguments are sub-expressions. The combiner type is
+ * always COMBINE_ALL in this case (changing the combiner type
+ * does not affect anything).
+ *
+ * The actual function maps to a C++ function available through
+ * a uhd::rfnoc::nocscript::function_table object.
+ *
+ * The recommended to use this is:
+ * 1. Create a function object giving its name (e.g. ADD)
+ * 2. Use the add() method to add all the function arguments
+ * in the right order (left to right).
+ * 3. Once step 2 is complete, the function object can be used.
+ * Call infer_type() to get the return value, if required.
+ * 4. Calling eval() will call into the function table. The
+ * argument expressions are evaluated, if so required, inside
+ * the function (lazy evalulation). Functions do not need
+ * to evaluate arguments.
+ */
+class expression_function : public expression_container
+{
+ public:
+ typedef boost::shared_ptr<expression_function> sptr;
+ typedef std::vector<expression::type_t> argtype_list_type;
+
+ //! Return an sptr to a function object without args
+ static sptr make(
+ const std::string &name,
+ const boost::shared_ptr<function_table> func_table
+ );
+
+ static std::string to_string(const std::string &name, const argtype_list_type &types);
+
+ expression_function(
+ const std::string &name,
+ const boost::shared_ptr<function_table> func_table
+ );
+
+ //! Add an argument expression
+ virtual void add(expression::sptr new_expr);
+
+ /*! Looks up the function type in the function table.
+ *
+ * Note that this will only work after all arguments have been
+ * added, as they are also used to look up a function's type in the
+ * function table.
+ */
+ expression::type_t infer_type() const;
+
+ /*! Evaluate all arguments, then the function itself.
+ */
+ expression_literal eval();
+
+ //! String representation
+ std::string repr() const;
+
+ private:
+ std::string _name;
+ const boost::shared_ptr<function_table> _func_table;
+ std::vector<expression::type_t> _arg_types;
+};
+
+
+/*! Variable expression
+ *
+ * Variables are like literals, only their type and value aren't known
+ * at parse-time. Instead, we provide a function object to look up
+ * variable's types and value.
+ */
+class expression_variable : public expression
+{
+ public:
+ typedef boost::shared_ptr<expression_variable> sptr;
+ typedef boost::function<expression::type_t(const std::string &)> type_getter_type;
+ typedef boost::function<expression_literal(const std::string &)> value_getter_type;
+
+ static sptr make(
+ const std::string &token_val,
+ type_getter_type type_getter,
+ value_getter_type value_getter
+ );
+
+ /*! Create a variable object from its token value
+ * (e.g. '$spp', i.e. including the '$' symbol). The variable
+ * does not have to exist at this point.
+ */
+ expression_variable(
+ const std::string &token_val,
+ type_getter_type type_getter,
+ value_getter_type value_getter
+ );
+
+ /*! Looks up the variable type in the variable table.
+ *
+ * \throws Depending on \p type_getter, this may throw when the variable does not exist.
+ * Recommended behaviour is to throw uhd::syntax_error.
+ */
+ expression::type_t infer_type() const;
+
+ /*! Look up a variable's value in the variable table.
+ *
+ * \throws Depending on \p value_getter, this may throw when the variable does not exist.
+ * Recommended behaviour is to throw uhd::syntax_error.
+ */
+ expression_literal eval();
+
+ private:
+ std::string _varname;
+ type_getter_type _type_getter;
+ value_getter_type _value_getter;
+};
+
+}}} /* namespace uhd::rfnoc::nocscript */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_EXPR_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/rfnoc/nocscript/function_table.cpp b/host/lib/rfnoc/nocscript/function_table.cpp
new file mode 100644
index 000000000..bebceb8dc
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/function_table.cpp
@@ -0,0 +1,113 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "function_table.hpp"
+#include "basic_functions.hpp"
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <map>
+
+using namespace uhd::rfnoc::nocscript;
+
+class function_table_impl : public function_table
+{
+ public:
+ struct function_info {
+ expression::type_t return_type;
+ function_ptr function;
+
+ function_info() {};
+ function_info(const expression::type_t return_type_, const function_ptr &function_)
+ : return_type(return_type_), function(function_)
+ {};
+ };
+ // Should be an unordered_map... sigh, we'll get to C++11 someday.
+ typedef std::map<std::string, std::map<expression_function::argtype_list_type, function_info> > table_type;
+
+ /************************************************************************
+ * Structors
+ ***********************************************************************/
+ function_table_impl()
+ {
+ _REGISTER_ALL_FUNCS();
+ }
+
+ ~function_table_impl() {};
+
+
+ /************************************************************************
+ * Interface implementation
+ ***********************************************************************/
+ bool function_exists(const std::string &name) const {
+ return bool(_table.count(name));
+ }
+
+ bool function_exists(
+ const std::string &name,
+ const expression_function::argtype_list_type &arg_types
+ ) const {
+ table_type::const_iterator it = _table.find(name);
+ return (it != _table.end()) and bool(it->second.count(arg_types));
+ }
+
+ expression::type_t get_type(
+ const std::string &name,
+ const expression_function::argtype_list_type &arg_types
+ ) const {
+ table_type::const_iterator it = _table.find(name);
+ if (it == _table.end() or (it->second.find(arg_types) == it->second.end())) {
+ throw uhd::syntax_error(str(
+ boost::format("Unable to retrieve return value for function %s")
+ % expression_function::to_string(name, arg_types)
+ ));
+ }
+ return it->second.find(arg_types)->second.return_type;
+ }
+
+ expression_literal eval(
+ const std::string &name,
+ const expression_function::argtype_list_type &arg_types,
+ expression_container::expr_list_type &arguments
+ ) {
+ if (not function_exists(name, arg_types)) {
+ throw uhd::syntax_error(str(
+ boost::format("Cannot eval() function %s, not a known signature")
+ % expression_function::to_string(name, arg_types)
+ ));
+ }
+
+ return _table[name][arg_types].function(arguments);
+ }
+
+ void register_function(
+ const std::string &name,
+ const function_table::function_ptr &ptr,
+ const expression::type_t return_type,
+ const expression_function::argtype_list_type &sig
+ ) {
+ _table[name][sig] = function_info(return_type, ptr);
+ }
+
+ private:
+ table_type _table;
+};
+
+function_table::sptr function_table::make()
+{
+ return sptr(new function_table_impl());
+}
diff --git a/host/lib/rfnoc/nocscript/function_table.hpp b/host/lib/rfnoc/nocscript/function_table.hpp
new file mode 100644
index 000000000..6c715308e
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/function_table.hpp
@@ -0,0 +1,93 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "expression.hpp"
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <vector>
+
+#ifndef INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_FUNCTABLE_HPP
+#define INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_FUNCTABLE_HPP
+
+namespace uhd { namespace rfnoc { namespace nocscript {
+
+class function_table
+{
+ public:
+ typedef boost::shared_ptr<function_table> sptr;
+ typedef boost::function<expression_literal(expression_container::expr_list_type&)> function_ptr;
+
+ static sptr make();
+ virtual ~function_table() {};
+
+ /*! Check if any function with a given name exists
+ *
+ * \returns True, if any function with name \p name is registered.
+ */
+ virtual bool function_exists(const std::string &name) const = 0;
+
+ /*! Check if a function with a given name and list of argument types exists
+ *
+ * \returns True, if such a function is registered.
+ */
+ virtual bool function_exists(
+ const std::string &name,
+ const expression_function::argtype_list_type &arg_types
+ ) const = 0;
+
+ /*! Get the return type of a function with given name and argument type list
+ *
+ * \returns The function's return type
+ * \throws uhd::syntax_error if no such function is registered
+ */
+ virtual expression::type_t get_type(
+ const std::string &name,
+ const expression_function::argtype_list_type &arg_types
+ ) const = 0;
+
+ /*! Calls the function \p name with the argument list \p arguments
+ *
+ * \param arg_types A list of types for each argument
+ * \param arguments An expression list of the arguments
+ * \returns The return value of the called function
+ * \throws uhd::syntax_error if no such function is found
+ */
+ virtual expression_literal eval(
+ const std::string &name,
+ const expression_function::argtype_list_type &arg_types,
+ expression_container::expr_list_type &arguments
+ ) = 0;
+
+ /*! Register a new function
+ *
+ * \param name Name of the function (e.g. 'ADD')
+ * \param ptr Function object
+ * \param return_type The function's return value
+ * \param sig The function signature (list of argument types)
+ */
+ virtual void register_function(
+ const std::string &name,
+ const function_ptr &ptr,
+ const expression::type_t return_type,
+ const expression_function::argtype_list_type &sig
+ ) = 0;
+};
+
+}}} /* namespace uhd::rfnoc::nocscript */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_FUNCTABLE_HPP */
+// vim: sw=4 et:
diff --git a/host/lib/rfnoc/nocscript/gen_basic_funcs.py b/host/lib/rfnoc/nocscript/gen_basic_funcs.py
new file mode 100755
index 000000000..702b3e884
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/gen_basic_funcs.py
@@ -0,0 +1,474 @@
+#!/usr/bin/env python
+"""
+Generate the function list for the basic NocScript functions
+"""
+
+import re
+import os
+import sys
+from mako.template import Template
+
+#############################################################################
+# This is the interesting part: Add new functions in here
+#
+# Notes:
+# - Lines starting with # are considered comments, and will be removed from
+# the output
+# - C++ comments will be copied onto the generated file if inside functions
+# - Docstrings start with //! and are required
+# - Function signature is RETURN_TYPE NAME(ARG_TYPE1, ARG_TYPE2, ...)
+# - Function body is valid C++
+# - If your function requires special includes, put them in INCLUDE_LIST
+# - End of functions is delimited by s/^}/, so take care with the indents!
+# - Use these substitutions:
+# - ${RETURN}(...): Create a valid return value
+# - ${args[n]}: Access the n-th argument
+#
+INCLUDE_LIST = """
+#include <boost/math/special_functions/round.hpp>
+#include <boost/thread/thread.hpp>
+"""
+FUNCTION_LIST = """
+CATEGORY: Math Functions
+//! Returns x + y
+INT ADD(INT, INT)
+{
+ ${RETURN}(${args[0]} + ${args[1]});
+}
+
+//! Returns x + y
+DOUBLE ADD(DOUBLE, DOUBLE)
+{
+ ${RETURN}(${args[0]} + ${args[1]});
+}
+
+//! Returns x * y
+DOUBLE MULT(DOUBLE, DOUBLE)
+{
+ ${RETURN}(${args[0]} * ${args[1]});
+}
+
+//! Returns x * y
+INT MULT(INT, INT)
+{
+ ${RETURN}(${args[0]} * ${args[1]});
+}
+
+//! Returns x / y
+DOUBLE DIV(DOUBLE, DOUBLE)
+{
+ ${RETURN}(${args[0]} / ${args[1]});
+}
+
+//! Returns true if x <= y (Less or Equal)
+BOOL LE(INT, INT)
+{
+ ${RETURN}(bool(${args[0]} <= ${args[1]}));
+}
+
+//! Returns true if x <= y (Less or Equal)
+BOOL LE(DOUBLE, DOUBLE)
+{
+ ${RETURN}(bool(${args[0]} <= ${args[1]}));
+}
+
+//! Returns true if x >= y (Greater or Equal)
+BOOL GE(INT, INT)
+{
+ ${RETURN}(bool(${args[0]} >= ${args[1]}));
+}
+
+//! Returns true if x >= y (Greater or Equal)
+BOOL GE(DOUBLE, DOUBLE)
+{
+ ${RETURN}(bool(${args[0]} >= ${args[1]}));
+}
+
+//! Returns true if x < y (Less Than)
+BOOL LT(INT, INT)
+{
+ ${RETURN}(bool(${args[0]} < ${args[1]}));
+}
+
+//! Returns true if x > y (Greater Than)
+BOOL GT(INT, INT)
+{
+ ${RETURN}(bool(${args[0]} > ${args[1]}));
+}
+
+//! Returns true if x < y (Less Than)
+BOOL LT(DOUBLE, DOUBLE)
+{
+ ${RETURN}(bool(${args[0]} < ${args[1]}));
+}
+
+//! Returns true if x > y (Greater Than)
+BOOL GT(DOUBLE, DOUBLE)
+{
+ ${RETURN}(bool(${args[0]} > ${args[1]}));
+}
+
+//! Round x and return it as an integer
+INT IROUND(DOUBLE)
+{
+ ${RETURN}(int(boost::math::iround(${args[0]})));
+}
+
+//! Returns true if x is a power of 2
+BOOL IS_PWR_OF_2(INT)
+{
+ if (${args[0]} < 0) return ${FALSE};
+ int i = ${args[0]};
+ while ( (i & 1) == 0 and (i > 1) ) {
+ i >>= 1;
+ }
+ ${RETURN}(bool(i == 1));
+}
+
+//! Returns floor(log2(x)).
+INT LOG2(INT)
+{
+ if (${args[0]} < 0) {
+ throw uhd::runtime_error(str(
+ boost::format("In NocScript function ${func_name}: Cannot calculate log2() of negative number.")
+ ));
+ }
+
+ int power_value = ${args[0]};
+ int log2_value = 0;
+ while ( (power_value & 1) == 0 and (power_value > 1) ) {
+ power_value >>= 1;
+ log2_value++;
+ }
+ ${RETURN}(log2_value);
+}
+
+//! Returns x % y
+INT MODULO(INT, INT)
+{
+ ${RETURN}(${args[0]} % ${args[1]});
+}
+
+//! Returns true if x == y
+BOOL EQUAL(INT, INT)
+{
+ ${RETURN}(bool(${args[0]} == ${args[1]}));
+}
+
+//! Returns true if x == y
+BOOL EQUAL(DOUBLE, DOUBLE)
+{
+ ${RETURN}(bool(${args[0]} == ${args[1]}));
+}
+
+//! Returns true if x == y
+BOOL EQUAL(STRING, STRING)
+{
+ ${RETURN}(bool(${args[0]} == ${args[1]}));
+}
+
+CATEGORY: Bitwise Operations
+//! Returns x >> y
+INT SHIFT_RIGHT(INT, INT)
+{
+ ${RETURN}(${args[0]} >> ${args[1]});
+}
+
+//! Returns x << y
+INT SHIFT_LEFT(INT, INT)
+{
+ ${RETURN}(${args[0]} << ${args[1]});
+}
+
+//! Returns x & y
+INT BITWISE_AND(INT, INT)
+{
+ ${RETURN}(${args[0]} & ${args[1]});
+}
+
+//! Returns x | y
+INT BITWISE_OR(INT, INT)
+{
+ ${RETURN}(${args[0]} | ${args[1]});
+}
+
+//! Returns x ^ y
+INT BITWISE_XOR(INT, INT)
+{
+ ${RETURN}(${args[0]} ^ ${args[1]});
+}
+
+CATEGORY: Boolean Logic
+//! Returns x xor y.
+BOOL XOR(BOOL, BOOL)
+{
+ ${RETURN}(${args[0]} xor ${args[1]});
+}
+
+//! Returns !x
+BOOL NOT(BOOL)
+{
+ ${RETURN}(not ${args[0]});
+}
+
+//! Always returns true
+BOOL TRUE()
+{
+ return ${TRUE};
+}
+
+//! Always returns false
+BOOL FALSE()
+{
+ return ${FALSE};
+}
+
+CATEGORY: Conditional Execution
+//! Executes x, if true, execute y. Returns true if x is true.
+BOOL IF(BOOL, BOOL)
+{
+ if (${args[0]}) {
+ ${args[1]};
+ ${RETURN}(true);
+ }
+ ${RETURN}(false);
+}
+
+//! Executes x, if true, execute y, otherwise, execute z. Returns true if x is true.
+BOOL IF_ELSE(BOOL, BOOL, BOOL)
+{
+ if (${args[0]}) {
+ ${args[1]};
+ ${RETURN}(true);
+ } else {
+ ${args[2]};
+ }
+ ${RETURN}(false);
+}
+
+CATEGORY: Execution Control
+//! Sleep for x seconds. Fractions are allowed. Millisecond accuracy.
+BOOL SLEEP(DOUBLE)
+{
+ int ms = ${args[0]} / 1000;
+ boost::this_thread::sleep(boost::posix_time::milliseconds(ms));
+ ${RETURN}(true);
+}
+"""
+# End of interesting part. The rest will take this and turn into a C++
+# header file.
+#############################################################################
+
+HEADER = """<% import time %>//
+///////////////////////////////////////////////////////////////////////
+// This file was generated by ${file} on ${time.strftime("%c")}
+///////////////////////////////////////////////////////////////////////
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+/****************************************************************************
+ * This file is autogenerated! Any manual changes in here will be
+ * overwritten by calling nocscript_gen_basic_funcs.py!
+ ***************************************************************************/
+
+#include "expression.hpp"
+#include "function_table.hpp"
+#include <uhd/exception.hpp>
+#include <boost/format.hpp>
+#include <boost/assign/list_of.hpp>
+${INCLUDE_LIST}
+
+#ifndef INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_BASICFUNCS_HPP
+#define INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_BASICFUNCS_HPP
+
+namespace uhd { namespace rfnoc { namespace nocscript {
+"""
+
+# Not a Mako template:
+FOOTER="""
+}}} /* namespace uhd::rfnoc::nocscript */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_BASICFUNCS_HPP */
+"""
+
+# Not a Mako template:
+FUNC_TEMPLATE = """
+expression_literal {NAME}(expression_container::expr_list_type &{ARGS})
+{BODY}
+"""
+
+REGISTER_MACRO_TEMPLATE = """#define _REGISTER_ALL_FUNCS()${registry}
+"""
+
+REGISTER_COMMANDS_TEMPLATE = """
+ % if len(arglist):
+ expression_function::argtype_list_type ${func_name}_args = boost::assign::list_of
+ % for this_type in arglist:
+ (expression::TYPE_${this_type})
+ % endfor
+ ;
+ % else:
+ expression_function::argtype_list_type ${func_name}_args;
+ % endif
+ register_function(
+ "${name}",
+ boost::bind(&${func_name}, _1),
+ expression::TYPE_${retval},
+ ${func_name}_args
+ );"""
+
+DOXY_TEMPLATE = """/*! \page page_nocscript_funcs NocScript Function Reference
+% for cat, func_by_name in func_list_tree.iteritems():
+- ${cat}
+% for func_name, func_info_list in func_by_name.iteritems():
+ - ${func_name}: ${func_info_list[0]['docstring']}
+% for func_info in func_info_list:
+ - ${func_info['arglist']} -> ${func_info['retval']}
+% endfor
+% endfor
+% endfor
+
+*/
+"""
+
+def parse_tmpl(_tmpl_text, **kwargs):
+ return Template(_tmpl_text).render(**kwargs)
+
+def make_cxx_func_name(func_dict):
+ """
+ Creates a unique C++ function name from a function description
+ """
+ return "{name}__{retval}__{arglist}".format(
+ name=func_dict['name'],
+ retval=func_dict['retval'],
+ arglist="_".join(func_dict['arglist'])
+ )
+
+def make_cxx_func_body(func_dict):
+ """
+ Formats the function body properly
+ """
+ type_lookup_methods = {
+ 'INT': 'get_int',
+ 'DOUBLE': 'get_double',
+ 'BOOL': 'get_bool',
+ 'STRING': 'get_string',
+ }
+ args_lookup = []
+ for idx, arg_type in enumerate(func_dict['arglist']):
+ args_lookup.append("args[{idx}]->eval().{getter}()".format(idx=idx, getter=type_lookup_methods[arg_type]))
+ return parse_tmpl(
+ func_dict['body'],
+ args=args_lookup,
+ FALSE='expression_literal(false)',
+ TRUE='expression_literal(true)',
+ RETURN='return expression_literal',
+ **func_dict
+ )
+
+def prep_function_list():
+ """
+ - Remove all comments
+ - Split the function list into individual functions
+ - Split the functions into return value, name, argument list and body
+ """
+ comment_remove_re = re.compile(r'^\s*#.*$', flags=re.MULTILINE)
+ func_list_wo_comments = comment_remove_re.sub('', FUNCTION_LIST)
+ func_splitter_re = re.compile(r'(?<=^})\s*$', flags=re.MULTILINE)
+ func_list_split = func_splitter_re.split(func_list_wo_comments)
+ func_list_split = [x.strip() for x in func_list_split if len(x.strip())]
+ func_list = []
+ last_category = ''
+ for func in func_list_split:
+ split_regex = r'(^CATEGORY: (?P<cat>[^\n]*)\s*)?' \
+ r'//!(?P<docstring>[^\n]*)\s*' + \
+ r'(?P<retval>[A-Z][A-Z0-9_]*)\s+' + \
+ r'(?P<funcname>[A-Z][A-Z0-9_]*)\s*\((?P<arglist>[^\)]*)\)\s*' + \
+ r'(?P<funcbody>^{.*)'
+ split_re = re.compile(split_regex, flags=re.MULTILINE|re.DOTALL)
+ mo = split_re.match(func)
+ if mo.group('cat'):
+ last_category = mo.group('cat').strip()
+ func_dict = {
+ 'docstring': mo.group('docstring').strip(),
+ 'name': mo.group('funcname'),
+ 'retval': mo.group('retval'),
+ 'arglist': [x.strip() for x in mo.group('arglist').split(',') if len(x.strip())],
+ 'body': mo.group('funcbody'),
+ 'category': last_category,
+ }
+ func_dict['func_name'] = make_cxx_func_name(func_dict)
+ func_list.append(func_dict)
+ return func_list
+
+def write_function_header(output_filename):
+ """
+ Create the .hpp file that defines all the NocScript functions in C++.
+ """
+ func_list = prep_function_list()
+ # Step 1: Write the prototypes
+ func_prototypes = ''
+ registry_commands = ''
+ for func in func_list:
+ func_prototypes += FUNC_TEMPLATE.format(
+ NAME=func['func_name'],
+ BODY=make_cxx_func_body(func),
+ ARGS="args" if len(func['arglist']) else ""
+ )
+ registry_commands += parse_tmpl(
+ REGISTER_COMMANDS_TEMPLATE,
+ **func
+ )
+ # Step 2: Write the registry process
+ register_func = parse_tmpl(REGISTER_MACRO_TEMPLATE, registry=registry_commands)
+ register_func = register_func.replace('\n', ' \\\n')
+
+ # Final step: Join parts and write to file
+ full_file = "\n".join((
+ parse_tmpl(HEADER, file = os.path.basename(__file__), INCLUDE_LIST=INCLUDE_LIST),
+ func_prototypes,
+ register_func,
+ FOOTER,
+ ))
+ open(output_filename, 'w').write(full_file)
+
+def write_manual_file(output_filename):
+ """
+ Write the Doxygen file for the NocScript functions.
+ """
+ func_list = prep_function_list()
+ func_list_tree = {}
+ for func in func_list:
+ if not func_list_tree.has_key(func['category']):
+ func_list_tree[func['category']] = {}
+ if not func_list_tree[func['category']].has_key(func['name']):
+ func_list_tree[func['category']][func['name']] = []
+ func_list_tree[func['category']][func['name']].append(func)
+ open(output_filename, 'w').write(parse_tmpl(DOXY_TEMPLATE, func_list_tree=func_list_tree))
+
+
+def main():
+ if len(sys.argv) < 2:
+ print("No output file specified!")
+ exit(1)
+ outfile = sys.argv[1]
+ if os.path.splitext(outfile)[1] == '.dox':
+ write_manual_file(outfile)
+ else:
+ write_function_header(outfile)
+
+if __name__ == "__main__":
+ main()
diff --git a/host/lib/rfnoc/nocscript/parser.cpp b/host/lib/rfnoc/nocscript/parser.cpp
new file mode 100644
index 000000000..bb7ed6cdf
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/parser.cpp
@@ -0,0 +1,363 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "parser.hpp"
+#include <uhd/utils/cast.hpp>
+#include <boost/spirit/include/lex_lexertl.hpp>
+#include <boost/format.hpp>
+#include <boost/bind.hpp>
+#include <boost/assign.hpp>
+#include <boost/make_shared.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
+ }
+
+ ~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 &e) {
+ 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
+ ));
+}
+
diff --git a/host/lib/rfnoc/nocscript/parser.hpp b/host/lib/rfnoc/nocscript/parser.hpp
new file mode 100644
index 000000000..32fecd2c0
--- /dev/null
+++ b/host/lib/rfnoc/nocscript/parser.hpp
@@ -0,0 +1,50 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "expression.hpp"
+#include "function_table.hpp"
+#include <boost/shared_ptr.hpp>
+
+#ifndef INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_PARSER_HPP
+#define INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_PARSER_HPP
+
+namespace uhd { namespace rfnoc { namespace nocscript {
+
+class parser
+{
+ public:
+ typedef boost::shared_ptr<parser> sptr;
+
+ static sptr make(
+ function_table::sptr ftable,
+ expression_variable::type_getter_type var_type_getter,
+ expression_variable::value_getter_type var_value_getter
+ );
+
+ /*! The main parsing call: Turn a string of code into an expression tree.
+ *
+ * Evaluating the returned object will execute the code.
+ *
+ * \throws uhd::syntax_error if \p code contains syntax errors
+ */
+ virtual expression::sptr create_expr_tree(const std::string &code) = 0;
+};
+
+}}} /* namespace uhd::rfnoc::nocscript */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_PARSER_HPP */
+// vim: sw=4 et: