mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
474 lines
12 KiB
Python
Executable file
474 lines
12 KiB
Python
Executable file
#!/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.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()
|