diff options
| author | Martin Braun <martin.braun@ettus.com> | 2016-08-01 18:17:41 -0700 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2016-08-09 12:42:52 -0700 | 
| commit | 3bf4b000f7d9a7f4af82c21753556ede7e8df6e3 (patch) | |
| tree | 2228d7eb58c4d83d91192cb9b6a908e4e49f6317 /host/lib/rfnoc/nocscript | |
| parent | c5b076173e2d866f3ee99c113a37183c5ec20f0b (diff) | |
| download | uhd-3bf4b000f7d9a7f4af82c21753556ede7e8df6e3.tar.gz uhd-3bf4b000f7d9a7f4af82c21753556ede7e8df6e3.tar.bz2 uhd-3bf4b000f7d9a7f4af82c21753556ede7e8df6e3.zip | |
Merging RFNoC support for X310
Diffstat (limited to 'host/lib/rfnoc/nocscript')
| -rw-r--r-- | host/lib/rfnoc/nocscript/CMakeLists.txt | 37 | ||||
| -rw-r--r-- | host/lib/rfnoc/nocscript/block_iface.cpp | 255 | ||||
| -rw-r--r-- | host/lib/rfnoc/nocscript/block_iface.hpp | 94 | ||||
| -rw-r--r-- | host/lib/rfnoc/nocscript/expression.cpp | 413 | ||||
| -rw-r--r-- | host/lib/rfnoc/nocscript/expression.hpp | 380 | ||||
| -rw-r--r-- | host/lib/rfnoc/nocscript/function_table.cpp | 113 | ||||
| -rw-r--r-- | host/lib/rfnoc/nocscript/function_table.hpp | 93 | ||||
| -rwxr-xr-x | host/lib/rfnoc/nocscript/gen_basic_funcs.py | 474 | ||||
| -rw-r--r-- | host/lib/rfnoc/nocscript/parser.cpp | 363 | ||||
| -rw-r--r-- | host/lib/rfnoc/nocscript/parser.hpp | 50 | 
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: | 
