diff options
Diffstat (limited to 'host/lib/rfnoc/nocscript/gen_basic_funcs.py')
-rwxr-xr-x | host/lib/rfnoc/nocscript/gen_basic_funcs.py | 465 |
1 files changed, 0 insertions, 465 deletions
diff --git a/host/lib/rfnoc/nocscript/gen_basic_funcs.py b/host/lib/rfnoc/nocscript/gen_basic_funcs.py deleted file mode 100755 index fec6b04ad..000000000 --- a/host/lib/rfnoc/nocscript/gen_basic_funcs.py +++ /dev/null @@ -1,465 +0,0 @@ -#!/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 <chrono> -#include <thread> -""" -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; - std::this_thread::sleep_for(std::chrono::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 -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -/**************************************************************************** - * 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.items(): -- ${cat} -% for func_name, func_info_list in func_by_name.items(): - - ${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 func['category'] not in func_list_tree: - func_list_tree[func['category']] = {} - if func['name'] not in func_list_tree[func['category']]: - 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() |