Repository: jonathf/matlab2cpp Branch: master Commit: b6e2cbaedb36 Files: 151 Total size: 627.2 KB Directory structure: gitextract_dk9ut9qi/ ├── .circleci/ │ └── config.yml ├── .coveragerc ├── .gitignore ├── LICENSE ├── PKG-INFO ├── README.rst ├── conftest.py ├── doc/ │ ├── previous_doc/ │ │ ├── Makefile │ │ └── source/ │ │ ├── conf.py │ │ ├── dev00_overview.rst │ │ ├── dev01_qfuncs.rst │ │ ├── dev02_tree.rst │ │ ├── dev03_node.rst │ │ ├── dev04_datatype.rst │ │ ├── dev05_configure.rst │ │ ├── dev06_collection.rst │ │ ├── dev07_rules.rst │ │ ├── dev08_supplement.rst │ │ ├── dev09_testsuite.rst │ │ ├── index.rst │ │ ├── usr00_intro.rst │ │ ├── usr01_interaction.rst │ │ ├── usr02_datatype.rst │ │ ├── usr03_rules.rst │ │ └── usr04_node.rst │ └── user_manual/ │ ├── Makefile │ └── source/ │ ├── conf.py │ ├── conf.py~ │ ├── dev00_overview.rst~ │ ├── index.rst │ ├── index.rst~ │ ├── usr00_intro.rst~ │ ├── usr02_datatype.rst │ ├── usr03_rules.rst │ └── usr04_node.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── src/ │ └── matlab2cpp/ │ ├── __init__.py │ ├── collection.py │ ├── configure/ │ │ ├── __init__.py │ │ ├── armadillo.py │ │ ├── backends.py │ │ ├── datatypes.py │ │ ├── frontend.py │ │ ├── funcs.py │ │ └── reserved.py │ ├── datatype.py │ ├── frontend.py │ ├── manual/ │ │ ├── __init__.py │ │ ├── usr00_introduction.py │ │ ├── usr01_interaction.py │ │ ├── usr02_datatype.py │ │ ├── usr03_rules.py │ │ ├── usr04_node.py │ │ └── usr05_installation.py │ ├── matlab_types.py │ ├── modify.py │ ├── mwhos.py │ ├── mwrapmat.py │ ├── node/ │ │ ├── __init__.py │ │ ├── backend.py │ │ ├── frontend.py │ │ ├── m2cpp.py │ │ └── reference.py │ ├── parser.py │ ├── pyplot.py │ ├── qfunctions.py │ ├── rules/ │ │ ├── __init__.py │ │ ├── _cell.py │ │ ├── _char.py │ │ ├── _code_block.py │ │ ├── _cube.py │ │ ├── _cx_cube.py │ │ ├── _cx_double.py │ │ ├── _cx_mat.py │ │ ├── _cx_rowvec.py │ │ ├── _cx_vec.py │ │ ├── _double.py │ │ ├── _expression.py │ │ ├── _fcube.py │ │ ├── _float.py │ │ ├── _fmat.py │ │ ├── _frowvec.py │ │ ├── _func_lambda.py │ │ ├── _func_return.py │ │ ├── _func_returns.py │ │ ├── _fvec.py │ │ ├── _icube.py │ │ ├── _imat.py │ │ ├── _int.py │ │ ├── _irowvec.py │ │ ├── _ivec.py │ │ ├── _mat.py │ │ ├── _matrix.py │ │ ├── _program.py │ │ ├── _reserved.py │ │ ├── _rowvec.py │ │ ├── _size_t.py │ │ ├── _string.py │ │ ├── _struct.py │ │ ├── _structs.py │ │ ├── _ucube.py │ │ ├── _umat.py │ │ ├── _unknown.py │ │ ├── _urowvec.py │ │ ├── _uvec.py │ │ ├── _uword.py │ │ ├── _vec.py │ │ ├── _verbatim.py │ │ ├── armadillo.py │ │ ├── assign.py │ │ ├── cube.py │ │ ├── function.py │ │ ├── mat.py │ │ ├── parallel.py │ │ ├── rowvec.py │ │ ├── variables.py │ │ └── vec.py │ ├── setpaths.py │ ├── supplement/ │ │ ├── __init__.py │ │ ├── functions.py │ │ ├── includes.py │ │ ├── structs.py │ │ ├── suggests.py │ │ └── verbatim.py │ └── tree/ │ ├── __init__.py │ ├── assign.py │ ├── branches.py │ ├── builder.py │ ├── codeblock.py │ ├── constants.py │ ├── expression.py │ ├── findend.py │ ├── functions.py │ ├── identify.py │ ├── iterate.py │ ├── misc.py │ ├── suppliment.py │ └── variables.py └── test/ ├── data/ │ ├── function_reference.hpp │ ├── function_reference.m │ ├── function_reference_2.hpp │ ├── function_reference_2.m │ ├── fx_decon.cpp │ ├── fx_decon.m │ ├── fx_decon.m.py │ ├── simple_assignment.cpp │ └── simple_assignment.m ├── test_conversion.py └── test_simple_assignment.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ version: 2 jobs: build: working_directory: ~/matlab2cpp docker: - image: circleci/python:2.7.14 steps: - checkout - restore_cache: key: reqs-{{ checksum "requirements.txt" }} - run: name: "Install Python requirements" command: | virtualenv venv . venv/bin/activate pip install -Ur requirements.txt pip install -e . - save_cache: key: reqs-{{ checksum "requirements.txt" }} paths: - venv - run: name: "Run tests" command: | . venv/bin/activate python setup.py test - run: name: "Coverage report" command: | . venv/bin/activate codecov ================================================ FILE: .coveragerc ================================================ [run] branch = True source = codecov [report] exclude_lines = coverage: ignore def __repr__ raise NotImplementedError if __name__ == .__main__.: ignore_errors = True precision = 2 skip_covered = True omit = doc/* test/* setup.py ================================================ FILE: .gitignore ================================================ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ ================================================ FILE: LICENSE ================================================ Copyright (c) 2015, jonathf All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of matlab2cpp nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: PKG-INFO ================================================ Metadata-Version: 1.0 Name: matlab2cpp Version: 0.2 Summary: A semi-automatic tool for converting Matlab to C++ Home-page: http://www.github.org/jonathf/matlab2cpp Author: Jonathan Feinberg Author-email: jonathan@feinberg.no License: BSD Description: UNKNOWN Platform: Mac, Linux and Windows ================================================ FILE: README.rst ================================================ .. attention:: Matlab2cpp er currently unmaintained. As a mainteiner this project ended up on the short end of the stick of what I unfortunatly have time for. Anyone who want to make changes to it, might do so. I am very open to a change in overship. I am sorry for the inconvinience. Jonathan ========== Matlab2Cpp ========== ``matlab2cpp`` is a semi-automatic tool for converting code from Matlab to C++. After installing, the ``matlab2cpp`` command line executable ``m2cpp`` will be available in path that can be used to convert Matlab code. Note that it is not meant as a complete tool for creating runnable C++ code. For example, the `eval`-function can not be supported because there is no general way to implement it in C++. Instead the program is a support tool, which aims at speed up the conversion process as much as possible for a user that needs to convert Matlab programs by hand anyway. The software does this by converting the basic structures of the Matlab-program (functions, branches, loops, etc.), adds variable declarations, and for some simple code, do a complete translation. And any problem the program encounters during conversion will be written in a log-file. From there manual conversions can be done by hand. Currently, the code will not convert the large library collection of functions that Matlab currently possesses. However, there is no reason for the code not to support these features in time. The extension library is easy to extend. Installation ------------ Installation by running the ``pip`` command:: pip install matlab2cpp The source-to-source parser do not have any requirements beyond having Python installed. However, the generated output does have a few requirements to be compilable. They are as follows. ``C++11`` Code produces follows the ``C++11`` standard. ``armadillo`` Armadillo is a linear algebra library for the C++ language. The Armadillo library can be found at `http://arma.sourceforge.net`_. Some functionality in Armadillo rely on a math library like LAPACK, BLAS, OpenBLAS or MKL. When installing Armadillo, it will look for installed math libraries. If Armadillo is installed, the library can be linked with the link flag ``-l armadillo``. Armadillo can also be linked directly, see the ``FAQ`` at the Armadillo webpage for more information. I believe MKL is the fastest math library and it can be downloaded for free at `https://software.intel.com/en-us/articles/free-mkl`_. ``TBB`` By inserting pragmas in the code, for loops can be marked by the user. The program can then either insert ``OpenMP`` or ``TBB`` code to parallelize the for loop. To compile ``TBB`` code, the ``TBB`` library has to be installed. See :ref:`parallel_flags` for more details. An illustrating Example ----------------------- Assuming Linux installation and `m2cpp` is available in path. Code works analogous in Mac and Windows. Consider a file `example.m` with the following content:: function y=f(x) y = x+4 end function g() x = [1,2,3] f(x) end Run conversion on the file: :: $ m2cpp example.m This will create two files: ``example.m.hpp`` and ``example.m.py``. In ``example.m.hpp``, the translated C++ code is placed. It looks as follows:: #include using namespace arma ; TYPE f(TYPE x) { TYPE y ; y = x+4 ; return y ; } void g() { TYPE x ; x = [1, 2, 3] ; f(x) ; } Matlab doesn't declare variables explicitly, so m2cpp is unable to complete the translation. To create a full conversion, the variables must be declared. Declarations can be done in the file ``example.m.py``. After the first run, it will look as follows:: # Supplement file # # Valid inputs: # # uint int float double cx_double # uvec ivec fvec vec cx_vec # urowvec irowvec frowvec rowvec cx_rowvec # umat imat fmat mat cx_mat # ucube icube fcube cube cx_cube # # char string struct structs func_lambda functions = { "f" : { "y" : "", "x" : "", }, "g" : { "x" : "", }, } includes = [ '#include ', 'using namespace arma ;', ] In addition to defining includes at the bottom, it is possible to declare variables manually by inserting type names into the respective empty strings. However, some times it is possible to guess some of the variable types from context. To let the software try to guess variable types, run conversion with the ``-s`` flag:: $ m2cpp example.m -s The file ``example.m.py`` will then automatically be populated with data types from context:: # ... functions = { "f" : { "y" : "irowvec", "x" : "irowvec", }, "g" : { "x" : "irowvec", }, } includes = [ '#include ', 'using namespace arma ;', ] It will not always be successful and some of the types might in some cases be wrong. It is therefore also possible to adjust these values manually at any time. Having run the conversion with the variables converted, creates a new output for ``example.m.hpp``:: #include using namespace arma ; irowvec f(irowvec x) { irowvec y ; y = x+4 ; return y ; } void g() { irowvec x ; int _x [] = [1, 2, 3] ; x = irowvec(_x, 3, false) ; f(x) ; } This is valid and runnable C++ code. For such a small example, no manual adjustments were necessary. ================================================ FILE: conftest.py ================================================ """Global configuration.""" import os import inspect import shutil import pytest import matlab2cpp from matlab2cpp import collection @pytest.fixture(scope="session") def workspace_folder(tmpdir_factory): """Create a temporary folder to perform tests from.""" return str(tmpdir_factory.mktemp("workspace")) @pytest.fixture(scope="function", autouse=True) def workspace(workspace_folder, doctest_namespace): """Fill temporary folder for each test.""" # move data to workspace: source = os.path.join(os.path.dirname(inspect.stack()[0][1]), "test", "data") if os.path.isdir(workspace_folder): shutil.rmtree(workspace_folder) shutil.copytree(source, workspace_folder) # add content to doctest namespace: doctest_namespace["workspace"] = workspace_folder doctest_namespace["matlab2cpp"] = matlab2cpp doctest_namespace["collection"] = collection # change to workspace: curdir = os.path.abspath(os.path.curdir) os.chdir(workspace_folder) yield workspace_folder # clean up: os.chdir(curdir) shutil.rmtree(workspace_folder) ================================================ FILE: doc/previous_doc/Makefile ================================================ # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/matlab2cpp.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/matlab2cpp.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/matlab2cpp" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/matlab2cpp" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ================================================ FILE: doc/previous_doc/source/conf.py ================================================ # -*- coding: utf-8 -*- # # matlab2cpp documentation build configuration file, created by # sphinx-quickstart on Mon Jun 8 10:41:13 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import shlex # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinxcontrib.autoprogram", "sphinxarg.ext"] napoleon_google_docstring = True napoleon_numpy_docstring = True napoleon_include_private_with_doc = False napoleon_include_special_with_doc = False napoleon_use_admonition_for_examples = False napoleon_use_admonition_for_notes = False napoleon_use_admonition_for_references = False napoleon_use_ivar = False napoleon_use_param = True napoleon_use_rtype = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'matlab2cpp' copyright = u'2015, Jonathan Feinberg' author = u'Jonathan Feinberg' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.5' # The full version, including alpha/beta/rc tags. release = '0.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'classic' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. html_split_index = True # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'matlab2cppdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'matlab2cpp.tex', u'matlab2cpp Documentation', u'Jonathan Feinberg', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'matlab2cpp', u'matlab2cpp Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'matlab2cpp', u'matlab2cpp Documentation', author, 'matlab2cpp', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False ================================================ FILE: doc/previous_doc/source/dev00_overview.rst ================================================ .. _dev00: Module overview =============== .. automodule:: matlab2cpp ================================================ FILE: doc/previous_doc/source/dev01_qfuncs.rst ================================================ .. _dev01: Quick translation functions --------------------------- .. automodule:: matlab2cpp.qfunctions .. autofunction:: matlab2cpp.build .. autofunction:: matlab2cpp.qcpp .. autofunction:: matlab2cpp.qhpp .. autofunction:: matlab2cpp.qpy .. autofunction:: matlab2cpp.qlog .. autofunction:: matlab2cpp.qscript .. autofunction:: matlab2cpp.qtree ================================================ FILE: doc/previous_doc/source/dev02_tree.rst ================================================ .. _dev02: The tree constructor ==================== .. automodule:: matlab2cpp.tree The Builder class ----------------- .. autoclass:: matlab2cpp.Builder :members: :special-members: Assignment constructors ----------------------- .. automodule:: matlab2cpp.tree.assign :members: Loop and branch constructors ---------------------------- .. automodule:: matlab2cpp.tree.branches :members: Code block constructor ---------------------- .. automodule:: matlab2cpp.tree.codeblock :members: Expression constructor ---------------------- .. automodule:: matlab2cpp.tree.expression :members: Function constructors --------------------- .. automodule:: matlab2cpp.tree.functions :members: Miscelenious constructors ------------------------- .. automodule:: matlab2cpp.tree.misc :members: Variable constructors --------------------- .. automodule:: matlab2cpp.tree.variables :members: Find-end functions ------------------ .. automodule:: matlab2cpp.tree.findend :members: Iterators --------- .. automodule:: matlab2cpp.tree.iterate :members: Identify structures ------------------- .. automodule:: matlab2cpp.tree.identify :members: Matlab constants ---------------- .. automodule:: matlab2cpp.tree.constants :members: ================================================ FILE: doc/previous_doc/source/dev03_node.rst ================================================ .. _dev03: Node representation =================== .. automodule:: matlab2cpp.node The Node class -------------- .. autoclass:: matlab2cpp.Node :members: Node backend ------------ .. automodule:: matlab2cpp.node.backend Quick references ---------------- .. automodule:: matlab2cpp.node.reference ================================================ FILE: doc/previous_doc/source/dev04_datatype.rst ================================================ .. _dev04: Datatypes ========= .. automodule:: matlab2cpp.datatype ================================================ FILE: doc/previous_doc/source/dev05_configure.rst ================================================ .. _dev05: Auto-configure datatype ======================= .. automodule:: matlab2cpp.configure ================================================ FILE: doc/previous_doc/source/dev06_collection.rst ================================================ .. _dev06: Collection ========== .. automodule:: matlab2cpp.collection .. autoclass:: matlab2cpp.collection.All .. autoclass:: matlab2cpp.collection.Assign .. autoclass:: matlab2cpp.collection.Assigns .. autoclass:: matlab2cpp.collection.Band .. autoclass:: matlab2cpp.collection.Bcomment .. autoclass:: matlab2cpp.collection.Block .. autoclass:: matlab2cpp.collection.Bor .. autoclass:: matlab2cpp.collection.Branch .. autoclass:: matlab2cpp.collection.Break .. autoclass:: matlab2cpp.collection.Case .. autoclass:: matlab2cpp.collection.Catch .. autoclass:: matlab2cpp.collection.Cell .. autoclass:: matlab2cpp.collection.Cget .. autoclass:: matlab2cpp.collection.Colon .. autoclass:: matlab2cpp.collection.Counter .. autoclass:: matlab2cpp.collection.Cset .. autoclass:: matlab2cpp.collection.Ctranspose .. autoclass:: matlab2cpp.collection.Cvar .. autoclass:: matlab2cpp.collection.Declares .. autoclass:: matlab2cpp.collection.Ecomment .. autoclass:: matlab2cpp.collection.Elementdivision .. autoclass:: matlab2cpp.collection.Elexp .. autoclass:: matlab2cpp.collection.Elif .. autoclass:: matlab2cpp.collection.Elmul .. autoclass:: matlab2cpp.collection.Else .. autoclass:: matlab2cpp.collection.End .. autoclass:: matlab2cpp.collection.Eq .. autoclass:: matlab2cpp.collection.Error .. autoclass:: matlab2cpp.collection.Exp .. autoclass:: matlab2cpp.collection.Expr .. autoclass:: matlab2cpp.collection.Fget .. autoclass:: matlab2cpp.collection.Float .. autoclass:: matlab2cpp.collection.For .. autoclass:: matlab2cpp.collection.Fset .. autoclass:: matlab2cpp.collection.Func .. autoclass:: matlab2cpp.collection.Funcs .. autoclass:: matlab2cpp.collection.Fvar .. autoclass:: matlab2cpp.collection.Ge .. autoclass:: matlab2cpp.collection.Get .. autoclass:: matlab2cpp.collection.Gt .. autoclass:: matlab2cpp.collection.Header .. autoclass:: matlab2cpp.collection.Headers .. autoclass:: matlab2cpp.collection.If .. autoclass:: matlab2cpp.collection.Imag .. autoclass:: matlab2cpp.collection.Include .. autoclass:: matlab2cpp.collection.Includes .. autoclass:: matlab2cpp.collection.Inline .. autoclass:: matlab2cpp.collection.Inlines .. autoclass:: matlab2cpp.collection.Int .. autoclass:: matlab2cpp.collection.Lambda .. autoclass:: matlab2cpp.collection.Land .. autoclass:: matlab2cpp.collection.Lcomment .. autoclass:: matlab2cpp.collection.Le .. autoclass:: matlab2cpp.collection.Leftelementdivision .. autoclass:: matlab2cpp.collection.Leftmatrixdivision .. autoclass:: matlab2cpp.collection.Log .. autoclass:: matlab2cpp.collection.Lor .. autoclass:: matlab2cpp.collection.Lt .. autoclass:: matlab2cpp.collection.Main .. autoclass:: matlab2cpp.collection.Matrix .. autoclass:: matlab2cpp.collection.Matrixdivision .. autoclass:: matlab2cpp.collection.Minus .. autoclass:: matlab2cpp.collection.Mul .. autoclass:: matlab2cpp.collection.Ne .. autoclass:: matlab2cpp.collection.Neg .. autoclass:: matlab2cpp.collection.Nget .. autoclass:: matlab2cpp.collection.Not .. autoclass:: matlab2cpp.collection.Nset .. autoclass:: matlab2cpp.collection.Opr .. autoclass:: matlab2cpp.collection.Otherwise .. autoclass:: matlab2cpp.collection.Params .. autoclass:: matlab2cpp.collection.Paren .. autoclass:: matlab2cpp.collection.Plus .. autoclass:: matlab2cpp.collection.Program .. autoclass:: matlab2cpp.collection.Project .. autoclass:: matlab2cpp.collection.Resize .. autoclass:: matlab2cpp.collection.Return .. autoclass:: matlab2cpp.collection.Returns .. autoclass:: matlab2cpp.collection.Set .. autoclass:: matlab2cpp.collection.Sget .. autoclass:: matlab2cpp.collection.Sset .. autoclass:: matlab2cpp.collection.Statement .. autoclass:: matlab2cpp.collection.String .. autoclass:: matlab2cpp.collection.Struct .. autoclass:: matlab2cpp.collection.Structs .. autoclass:: matlab2cpp.collection.Switch .. autoclass:: matlab2cpp.collection.Transpose .. autoclass:: matlab2cpp.collection.Try .. autoclass:: matlab2cpp.collection.Tryblock .. autoclass:: matlab2cpp.collection.Var .. autoclass:: matlab2cpp.collection.Vector .. autoclass:: matlab2cpp.collection.Warning .. autoclass:: matlab2cpp.collection.While ================================================ FILE: doc/previous_doc/source/dev07_rules.rst ================================================ .. _dev07: Translation rules ================= .. automodule:: matlab2cpp.rules Datatype driven rules --------------------- .. automodule:: matlab2cpp.rules._cell .. automodule:: matlab2cpp.rules._char .. automodule:: matlab2cpp.rules._cube .. automodule:: matlab2cpp.rules._cx_cube .. automodule:: matlab2cpp.rules._cx_double .. automodule:: matlab2cpp.rules._cx_mat .. automodule:: matlab2cpp.rules._cx_rowvec .. automodule:: matlab2cpp.rules._cx_vec .. automodule:: matlab2cpp.rules._double .. automodule:: matlab2cpp.rules._fcube .. automodule:: matlab2cpp.rules._float .. automodule:: matlab2cpp.rules._fmat .. automodule:: matlab2cpp.rules._frowvec .. automodule:: matlab2cpp.rules._fvec .. automodule:: matlab2cpp.rules._icube .. automodule:: matlab2cpp.rules._imat .. automodule:: matlab2cpp.rules._int .. automodule:: matlab2cpp.rules._irowvec .. automodule:: matlab2cpp.rules._ivec .. automodule:: matlab2cpp.rules._mat .. automodule:: matlab2cpp.rules._rowvec .. automodule:: matlab2cpp.rules._string .. automodule:: matlab2cpp.rules._struct .. automodule:: matlab2cpp.rules._structs .. automodule:: matlab2cpp.rules._ucube .. automodule:: matlab2cpp.rules._umat .. automodule:: matlab2cpp.rules._urowvec .. automodule:: matlab2cpp.rules._uvec .. automodule:: matlab2cpp.rules._uword .. automodule:: matlab2cpp.rules._vec Other rules ----------- .. automodule:: matlab2cpp.rules._code_block .. automodule:: matlab2cpp.rules._expression .. automodule:: matlab2cpp.rules._func_lambda .. automodule:: matlab2cpp.rules._func_return .. automodule:: matlab2cpp.rules._func_returns .. automodule:: matlab2cpp.rules._matrix .. automodule:: matlab2cpp.rules._program .. automodule:: matlab2cpp.rules._reserved .. automodule:: matlab2cpp.rules._unknown .. automodule:: matlab2cpp.rules._verbatim ================================================ FILE: doc/previous_doc/source/dev08_supplement.rst ================================================ .. _dev08: Datatype scope ============== .. automodule:: matlab2cpp.supplement Input/Output classes -------------------- .. autoclass:: matlab2cpp.supplement.Ftypes .. autoclass:: matlab2cpp.supplement.Stypes .. autoclass:: matlab2cpp.supplement.Itypes .. autoclass:: matlab2cpp.supplement.Vtypes Stringify supplement file ------------------------- .. autofunction:: matlab2cpp.supplement.str_variables ================================================ FILE: doc/previous_doc/source/dev09_testsuite.rst ================================================ .. _dev09: Testsuite ========= .. automodule:: matlab2cpp.testsuite ================================================ FILE: doc/previous_doc/source/index.rst ================================================ Matlab2cpp Manual ================= =========== User Manual =========== .. toctree:: :maxdepth: 3 usr00_intro usr01_interaction usr02_datatype usr03_rules usr04_node ================ Developer Manual ================ .. toctree:: :maxdepth: 3 dev00_overview dev01_qfuncs dev02_tree dev03_node dev04_datatype dev05_configure dev06_collection dev07_rules dev08_supplement dev09_testsuite ================================================ FILE: doc/previous_doc/source/usr00_intro.rst ================================================ .. _usr00: Introduction ============ .. include:: ../../README.md ================================================ FILE: doc/previous_doc/source/usr01_interaction.rst ================================================ .. automodule:: matlab2cpp.manual.usr01_interaction ================================================ FILE: doc/previous_doc/source/usr02_datatype.rst ================================================ .. automodule:: matlab2cpp.manual.usr02_datatype ================================================ FILE: doc/previous_doc/source/usr03_rules.rst ================================================ .. automodule:: matlab2cpp.manual.usr03_rules ================================================ FILE: doc/previous_doc/source/usr04_node.rst ================================================ .. automodule:: matlab2cpp.manual.usr04_node ================================================ FILE: doc/user_manual/Makefile ================================================ # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/matlab2cpp.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/matlab2cpp.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/matlab2cpp" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/matlab2cpp" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ================================================ FILE: doc/user_manual/source/conf.py ================================================ # -*- coding: utf-8 -*- # # matlab2cpp documentation build configuration file, created by # sphinx-quickstart on Mon Jun 8 10:41:13 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import shlex # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../../..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinxcontrib.autoprogram", "sphinxarg.ext"] napoleon_google_docstring = True napoleon_numpy_docstring = True napoleon_include_private_with_doc = False napoleon_include_special_with_doc = False napoleon_use_admonition_for_examples = False napoleon_use_admonition_for_notes = False napoleon_use_admonition_for_references = False napoleon_use_ivar = False napoleon_use_param = True napoleon_use_rtype = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'matlab2cpp' copyright = u'2015, Jonathan Feinberg' author = u'Jonathan Feinberg' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.5' # The full version, including alpha/beta/rc tags. release = '0.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'classic' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. html_split_index = True # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'matlab2cppdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'matlab2cpp.tex', u'matlab2cpp User Documentation', u'Jonathan Feinberg', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'matlab2cpp', u'matlab2cpp Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'matlab2cpp', u'matlab2cpp Documentation', author, 'matlab2cpp', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False ================================================ FILE: doc/user_manual/source/conf.py~ ================================================ # -*- coding: utf-8 -*- # # matlab2cpp documentation build configuration file, created by # sphinx-quickstart on Mon Jun 8 10:41:13 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import shlex # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../../..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinxcontrib.autoprogram", "sphinxarg.ext"] napoleon_google_docstring = True napoleon_numpy_docstring = True napoleon_include_private_with_doc = False napoleon_include_special_with_doc = False napoleon_use_admonition_for_examples = False napoleon_use_admonition_for_notes = False napoleon_use_admonition_for_references = False napoleon_use_ivar = False napoleon_use_param = True napoleon_use_rtype = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'matlab2cpp' copyright = u'2015, Jonathan Feinberg' author = u'Jonathan Feinberg' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.5' # The full version, including alpha/beta/rc tags. release = '0.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'classic' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. html_split_index = True # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'matlab2cppdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'matlab2cpp.tex', u'matlab2cpp Documentation', u'Jonathan Feinberg', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'matlab2cpp', u'matlab2cpp Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'matlab2cpp', u'matlab2cpp Documentation', author, 'matlab2cpp', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False ================================================ FILE: doc/user_manual/source/dev00_overview.rst~ ================================================ .. _dev00: Module overview =============== .. automodule:: matlab2cpp ================================================ FILE: doc/user_manual/source/index.rst ================================================ Matlab2cpp User Manual ================= =========== User Manual =========== .. toctree:: :maxdepth: 3 intro installation interaction usr02_datatype usr03_rules usr04_node ================================================ FILE: doc/user_manual/source/index.rst~ ================================================ Matlab2cpp User Manual ================= =========== User Manual =========== .. toctree:: :maxdepth: 3 usr00_intro usr01_interaction usr02_datatype usr03_rules usr04_node ================================================ FILE: doc/user_manual/source/usr00_intro.rst~ ================================================ .. _usr00: Introduction ************ .. .. include:: ../../../README.md .. automodule:: matlab2cpp.manual.usr00_introduction ================================================ FILE: doc/user_manual/source/usr02_datatype.rst ================================================ .. .. automodule:: matlab2cpp.manual.usr02_datatype .. _usr02: Configuring translation ======================= One of the translation challenges is how each variable type is determined. In C++ all variables have to be explicitly declared, while in Matlab they are declared implicitly at creation. When translating between the two languages, there are many variables where the data types are unknown and impossible for the Matlab2cpp software to translate. How to translate the behavior of an integer is vastly different from an float matrix. To differentiate between types, each node have an attribute :py:attr:`~matlab2cpp.Node.type` which represents the node datatype. Datatypes can be roughly split into two groups: **numerical** and **non-numerical** types. The numerical types are as follows: +---------------+--------------+---------+---------+--------+-----------+ | | unsigned int | integer | float | double | complex | +===============+==============+=========+=========+========+===========+ | `scalar` | uword | int | float | double | cx_double | +---------------+--------------+---------+---------+--------+-----------+ | `vector` | uvec | ivec | fvec | vec | cx_vec | +---------------+--------------+---------+---------+--------+-----------+ | `row\-vector` | urowvec | irowvec | frowvec | rowvec | cx_rowvec | +---------------+--------------+---------+---------+--------+-----------+ | `matrix` | umat | imat | fmat | mat | cx_mat | +---------------+--------------+---------+---------+--------+-----------+ | `cube` | ucube | icube | fcube | cube | cx_cube | +---------------+--------------+---------+---------+--------+-----------+ Values along the horizontal axis represents the amount of memory reserved per element, and the along the vertical axis represents the various number of dimensions. The names are equivalent to the ones in the Armadillo package. The non-numerical types are as follows: +----------------------------------+------------------------+ | Name | Description | +==================================+========================+ | `char` | Single text character | +----------------------------------+------------------------+ | `string` | Text string | +----------------------------------+------------------------+ | :ref:`struct ` | Struct container | +----------------------------------+------------------------+ | :ref:`structs ` | Struct array container | +----------------------------------+------------------------+ | :ref:`func_lambda ` | Anonymous function | +----------------------------------+------------------------+ Function scope -------------- If not specified otherwise, the program will not assign datatype types to any of variables. The user could in theory navigate the node tree and assign the variables one by one using the node attributes to navigate. (See section :ref:`usr04` for details.) However that would be very cumbersome. Instead the datatypes are define collectively inside their scope. In the case of variables in functions, the scope variables are the variables declaration :py:class:`~matlab2cpp.Declares` and function parameters :py:class:`~matlab2cpp.Params`. To reach the variable that serves as a scope-wide type, the node attribute :py:attr:`~matlab2cpp.Node.declare` can be used. Manually interacting with the variable scope is simpler then iterating through the full tree, but can in many cases still be cumbersome. To simplefy interaction with datatype scopes, each program has an suppliment attribute :py:attr:`~matlab2cpp.Node.ftypes`. The attribute is a nested dictionary where the outer shell represents the function name the variables are defined. The inner shell is the variables where keys are variable names and values are types. It can be used to quickly retrievieng and inserting datatypes. For example:: >>> tree = mc.build("function f(a)") >>> print tree.ftypes {'f': {'a': ''}} >>> tree.ftypes = {"f": {"a": "int"}} >>> print mc.qscript(tree) void f(int a) { // Empty block } .. _func_lambda: Anonymous functions ------------------- In addition to normal function, Matlab have support for anonymous function through the name prefix ``@``. For example:: >>> print mc.qscript("function f(); g = @(x) x^2; g(4)") void f() { std::function g ; g = [] (int x) {pow(x, 2) ; } ; g(4) ; } The translator creates an ``C++11`` lambda function with equivalent functionality. To achieve this, the translator creates an extra function in the node-tree. The name of the function is the same as assigned variable with a ``_``-prefix (and a number postfix, if name is taken). The information about this function dictate the behaviour of the output The supplement file have the following form:: >>> print mc.qpy("function f(); g = @(x) x^2; g(4)") functions = { "_g" : { "x" : "int", }, "f" : { "g" : "func_lambda", }, } includes = [ '#include ', 'using namespace arma ;', ] The function `g` is a variable inside `f`'s function scope. It has the datatype `func_lambda` to indicate that it should be handled as a function. The associated function scope `_g` contains the variables inside the definition of the anonymous function. .. _struct: Data structure -------------- Data structures in Matlab can be constructed explicitly through the ``struct``-function. However, they can also be constructed implicitly by direct assignment. For example will ``a.b=4`` create a ``struct`` with name ``a`` that has one field ``b``. When translating such a snippet, it creates a C++-struct, such that:: >>> print mc.qhpp("function f(); a.b = 4.", suggest=True) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; struct _A { double b ; } ; void f() { _A a ; a.b = 4. ; } #endif In the suppliment file, the local variable `a` will be assigned as a `struct`. In addition, since the struct has content, the suppliment file creates a new section for structs. It will have the following form:: >>> print mc.qpy("function f(); a.b = 4.", suggest=True) functions = { "f" : { "a" : "struct", }, } structs = { "a" : { "b" : "double", }, } includes = [ '#include ', 'using namespace arma ;', ] Quick retrieving and inserting struct variables can be done through the :py:attr:`~matlab2cpp.Node.stypes` attribute:: >>> tree = mc.build("a.b = 4") >>> tree.ftypes = {"f": {"a": "struct"}} >>> tree.stypes = {"a": {"b": "double"}} >>> print mc.qcpp(tree) #include using namespace arma ; struct _A { double b ; } ; int main(int argc, char** argv) { _A a ; a.b = 4 ; return 0 ; } .. _structs: Struct tables ------------- Given that the data structure is indexed, e.g. ``a(1).b``, it forms a struct table. Very similar to regular :ref:`structs `, which only has one value per element. There are a couple of differences in the translation. First, the struct is declared as an array: >>> print mc.qhpp("function f(); a(1).b = 4.", suggest=True) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; struct _A { double b ; } ; void f() { _A a[100] ; a[0].b = 4. ; } #endif The translation assigned reserves 100 pointers for the content of ``a``. Obviously, there are situations where this isn't enough (or too much), and the number should be increased. So second, to adjust this number, the suppliment file specifies the number of elements in the integer ``_size``: >>> print mc.qpy("function f(); a(1).b = 4.", suggest=True) functions = { "f" : { "a" : "structs", }, } structs = { "a" : { "_size" : 100, "b" : "double", }, } includes = [ '#include ', 'using namespace arma ;', ] .. _usr02_suggestion_engine: Suggestion engine ----------------- The examples so far, when the functions :py:func:`~matlab2cpp.qcpp`, :py:func:`~matlab2cpp.qhpp` and :py:func:`~matlab2cpp.qpy` are used, the argument ``suggest=True`` have been used, and all variable types have been filled in. Consider the following program where this is not the case:: >>> print mc.qhpp("function c=f(); a = 4; b = 4.; c = a+b", suggest=False) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; TYPE f() { TYPE a, b, c ; a = 4 ; b = 4. ; c = a+b ; return c ; } #endif Since all variables are unknown, the program decides to fill in the dummy variable ``TYPE`` for each unknown variable. Any time variables are unknown, ``TYPE`` is used. The supplement file created by `m2cpp` or :py:func:`~matlab2cpp.qpy` reflects all these unknown variables as follows:: >>> print mc.qpy("function c=f(); a = 4; b = 4.; c = a+b", suggest=False) functions = { "f" : { "a" : "", # int "b" : "", # double "c" : "", }, } includes = [ '#include ', 'using namespace arma ;', ] By flipping the boolean to ``True``, all the variables get assigned datatypes:: >>> print mc.qpy("function c=f(); a = 4; b = 4.; c = a+b", suggest=True) functions = { "f" : { "a" : "int", "b" : "double", "c" : "double", }, } includes = [ '#include ', 'using namespace arma ;', ] The resulting program will have the following complete form: >>> print mc.qhpp( ... "function c=f(); a = 4; b = 4.; c = a+b", suggest=True) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; double f() { double b, c ; int a ; a = 4 ; b = 4. ; c = a+b ; return c ; } #endif Note here though that the variable ``c`` didn't have a suggestion. The suggestion is an interactive process such that ``a`` and ``b`` both must be known beforehand. The variable ``a`` and ``b`` get assigned the datatypes ``int`` and ``double`` because of the direct assignment of variable. After this, the process starts over and tries to find other variables that suggestion could fill out for. In the case of the ``c`` variable, the assignment on the right were and addition between ``int`` and ``double``. To not loose precision, it then chooses to keep `double`, which is passed on to the ``c`` variable. In practice the suggestions can potentially fill in all datatypes automatically in large programs, and often quite intelligently. For example, variables get suggested across function call scope:: >>> print mc.qscript('function y=f(x); y=x; function g(); z=f(4)') int f(int x) { int y ; y = x ; return y ; } void g() { int z ; z = f(4) ; } And accross multiple files:: >>> builder = mc.Builder() >>> builder.load("f.m", "function y=f(x); y=x") >>> builder.load("g.m", "function g(); z=f(4)") >>> builder.configure(suggest=True) >>> tree_f, tree_g = builder[:] >>> print mc.qscript(tree_f) int f(int x) { int y ; y = x ; return y ; } >>> print mc.qscript(tree_g) void g() { int z ; z = f(4) ; } Verbatim translations --------------------- In some cases, the translation can not be performed. For example, the Matlab function ``eval`` can not be properly translated. Matlab is interpreted, and can easily take a string from local name space, and feed it to the interpreter. In C++ however, the code must be pre-compiled. Not knowing what the string input is before runtime, makes this difficult. So instead it makes more sense to make some custom translation by hand. Since ``matlab2cpp`` produces C++ files, it is possible to edit them after creation. However, if changes are made to the Matlab-file at a later point, the custom edits have to be added manually again. To resolve this, ``matlab2cpp`` supports verbatim translations through the suppliment file ``.py`` and through the node attribute :py:attr:`~matlab2cpp.Node.vtypes`. :py:attr:`~matlab2cpp.node.vtype` is a dictionary where the keys are string found in the orginal code, and the values are string of the replacement. Performing a verbatim replacement has to be done before the node tree is constructed. Assigning :py:attr:`~matlab2cpp.Node.vtypes` doesn't work very well. Instead the replacement dictionary can be bassed as argument to :py:func:`~matlab2cpp.build`:: >>> tree = mc.build('''a=1 ... b=2 ... c=3''', vtypes = {"b": "_replaced_text_"}) >>> print mc.qscript(tree) a = 1 ; // b=2 _replaced_text_ c = 3 ; Note that when a match is found, the whole line is replaced. No also how the source code is retained a comment above the verbatim translation. The verbatim key can only match a single line, however the replacement might span multiple lines. For example:: >>> replace_code = '''one line ... two line ... three line''' >>> tree = mc.build('''a=1 ... b=2 ... c=3''', vtypes={"b": replace_code}) >>> print mc.qscript(tree) a = 1 ; // b=2 one line two line three line c = 3 ; Verbatims can also be utilized by modifying the .py file. Consider the Matlab script:: a = 1 ; b = 2 ; c = 3 ; Using the m2cpp script to translate the Matlab script produces a C++ file and a .py file. By adding code to the .py file, verbatim translation can be added. This is done by using the keyword verbatims and setting it to a python dictionary. Similar to vtype, keys are strings found in the original code, and the values are string of the replacement:: functions = { "main" : { "a" : "int", "b" : "int", "c" : "int", }, } includes = [ '#include ', 'using namespace arma ;', ] verbatims = {"b = 2 ;" : '''one line two line tree line''' } In the generated C++ file the second assignment is replaced with the verbatim translation:: int main(int argc, char** argv) { int a, c ; a = 1 ; // b = 2 ; one line two line tree line c = 3 ; return 0 ; } ================================================ FILE: doc/user_manual/source/usr03_rules.rst ================================================ .. automodule:: matlab2cpp.manual.usr03_rules ================================================ FILE: doc/user_manual/source/usr04_node.rst ================================================ .. automodule:: matlab2cpp.manual.usr04_node ================================================ FILE: requirements.txt ================================================ pytest==3.2.2 pytest-runner==2.12.1 pytest-cov==2.5.1 codecov==2.0.16 ================================================ FILE: setup.cfg ================================================ [aliases] test = pytest [tool:pytest] norecursedirs = test/data testpaths = src/matlab2cpp test addopts = --doctest-modules --cov=./src [metadata] description-file = README.md ================================================ FILE: setup.py ================================================ """Main installer.""" from setuptools import setup, find_packages setup( name="matlab2cpp", version="2.0.1", packages=find_packages("src"), package_dir={"": "src"}, entry_points={"console_scripts": ["m2cpp = matlab2cpp:m2cpp"]}, url='http://github.com/jonathf/matlab2cpp', license='BSD', author="Jonathan Feinberg", author_email="jonathan@feinberg.no", description="Matlab to C++ transpiler", classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Natural Language :: English', 'Programming Language :: Python', 'Topic :: Software Development :: Compilers', ], tests_require=["pytest", "pytest-runner"], ) ================================================ FILE: src/matlab2cpp/__init__.py ================================================ #!/usr/bin/env python # PYTHON_ARGCOMPLETE_OK """ The toolbox is sorted into the following modules: +----------------------------------+----------------------------------------+ | Module | Description | +==================================+========================================+ | :py:mod:`~matlab2cpp.qfunctions` | Functions for performing simple | | | translations | +----------------------------------+----------------------------------------+ | :py:class:`~matlab2cpp.Builder` | Constructing a tree from Matlab code | +----------------------------------+----------------------------------------+ | :py:class:`~matlab2cpp.Node` | Components in the tree representation | | | of the code | +----------------------------------+----------------------------------------+ | :py:mod:`~matlab2cpp.collection` | The collcetion of various node | +----------------------------------+----------------------------------------+ | :py:mod:`~matlab2cpp.configure` | Rutine for setting datatypes and | | | backends of the various nodes | +----------------------------------+----------------------------------------+ | :py:mod:`~matlab2cpp.rules` | Translation rules | +----------------------------------+----------------------------------------+ | :py:mod:`~matlab2cpp.supplement` | Functions for inserting and extraction | | | datatypes | +----------------------------------+----------------------------------------+ | :py:mod:`~matlab2cpp.testsuite` | Suite for testing software | +----------------------------------+----------------------------------------+ The simplest way to use the library is to use the quick translation functions. They are available through the `matlab2cpp.qfunctions` module and mirrors the functionality offered by the `m2cpp` function. """ try: import argcomplete except ImportError: argcomplete = None from .parser import create_parser from .qfunctions import * __version__ = "2.0" def m2cpp(args=None): """ Execute main parser. Args: args (Optional[List[str]]): Argument to be parsed. If omitted, use ``sys.args``. """ parser = create_parser() if argcomplete is not None: argcomplete.autocomplete(parser) args = parser.parse_args(args) from matlab2cpp.frontend import execute_parser execute_parser(args) ================================================ FILE: src/matlab2cpp/collection.py ================================================ # encoding: utf-8 r""" A full summary of all nodes. +---------------------+--------------------+----------------+------------------------------+ | Name | Children | Example | Description | +=====================+====================+================+==============================+ | All | | `:` | Colon operator w/o range | +---------------------+--------------------+----------------+------------------------------+ | Assign | `Expr Expr` | `a=b` | Assignment one var | +---------------------+--------------------+----------------+------------------------------+ | Assigns | `Expr Expr+` | `[a,b]=c` | Assignment multi vars | +---------------------+--------------------+----------------+------------------------------+ | Band | `Expr Expr+` | `a&b` | Binary AND operator | +---------------------+--------------------+----------------+------------------------------+ | Bcomment | | `%{ . %}` | Block comment | +---------------------+--------------------+----------------+------------------------------+ | Block | `Line*` | `a` | Code block | +---------------------+--------------------+----------------+------------------------------+ | Bor | `Expr Expr+` | `a|b` | Binary OR operator | +---------------------+--------------------+----------------+------------------------------+ | Branch | `If Ifse* Else?` | `if a; end` | If chain container | +---------------------+--------------------+----------------+------------------------------+ | Break | | `break` | Break statement | +---------------------+--------------------+----------------+------------------------------+ | Case | `Var Block` | `case a` | Case part of Switch | +---------------------+--------------------+----------------+------------------------------+ | Catch | `Block` | `catch a` | Catch part of Tryblock | +---------------------+--------------------+----------------+------------------------------+ | Cell | `Expr*` | `{a}` | Cell array | +---------------------+--------------------+----------------+------------------------------+ | Cget | `Expr+` | `a{b}(c)` | Cell retrival | +---------------------+--------------------+----------------+------------------------------+ | Colon | `Expr Expr Expr?` | `a:b` | Colon operator w range | +---------------------+--------------------+----------------+------------------------------+ | Counter | | | Struct array size | +---------------------+--------------------+----------------+------------------------------+ | Cset | `Expr+` | `a{b}(c)=d` | Cell array assignment | +---------------------+--------------------+----------------+------------------------------+ | Ctranspose | `Expr` | `a'` | Complex transform | +---------------------+--------------------+----------------+------------------------------+ | Cvar | `Expr+` | `a{b}` | Cell variable | +---------------------+--------------------+----------------+------------------------------+ | Declares | `Var*` | | Declared variable list | +---------------------+--------------------+----------------+------------------------------+ | Ecomment | | `a%b` | End-of-line comment | +---------------------+--------------------+----------------+------------------------------+ | Elementdivision | `Expr Expr+` | `a./b` | Sclars division | +---------------------+--------------------+----------------+------------------------------+ | Elexp | `Expr Expr+` | `a.^b` | Element-wise exponent | +---------------------+--------------------+----------------+------------------------------+ | Elif | `Expr Block` | `elseif a` | Else-if part of Branch | +---------------------+--------------------+----------------+------------------------------+ | Elmul | `Expr Expr+` | `a.*b` | Element-wise multiplication | +---------------------+--------------------+----------------+------------------------------+ | Else | `Block` | `else` | Else part of Branch | +---------------------+--------------------+----------------+------------------------------+ | End | | `end` | End-expression | +---------------------+--------------------+----------------+------------------------------+ | Eq | `Expr Expr` | `a==b` | Equallity sign | +---------------------+--------------------+----------------+------------------------------+ | Error | | | Error node | +---------------------+--------------------+----------------+------------------------------+ | Exp | `Expr Expr+` | `a^b` | Exponential operator | +---------------------+--------------------+----------------+------------------------------+ | Fget | `Expr*` | `a.b(c)` | Fieldarray retrival | +---------------------+--------------------+----------------+------------------------------+ | Float | | `4.` | Float-point number | +---------------------+--------------------+----------------+------------------------------+ | For | `Var Expr Block` | `for a=b;end` | For-loop container | +---------------------+--------------------+----------------+------------------------------+ | Fset | `Expr Expr+` | `a.b(c)=d` | Fieldname assignment | +---------------------+--------------------+----------------+------------------------------+ | Func | `Declares Returns` | `function f()` | Function container | | | `Params Block` | `end` | | +---------------------+--------------------+----------------+------------------------------+ | Funcs | `[Main Func+]` | | Root of all functions | +---------------------+--------------------+----------------+------------------------------+ | Fvar | | `a.b` | Fieldname variable | +---------------------+--------------------+----------------+------------------------------+ | Ge | `Expr Expr` | `a>=b` | Greater-or-equal operator | +---------------------+--------------------+----------------+------------------------------+ | Get | `Expr*` | `a(b)` | Function or retrival | +---------------------+--------------------+----------------+------------------------------+ | Gt | `Expr Expr` | `a>b` | Greater operator | +---------------------+--------------------+----------------+------------------------------+ | Header | | | File header element | +---------------------+--------------------+----------------+------------------------------+ | Headers | | | Collection header lines | +---------------------+--------------------+----------------+------------------------------+ | If | `Expr Block` | `if a` | If part of Branch | +---------------------+--------------------+----------------+------------------------------+ | Imag | | `i` | Imaginary unit | +---------------------+--------------------+----------------+------------------------------+ | Include | | | Include statement | +---------------------+--------------------+----------------+------------------------------+ | Includes | | | Collection of includes | +---------------------+--------------------+----------------+------------------------------+ | Int | | `1` | Integer value | +---------------------+--------------------+----------------+------------------------------+ | Lambda | | `f=@()1` | Lambda function expression | +---------------------+--------------------+----------------+------------------------------+ | Land | `Expr Expr+` | `a&&b` | Logical AND operator | +---------------------+--------------------+----------------+------------------------------+ | Lcomment | | `%a` | Line-comment | +---------------------+--------------------+----------------+------------------------------+ | Le | `Expr Expr` | `a<=b` | Less-or-equal operator | +---------------------+--------------------+----------------+------------------------------+ | Leftelementdivision | `Expr Expr+` | `a.\b` | Left sclar division | +---------------------+--------------------+----------------+------------------------------+ | Leftmatrixdivision | `Expr Expr+` | `a\b` | Left matrix division | +---------------------+--------------------+----------------+------------------------------+ | Log | `[Error Warning]+` | | Collection of Errors | +---------------------+--------------------+----------------+------------------------------+ | Lor | `Expr Expr` | `a||b` | Logical OR operator | +---------------------+--------------------+----------------+------------------------------+ | Lt | `Expr Expr` | `a>> print(matlab2cpp.qtree("a.b = 4.4; c = [a.b]", core=True, suggest=True)) #doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Assign double double 1 1| | Fvar struct double a 1 7| | Float double double 1 12| Assign struct double 1 12| | Var double double c 1 16| | Matrix struct double 1 17| | | Vector matrix double 1 17| | | | Fvar struct double a """ declare = node.func[0][node.name] if declare.backend in ("struct", "structs"): node.backend = declare.backend Cvar = "cell" Cget = "cell" Fget = "structs" Sget = "structs" Nget = "struct" Cset = "cell" Fset = "structs" Sset = "structs" Nset = "struct" Resize = "cube_common" Verbatim = "verbatim" def Var(node): if node.type != "TYPE": node.backend = node.type def Get(node): if node.type != "TYPE": node.backend = node.type def Set(node): if node.type != "TYPE": node.backend = node.type def Func(node): returns = node[1] if node.name[:1] == "_": node.backend = "func_lambda" elif len(returns) == 1: node.backend = "func_return" else: node.backend = "func_returns" def Returns(node): if node.parent.name[:1] == "_": node.backend = "func_lambda" elif len(node) == 1 or node.parent.cls == "Main": node.backend = "func_return" else: node.backend = "func_returns" def Params(node): returns = node.parent[1] if node.parent.name[:1] == "_": node.backend = "func_lambda" elif len(returns) == 1 or node.parent.cls == "Main": node.backend = "func_return" else: node.backend = "func_returns" def Declares(node): returns = node.parent[1] if node.parent.name[:1] == "_": node.backend = "func_lambda" elif len(returns) == 1 or node.parent.cls == "Main": node.backend = "func_return" else: node.backend = "func_returns" def Assign(node): node.backend = node[-1].backend if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/configure/datatypes.py ================================================ from .funcs import funcs from . import armadillo, backends, frontend Counter = "structs" def Var(node): """ Example: >>> print(matlab2cpp.qcpp("a.b = 4; c = a")) #include using namespace arma ; struct _A { int b ; } ; int main(int argc, char** argv) { _A a, c ; a.b = 4 ; c = a ; return 0 ; } """ if funcs(node): return if node.parent.cls == "Assign" and node.parent[0] is node: #assign b = [a.val], a is a structs, suggest vec -> dim=1 if node.parent[1].cls == "Matrix" and \ node.parent[1].backend == "structs" and len(node.parent[1][0]) == 1: node.declare.suggest = (1, node.parent[1].mem) #assign b = a, where a is a struct/structs. #This code sets b backend and type to a's backend and type elif node.parent[1].cls in ("Var",)\ and node.parent[1].backend in ("struct", "structs"): backend = node.parent[1].backend node.parent[1].declare if hasattr(node.parent[1], "_declare"): node.declare.type = backend node.declare.backend = backend node.declare._declare = node.parent[1]._declare node._declare = node.parent[1]._declare node.backend = backend else: node.declare.suggest = node.parent[1].type if node.declare.type != "TYPE": node.type = node.declare.type def Get(node): if funcs(node): return #in backends.py backend is set to datatype. If a is rowvec, #i want a(2) to have backend rowvec and and datatype double #This code sets backend to rowvec, and below datatype is set #Thus backend is set before datatype is changed to double #backends.Get(node) #vec if node.declare.dim == 1: backends.Get(node) armadillo.vec(node) return #rowvec if node.declare.dim == 2: backends.Get(node) armadillo.rowvec(node) return #mat if node.declare.dim == 3: backends.Get(node) armadillo.mat(node) return #cube if node.declare.dim == 4: backends.Get(node) armadillo.cube(node) return if node.parent.cls == "Assign" and node.parent[0] is node: node.declare.suggest = node.parent[1].type if node.declare.type != "TYPE": node.type = node.declare.type def Set(node): #in backends.py backend is set to datatype. If a is rowvec, #i want a(2) to have backend rowvec and and datatype double #This code sets backend to rowvec, and below datatype is set #Thus backend is set before datatype is changed to double #backends.Get(node) #vec if node.declare.dim == 1: backends.Get(node) armadillo.vec(node) return #rowvec if node.declare.dim == 2: backends.Get(node) armadillo.rowvec(node) return #mat if node.declare.dim == 3: backends.Get(node) armadillo.mat(node) return #cube if node.declare.dim == 4: backends.Get(node) armadillo.cube(node) return if node.parent.cls == "Assign": node.declare.suggest = node.parent[1].type if node.declare.type != "TYPE": node.type = node.declare.type def Fvar(node): if node.parent.cls == "Assign" and node.parent[0] is node: node.declare.suggest = node.parent[1].type if node.declare.type != "TYPE": node.type = node.declare.type def Fset(node): if node.parent.cls == "Assign": node.declare.suggest = node.parent[1].type if node.declare.type != "TYPE": node.type = node.declare.type def Sset(node): if node.parent.cls == "Assign": node.declare.suggest = node.parent[1].type if node.declare.type != "TYPE": node.type = node.declare.type def Assign(node): if node[1].type == "TYPE": return node.type = node[1].type # node[0].declare.suggest = node[1].type def Vector(node): # default to common denominator node.type = [n.type for n in node] # dimensionality in vector dims = {n.dim for n in node} # non-numerical elements in vector isn't addresed if None in dims or [n for n in node if not n.num]: # keep it simple of single elements if len(node) == 1: node.type = node[0].type return # single element in vector if len(node) == 1: if dims == {0}: # holder value to determine if vector is in decomposed state node.value = "scalarsonly" else: node.value = "" node.dim = list(dims)[0] return # only colvecs elif dims == {1}: node.dim = 1 nodes = [str(n) for n in node] # Decomposed row if dims == {0}: node.value = "scalarsonly" node.dim = 2 # only rowvecs elif dims == {2}: node.dim = 2 # mix of scalars and rowvecs elif dims == {0, 2}: node.dim = 2 # mix of matrices and colvecs elif dims in ({3}, {1, 3}): node.dim = 3 def Matrix(node): node.type = [n.type for n in node] dims = {n.dim for n in node} # single vector with no content if len(node) == 1 and len(node[0]) == 0: node.num = False return # everything on scalarsonly form elif all([n.value for n in node]): node.value = "scalarsonly" # set dimensions ax0, ax1 = len(node), len(node[0]) if ax0 > 1: if ax1 > 1: node.dim = 3#matrix else: node.dim = 1#rowvec else: if ax1 > 1: node.dim = 2#colvec else: node.dim = 0#scalar elif dims in ({0,1}, {1}): # configure dimensions if len(node[0])>1: node.dim = 3#matrix else: node.dim = 1#colvec # mix of rowvecs and matrices elif dims in ({2}, {3}, {2,3}): # configure dimensiosn if dims == {2} and len(node)==1: node.dim = 2#rowvec else: node.dim = 3#matrix def Transpose(node): node.type = node[0].type if node[0].num: if node[0].dim == 1: node.dim = 2 return elif node[0].dim == 2: node.dim = 1 return Ctranspose = Transpose def For(node): node[0].suggest = "int" #node[0].suggest = "uword" #index = node.parent.children.index(node) #tbb = node.parent.children[index - 1].cls #if tbb == "Tbb_for": # node[0].type = "uword" def Neg(node): node.type = node[0].type if node[0].mem == 0: node.mem = 1 def opr(node): node.type = [n.type for n in node] Plus = opr def Minus(node): opr(node) if node.mem == 0: node.mem = 1 def Mul(node): opr(node) if "TYPE" in (n.type for n in node): return mem = max([n.mem for n in node]) if node[0].dim == 2 and node[1].dim == 1: node.type = (0, mem) elif node.dim == 1 and node[1].dim == 2: node.type = (3, mem) Elmul = opr Paren = opr def Exp(node): opr(node) if node.num and node.mem < 2: node.mem = 3 def Elexp(node): node.type = [n.type for n in node] End = "int" Int = "int" Float = "double" String = "string" Imag = "cx_double" def division(node): opr(node) if node.num and node.mem < 2: node.mem = 3 Matrixdivision = division Elementdivision = division Leftmatrixdivision = division Leftelementdivition = division All = "uvec" def Colon(node): # context: array argument (must always be uvec) if node.group.cls in ("Get", "Cget", "Nget", "Fget", "Sget", "Set", "Cset", "Nset", "Fset", "Sset") and \ node.parent.backend not in ("func_return", "func_returns", "reserved", "func_lambda"): node.type = "uvec" else: # context: matrix concatination if node.group.cls in ("Matrix",) and node.group.num: node.type = "rowvec" # context: pass to function elif node.parent.cls in ("Get", "Cget", "Nget", "Fget", "Sget", "Set", "Cset", "Nset", "Fset", "Sset"): node.type = "rowvec" # context: assignment elif node.group.cls in ("Assign",) and node.group[0].num: node.type = "rowvec" else: node.type = "rowvec" def Lambda(node): # lambda function are created as full functions, but referenced to be # written inline lfunc = node.program[1][node.name] ldeclares, lreturns, lparams, lblock = lfunc lnames = lparams.names + ldeclares.names expr = lblock[0][1] # location for where lambda is created func = node.func declares, returns, params, block = func # Lambda creates a local scope # e.g. in expressions like '@(x) x*y' # 'x' is in one scope and 'y' is in another. # This little hack iterates the expression of the function in # search for vars/calls etc. like 'y' and declares them with # the right types. nodes = [expr] for node_ in nodes: nodes.extend(node_[:]) # a variable if node_.cls in ["Var", "Cvar", "Fvar", "Get", "Cget", "Fget", "Nget"]: name = node_.name # not in lambda scope if name not in lnames: # defined as a parameter in function if name in params.names: type = params[params.names.index(name)].type node_.type = type node_.declare.type = type # declared in function elif name in declares.names: type = declares[declares.names.index(name)].type node_.type = type node_.declare.type = type frontend.configure(lfunc) # declare list in lambda function if ldeclares["_retval"].type != "TYPE": declares[node.name[1:]].type = "func_lambda" node.parent.type = "func_lambda" node.parent[0].type = "func_lambda" node.type = "func_lambda" def Assigns(node): if node[-1].type != "TYPE": node.type = node[-1].type if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/configure/frontend.py ================================================ def configure(root, suggest=True, **kws): """ configure backend See also: :py:func:`matlab2cpp.Builder.configure ` """ from .. import tree if isinstance(root, tree.Builder): root = root.project loop(root, suggest) loop(root, suggest) def loop(root, suggest): from . import datatypes, backends, reserved nodes = root.flatten(False, True, True) while True: # loop and configure for node in nodes: # reserved stuff if node.cls + "_" + node.name in reserved.__dict__: rule = reserved.__dict__[node.cls+"_"+node.name] if isinstance(rule, str): node.type = rule else: rule(node) # Datatype stuff if node.prop["type"] != "TYPE": pass elif node.cls in datatypes.__dict__: datatype = datatypes.__dict__[node.cls] if isinstance(datatype, str): node.type = datatype else: datatype(node) # Backend stuff if node.backend != "unknown": pass elif node.cls in backends.__dict__: backend = backends.__dict__[node.cls] if isinstance(backend, str): node.backend = backend else: backend(node) # determine if done if suggest: complete = True for program in root.project: suggests = program.suggest program.stypes = suggests program.ftypes = suggests complete = complete and not any([any(v) for v in suggests.values()]) if complete: break else: break # delete log, if any (create on translate) for program in root.project: program[-1].children = [] ================================================ FILE: src/matlab2cpp/configure/funcs.py ================================================ import os def funcs(node): func = None # lambda scope if "_" + node.name in node.program[1]: func = node.program[1]["_" + node.name] # local scope elif node in node.program[1]: func = node.program[1][node] # external file in same folder else: for program in node.project: # don't use the file your in as external library if program is node.program: continue if os.path.basename(program.name) == node.name+".m": func = program[1][0] break else: return False node.backend = func.backend if node.backend == "func_return": node.backend = func.backend node.declare.type = func[1][0].type params = func[2] for i in range(len(node)): try: params[i].suggest = node[i].type node[i].suggest = params[i].type except: pass elif node.backend == "func_returns": node.backend = func.backend params = func[2] for j in range(len(params)): try: params[j].suggest = node[j].type node[j].suggest = params[j].type except: pass if node.parent.cls == "Assigns": # node.parent.backend = "func_returns" returns = func[1] # Got out_of_bounds error, len(returns) where longer than LHS, # so i changed for range to min LHS vars and returns from function for j in range(min(len(node.parent), len(returns))): returns[j].suggest = node.parent[j].type node.parent[j].suggest = returns[j].type elif node.backend == "func_lambda": ret = func[1][0] node.suggest = ret.type ret.suggest = node.type if node.type != "TYPE": node.declare.type = "func_lambda" params = func[2] for i in range(len(node)): params[i].suggest = node[i].type return True ================================================ FILE: src/matlab2cpp/configure/reserved.py ================================================ Var_false = "int" Var_true = "int" Var_pi = "double" Get_linspace = "rowvec" def Get_exp(node): node.type = node[0].type def Get_log(node): node.type = node[0].type def Get_log2(node): node.type = node[0].type def Get_log10(node): node.type = node[0].type def Get_power(node): node.type = node[0].type def Get_floor(node): node.type = node[0].type def Get_ceil(node): node.type = node[0].type def Get_fix(node): node.type = node[0].type def Get_round(node): if len(node) == 1: #int, float, double, uword if node[0].dim == 0 and node[0].mem != 4: node.type = "double" #arma types elif node[0].dim != 0: node.type = node[0].type def Get_cos(node): node.type = node[0].type def Get_acos(node): node.type = node[0].type def Get_cosh(node): node.type = node[0].type def Get_acosh(node): node.type = node[0].type def Get_sin(node): node.type = node[0].type def Get_asin(node): node.type = node[0].type def Get_sinh(node): node.type = node[0].type def Get_asinh(node): node.type = node[0].type def Get_sqrt(node): #if len(node) > 0 ... if len(node) and node[0].cls == "Neg": node.type = "cx_double" elif len(node): node.type = node[0].type def Get_mod(node): node.type = node[0].type def Get_abs(node): if node[0].type in ("cx_double", "cx_mat"): node.type = "mat" else: node.type = node[0].type def Get_tic(node): node.type = "double" def Assign_tic(node): node[0].declare.type = "double" node[0].type = "double" def Get_toc(node): node.type = "double" def Assign_toc(node): node[0].declare.type = "double" node[0].type = "double" def Get_any(node): if not node[0].num: return node.type = node[0].type # colvec or rowvec if node.dim in (1,2): node.dim = 0 # matrix elif node.dim == 3: # axis input decides by second input if len(node) == 2: if node[1].cls == "Int": val = node[1].value if val == "1": node.dim = 2 elif val == "2": node.dim = 1 # problem if arg not explicit else: node.num = False # cube else: node.dim = 3 Get_all = Get_any Get_isequal = "int" def Get_size(node): # unknown input if node[0].type == "TYPE" or node.parent.cls == "Assigns": return var = str(node[0]) # multiple args if len(node) > 1: # determine ax from second arg node.type = "uword" # colvec or rowvec elif node[0].dim in (1,2): """ if node.parent.backend == "reserved" and\ node.parent.name in ("min", "max"): node.type = "uword" return if len(node) == 1: node.type = "urowvec" return node.type = "uword" """ node.type = "urowvec" if node.parent.backend == "reserved" and\ node.parent.name in ("min", "max"): node.type = "uword" return if len(node) == 1: node.type = "urowvec" return if node.parent.cls == "Get": return # inline calls moved to own line if node.parent.cls not in ("Statement", "Assign"): return node.parent.backend = "reserved" node.parent.name = "size" # matrix (returns two values) elif node[0].dim == 3: node.type = "urowvec" if node.parent.backend == "reserved" and\ node.parent.name in ("min", "max"): node.type = "uword" return if node.parent.cls == "Get": return # inline calls moved to own line if node.parent.cls not in ("Statement", "Assign"): return node.parent.backend = "reserved" node.parent.name = "size" # cube (return three values) elif node[0].dim == 4: node.type = "urowvec" if node.parent.cls == "Get": return # inline calls moved to own line if node.parent.cls not in ("Statement", "Assign"): return node.parent.backend = "reserved" node.parent.name = "size" def Assigns_size(node): # suggest some types for matrix if len(node)==3: node[0].suggest = "int" node[1].suggest = "int" # suggest some types for cube if len(node)==4: node[0].suggest = "int" node[1].suggest = "int" node[2].suggest = "int" def Get_length(node): node.type = "uword" def Get_min(node): # everything scalar if not all([n.num for n in node]) or all([(n.dim < 1) for n in node]): if any([n.mem == 4 for n in node]): node.type = "cx_mat" return node.type = node[0].type # single arg if len(node) == 1: # determine node dimensions if node.dim == 2: node.dim = 0 else: node.dim = node.dim-1 # three args if len(node) == 3: if node[2].dim == 0: # assues third arg is int and sets axis val = node[2].value if val == "1": node.dim = 2 elif val == "2": node.dim = 1 else: node.num = False def Assigns_min(node): assert len(node) == 3 var = node[2][0] # non-numerical assignment if not var.num: pass else: node[0].suggest = (0, var.mem) node[1].suggest = "int" Get_max = Get_min def Assigns_max(node): assert len(node) == 3 # right hand side of assignment var = node[-1] # non-numerical input if not var.num: pass else: node[0].suggest = (0, var.mem) node[1].suggest = "int" def Get_fliplr(node): if len(node) > 0: node.type = node[0].type def Get_flipud(node): if len(node) > 0: node.type = node[0].type def Get_eye(node): #set eye type to cx_mat if LHS is complex type if node.group.cls == "Assign" and node.group[0].mem == 4: node.type = "cx_mat" else: node.type = "mat" def Get_diag(node): if len(node) > 0: if node[0].dim == 3: node.type = (1, node[0].mem) elif node[0].dim in (1, 2): node.type = (3, node[0].mem) def Get_tril(node): if node[0].mem: node.type = (3, node[0].mem) def Get_triu(node): if node[0].mem: node.type = (3, node[0].mem) Var_eye = Get_eye def Get_trace(node): node.type = node[0].type def Get_transpose(node): """Simple transpose """ # colvec -> rowvec if node[0].dim == 1: node.type = (2, node[0].mem) # rowvec -> colvec elif node[0].dim == 2: node.type = (1, node[0].mem) else: node.type = node[0].type def Get_ctranspose(node): """Complex transpose """ # colvec -> rowvec if node[0].dim == 1: node.type = (2, node[0].mem) # rowvec -> colvec elif node[0].dim == 2: node.type = (1, node[0].mem) else: node.type = node[0].type def Get_zeros(node): node.type = "uword" dim, mem = node.suggest_datatype() # set memory type if not (mem is None): node.mem = mem else: node.mem = 3 #if node.group.cls == "Matrix" and node.group.group.cls == "Assign" and len(node.group.group) == 2: # if node.group.group[0].mem == 4: # node.mem = 4 # reset to uword if arg of array-node if node.group.cls in ("Get", "Cget", "Fget", "Nget", "Sget", "Set", "Cset", "Fset", "Nset", "Sset") and node.group.num: node.mem = 0 if len(node) == 2 and node[0].cls == "Int" and node[0].value == "1": node.dim = 1 return # one argument if len(node) == 1: # arg input is vector if node[0].num and node[0].dim in (1,2): pass else: # use suggestions or defualts if dim in (1,2,3): node.dim = dim else: node.dim = 3 # default # double argument creates colvec/rowvec/matrix depending on context elif len(node) == 2: # use matrix, if suggested if dim == 3: node.dim = 3 # use colvec if first index is '1' elif node[0].cls == "Int" and node[0].value == "1": node.dim = 2 # use rowvec if second index is '1' elif node[1].cls == "Int" and node[1].value == "1": node.dim = 1 # default to matrix else: node.dim = 3 # triple arg create cube elif len(node) == 3: node.dim = 4 Get_ones = Get_zeros Var_rand = "vec" def Get_rand(node): # Get type from left hand side of assignment if node.group.cls == "Assign": if node.group[0].type != "TYPE": node.type = node.group[0].type return # one arg if len(node) == 1: node.type = "vec" # two args elif len(node) == 2: node.type = "mat" # three args -> cube elif len(node) == 3: node.type = "cube" def Get_reshape(node): if node[0].mem: node.type = (3, node[0].mem) def Get_nextpow2(node): node.type = "int" def Get_fft(node): node.type = node[0].type if node.type != 'TYPE': node.mem = 4 #if node.mem == 4: # node.mem = 4 #elif node.mem == 3: # node.mem = 4 def Get_ifft(node): #assert(node[0].mem == 4) node.type = node[0].type if node.type != 'TYPE': node.mem = 4 #if node.mem == 4: # node.mem = 3 #elif node.mem == 3: # node.mem = 3 # unknown input #if not node.num: # pass #else: # node.mem = 4 def Get_interp1(node): if len(node): node.type = node[0].type def Get_sum(node): arg = node[0] # unknown input if not arg.num or arg.dim == 0: return node.type = arg.type # determine output dimensions if arg.dim == 2: dim = 0 elif arg.dim == 3: # sum along an axis if len(node) == 2 and node[1].cls == "Int" and node[1].value == "2": dim = 1 else: dim = 2 else: if arg.dim == 3: dim = 2 elif arg.dim == 2: arg.dim == 0 else: dim = arg.dim-1 node.dim = dim def Get_cumsum(node): node.type = node[0].type def Get_conj(node): node.type = node[0].type def Get_real(node): if node[0].dim: node.type = (node[0].dim, 3) #arg = node[0] # # output always real #if arg.mem: # node.type = (3, arg.mem) def Get_convmtx(node): node.type = node[0].type def Get_conv2(node): node.type = [node[0].type, node[1].type] def Get_logspace(node): node.type = "rowvec" def Get_find(node): node.type = "uvec" Get_tic = "string" Get_toc = "string" ================================================ FILE: src/matlab2cpp/datatype.py ================================================ """ The follwing constructor classes exists here: +------------------------------------------+---------------------------------------+ | Class | Description | +==========================================+=======================================+ | :py:class:`~matlab2cpp.datatype.Type` | Frontend for the datatype string | +------------------------------------------+---------------------------------------+ | :py:class:`~matlab2cpp.datatype.Dim` | Reference to the number of dimensions | +------------------------------------------+---------------------------------------+ | :py:class:`~matlab2cpp.datatype.Mem` | Reference to the memory type | +------------------------------------------+---------------------------------------+ | :py:class:`~matlab2cpp.datatype.Num` | Numerical value indicator | +------------------------------------------+---------------------------------------+ | :py:class:`~matlab2cpp.datatype.Suggest` | Frontend for suggested datatype | +------------------------------------------+---------------------------------------+ """ from . import supplement dim0 = {"int", "float", "uword", "double", "cx_double", "size_t"} dim1 = {"ivec", "fvec", "uvec", "vec", "cx_vec"} dim2 = {"irowvec", "frowvec", "urowvec", "rowvec", "cx_rowvec"} dim3 = {"imat", "fmat", "umat", "mat", "cx_mat"} dim4 = {"icube", "fcube", "ucube", "cube", "cx_cube"} dims = [dim0, dim1, dim2, dim3, dim4] mem0 = {"uword", "uvec", "urowvec", "umat", "ucube"} mem1 = {"int", "ivec", "irowvec", "imat", "icube"} mem2 = {"float", "fvec", "frowvec", "fmat", "fcube"} mem3 = {"double", "vec", "rowvec", "mat", "cube"} mem4 = {"cx_double", "cx_vec", "cx_rowvec", "cx_mat", "cx_cube"} mems = [mem0, mem1, mem2, mem3, mem4] others = {"char", "string", "TYPE", "func_lambda", "struct", "structs", "cell", "wall_clock", "SPlot"} def common_loose(vals): """Common denominator among several names. Loose enforcment""" if not isinstance(vals, (tuple, list)) or \ isinstance(vals[0], int): vals = [vals] vals = list(vals) for i in range(len(vals)): if isinstance(vals[i], str): continue if isinstance(vals[i][0], int): vals[i] = get_name(*vals[i]) vals = set(vals) if len(vals) == 1: return vals.pop() vals.discard("TYPE") if len(vals) == 1: return vals.pop() for other in others: vals.discard(other) if len(vals) == 0: return "TYPE" elif len(vals) == 1: return vals.pop() dims_ = map(get_dim, vals) if dims_: dim = max(*dims_) else: return "TYPE" if dim == 2 and 1 in dims_: dim = 3 types = map(get_mem, vals) type = max(*types) val = get_name(dim, type) return val def common_strict(vals): """Common denominator among several names. Strict enforcment""" if not isinstance(vals, (tuple, list)) \ or isinstance(vals[0], int): vals = [vals] vals = list(vals) for i in range(len(vals)): if isinstance(vals[i], str): continue if isinstance(vals[i][0], int): vals[i] = get_name(*vals[i]) vals = set(vals) if len(vals) == 1: return vals.pop() for other in others: if other in vals: return "TYPE" dims_ = map(get_dim, vals) dim = max(*dims_) if dim == 2 and 1 in dims_: return "TYPE" types = map(get_mem, vals) type = max(*types) val = get_name(dim, type) return val def pointer_split(name): p = name.count("*") if not p: return 0, name return p, name[:-p] def get_dim(val): while val[-1] == "*": val = val[:-1] if val in dim0: dim = 0 elif val in dim1: dim = 1 elif val in dim2: dim = 2 elif val in dim3: dim = 3 elif val in dim4: dim = 4 elif val in others: dim = None else: raise ValueError("Datatype '%s' not recognized" % val) return dim def get_mem(val): while val[-1] == "*": val = val[:-1] if val in mem0: mem = 0 elif val in mem1: mem = 1 elif val in mem2: mem = 2 elif val in mem3: mem = 3 elif val in mem4: mem = 4 elif val in others: mem = None else: raise ValueError("Datatype '%s' not recognized" % val) return mem def get_num(val): while val[-1] == "*": val = val[:-1] if val in others: num = False else: num = True return num def get_name(dim, mem): return dims[dim].intersection(mems[mem]).pop() def get_type(instance): if instance.prop["type"] == "TYPE": instance = instance.declare return instance.prop["type"] class Dim(object): """ The `node.dim` is a help variable for handling numerical datatype. It represents the number of dimension a numerical object represents: +-------+--------------+ | *dim* | Description | +=======+==============+ | 0 | scalar | +-------+--------------+ | 1 | (col-)vector | +-------+--------------+ | 2 | row-vector | +-------+--------------+ | 3 | matrix | +-------+--------------+ | 4 | cube | +-------+--------------+ | None | Other | +-------+--------------+ The variable can be both read and set in real time: >>> node = collection.Var(None, "name") >>> node.type="float" >>> print(node.dim) 0 >>> node.dim = 3 >>> print(node.type) fmat """ def __get__(self, instance, owner): if instance is None: return self return get_dim(get_type(instance)) def __set__(self, instance, value): mem = get_mem(get_type(instance)) instance.prop["type"] = get_name(value, mem) class Mem(object): """ The `node.mem` is a help variable for handling numerical datatype. It represents the internal basic datatype represented in memory: +-------+-------------+ | *mem* | Description | +=======+=============+ | 0 | unsiged int | +-------+-------------+ | 1 | integer | +-------+-------------+ | 2 | float | +-------+-------------+ | 3 | double | +-------+-------------+ | 4 | complex | +-------+-------------+ | None | Other | +-------+-------------+ The variable can be both read and set in real time: >>> node = collection.Var(None, "name") >>> node.type="float" >>> print(node.mem) 2 >>> node.mem = 3 >>> print(node.type) double """ def __get__(self, instance, owner): if instance is None: return self return get_mem(get_type(instance)) def __set__(self, instance, value): dim = get_dim(get_type(instance)) instance.prop["type"] = get_name(dim, value) class Num(object): """ The `node.num` is a help variable for handling numerical datatype. It is a boolean values which is true given that the datatype is of numerical type. """ def __get__(self, instance, owner): if instance is None: return self return get_num(get_type(instance)) def __set__(self, instance, value): if not value: instance.prop["type"] = "TYPE" else: raise AttributeError("num can not be set True consistently") class Type(object): """ Datatypes can be roughly split into two groups: **numerical** and **non-numerical** types. The numerical types are as follows: +-------------+--------------+-----------+-----------+----------+-------------+ | | unsigned int | int | float | double | complex | +=============+==============+===========+===========+==========+=============+ | scalar | *uword* | *int* | *float* | *double* | *cx_double* | +-------------+--------------+-----------+-----------+----------+-------------+ | vector | *uvec* | *ivec* | *fvec* | *vec* | *cx_vec* | +-------------+--------------+-----------+-----------+----------+-------------+ | row\-vector | *urowvec* | *irowvec* | *frowvec* | *rowvec* | *cx_rowvec* | +-------------+--------------+-----------+-----------+----------+-------------+ | matrix | *umat* | *imat* | *fmat* | *mat* | *cx_mat* | +-------------+--------------+-----------+-----------+----------+-------------+ | cube | *ucube* | *icube* | *fcube* | *cube* | *cx_cube* | +-------------+--------------+-----------+-----------+----------+-------------+ Values along the horizontal axis represents the amount of memory reserved per element, and the along the vertical axis represents the various number of dimensions. The names are equivalent to the ones in the Armadillo package. The non-numerical types are as follows: +---------------+------------------------+ | Name | Description | +===============+========================+ | *char* | Single text character | +---------------+------------------------+ | *string* | Text string | +---------------+------------------------+ | *struct* | Struct container | +---------------+------------------------+ | *structs* | Struct array container | +---------------+------------------------+ | *func_lambda* | Anonymous function | +---------------+------------------------+ The node datatype can be referenced by any node through `node.type` and can be inserted as placeholder through `%(type)s`. """ def __get__(self, instance, owner): if instance is None: return self return get_type(instance) def __set__(self, instance, value): value = value or "TYPE" if isinstance(value, str): p, value = pointer_split(value) instance.pointer = p else: value = common_strict(value) instance.prop["type"] = value class Suggest(object): """Same as Type, but for suggested value. """ def __set__(self, instance, value): if value == "TYPE": return instance.declare.prop["suggest"] = value def __get__(self, instance, owner): return supplement.suggests.get(instance) if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/frontend.py ================================================ """Execute main parser.""" import time from datetime import datetime as date import os from os.path import sep import imp from . import supplement, tree, qfunctions, modify, setpaths from .__init__ import __version__ def execute_parser(args): """ Initiate the interpretation and conversion process. Args: args (ArgumentParser): arguments parsed through m2cpp """ builder = tree.builder.Builder( disp=args.disp, comments=args.comments, original=args.original, enable_omp=args.enable_omp, enable_tbb=args.enable_tbb, reference=args.reference, ) paths_from_file = [] #read setpath.m file and return string list of paths if args.paths_file: if os.path.isfile(args.paths_file): paths_from_file = setpaths.multiple_folder_paths(args.paths_file) else: raise IOError("File '" + args.paths_file + "' not found") #pathOne = os.path.dirname(os.path.abspath(args.filename)) if os.path.isfile(args.filename): paths = [os.path.abspath(os.path.dirname(args.filename))] paths += paths_from_file if args.disp: print("building tree...") filenames = [os.path.abspath(args.filename)] stack = [] while filenames: filename = filenames.pop(0) assert os.path.isfile(filename) if filename in stack: continue if args.disp: print("loading", filename) stack.append(filename) f = open(filename, "rU") code = f.read() f.close() #code = re.sub('%#', '##', code) #Here you have to change filename to current folder for .py files #local_name = pathOne + sep + os.path.basename(filename) local_name = os.getcwd() + sep + os.path.basename(filename) if os.path.isfile(local_name + ".py") and not args.reset: try: cfg = imp.load_source("cfg", local_name + ".py") except: raise ImportError("""Supplement file: %s.py is formated incorrectly. Change the format or convert with '-r' option to create a new file.""" % local_name) if "verbatims" in cfg.__dict__ and cfg.verbatims: verbatims = cfg.verbatims code = supplement.verbatim.set(verbatims, code) builder.load(filename, code) program = builder[-1] if "functions" in cfg.__dict__: funcs = program.ftypes for name in funcs.keys(): if name in cfg.functions: for key in cfg.functions[name].keys(): funcs[name][key] = cfg.functions[name][key] program.ftypes = funcs if "structs" in cfg.__dict__: structs = program.stypes for name in structs.keys(): if name in cfg.structs: for key in cfg.structs[name].keys(): structs[name][key] = cfg.structs[name][key] program.stypes = structs if "includes" in cfg.__dict__: includes = program.itypes for key in cfg.includes: if key not in includes: includes.append(key) includes = [i for i in includes if supplement.includes.write_to_includes(i)] program.itypes = includes else: builder.load(filename, code) program = builder[-1] # add unknown variables to stack if they exists as files unknowns = builder.get_unknowns(filename) for i in range(len(unknowns)-1, -1, -1): #print(i) for path in paths: #print(path) if os.path.isfile(path + sep + unknowns[i] + ".m"): unknowns[i] = unknowns[i] + ".m" if os.path.isfile(path + sep + unknowns[i]): program.include(path + sep + unknowns[i]) #filenames.append(path + sep + unknowns.pop(i)) filenames.append(path + sep + unknowns[i]) else: builder.load("unnamed", args.filename) program = builder[-1] #--- work in progress --- #Run this mlabwrap code #Have this in a try-except block #import mwrapmat #wrapmat = mwrapmat.Wrapmat() #wrapmat.eval_code(builder) #------------------------ #--- work in progress --- #Get data types from matlab if args.matlab_suggest: from . import matlab_types builder = matlab_types.mtypes(builder, args) #------------------------ if args.disp: print("configure tree") builder.configure(suggest=(2*args.suggest or args.matlab_suggest)) #--- work in progress --- #Modify the Abstract Syntax Tree (AST) builder.project = modify.preorder_transform_AST( builder.project, args.nargin, suggest=(2*args.suggest or args.matlab_suggest), ) #------------------------ if args.disp: print(builder.project.summary()) print("generate translation") builder.project.translate(args) #post order modify project builder.project = modify.postorder_transform_AST(builder.project) t = time.time() stamp = date.fromtimestamp(t).strftime('%Y-%m-%d %H:%M:%S') for program in builder.project: #name = program.name #if os.path.isfile(args.filename): # name = pathOne + sep + os.path.basename(name) #print(name) name = os.getcwd() + sep + os.path.basename(program.name) #print(name) cpp = qfunctions.qcpp(program) hpp = qfunctions.qhpp(program) py = qfunctions.qpy(program, prefix=True) log = qfunctions.qlog(program) if args.disp: print("Writing files...") if args.reset: for ext in [".cpp", ".hpp", ".log", ".py"]: if os.path.isfile(name+ext): os.remove(name+ext) if cpp: cpp = """// Automatically translated using m2cpp %s on %s %s""" % (__version__, stamp, cpp) f = open(name+".cpp", "w") f.write(cpp) f.close() if hpp: hpp = """// Automatically translated using m2cpp %s on %s %s""" % (__version__, stamp, hpp) f = open(name+".hpp", "w") f.write(hpp) f.close() if log: log = "Automatically translated using m2cpp %s on %s\n\n%s"\ % (__version__, stamp, log) f = open(name+".log", "w") f.write(log) f.close() if py: py = """# Automatically translated using m2cpp %s on %s # %s""" % (__version__, stamp, py) f = open(name+".py", "w") f.write(py) f.close() if os.path.isfile(name+".pyc"): os.remove(name+".pyc") program = builder[0] if args.tree_full: print(program.summary(args)) elif args.tree: if program[1][0].cls == "Main": print(program[1][0][3].summary(args)) else: print(program[1].summary(args)) elif args.line: nodes = program[1].flatten(False, False, False) for node_ in nodes: if node_.line == args.line and node_.cls != "Block": print(node_.str.replace("__percent__", "%")) break else: print(program[1].str.replace("__percent__", "%")) ================================================ FILE: src/matlab2cpp/manual/__init__.py ================================================ """ Source files for the manual. Placed here so that doctest might pick up on them. """ #import usr00_introduction from . import usr01_interaction, usr02_datatype, usr03_rules, usr04_node ================================================ FILE: src/matlab2cpp/manual/usr00_introduction.py ================================================ """ .. _usr00: Matlab2cpp is a semi-automatic tool for converting code from Matlab to C++. matlab2cpp is written in Python and is a Python module. Installing the Matlab2cpp module, makes the module available in Python as a module and can be loaded with "import matlab2cpp". In addition to the module, the script "m2cpp is copied to a folder on the system so that it can be executed by typing "m2cpp" in the terminal (Windows: cmd, cygwin). The "m2cpp" is a small script to handle the input arguments which are then used by the functionality in the module Matlab2cpp. Note that Matlab2cpp/m2cpp is not meant as a complete tool for creating runnable C++ code. For example, the `eval`-function can not be supported because there is no general way to implement it in C++. Instead the program is aimed as a support tool, which aims at speed up the conversion process as much as possible for a user that needs to convert Matlab programs by hand anyway. The software does this by converting the basic structures of the Matlab-program (functions, branches, loops, etc.), adds variable declarations, and for some simple code, do a complete translation. From there manual conversions can be done by hand. Matlab2cpp generate C++ code that relies on the linear algebra library Armadillo (http://arma.sourceforge.net/). So while Armadillo isn't required to generate the C++ code, Armadillo is required to compile the program. Armadillo's API is quite similar to Matlab and cover some of Matlab's functionaliy. Matlab2cpp aims to correctly translate Matlab to the corresponding C++ Armadillo code. Currently, the code will not convert the large library collection of functions that Matlab currently possesses. However, there is no reason for the code not to support more of these features in time. """ ================================================ FILE: src/matlab2cpp/manual/usr01_interaction.py ================================================ """ .. _usr01: User interaction ================ The simplest way to interact with the `Matlab2cpp`-toolbox is to use the `m2cpp` frontend. The script automatically creates files with various extensions containing translations and/or meta-information. .. autoprogram:: m2cpp:parser :prog: m2cpp For the user, the flags -o, -c, -s, -S, -r, -p -omp, -tbb are the useful flags. The flags -t, -T are good for debugging because they print the structure of the Abstract Syntax Tree (AST). The -d flag gives useful information on the parsing of the Matlab code and insight in how the AST is built. Suggest flags, -s, -S --------------------- Read the section :ref:`usr02_suggestion_engine` first. When using m2cpp the corresponding suggest is set with the flag -s. The suggest engine works well for simple cases. For more complex cases, not all the variables get a type suggestion and the suggested type could be wrong. The other suggest flag -S get the datatypes by running the (Matlab) code with Matlab. Information of the datatypes are written to files which can be extracted by the code translator. For this flag to work, in addition to having Matlab installed, the Matlab Engine API for Python has to be installed (see: `Install MATLAB Engine API for Python `_). Matlab has to be able to run the code to extract the datatypes. So if the code require datafiles or special Matlab modules (e.g. numerical modules), these have to be available for this option to work. The Matlab suggest option is not 100%, but still quite good at suggesting datatypes. A downside with the using Matlab to suggest datatypes, is that Matlab takes some time to start up and then run the (Matlab) code. Multiple directories, -p paths_file ----------------------------------- In Matlab the script and function files have to be in the same folder for the function files to be found. To call a function script located in a different folder, the folder has to be added to path. This can be done with `addpath` or `path`. In a separate file from the Matlab main and function scripts, a separate script can be written to set the path to different folders:: Dir='/path_to_folder/SeismicLab/codes/'; path(path, strcat(Dir,'bp_filter/')); path(path, strcat(Dir,'decon')); path(path, strcat(Dir,'dephasing')); path(path, strcat(Dir,'fx')); ... The flag option `-p paths_file` can be set to parse such a file. Then Matlab as well as m2cpp can find function scripts that are located in other directories. .. _parallel_flags: Parallel flags, -omp, -tbb -------------------------- The program m2cpp can do parallelization of simple for loops (so called embarrasingly parallel). To let the program know which loops the user wants to parallelize, use the pragma `%#PARFOR` before the loop (similar to the way its done in OpenMP). The flags -omp and -tbb can then be used to chose if OpenMP code or TBB code will be inserted to parallelize the code. Matlab's `parfor` doesn't require the pragma `%#PARFOR` to parallelize. If neither -omp nor -tbb flag is used, no OpenMP or TBB code is inserted and we will get a sequential for loop. When compiling, try link flags `-fopenmp` for OpenMP and `-ltbb` for TBB. OpenMP is usually available for the compiler out of the box. TBB needs to be installed (see: https://www.threadingbuildingblocks.org/). The TBB code makes use of lambda functions which is a C++ feature. C++11 is probably not set as standard for the compiler, i.e., in the GNU compiler g++, the flag `-std=c++11` is required to make use of C++11 features. Quick translation functions --------------------------- Even though `m2cpp` is sufficient for performing all code translation, many of the examples in this manual are done through a python interface, since some of the python functionality also will be discussed. Given that `Matlab2cpp` is properly installed on your system, the python library is available in Python's path. The module is assumed imported as:: >>> import matlab2cpp Quick functions collection of frontend tools for performing code translation. Each of the function :py:func:`~matlab2cpp.qcpp`, :py:func:`~matlab2cpp.qhpp`, :py:func:`~matlab2cpp.qpy` and :py:func:`~matlab2cpp.qlog` are directly related to the functionality of the :program:`m2cpp` script. The name indicate the file extension that the script will create. In addition there are the three functions :py:func:`~matlab2cpp.qtree` and :py:func:`~matlab2cpp.qscript`. The former represents a summary of the created node tree. The latter is a simple translation tool that is more of a one-to-one translation. For an overview of the various quick-functions, see :ref:`dev01`. Plotting functionality ---------------------- Plotting functionality is available through a wrapper, which calls Python's matplotlib. If a Matlab code with plotting calls is translated, the file `SPlot.h` is generated. The C++ file that is generated also `#include` this file. To compile the generated code, the Python have to be included. The code in `SPlot.h` makes of C++11 features, so compiler options for C++11 may be needed as well. With the GNU compiler g++, I can compile the generated code with: `g++ my_cpp_file.cpp -o runfile -I /usr/include/python2.7/ -lpython2.7 -larmadillo -std=c++11` Additional flags could be -O3 (optimization) -ltbb (in case of TBB parallelization) """ ================================================ FILE: src/matlab2cpp/manual/usr02_datatype.py ================================================ """ .. _usr02: Configuring translation ======================= One of the translation challenges is how each variable type is determined. In C++ all variables have to be explicitly declared, while in Matlab they are declared implicitly at creation. When translating between the two languages, there are many variables where the data types are unknown and impossible for the Matlab2cpp software to translate. How to translate the behavior of an integer is vastly different from an float matrix. To differentiate between types, each node have an attribute :py:attr:`~matlab2cpp.Node.type` which represents the node datatype. Datatypes can be roughly split into two groups: **numerical** and **non-numerical** types. The numerical types are as follows: +---------------+--------------+---------+---------+--------+-----------+ | | unsigned int | integer | float | double | complex | +===============+==============+=========+=========+========+===========+ | `scalar` | uword | int | float | double | cx_double | +---------------+--------------+---------+---------+--------+-----------+ | `vector` | uvec | ivec | fvec | vec | cx_vec | +---------------+--------------+---------+---------+--------+-----------+ | `row\-vector` | urowvec | irowvec | frowvec | rowvec | cx_rowvec | +---------------+--------------+---------+---------+--------+-----------+ | `matrix` | umat | imat | fmat | mat | cx_mat | +---------------+--------------+---------+---------+--------+-----------+ | `cube` | ucube | icube | fcube | cube | cx_cube | +---------------+--------------+---------+---------+--------+-----------+ Values along the horizontal axis represents the amount of memory reserved per element, and the along the vertical axis represents the various number of dimensions. The names are equivalent to the ones in the Armadillo package. The non-numerical types are as follows: +----------------------------------+------------------------+ | Name | Description | +==================================+========================+ | `char` | Single text character | +----------------------------------+------------------------+ | `string` | Text string | +----------------------------------+------------------------+ | :ref:`struct ` | Struct container | +----------------------------------+------------------------+ | :ref:`structs ` | Struct array container | +----------------------------------+------------------------+ | :ref:`func_lambda ` | Anonymous function | +----------------------------------+------------------------+ Function scope -------------- If not specified otherwise, the program will not assign datatype types to any of variables. The user could in theory navigate the node tree and assign the variables one by one using the node attributes to navigate. (See section :ref:`usr04` for details.) However that would be very cumbersome. Instead the datatypes are define collectively inside their scope. In the case of variables in functions, the scope variables are the variables declaration :py:class:`~matlab2cpp.Declares` and function parameters :py:class:`~matlab2cpp.Params`. To reach the variable that serves as a scope-wide type, the node attribute :py:attr:`~matlab2cpp.Node.declare` can be used. Manually interacting with the variable scope is simpler then iterating through the full tree, but can in many cases still be cumbersome. To simplefy interaction with datatype scopes, each program has an suppliment attribute :py:attr:`~matlab2cpp.Node.ftypes`. The attribute is a nested dictionary where the outer shell represents the function name the variables are defined. The inner shell is the variables where keys are variable names and values are types. It can be used to quickly retrievieng and inserting datatypes. For example:: >>> tree = matlab2cpp.build("function f(a)") >>> print(tree.ftypes) {'f': {'a': ''}} >>> tree.ftypes = {"f": {"a": "int"}} >>> print(matlab2cpp.qscript(tree)) void f(int a) { // Empty block } .. _func_lambda: Anonymous functions ------------------- In addition to normal function, Matlab have support for anonymous function through the name prefix ``@``. For example:: >>> print(matlab2cpp.qscript("function f(); g = @(x) x^2; g(4)")) void f() { std::function g ; g = [] (int x) {pow(x, 2) ; } ; g(4) ; } The translator creates an ``C++11`` lambda function with equivalent functionality. To achieve this, the translator creates an extra function in the node-tree. The name of the function is the same as assigned variable with a ``_``-prefix (and a number postfix, if name is taken). The information about this function dictate the behaviour of the output The supplement file have the following form:: >>> print(matlab2cpp.qpy("function f(); g = @(x) x^2; g(4)")) functions = { "_g" : { "x" : "int", }, "f" : { "g" : "func_lambda", }, } includes = [ '#include ', 'using namespace arma ;', ] The function `g` is a variable inside `f`'s function scope. It has the datatype `func_lambda` to indicate that it should be handled as a function. The associated function scope `_g` contains the variables inside the definition of the anonymous function. .. _struct: Data structure -------------- Data structures in Matlab can be constructed explicitly through the ``struct``-function. However, they can also be constructed implicitly by direct assignment. For example will ``a.b=4`` create a ``struct`` with name ``a`` that has one field ``b``. When translating such a snippet, it creates a C++-struct, such that:: >>> print(matlab2cpp.qhpp("function f(); a.b = 4.", suggest=True)) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; struct _A { double b ; } ; void f() { _A a ; a.b = 4. ; } #endif In the suppliment file, the local variable `a` will be assigned as a `struct`. In addition, since the struct has content, the suppliment file creates a new section for structs. It will have the following form:: >>> print(matlab2cpp.qpy("function f(); a.b = 4.", suggest=True)) functions = { "f" : { "a" : "struct", }, } structs = { "a" : { "b" : "double", }, } includes = [ '#include ', 'using namespace arma ;', ] Quick retrieving and inserting struct variables can be done through the :py:attr:`~matlab2cpp.Node.stypes` attribute:: >>> tree = matlab2cpp.build("a.b = 4") >>> tree.ftypes = {"f": {"a": "struct"}} >>> tree.stypes = {"a": {"b": "double"}} >>> print(matlab2cpp.qcpp(tree)) #include using namespace arma ; struct _A { double b ; } ; int main(int argc, char** argv) { _A a ; a.b = 4 ; return 0 ; } .. _structs: Struct tables ------------- Given that the data structure is indexed, e.g. ``a(1).b``, it forms a struct table. Very similar to regular :ref:`structs `, which only has one value per element. There are a couple of differences in the translation. First, the struct is declared as an array: >>> print(matlab2cpp.qhpp("function f(); a(1).b = 4.", suggest=True)) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; struct _A { double b ; } ; void f() { _A a[100] ; a[0].b = 4. ; } #endif The translation assigned reserves 100 pointers for the content of ``a``. Obviously, there are situations where this isn't enough (or too much), and the number should be increased. So second, to adjust this number, the suppliment file specifies the number of elements in the integer ``_size``: >>> print(matlab2cpp.qpy("function f(); a(1).b = 4.", suggest=True)) functions = { "f" : { "a" : "structs", }, } structs = { "a" : { "_size" : 100, "b" : "double", }, } includes = [ '#include ', 'using namespace arma ;', ] .. _usr02_suggestion_engine: Suggestion engine ----------------- The examples so far, when the functions :py:func:`~matlab2cpp.qcpp`, :py:func:`~matlab2cpp.qhpp` and :py:func:`~matlab2cpp.qpy` are used, the argument ``suggest=True`` have been used, and all variable types have been filled in. Consider the following program where this is not the case:: >>> print(matlab2cpp.qhpp("function c=f(); a = 4; b = 4.; c = a+b", suggest=False)) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; TYPE f() { TYPE a, b, c ; a = 4 ; b = 4. ; c = a+b ; return c ; } #endif Since all variables are unknown, the program decides to fill in the dummy variable ``TYPE`` for each unknown variable. Any time variables are unknown, ``TYPE`` is used. The supplement file created by `m2cpp` or :py:func:`~matlab2cpp.qpy` reflects all these unknown variables as follows:: >>> print(matlab2cpp.qpy("function c=f(); a = 4; b = 4.; c = a+b", suggest=False)) functions = { "f" : { "a" : "", # int "b" : "", # double "c" : "", }, } includes = [ '#include ', 'using namespace arma ;', ] By flipping the boolean to ``True``, all the variables get assigned datatypes:: >>> print(matlab2cpp.qpy("function c=f(); a = 4; b = 4.; c = a+b", suggest=True)) functions = { "f" : { "a" : "int", "b" : "double", "c" : "double", }, } includes = [ '#include ', 'using namespace arma ;', ] The resulting program will have the following complete form: >>> print(matlab2cpp.qhpp( ... "function c=f(); a = 4; b = 4.; c = a+b", suggest=True)) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; double f() { double b, c ; int a ; a = 4 ; b = 4. ; c = a+b ; return c ; } #endif Note here though that the variable ``c`` didn't have a suggestion. The suggestion is an interactive process such that ``a`` and ``b`` both must be known beforehand. The variable ``a`` and ``b`` get assigned the datatypes ``int`` and ``double`` because of the direct assignment of variable. After this, the process starts over and tries to find other variables that suggestion could fill out for. In the case of the ``c`` variable, the assignment on the right were and addition between ``int`` and ``double``. To not loose precision, it then chooses to keep `double`, which is passed on to the ``c`` variable. In practice the suggestions can potentially fill in all datatypes automatically in large programs, and often quite intelligently. For example, variables get suggested across function call scope:: >>> print(matlab2cpp.qscript('function y=f(x); y=x; function g(); z=f(4)')) int f(int x) { int y ; y = x ; return y ; } void g() { int z ; z = f(4) ; } And accross multiple files:: >>> from matlab2cpp.tree import Builder >>> builder = Builder() >>> builder.load("f.m", "function y=f(x); y=x") >>> builder.load("g.m", "function g(); z=f(4)") >>> builder.configure(suggest=True) >>> tree_f, tree_g = builder[:] >>> print(matlab2cpp.qscript(tree_f)) int f(int x) { int y ; y = x ; return y ; } >>> print(matlab2cpp.qscript(tree_g)) void g() { int z ; z = f(4) ; } Verbatim translations --------------------- In some cases, the translation can not be performed. For example, the Matlab function ``eval`` can not be properly translated. Matlab is interpreted, and can easily take a string from local name space, and feed it to the interpreter. In C++ however, the code must be pre-compiled. Not knowing what the string input is before runtime, makes this difficult. So instead it makes more sense to make some custom translation by hand. Since ``matlab2cpp`` produces C++ files, it is possible to edit them after creation. However, if changes are made to the Matlab-file at a later point, the custom edits have to be added manually again. To resolve this, ``matlab2cpp`` supports verbatim translations through the suppliment file ``.py`` and through the node attribute :py:attr:`~matlab2cpp.Node.vtypes`. :py:attr:`~matlab2cpp.node.vtype` is a dictionary where the keys are string found in the orginal code, and the values are string of the replacement. Performing a verbatim replacement has to be done before the node tree is constructed. Assigning :py:attr:`~matlab2cpp.Node.vtypes` doesn't work very well. Instead the replacement dictionary can be bassed as argument to :py:func:`~matlab2cpp.build`:: >>> tree = matlab2cpp.build('''a=1 ... b=2 ... c=3''', vtypes = {"b": "_replaced_text_"}) >>> print(matlab2cpp.qscript(tree)) a = 1 ; // b=2 _replaced_text_ c = 3 ; Note that when a match is found, the whole line is replaced. No also how the source code is retained a comment above the verbatim translation. The verbatim key can only match a single line, however the replacement might span multiple lines. For example:: >>> replace_code = '''one line ... two line ... three line''' >>> tree = matlab2cpp.build('''a=1 ... b=2 ... c=3''', vtypes={"b": replace_code}) >>> print(matlab2cpp.qscript(tree)) a = 1 ; // b=2 one line two line three line c = 3 ; Verbatims can also be utilized by modifying the .py file. Consider the Matlab script:: a = 1 ; b = 2 ; c = 3 ; Using the m2cpp script to translate the Matlab script produces a C++ file and a .py file. By adding code to the .py file, verbatim translation can be added. This is done by using the keyword verbatims and setting it to a python dictionary. Similar to vtype, keys are strings found in the original code, and the values are string of the replacement:: functions = { "main" : { "a" : "int", "b" : "int", "c" : "int", }, } includes = [ '#include ', 'using namespace arma ;', ] verbatims = {"b = 2 ;" : '''one line two line tree line''' } In the generated C++ file the second assignment is replaced with the verbatim translation:: int main(int argc, char** argv) { int a, c ; a = 1 ; // b = 2 ; one line two line tree line c = 3 ; return 0 ; } """ ================================================ FILE: src/matlab2cpp/manual/usr03_rules.py ================================================ """ .. _usr03: Translation rules ================= In Matlab2cpp, the simplest form for translation is a simple string saved to a variable. For example:: >>> Int = "6" The name :py:class:`~matlab2cpp.collection.Int` (with capital letter) represents the node the rule is applicable for integers. The right hand side when it is a string, will be used as the translation every time :py:class:`~matlab2cpp.collection.Int` occurs. To illustrate this, consider the following simple example, where we pass a snippet to the :py:func:`~matlab2cpp.qscript` function:: >>> print(matlab2cpp.qscript("5")) 5 ; To implement the new rule we (globally) insert the rule for all instances of :py:class:`~matlab2cpp.collection.Int` as follows:: >>> print(matlab2cpp.qscript("5", Int=Int)) 6 ; Obviously, this type of translation is not very useful except for a very few exceptions. First of all, each :py:class:`~matlab2cpp.collection.Int` (and obviously many other nodes) contain a value. To represent this value, the translation rule uses string interpolation. This can be implemented as follows:: >>> Int = "|%(value)s|" >>> print(matlab2cpp.qscript("5", Int=Int)) |5| ; There are also other attributes than `value`. For example variables, represented through the node `Var` have a name, which refer to it's scope defined name. For example:: >>> Var = "__%(name)s__" >>> print(matlab2cpp.qscript("a = 4", Var=Var)) __a__ = 4 ; Since all the code is structured as a node tree, many of the nodes have node children. The translation is performed leaf-to-root, implying that at the time of translation of any node, all of it's children are already translated and available in interpolation. The children are indexed by number, counting from 0. Consider the simple example of a simple addition:: >>> print(matlab2cpp.qtree("2+3", core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Plus expression int 1 1| | | Int int int 1 3| | | Int int int >>> print(matlab2cpp.qscript("2+3")) 2+3 ; The node tree (at it's core) consists of a :py:class:`~matlab2cpp.collection.Block`. That code :py:class:`~matlab2cpp.collection.Block` contains one :py:class:`~matlab2cpp.collection.Statement`. The :py:class:`~matlab2cpp.collection.Statement` contains the :py:class:`~matlab2cpp.collection.Pluss` operator, which contains the two :py:class:`~matlab2cpp.collection.Int`. Each :py:class:`~matlab2cpp.Node` in the tree represents one token to be translated. From the perspective of the addition :py:class:`~matlab2cpp.collection.Plus`, the two node children of class :py:class:`~matlab2cpp.collection.Int` are available in translation respectivly as index 0 and 1. In interpolation we can do as follows:: >>> Plus = "%(1)s+%(0)s" >>> print(matlab2cpp.qscript("2+3", Plus=Plus)) 3+2 ; One obvious problem with this approach is that the number of children of a node might not be fixed. For example the `Plus` in "2+3" has two children while "1+2+3" has three. To address nodes with variable number of node children, alternative representation can be used. Instead of defining a string, a tuple of three string can be used. They represents prefix, infix and postfix between each node child. For example:: >>> Plus = "", "+", "" It implies that there should be a "+" between each children listed, and nothing at the ends. In practice we get:: >>> print(matlab2cpp.qscript("2+3", Plus=Plus)) 2+3 ; >>> print(matlab2cpp.qscript("1+2+3", Plus=Plus)) 1+2+3 ; And this is the extent of how the system uses string values. However, in practice, they are not used much. Instead of strings and tuples functions are used. They are defined with the same name the string/tuple. This function should always take a single argument of type :py:class:`~matlab2cpp.Node` which represents the current node in the node tree. The function should return either a :py:class:`~matlab2cpp.str` or :py:class:`~matlab2cpp.tuple` as described above. For example, without addressing how one can use `node`, the following is equivalent:: >>> Plus = "", "+ ", "" >>> print(matlab2cpp.qscript("2+3", Plus=Plus)) 2+ 3 ; >>> def Plus(node): ... return "", " +", "" ... >>> print(matlab2cpp.qscript("2+3", Plus=Plus)) 2 +3 ; One application of the ``node`` argument is to use it to configure datatypes. As discussed in the previous section :ref:`usr02`, the node attribute :py:attr:`~matlab2cpp.Node.type` contains information about datatype. For example:: >>> def Var(node): ... if node.name == "x": node.type = "vec" ... if node.name == "y": node.type = "rowvec" ... return node.name >>> print(matlab2cpp.qscript("function f(x,y)", Var=Var)) void f(vec x, rowvec y) { // Empty block } For more details on the behavior of the :py:mod:`~matlab2cpp.node` argument, see section on node :ref:`usr04`. For an extensive list of the various nodes available, see developer manual :ref:`dev06`. Rule context ------------ In the basic translation rule described above, each node type have one universal rule. However, depending on context, various nodes should be addressed differently. For example the snippet `sum(x)` lend itself naturally to have a rule that targets the name `sum` which is part of the Matlab standard library. Its translation should is as follows:: >>> print(matlab2cpp.qscript("sum(x)")) arma::sum(x) ; However, if there is a line `sum = [1,2,3]` earlier in the code, then the translation for `sum(x)` become very different. `sum` is now an array, and the translation adapts:: >>> print(matlab2cpp.qscript("sum=[1,2,3]; sum(x)")) sword _sum [] = {1, 2, 3} ; sum = irowvec(_sum, 3, false) ; sum(x-1) ; To address this in the same node will quickly become very convoluted. So instead, the rules are split into various backends. This simplifies things for each rule that have multiple interpretations, but also ensures that code isn't to bloated. For an overview of the various backend, see the developer manual :ref:`dev07`. Reserved rules -------------- The example above with `sum(x)` is handled by two rules. In the second iteration, it is a :py:mod:`~matlab2cpp.datatype` of type `irowvec` and is therefore processed in the corresponding rule for `irowvec`. However, in the former case, `sum` is a function from the Matlab standard library. In principle there is only one rule for all function calls like this. However, since the standard library is large, the rules are segmented into rules for each name. In the rule :py:mod:`rules._reserved `, each function in the standard library (which matlab2cpp supports) is listed in the variable `rules.reserved`. The context for reserved function manifest itself into the rules for function calls :py:class:`~matlab2cpp.collection.Get`, variables :py:class:`~matlab2cpp.collection.Var` and in some cases, multivariate assignment :py:class:`~matlab2cpp.collection.Assigns`. As described above, the rules should then have these names respectively. However to indicate the name, the rules also includes node names as suffix. For example, the function call for `sum` is handled in the rule :py:func:`~matlab2cpp.rules._reserved.Get_sum`. In practice this allows us to create specific rules for any node with names, which includes variables, function calls, functions, to name the obvious. For example, if we want to change the summation function from armadillo to the `accumulation` from `numeric` module, it would be implemented as follows:: >>> Get_sum = "std::accumulate(", ", ", ")" >>> print(matlab2cpp.qscript("sum(x)", Get_sum=Get_sum)) std::accumulate(x) ; This rules is specific for all function calls with name `sum` and wount be applied for other functions:: >>> Get_sum = "std::accumulate(", ", ", ")" >>> print(matlab2cpp.qscript("min(x)", Get_sum=Get_sum)) min(x) ; There are many rules to translation rule backends in matlab2cpp. This is mainly because each datatype have a corresponding backend. """ ================================================ FILE: src/matlab2cpp/manual/usr04_node.py ================================================ """ .. _usr04: Behind the frontends ==================== Common for all the various frontends in :py:mod:`~matlab2cpp.qfunctions` are two classes: :py:class:`~matlab2cpp.Builder` and :py:class:`~matlab2cpp.Node`. The former scans Matlab code and constructs a node tree consiting of instances of the latter. The Builder class ----------------- Iterating through Matlab code always starts with constructing a :py:class:`~matlab2cpp.Builder`:: >>> from matlab2cpp.tree import Builder >>> builder = Builder() This is an empty shell without any content. To give it content, we supply it with code:: >>> builder.load("file1.m", "a = 4") The function saves the code locally as `builder.code` and initiate the `create_program` method with index 0. The various `create_*` methods are then called and used to populate the node tree. The code is considered static, instead the index, which refer to the position in the code is increased to move forward in the code. The various constructors uses the support modules in the :py:mod:`~matlab2cpp.qtree` to build a full toke tree. The result is as follows:: >>> print(builder) # doctest: +NORMALIZE_WHITESPACE Project unknown TYPE | Program unknown TYPE file1.m | | Includes unknown TYPE 1 1| | Funcs unknown TYPE file1.m 1 1| | | Main unknown TYPE main 1 1| | | | Declares unknown TYPE 1 1| | | | | Var unknown TYPE a 1 1| | | | Returns unknown TYPE 1 1| | | | Params unknown TYPE 1 1| | | | Block unknown TYPE 1 1| | | | | Assign unknown TYPE 1 1| | | | | | Var unknown TYPE a 1 5| | | | | | Int unknown TYPE | | Inlines unknown TYPE file1.m | | Structs unknown TYPE file1.m | | Headers unknown TYPE file1.m | | Log unknown TYPE file1.m It is possible to get a detailed output of how this process is done, by turning the ``disp`` flag on:: >>> builder = Builder(disp=True) >>> builder.load("file1.m", "a = 4") # doctest: +NORMALIZE_WHITESPACE loading file1.m Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Assign assign.single 'a = 4' 0 Var variables.assign 'a' 4 Expression expression.create '4' 4 Int misc.number '4' This printout lists the core Matlab translation. In the four columns the first is the index to the position in the Matlab code, the second is the node created, the third is the file and function where the node was created, and lastly the fourth column is a code snippet from the Matlab code. This allows for quick diagnostics about where an error in interpretation might have occurred. Note that the tree above for the most part doesn't have any backends or datatypes configured. They are all set to ``unknown`` and ``TYPE`` respectivly. To configure backends and datatypes, use the :py:func:`~matlab2cpp.Builder.configure` method:: >>> builder.configure(suggest=True) >>> print(builder) # doctest: +NORMALIZE_WHITESPACE Project program TYPE | Program program TYPE file1.m | | Includes program TYPE 1 1| | Funcs program TYPE file1.m 1 1| | | Main func_return TYPE main 1 1| | | | Declares func_return TYPE 1 1| | | | | Var int int a 1 1| | | | Returns func_return TYPE 1 1| | | | Params func_return TYPE 1 1| | | | Block code_block TYPE 1 1| | | | | Assign int int 1 1| | | | | | Var int int a 1 5| | | | | | Int int int | | Inlines program TYPE file1.m | | Structs program TYPE file1.m | | Headers program TYPE file1.m | | Log program TYPE file1.m Multiple program can be loaded into the same builder. This allows for building of projects that involves multiple files. For example:: >>> builder = Builder() >>> builder.load("a.m", "function y=a(x); y = x+1") >>> builder.load("b.m", "b = a(2)") The two programs refer to each other through their names. This can the suggestion engine use:: >>> print(matlab2cpp.qscript(builder[0])) int a(int x) { int y ; y = x+1 ; return y ; } >>> print(matlab2cpp.qscript(builder[1])) b = a(2) ; Note that the frontend functions (like :py:func:`~matlab2cpp.qscript`) configure the tree if needed. The Node class -------------- So far the translation has been for the most part fairly simple, where the translation is reduced to either a string or a tuple of strings for weaving sub-nodes together. Consider the following example:: >>> def Plus(node): ... return "", " +", "" ... >>> print(matlab2cpp.qscript("2+3", Plus=Plus)) 2 +3 ; Though not used, the argument `node` represents the current node in the tree as the tree is translated. We saw this being used in the last section :ref:`usr03` to get and set node datatype. However, there are much more you can get out of the node. First, to help understand the current situation from a coding perspective, one can use the help function :py:func:`~matlab2cpp.Node.summary`, which gives a quick summary of the node and its node children. It works the same way as the function :py:func:`~matlab2cpp.qtree`, but can be used mid translation. For example:: >>> def Plus(node): ... print(node.summary()) ... return "", " +", "" ... >>> print(matlab2cpp.qscript("2+3", Plus=Plus)) # doctest: +NORMALIZE_WHITESPACE 1 1Plus expression int 1 1| Int int int 1 3| Int int int 2 +3 ; The first line represent the current node :py:class:`~matlab2cpp.collection.Plus`. introduce the node tree structure and how the nodes relate to each other. This will vary from program to program, so a printout of the current state is a good startingpoint. This can either be done through the function :py:mod:`~matlab2cpp.qtree`, or manually as follows:: >>> builder = Builder() >>> builder.load("unnamed", "function y=f(x); y=x+4") >>> builder[0].ftypes = {"f" : {"x": "int", "y": "double"}} >>> builder.translate() >>> print(builder) # doctest: +NORMALIZE_WHITESPACE Project program TYPE | Program program TYPE unnamed | | Includes program TYPE | | | Include program TYPE #include | | | Include program TYPE using namespace arma ; 1 1| | Funcs program TYPE unnamed 1 1| | | Func func_return TYPE f 1 1| | | | Declares func_return TYPE 1 1| | | | | Var double double y 1 1| | | | Returns func_return TYPE 1 10| | | | | Var double double y 1 13| | | | Params func_return TYPE 1 14| | | | | Var int int x 1 16| | | | Block code_block TYPE 1 18| | | | | Assign expression int 1 18| | | | | | Var double double y 1 20| | | | | | Plus expression int 1 20| | | | | | | Var int int x 1 22| | | | | | | Int int int | | Inlines program TYPE unnamed | | Structs program TYPE unnamed | | Headers program TYPE unnamed | | | Header program TYPE f | | Log program TYPE unnamed The Project is the root of the tree. To traverse the tree in direction of the leafs can be done using indexing:: >>> funcs = builder[0][1] >>> func = funcs[0] >>> assign = func[3][0] >>> var_y, plus = assign >>> var_x, int_4 = plus Moving upwards towards the root of the tree is done using the :py:attr:`~matlab2cpp.Node.parent` reference:: >>> block = assign.parent >>> print(assign is var_y.parent) True The node provided in the node function is the current node for which the parser is trying to translate. This gives each node full control over context of which is is placed. For example, consider the following:: >>> print(matlab2cpp.qtree("x(end, end)", core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Get unknown TYPE x 1 3| | | End expression int 1 8| | | End expression int >>> def End(node): ... if node is node.parent[0]: ... return node.parent.name + ".n_rows" ... if node is node.parent[1]: ... return node.parent.name + ".n_cols" ... >>> print(matlab2cpp.qscript("x(end, end)", End=End)) x(x.n_rows, x.n_cols) ; Here the rule `End` was called twice, where the if-test produces two different results. Also, information about the parent is used in the value returned. See also: :py:class:`~matlab2cpp.Builder` """ ================================================ FILE: src/matlab2cpp/manual/usr05_installation.py ================================================ r""" .. usr05: Installation ============ Requirements: * Python 2.7.3 * Armadillo (Not required for running, but generator creates armadillo code.) Optional: * C++11 (Plotting and TBB require C++11) * Intel Threading Building Blocks (TBB) * Intel Math Kernel Library (MKL) * MATLAB Engine API for Python * Matplotlib (Python module required for plotting) * Sphinx (for compiling documentation) * Argcomplete (for tab-completion support) Installing Python ----------------- Installing Armadillo -------------------- Armadillo can be downloaded at http://arma.sourceforge.net/ . Some of the functionality of Armadillo is a kind of wrapper and relies on a math library as BLAS, OpenBLAS, LAPACK, MKL. To compute x = A\\b or perform fast matrix-matrix multiplication one or more of these math libraries are required. See the Armadillo webpage for details. Install the required math library before installing Armadillo. Armadillo creates a dynamic library when it is compiled and installed. This dynamic library is configured to use one or more of the math libraries which were found when it was created. You don't need to install Armadillo and use link Armadillo to your program with the dynamic library. It is possible to give include and library path to Armadillo as well as link in one or more math libraries (BLAS, OpenBLAS, LAPACK, MKL). You could think of this as manually specifying the linking versus linking the dynamic Armadillo library where which math library to use has already been set. Even if you install Armadillo you have the option to link Armadillo by setting include, linking path and additional flags to link the math library. Installing Matlab2cpp --------------------- Matlab2cpp is written in Python. When running the translator program the Matlab2cpp is imported as a module its functionality is used to parse and translate Matlab code to C++. Because of this Python 2.7 is a requirement. Matlab2cpp is Open-source and is available at Github (https://github.com/emc2norway/m2cpp) . With the `git` software installed the software can be downloaded to the current folder by typing:: git clone git@github.com:emc2norway/m2cpp.git in a terminal (in Windows: cmd, cygwin). Move to the downloaded code with `"cd m2cpp"`. Then the software can be installed by typing Windows:: python setup.py install Mac/Linux:: sudo python setup.py install To update the Matlab2cpp to a newer version on git, enter a terminal and move to the folder where you downloaded Matlab2cpp with git clone. To pull the latest version on Github type:: git pull and then re-install with the same command used to install the first time as described above. When installing the module Installing optional packages ---------------------------- Among these optional packages, TBB, MKL and configuring Matlab to suggest datatypes are probably the most intereting/useful features. By inserting pragmas in the Matlab code, for loops can be marked by the user. The program can then either insert OpenMP or TBB code to parallelize the for loop. By putting this pragma before the for loop the user guarranties that the iterations are independent and write to different memory locations. To compile TBB code, the TBB library has to be installed. Most compilers now adays come with built in support for OpenMP, therefore no installation should be necessary. It may not be well known, but MKL is available for free. MKL isn't necessarry as BLAS, OpenBLAS, LAPACK could be used intead.u C++ is a static language. This means that the variables have to be declared and have the specified datatype. At the moment some of variable types in the generated C++ code can be deduced from the Matlab code. Another option is to run the Matlab code with Matlab and get the datatypes Matlab used when executing the Matlab code. To make use of this option, you need a sufficiently new Matlab version installed (works on R2015b) on your computer. The plotting functional provided by Matlab2cpp is done by a wrapper for Python's matplotlib module. This wrapper also makes use of C++11 functionality. Compiling the generated C++ code require C++11, Python and Matplotlib. Intel Threading Building Blocks (TBB) +++++++++++++++++++++++++++++++++++++ TBB can be downloaded at https://www.threadingbuildingblocks.org/. On the linux distribution Ubuntu, TBB can be installed from the terminal with the command: `sudo apt-get install libtbb-dev` Intel Math Kernel Library (MKL) +++++++++++++++++++++++++++++++ MKL can be downloaded for free at https://software.intel.com/en-us/articles/free-mkl MATLAB Engine API for Python ++++++++++++++++++++++++++++ Follow the instrucion at: http://se.mathworks.com/help/matlab/matlab_external/install-the-matlab-engine-for-python.html Matplotlib (Python module required for plotting) ++++++++++++++++++++++++++++++++++++++++++++++++ Matplotlib could be part of your Python installation by default. Else it can be installed with :: pip install matplotlib Sphinx (for compiling documentation) ++++++++++++++++++++++++++++++++++++ :: pip install sphinx pip install sphinxcontrib-autoprogram pip install sphinxcontrib-napoleon pip install sphinx-argparse Argcomplete (for tab-completion support) ++++++++++++++++++++++++++++++++++++++++ :: pip intall argcomplete activate-global-python-argcomplete This only works for Bash and would require a restart of terminal emulator. """ ================================================ FILE: src/matlab2cpp/matlab_types.py ================================================ import os import shutil #copy files from current dir to m2cpp_temp #import re import matlab2cpp.mwhos from matlab2cpp.datatype import get_mem from itertools import takewhile def lstripped(s): return ''.join(takewhile(str.isspace, s)) #def rstripped(s): # return ''.join(reversed(tuple(takewhile(str.isspace, reversed(s))))) #def stripped(s): # return lstripped(s), rstripped(s) def mtypes(builder, args): #Current working directory src_dir = os.getcwd() + os.path.sep dst_dir = src_dir + "m2cpp_data_type_temp" + os.path.sep # Delete the temporary folder which the datatype info if os.path.isdir(dst_dir): shutil.rmtree(dst_dir) #if directory m2cpp_data_type_temp does not exist, create it if not os.path.isdir(dst_dir): os.mkdir(dst_dir) #copy file in directory to m2cpp_temp #If there are data files in directory, then they have to be copied, or abspath to datafiles in program src_file_dir = os.path.dirname(os.path.abspath(args.filename)) + os.path.sep src_files = os.listdir(os.path.dirname(os.path.abspath(args.filename)) + os.path.sep) for file_name in src_files: file_src = src_file_dir + file_name if os.path.isfile(file_src): if file_src[-2:] != ".m": shutil.copy(file_src, dst_dir) #write whos_f.m to folder. Similar to matlab whos function, #but writes to file f = open(dst_dir + "whos_f.m", "w") f.write(matlab2cpp.mwhos.code) f.close() ##Create new matlab file in m2cpp_temp #Loop for each script and function .m file #print("- Program loop start\n\n") for program in builder.project: #get program code from matlab file f = open(program.name, "r") code = f.read() f.close() #Get name of file after splitting path, set file path "m2cpp_temp/file" file_path = dst_dir + program.name.split(os.path.sep)[-1] #print(file_path) #Program is main script file, add whos_f at end of file #program[1][0].cls is Main if script file if program[1][0].cls == "Main": #Modify the code, main file file_name = (program.name.split(os.path.sep)[-1])[:-2] code += "\nwhos_f" else: #Modify the code, funtion file code = function_code(program, code) #insert whos_f before return statements func_name = "whos_f" lines = code.splitlines() code_temp = [] for line in lines: #lstrip line and compare with "return" if line.lstrip()[:6] == "return": #Insert left stripped whitespace + func_name code_temp.append(lstripped(line) + func_name) #Add the "original" code line code_temp.append(line) #Convert string list to string code = "\n".join(code_temp) #write file to m2cpp_temp/ #print("writing file: " + file_path) f = open(file_path, "w") f.write(code) f.close() try: import matlab.engine #Set path to -p "setpath.m" before changing current working directory if args.paths_file: matlab_cmd = "run(\'"+ os.path.abspath(args.paths_file).rstrip(".m") + "\')" cwdir = os.getcwd() os.chdir(dst_dir) engine = matlab.engine.start_matlab() if args.paths_file: engine.evalc(matlab_cmd, nargout=0) engine.evalc(file_name, nargout=0) os.chdir(cwdir) except: print("matlab did not load correctly, check that you have matlab engine API for python installed") ##Process .m.txt files to extract data types #I could have this under the previous loop, #but then the loop becomes so long program_number = 0 for program in builder.project: #reset funcs_types dictionary for each iteration funcs_types = {} file_path = dst_dir + program.name.split(os.path.sep)[-1] + ".txt" #print(file_path) funcs_types = extract_ftypes(program, funcs_types, file_path) ##Copy data types to program.ftypes funcs = program.ftypes for func_key in funcs_types.keys(): for var_key in funcs_types[func_key].keys(): funcs[func_key][var_key] = funcs_types[func_key][var_key] #set ftypes for the current program builder[program_number].ftypes = funcs program_number += 1 return builder def extract_ftypes(program, funcs_types, file_path): #Check if file exists, if not return if not os.path.isfile(file_path): return funcs_types #Read file f = open(file_path, "r") lines = f.read().splitlines() #change to read, and then splitlines f.close() #while line j = 0 while j < len(lines): line = lines[j] #print(str(j) + ":" + line) #When function is found, loop over variables and cols = line.split(":") #add them to dict funcs_types if cols[0] == "#function_name": #print(line) #funcs_name = cols[1].split(",")[0] f_names = cols[1].split(",") #funcs_name = f_names[0].lstrip() if not len(f_names) == 2 else f_names[1].lstrip() funcs_name = f_names[0].lstrip() if program[1][0].cls != "Main" else f_names[1].lstrip() #skip next line j += 2 #make double indexed dictionary if not funcs_types.get(funcs_name): funcs_types[funcs_name] = {} while j < len(lines) and lines[j] != "": cols = lines[j].split(", ") #cols contain: name, size, class, complex, integer #extract variables to be more readable var_name = cols[0] #print(lines[j]) #extract the type in string format data_type = datatype_string(cols) #print(var_name + ": " + data_type) #insert in dictionary #check if entry in dictionary exist data_type_old = funcs_types[funcs_name].get(var_name) #complex type should stay complex type if data_type_old: #get if datatype is comples, then mem_value should be 4 #mem_value_old = get_mem(data_type_old) mem_value_new = get_mem(data_type) #print("mem_value_new: ") #print(mem_value_new) if mem_value_new == 4: funcs_types[funcs_name][var_name] = data_type else: funcs_types[funcs_name][var_name] = data_type j += 1 j += 1 return funcs_types def datatype_string(cols): #Convert data from cols to a string, representing the datatype M, N = [int(s) for s in cols[1].split("x")] var_type = cols[2] complex_number = int(cols[3]) integer = int(cols[4]) data_type = "" if var_type == "double": #non complex if not complex_number: #scalar if M == 1 and N == 1: if integer: data_type = "int" else: data_type = "double" #vec if M > 1 and N == 1: #data_type = "mat" data_type = "vec" #rowvec if M == 1 and N > 1: #data_type = "mat" data_type = "rowvec" #matrix if (M > 1 and N > 1) or M == 0 or N == 0: #if empty matrix, set to mat data_type = "mat" #complex value elif complex_number: #scalar if M == 1 and N == 1: data_type = "cx_double" #vec if M > 1 and N == 1: #data_type = "cx_mat" data_type = "cx_vec" #rowvec if M == 1 and N > 1: #data_type = "cx_mat" data_type = "cx_rowvec" #matrix if M > 1 and N > 1 or M == 0 or N == 0: #if empty matrix, set to mat: data_type = "cx_mat" return data_type def detect_string(code, k): import string if code[k] != "'": syntaxerror(k, "start of string character (')") if code[k-1] == ".": return False j = k-1 while code[j] in " \t": j -= 1 if code[j] in string.letters+string.digits+")]}_": # special cases if code[j-3:j+1] == "case": return True return False return True def function_code(program, code): #print(program.summary()) #count number of if, while, for, switch and number of end #Flatten nodes, count number of nodes of type if, while for #num_if = 0 #num_while = 0 #num_for = 0 #num_switch = 0 import re iwfs_ends = 0 nodes = program.flatten(False, True, False) for node in nodes: if node.cls in ["If", "While", "For", "Switch"]: iwfs_ends += 1 #print("iwfs_ends: " + str(iwfs_ends)) #Get number of 'end' on separate lines iquote = [i for i in range(len(code)) if code[i] == '\''] istring = [i for i in iquote if detect_string(code, i)] string_ends = [] #for i in istring: # k = next(j for j in range(i + 1,len(code)) if code[j] == '\'' and (j + 1 == len(code) or code[j + 1] != '\'')) # string_ends.append(k) # #code = code[:i + 1] + code[k:] #if len(istring) > 0: # ncode = code[:istring[0] + 1] # for i in range(len(istring) - 1): # ncode += code[string_ends[i]:istring[i + 1] + 1] # #if (string_ends[-1] + 1 < len(code)): # ncode += code[string_ends[-1]:] # code = ncode #lines = code.splitlines() #for i in range(0,len(lines)): # #lines[i] = re.sub(r'\'(.+)\'',"''", lines[i]) # #lines[i] = re.sub(r'\"(.+)\"',"''", lines[i]) # lines[i] = lines[i].split('%')[0] #code = '\n'.join(lines) #esplit = code.replace(';', '\n').replace(',','\n').split() ##elines = esplit.splitlines() #num_end = 0 #for t in esplit: # if t == "end": # num_end += 1 #for line in elines: # if line.strip() == "end": # num_end += 1 #print(line) #print("num_end: " + str(num_end)) #insert whos_f before return and before function ..... (here)end (next fun) #Comparing num_end with num_if, num_while, num_for and switch case find if #functions are ending with "end" keyword or not. #flag set to true if functions end with end keyword #function_end = num_end > iwfs_ends #get number of functions in .m function file num_funcs = len(program[1]) #insert whos_f before end of function or before new function func_name = "whos_f\n" ids = [] for func in program[1]: block = func[3] block_end = block.cur + len(block.code) if block.is_end_terminated: fs = code.rfind("end", block.cur, block_end) ids.append(fs) else: ids.append(block_end) ids.sort() ncode = code[:ids[0]] for i in range(len(ids) - 1): ncode = ncode + func_name + code[ids[i]:ids[i+1]] ncode = ncode + func_name + code[ids[-1]:] code = ncode #functions end with end keyword #index = len(code) #index = len(lines) ##print(code) #if function_end: # #should stop when num_funcs becomes zero # while num_funcs: # #search for keyword end # #index = code.rfind("end", 0, index) # index = next(i for i in range(index - 1,-1,-1) if lines[i].strip() == 'end') # #add function name to code # lines = lines[:index] + [func_name] + lines[index:] # #index += len(func_name) # #Search for previous function # if num_funcs != 1: # #index = code.rfind("\nfunction", 0, index) # index = next(i for i in range(index - 1,-1,-1) if len(lines[i].split()) > 0 and lines[i].split()[0] == 'function') # num_funcs -= 1 #else: # #function does not end with end keyword # while num_funcs: # #code = code[:index] + func_name + code[index:] # lines = lines[:index] + [func_name] + lines[index:] # if num_funcs != 1: # #index = code.rfind("\nfunction", 0, index) # index = next(i for i in range(index - 1,-1,-1) if len(lines[i].split()) > 0 and lines[i].split()[0] == 'function') # num_funcs -= 1 #print(code) #code = '\n'.join(lines) return code ================================================ FILE: src/matlab2cpp/modify.py ================================================ import matlab2cpp import matlab2cpp.node as nmodule def preorder_transform_AST(node, nargin = False, suggest = False): # Modify the abstract syntax tree (AST), also try to overload funtions # node is project node project = node.project nodes = nmodule.Node.flatten(project, False, False, False) # remove the nodes for clear, close and clc so they are not included in the translation nodes = remove_close_clear_clc(nodes) # Change right hand side variable to uvec if assigned with find, b = find(a==3) if suggest: nodes = modify_find(nodes) #a multiplication with a complex double results in complex double #works with fx_decon_demo.m needs more testing and maybe a refactoring if suggest: nodes = complex_mul(nodes) # remove nargin if args.nargin == False, Thus by default. Use -n flag to keep nargin if nargin == False: project = remove_nargin(project) # add temporary variables for multiple return function project = add_parameters(project) # change data type from real to complex, if left hand side is real and right hand side is complex in assignment if suggest: change_to_complex(project) return project def postorder_transform_AST(node): project = node.project # move the "using namespace arma ;" node last in the includes list project = modify_arma_last(project) #move #define NOMINMAX to first position project = modify_define_first(project) return project # node is project node def change_to_complex(project): # for each program in project for idx, program in enumerate(project): dictionary = program.ftypes new_complex_types = {} new_complex_dim = {} # for each function (in Funcs) in program for func in program[1]: func_name = func.name # flatten nodes in the function nodes = func.flatten(False, False, False) #look for assignment for n in nodes: if n.cls in "Assign" and len(n) == 2: lhs, rhs = n if lhs.mem and lhs.mem != 4 and rhs.mem == 4: lhs.mem = 4 # add variable name as key and complex type as type if (lhs.name in new_complex_dim) and (new_complex_dim[lhs.name] < lhs.dim): new_complex_dim[lhs.name] = lhs.dim lhs.type = (lhs.dim, lhs.mem) new_complex_types[lhs.name] = lhs.type else: new_complex_dim[lhs.name] = lhs.dim lhs.type = (lhs.dim, lhs.mem) new_complex_types[lhs.name] = lhs.type dictionary[func_name][lhs.name] = new_complex_types[lhs.name] # clear the types in the program nodes = program.flatten(False, False, False) for idy, node in enumerate(nodes): node.type = "TYPE" # use dictionary to set ftypes program.ftypes = dictionary # unset the configured flag project.builder.configured = False # configure again project.builder.configure(True) #print(dictionary) def complex_mul(nodes): for node in nodes: if node.cls == "Assign": lhs, rhs = node if rhs.cls == "Mul": if rhs[0].type == "cx_double": declares = node.func[0] for var in declares: if var.name == lhs.name: var.type = "cx_double" return nodes # remove the nodes for clear, close and clc so they are not included in the translation def remove_close_clear_clc(nodes): for n in nodes: if n.backend == "reserved" and n.name in ("clear", "close", "clc"): index = n.parent.parent.children.index(n.parent) del n.parent.parent.children[index] return nodes # Change right hand side variable to uvec if assigned with find, b = find(a==3) def modify_find(nodes): for n in nodes: if n.cls == "Assign": lhs, rhs = n if rhs.name == "find": declares = n.func[0] #print(declares.cls) for var in declares: if var.name == lhs.name: var.type = "uvec" return nodes # move the "using namespace arma ;" node last in the includes list def modify_define_first(project): for program in project: includes = program[0] index = 0 for include in includes: if include != includes[0] and include.name[:7] == "#define": define_include = includes.children.pop(index) includes.children.insert(0, define_include) index += 1 return project # move the "using namespace arma ;" node last in the includes list def modify_arma_last(project): for program in project: includes = program[0] index = 0 for include in includes: if include != includes[-1] and include.name == "using namespace arma ;": #includes += [includes.pop(index)] # remove and append arma include node using_arma = includes.children.pop(index) includes.children.append(using_arma) index += 1 return project # remove nargin if args.nargin == False, Thus by default. Use -n flag to keep nargin def remove_nargin(project): # node is project for program in project: # for func in funcs funcs = program[1] for func in funcs: #print(func.summary()) block = func[3] # find node.name == nargin found_nargin = True while found_nargin: found_nargin = False nodes = nmodule.Node.flatten(block, False, False, False) # remove if node.group is branch for n in nodes: if n.name == "nargin": # remove branch if n.group.cls in ("Branch", "Switch"): parent = n.group.parent # print(parent.summary()) del parent.children[parent.children.index(n.group)] # node.group.parent.children.index(node.group) found_nargin = True break else: # node.group is not a branch found_nargin = False break return project # add temporary variables for multiple return function def add_parameters(project): nodes = nmodule.Node.flatten(project, False, False, False) for n in nodes: if n.cls == "Get" and n.backend == "func_returns": func_name = n.name func_ret_num = 0 if n.parent.cls in ("Assign", "Assigns"): for sub_node in n.parent: if sub_node.cls != "Get": func_ret_num += 1 else: break #print(n.summary()) # find the multiple return function # Check if function is in the same program # Look first for function in current file funcs = n.program[1] found_func = False func = [] for f in funcs: if f.name == n.name: func_found = True #programs = n.program func = f # Look for function in external file if found_func == False: for program in n.project: f = program[1][0] if f.name == n.name: func = f break #programs = [p[1][0] for p in project if p[1][0]= n.program] # add needed temporary variables to params if func: return_params = func[1] # dictionary which is used as a counter type_counter = {"int" : 0, "float" : 0, "double" : 0, "uword" : 0, "cx_double" : 0, "ivec" : 0, "fvec" : 0, "uvec" : 0, "vec" : 0, "cx_vec" : 0, "irowvec" : 0, "frowvec" : 0, "urowvec" : 0, "rowvec" : 0, "cx_rowvec" : 0, "imat" : 0, "fmat" : 0, "umat" : 0, "mat" : 0, "cx_mat" : 0, "icube" : 0, "fcube" : 0, "ucube" : 0, "cube" : 0, "cx_cube" : 0} # add or reuse variables, also add change while func_ret_num < len(return_params): if return_params[func_ret_num].type not in ("TYPE", "string"): type_counter[return_params[func_ret_num].type] += 1 name = "_tmp_" + return_params[func_ret_num].type + \ str(type_counter[return_params[func_ret_num].type]) # Allocate Var node. n.parent is the assign node swap_var = matlab2cpp.collection.Var(n.parent, name) swap_var.type = return_params[func_ret_num].type swap_var.backend = return_params[func_ret_num].backend swap_var.create_declare() # swap Var and Get (function_returns) n.parent.children[func_ret_num] = swap_var n.parent.children[-1] = n #index += 1 func_ret_num += 1 return project ================================================ FILE: src/matlab2cpp/mwhos.py ================================================ code=r"""function whos_f() %Static variables %persistent l %cellFile counter %persistent m %cellFunc counter %persistent cellFile %persistent cellFunc %initialize counter k %if isempty(l) % l = 1; % m = 1; %end %Get info about caller, filename ST = dbstack(); if (length(ST) >= 2) file = strcat(ST(2).file, '.txt'); else file = 'command_window.txt'; end if (length(ST) > 2) func = ['#function_name: ', ST(2).name]; elseif (length(ST) == 2) func = ['#function_name: ' , ST(2).name, ', main']; else func = '#command window:'; end %should only run this function once for every function in file %if(~any(ismember(file, cellFile)) || ~any(ismember(func, cellFunc))) %Calls whos on caller workspace, then processes and prints the data cmdstr = 'whos;'; my_vars = evalin('caller', cmdstr) ; %numer of workspace variables Nvars = size(my_vars, 1); %fprintf('#name, size, class, complex, integer\n') str = sprintf('%s\n%s', func, '#name, size, class, complex, integer'); for j = 1:Nvars %Test if all values are integer in variable, variable comes from caller workspace name = my_vars(j).name; cmd = ['all(' name ' == double(uint64(' name ')));']; %got error with struct, so I test if it works try integer = evalin('caller', cmd); catch integer = -1; end if (integer ~= -1) %Have to call all one more time for matrixes, two for cubes while (length(integer) > 1) integer = all(integer); end %Print the fields in the struct %fprintf('%s,%dx%d, %s, %d, %d\n', ... % name, my_vars(j).size, my_vars(j).class, ... % my_vars(j).complex, integer) temp = sprintf('\n%s, %dx%d, %s, %d, %d', ... name, my_vars(j).size, my_vars(j).class, ... my_vars(j).complex, integer); str = [str, temp]; end end %check if written to file, if not, new file, else append %if (~ismember(file, cellFile)) % cellFile{l} = file; % l = l + 1; % fp = fopen(file, 'w'); %else fp = fopen(file, 'a'); fprintf(fp, '\n'); %end %add function to static cellarray cellFunc %cellFunc{m} = func; %m = m + 1; %write whos to file fprintf(fp, '%s\n', str); fclose(fp); %end end """ ================================================ FILE: src/matlab2cpp/mwrapmat.py ================================================ try: import mlabraw except: pass class Wrapmat: #run matlab code with mlabraw def __init__(self): #get matlab session try: self.session = mlabraw.open() except Exception as e: print("Error: %s" % str(e)) def __del__(self): mlabraw.close(self.session) def eval_code(self, builder): self.builder = builder #should get a list of func names func_names = [] for program in builder.project: print(program.summary()) #flatten tree and find Func.names nodes = program.flatten(False, False, False) for node in nodes: if node.cls == "Func": func_names.append(node.name) #print(func_names) self.func_names = func_names self.called_func_names = [] #check if main func = builder[0][1][0] if func.name == 'main': code_block = func[3] #evaluate, recursive function self._evaluate(code_block) mlabraw.eval(self.session, 'whos_f') else: print('matlab have to run script file') def _evaluate(self, code_block): #eval line by line in code_block for node in code_block: func_nodes = [] #multiline code -> for, while, if, case #if statement #one line code if node.cls in ('Assign', 'Assigns', 'Statement'): #flatten the node, check for function: Var/Get unknown type name sub_nodes = node.flatten(False, False, False) #check if Var/Get unknown type name -> self written function for sub_node in sub_nodes: if ((sub_node.cls == 'Get' or sub_node.cls == 'Var') and \ sub_node.backend == 'unknown' and \ sub_node.type == 'TYPE' and \ sub_node.name in self.func_names): #Test if function processed before if sub_node.name not in self.called_func_names: func_nodes.append(sub_node) self.called_func_names.append(sub_node.name) #loop over func_nodes for func_node in func_nodes: #print(func_node.name) params = [] function = False #save and store variables, if entered function # node.func.name is name of caller function mlabraw.eval(self.session, 'save mconvert_' + code_block.func.name) #clear variables, set params, Get function have params mlabraw.eval(self.session, 'clear all') #find function node that is called #check current file for function -> code_block funcs = code_block.func.parent #funcs for func in funcs: if func_node.name == func.name: new_code_block = func[3] function = True #check other file for function -> code_block if not function: for program in node.project: func = program[1][0] if func_node.name == func.name: params = func[2] new_code_block = func[3] function = True #set params for var, param in zip(func_node, params): my_string = "load (\'mconvert_" + code_block.func.name + "\', \'" + var.name + "\')" mlabraw.eval(self.session, my_string) mlabraw.eval(self.session, param.name + ' = ' + var.name) if var.name != param.name: mlabraw.eval(self.session, 'clear ' + var.name) #jump into code_block of the function self._evaluate(new_code_block) mlabraw.eval(self.session, 'whos_f') #load previous variables mlabraw.eval(self.session, 'clear all') mlabraw.eval(self.session, 'load mconvert_' + code_block.func.name) #eval code line mlabraw.eval(self.session, node.code) ================================================ FILE: src/matlab2cpp/node/__init__.py ================================================ """ The module contains the following submodules. """ from .frontend import Node __all__ = ["Node"] if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/node/backend.py ================================================ import logging import re import os from os.path import sep import matlab2cpp from . import m2cpp import matlab2cpp.pyplot from . import reference def flatten(node, ordered=False, reverse=False, inverse=False): """ Backend for the :py:func:`~matlab2cpp.Node.flatten` function. Args: node (Node): Root node to start from ordered (bool): If True, make sure the nodes are hierarcically ordered. reverse (bool): If True, children are itterated in reverse order. inverse (bool): If True, tree is itterated in reverse order. See also: :py:func:`~matlab2cpp.Node.flatten` """ o = bool(ordered) r = bool(reverse) i = bool(inverse) out = [] if o: nodes = [node] for node in nodes: nodes.extend(node.children[::1-2*(r ^ i)]) out.extend(nodes[::1-2*i]) else: if i: def foo(node): for child in node[::1-2*r]: foo(child) out.append(node) else: def foo(node): out.append(node) for child in node[::1-2*r]: foo(child) foo(node) return out def summary(node, opt): """ Backend for creating summary of the node tree. See :py:func:`~matlab2cpp.qtree` for behavior. Args: node (Node): Relative root of the tree Returns: str: string representation of the node See also: :py:func:`~matlab2cpp.qtree` """ nodes = flatten(node, False, False, False) if not (opt is None) and opt.disp: print("iterating over %d nodes" % len(nodes)) if not (opt is None) and not (opt.line is None): for node in nodes: if node.cls != "Block" and node.line == opt.line: nodes = flatten(node, False, False, False) break indent = [] outl = [] nl = len(str(nodes[-1].line))+1 nc = len(str(nodes[-1].cur+1))+1 for node in nodes: out = "" if node.line: nl_ = len(str(node.line)) out += " "*(nl-nl_) + str(node.line) + " " nc_ = len(str(node.cur+1)) out += " "*(nc-nc_) + str(node.cur+1) else: out += " "*(nl+nc+1) # indentation while indent and not (node.parent is indent[-1]): indent.pop() out += "| "*(len(indent)) indent.append(node) out += node.cls.ljust(11) out += node.backend.ljust(13) # define type if node.type == "TYPE": type = node.declare.prop.get("suggest", "TYPE") if type != "TYPE": type = "(" + type + ")" else: type = node.type out += type.ljust(8) out += node.name outl.append(out) out = "\n".join(outl) out = re.sub(r"(\\n){2,}", "", out) return out def auxillary(node, type, convert): """ Backend for the :py:func:`~matlab2cpp.Node.auxillary` function. Args: node (Node): Root of the tree where split into new line will occour. type (str, None): If provided, auxiliary variable type will be converted convert (bool): If true, add an extra function call ``conv_to`` to convert datatype in Armadillo. See also: :py:func:`~matlab2cpp.Node.auxiliary` """ assert node.parent.cls != "Assign",\ ".auxiliary() must be triggered mid expression." type = type or node.type if not isinstance(type, str): if isinstance(type[0], int): type = matlab2cpp.datatype.get_name(*type) else: type = matlab2cpp.datatype.common_strict(type) matrix_mode = False if node.cls == "Matrix": matrix_mode = True if matrix_mode and type == "int" and node.group.cls in ("Get", "Set"): type = "uword" line = node while line.parent.cls != "Block": line = line.parent block = line.parent # Create new var i = 1 declares = node.func[0] while "_aux_" + type + "_" + str(i) in declares: i += 1 var = "_aux_" + type + "_" + str(i) # Create Assign assign = matlab2cpp.collection.Assign(block, code=node.code) assign.type = type if matrix_mode: assign.backend = "matrix" # Return value aux_var = matlab2cpp.collection.Var(assign, var) aux_var.type = type aux_var.backend = type aux_var.create_declare() if convert: rhs = matlab2cpp.collection.Get(assign, "_conv_to") rhs.type = type else: rhs = assign swap_var = matlab2cpp.collection.Var(rhs, var) swap_var.declare.type = type # Place Assign correctly in Block i = block.children.index(line) block.children = block[:i] + block[-1:] + block[i:-1] # Swap node and Var index = node.parent.children.index(node) node.parent.children[index] = swap_var rhs.children[-1] = node swap_var.parent, node.parent = node.parent, swap_var.parent # generate code node.translate() swap_var.translate(only=True) aux_var.translate(only=True) if convert: rhs.translate(only=True) assign.translate(only=True) if convert: assert node.type != swap_var.type return swap_var def resize(node): """ Backend for the :py:func:`~matlab2cpp.Node.resize` function. Args: node (Node): node to be resized See also: :py:func:`~matlab2cpp.Node.resize` """ if "_resize" in node.prop: return node["_resize"] = True type = node.type node.dim = 3 line = node while line.parent.cls != "Block": line = line.parent resize = matlab2cpp.collection.Resize(line.parent, name=node.name) resize.type = type i = line.parent.children.index(line) ps = line.parent.children line.parent.children = ps[:i] + ps[-1:] + ps[i:-1] resize.translate(False, only=True) def error(node, msg, onlyw=False): """ Add an error or warning to the log subtree. Args: node (Node): node where error occoured msg (str): error message content onlyw (bool): if true, use warning instead of error See also: :py:func:`~matlab2cpp.Node.error` :py:func:`~matlab2cpp.Node.warning` """ msg = msg % node.properties() code = node.program.code cur = node.cur end = cur+len(node.code) start = cur while code[start] != "\n" and start != 0: start -= 1 if end >= len(code): end = len(code)-1 finish = end while code[finish] != "\n" and finish != len(code)-1: finish += 1 code = code[start:finish] pos = cur-start name = node.cls + ":" + str(cur) errors = node.program[5] if name in errors.names: return if onlyw: err = matlab2cpp.collection.Warning(errors, name=name, line=node.line, cur=pos, value=msg, code=code) else: err = matlab2cpp.collection.Error(errors, name=name, line=node.line, cur=pos, value=msg, code=code) err.backend="program" def create_declare(node): """ Backend for the :py:func:`~matlab2cpp.Node.create_declare` function. Args: node (Node): Node to create declare from Returns: Node : the (newly) declared node """ if not (node is node.declare): return node if node.cls in reference.structvars: if node.cls in ("Nget", "Nset"): if node[0].cls == "String": return None value = node[0].value else: value = node.value structs = node.program[3] assert structs.cls == "Structs" if node not in structs: struct = matlab2cpp.collection.Struct(structs, name=node.name) else: struct = structs[node] if value in struct.names: return struct[struct.names.index(value)] declares = node.func[0] if node.cls in ("Sset", "Sget"): sname = "_size" if sname not in struct.names: matlab2cpp.collection.Counter(struct, sname, value="100") if node.name not in declares.names: var = matlab2cpp.collection.Var(declares, name=node.name, value=value) var.type="structs" else: if node.name not in declares.names: var = matlab2cpp.collection.Var(declares, name=node.name, value=value) var.type="struct" return matlab2cpp.collection.Var(struct, name=value) parent = struct else: parent = node.func[0] if node in parent: declare = parent[node] declare.type = node.type declare.pointer = node.pointer return declare out = matlab2cpp.collection.Var(parent, name=node.name, pointer=node.pointer, value=node.value) out.type = node.type return out def suggest_datatype(node): """ Backend for the :py:func:`~matlab2cpp.Node.suggest_datatype` function. Args: node (Node): Node to suggest datatype for. Returns: (tuple): Suggestion on the form ``(dim, mem)`` See also: :py:func:`~matlab2cpp.Node.suggest_datatype` """ if node.group.cls in ("Transpose", "Ctranspose"): dim, mem = suggest_datatype(node.group) if dim == 1: dim = 2 elif dim == 2: dim = 2 return dim, mem elif node.group.cls == "Assign": if node.group[0].num: return node.group[0].dim, node.group[0].mem elif node.group.cls == "Matrix": mems = set([]) if node.group.value: # decomposed ax0, ax1 = len(node.group), len(node.group[0]) if ax0 > 1: if ax1 > 1: dim = 3 else: dim = 1 else: if ax1 > 1: dim = 2 else: dim = 0 for vec in node.group: for elem in vec: if elem.num: mems.add(elem.mem) # rowvec definition elif len(node.group) == 1: if len(node.group[0]) == 1: return None, None for elem in node.group[0]: if elem.num: mems.add(elem.mem) dim = 3 # colvec definition elif len(node.group[0]) == 1: for vec in node.group: if vec[0].num: mems.add(vec[0].mem) dim = 3 else: for vec in node.group: for elem in vec: if elem.num: mems.add(elem.mem) dim = 3 if len(mems) == 1: return dim, mems.pop() elif len(mems) > 1: return dim, max(*mems) else: return None, None return None, None # small hack to ensure that log isn't cleaned mid translation mid_translation = [0] def translate(node, opt=None): """ Backend for performing translation of subtree Args: node (Node): Root of the translation opt (argparse.Namespace, optional): optional arguments from frontend See also: :py:func:`~matlab2cpp.Node.translate` """ # translate for every program if node.cls == "Project": map(translate, node) return node if mid_translation[0] == 0: log = node.program[5] log.children = [] mid_translation[0] += 1 nodes = flatten(node, False, True, False) if not (opt is None) and opt.disp: print("iterating %d nodes" % len(nodes)) for node in nodes[::-1]: translate_one(node, opt) mid_translation[0] -= 1 if not mid_translation[0]: logs = flatten(log, False, True, False) for node in logs[::-1]: translate_one(node, opt) return node def translate_one(node, opt): """ Backend for performing translation of single node Args: node (Node): Node to perform translation on opt (argparse.Namespace, optional): optional arguments from frontend See also: :py:func:`~matlab2cpp.Node.translate` """ logger = logging.getLogger(__name__) # e.g. Get_a from user value = node.program.parent.kws.get(node.cls+"_"+node.name, None) # e.g. Get from user if value is None: value = node.program.parent.kws.get(node.cls, None) if value is None: backend = node.backend if backend == "TYPE": backend = "unknown" assert "_"+backend in matlab2cpp.rules.__dict__, ( "No rule {}; ensure your .py file is properly set up.".format(backend)) try: target = matlab2cpp.rules.__dict__["_"+backend] except KeyError as err: logger.warning( "'%s', File %s. Datatype defined in the .py file, might be wrong.", err.message, node.file) raise specific_name = node.cls + "_" + node.name # e.g. Get_a (reserved typically) if specific_name in target.__dict__: value = target.__dict__[specific_name] # e.g. Get (normal behavior) elif node.cls in target.__dict__: value = target.__dict__[node.cls] else: print(node.program.summary()) raise KeyError( "Expected to find rule for '%s' in the file '_%s.py. Crash with file: %s, on line: %s'" %\ (node.cls, node.backend, node.file, node.line)) # let rule create a translation if not isinstance(value, (str, list, tuple)): #print(node.code) #print("\n\n") value = value(node) # not quite right format if isinstance(value, (matlab2cpp.node.frontend.Node)): value = str(value) elif value is None: #print("\n\nerror:") #print(node.code) #print(node.parent.code) #print(node.parent.parent.code) #print("\n") raise ValueError( "missing return in function %s in file %s, Matlab: Crash with file: %s, on line: %s" %\ (node.cls, node.backend, node.file, node.line)) node.ret = repr(value) # interpolate tuples/lists if not isinstance(value, str): value = list(value) children = ["%("+str(i)+")s" for i in range(len(node))] if len(value) == 2: value.insert(1, "") value = value[:-1] + [value[-2]] *\ (len(children)-len(value)+1) + value[-1:] if len(children) == 0: value = value[0] + value[-1] elif len(children) == 1: value = value[0] + children[0] + value[-1] else: out = value[0] for i in range(len(children)): out += children[i] + value[i+1] value = out # interpolate string try: value = value % node.properties() except: #print("..........") #print(node.code) #print("----------") #print("\n\n") raise SyntaxError("interpolation in " + node.backend + "." +\ node.cls + " is misbehaving\n'" + value + "'\n" +\ str(node.prop) + "\nCrash with file: " + str(node.file) + " , on line: " + str(node.line) +\ ":\n" + node.code) if node.cls in ("Assign", "Assigns", "Statement", "If", "Elif", "For", "Parfor", "While") and node.project.builder.original: code_tmp = ["// " + line for line in node.code.splitlines()] value = "\n".join(code_tmp) + "\n" + value value = value.replace("%", "__percent__") node.str = value #wanted a static variable in function include below created_file = [] def include(node, name, **kws): """ Backend for the :py:func:`~matlab2cpp.Node.include` function. Args: node (Node): node in program where to where the header is placed name (str): name of header **kws (str, optional): Optional args for header. Mostly not in use. See also: :py:func:`~matlab2cpp.Node.include` """ if os.path.isfile(name): #name = os.path.relpath(name, os.path.dirname(node.program.name)) name = os.path.basename(name) include_code = '#include "%s.hpp"' % name library_code = "" if node.name == name: include_code = "" else: library_code = "" if name == "SPlot": include_code = '#include "SPlot.h"' #check if file in directory try: #file_path = node.program[1].name #index = file_path.rindex(sep) #output_file_path = file_path[:index] + sep + "SPlot.h" output_file_path = os.getcwd() + sep + "SPlot.h" #if mconvert.h not found in directory, create the file if not os.path.isfile(output_file_path) or "SPlot.h" not in created_file: f = open(output_file_path, "w") f.write(matlab2cpp.pyplot.code) f.close() created_file.append("SPlot.h") except: pass elif name == "m2cpp": include_code = '#include "mconvert.h"' #check if file in directory try: #file_path = node.program[1].name #index = file_path.rindex(sep) #output_file_path = file_path[:index] + sep + "mconvert.h" output_file_path = os.getcwd() + sep + "mconvert.h" #if mconvert.h not found in directory, create the file if not os.path.isfile(output_file_path) or "mconvert.h" not in created_file: f = open(output_file_path, "w") f.write(matlab2cpp.m2cpp.code) f.close() created_file.append("mconvert.h") except: pass elif name == "arma": include_code = "#include " elif name == "iostream": include_code = "#include " elif name == "cstdio": include_code = "#include " elif name == "complex": include_code = "#include " elif name == "cmath": include_code = "#include " elif name == "algorithm": include_code = "#include " elif name == "omp": include_code = "#include " elif name == "tbb": include_code = "#include " elif name == "no_min_max": include_code = "#define NOMINMAX" else: include_code = "" includes = node.program[0] if include_code and include_code not in includes.names: include = matlab2cpp.collection.Include(includes, include_code, value=includes.value) include.backend="program" #node.program[2] is inlines. I don't think inlines are used anymore #if you look at variable library_code above, it is set to "" inlines_ = node.program[2] if library_code and library_code not in inlines_.names: inline = matlab2cpp.collection.Inline(inlines_, library_code) inline.backend="program" def wall_clock(node): """ Backend for the :py:func:`~matlab2cpp.Node.wall_clock` function. Args: node (Node): node in function where ``wall_clock _timer`` should be declared. See also: :py:func:`~matlab2cpp.Node.wall_clock` """ declares = node.func[0] if "_timer" not in declares: clock = matlab2cpp.collection.Var(declares, name="_timer") clock.type="wall_clock" def plotting(node): """ Backend of the :py:func:`~matlab2cpp.Node.plotting` function. Args: node (Node): node in the function where plotting should be implemented. See also: :py:func:`~matlab2cpp.Node.plotting` """ declares = node.func[0] # only do this once if "_plot" in declares: return # add splot to header node.include("SPlot") # add a variable for Splot in declare var = matlab2cpp.collection.Var(declares, name="_plot") var.type = "SPlot" # get function variable func = node.func # get function block block = func[3] # create new statement statement = matlab2cpp.collection.Statement(block, code="") statement.backend="code_block" # fill it with new Get _splot get = matlab2cpp.collection.Get(statement, name="_splot") get.backend="reserved" # translate the new nodes statement.translate() # swap with last statement, if it is a return-statement if len(block)>1 and block[-2] and block[-2][0].cls == "Return": block.children[-1], block.children[-2] = \ block.children[-2], block.children[-1] ================================================ FILE: src/matlab2cpp/node/frontend.py ================================================ from . import ( reference as ref, backend, ) from .. import datatype as dt, supplement as sup class Node(object): """ A representation of a node in a node tree. Attributes: backend (str): The currently set translation backend. Available in the string format as `%(backend)s`. children (list): A list of node children ordered from first to last child. Accessible using indexing (`node[0]`, `node[1]`, ...). Alse available in the string format as `%(0)s`, `%(1)s`, ... cls (str): A string representation of the class name. Avalable in the string format as `%(class)s` code (str): The code that concived this node. cur (int): The index to the position in the code where this node was concived. It takes the value 0 for nodes not created from code. declare (Node): A reference to the node of same name where it is defined. This would be under `Declares`, `Params` or `Struct`. Useful for setting scope defined common datatypes. Returns itself if no declared variable has the same name as current node. dim (int): The number of dimensions in a numerical datatype. The values 0 through 4 represents scalar, column vector, row vector, matrix and cube respectively. The value is None if datatype is not numerical. Interconnected with `type`. file (str): Name of the program. In projects, it should be the absolute path to the Matlab source file. Available in the string format as `%(file)s`. ftypes (dict): Input/output function scoped datatypes. func (Node): A reference to Func (function) ancestor. Uses root if not found. group (Node): A reference to the first ancestor where the datatype does not automatically affect nodes upwards. A list of these nodes are listed in `matlab2cpp.reference.groups`. itype (list): Input/output include scope statements line (int): The codeline number in original code where this node was conceived. It takes the value 0 for nodes not created from code. mem (int): The amount of type-space reserved per element in a numerical datatype. The value 0 through 4 represents unsigned int, int, float, double and complex. The value is None if datatype is not numerical. Interconnected with `type`. name (str): The name of the node. Available in the string format as `%(name)s`. names (list): A list of the names (if any) of the nodes children. num (bool): A bool value that is true if and only if the datatype is numerical. Interconnected with `type`. parent (Node): A reference to the direct node parent above the current one. pointer (int): A numerical value of the reference count. The value 0 imply that the node refer to the actual variable, 1 is a reference to the variable, 2 is a reference of references, and so on. program (Node): A reference to program ancestor. Uses root if not found. project (Node): A reference to root node. reference (Node): If node is a lambda function (backend `func_lambda`), the variable is declared locally, but it's content might be available in it's own function. If so, the node will have a `reference` attribute to that function. Use `hasattr` to ensure it is the case. ret (tuple): The raw translation of the node. Same as (str): `node.str`, but on the exact form the tranlsation rule returned it. str (str): The translation of the node. Note that the code is translated leaf to root, and parents will not be translated before after current node is translated. Current and all ancestors will have an empty string. stypes (dict): Input/Output struct scoped datatypes. suggest (str): A short string representation of the suggested datatype. It is used for suggesting datatype in general, and can only be assigned, not read. Typically only the declared variables will be read, so adding a suggestion is typically done `node.declare.type = "..."`. type (str): A short string representation of the nodes datatype. Interconnected with `dim`, `mem` and `num`. Available in string format as `%(type)s` value (str): A free variable resereved for content. The use varies from node to node. Available in the string format as `%(value)s`. vtypes (dict): Verbatim translation in tree (read-only) """ backend = ref.Property_reference("backend") cls = ref.Property_reference("class") code = ref.Recursive_property_reference("code") cur = ref.Recursive_property_reference("cur") dim = dt.Dim() #file = ref.Recursive_property_reference("file") file = ref.File_reference() line = ref.Line_reference() mem = dt.Mem() name = ref.Property_reference("name") names = ref.Names() num = dt.Num() pointer = ref.Property_reference("pointer") ret = ref.Property_reference("ret") str = ref.Property_reference("str") suggest = dt.Suggest() type = dt.Type() value = ref.Property_reference("value") func = ref.Func_reference() program = ref.Program_reference() project = ref.Project_reference() group = ref.Group_reference() declare = ref.Declare_reference() ftypes = sup.Ftypes() itypes = sup.Itypes() stypes = sup.Stypes() vtypes = sup.Vtypes() def __init__(self, parent=None, name="", value="", pointer=0, line=None, cur=None, code=None): """ Keyword Args: code (str): source code cur (int): cursor position (inherited) line (int): Line number (inherited) name (str): Optional name of the node parent (Node): Node parent in the Node tree pointer (int): is reference to object (not currently used) str (str): Translation content value (str): Default node content placeholder """ self.children = [] self.prop = {"type":"TYPE", "suggest":"TYPE", "value":value, "str":"", "name":name, "pointer":pointer, "backend":"unknown", "line":line, "cur":cur, "code":code, "ret":"", "class":self.__class__.__name__} # Parental relationship self.parent = parent if self is self.parent or (not hasattr(parent, "children")): pass else: parent.children.append(self) def summary(self, args=None): """ Generate a summary of the tree structure with some meta-information. Returns: str: Summary of the node tree See also: `matlab2cpp.qtree` """ return backend.summary(self, args) def translate(self, opt=None, only=False): """Generate code translation Args: opt (argparse.Namespace, optional): Extra arguments provided by argparse only (bool): If true, translate current node only. """ # configure if not configured if not self.project.builder.configured: self.project.builder.configure() if only: backend.translate_one(self, opt) else: backend.translate(self, opt) def properties(self): """ Retrieve local node properties. The following properties are included: +-------------+-------------------------------------+-----------------------------+ | Name | Attribute | Description | +=============+=====================================+=============================+ | ``backend`` | :py:attr:`~matlab2cpp.Node.backend` | Name of translation backend | +-------------+-------------------------------------+-----------------------------+ | ``class`` | :py:attr:`~matlab2cpp.Node.cls` | Node class name | +-------------+-------------------------------------+-----------------------------+ | ``code`` | :py:attr:`~matlab2cpp.Node.code` | Matlab code equivalent | +-------------+-------------------------------------+-----------------------------+ | ``cur`` | :py:attr:`~matlab2cpp.Node.cur` | Position in Matlab code | +-------------+-------------------------------------+-----------------------------+ | ``line`` | :py:attr:`~matlab2cpp.Node.line` | Line number in Matlab code | +-------------+-------------------------------------+-----------------------------+ | ``name`` | :py:attr:`~matlab2cpp.Node.name` | Node name | +-------------+-------------------------------------+-----------------------------+ | ``pointer`` | :py:attr:`~matlab2cpp.Node.pointer` | Pointer reference object | +-------------+-------------------------------------+-----------------------------+ | ``str`` | :py:attr:`~matlab2cpp.Node.str` | Node translation | +-------------+-------------------------------------+-----------------------------+ | ``suggest`` | :py:attr:`~matlab2cpp.Node.suggest` | Suggested datatype | +-------------+-------------------------------------+-----------------------------+ | ``type`` | :py:attr:`~matlab2cpp.Node.type` | Node datatype | +-------------+-------------------------------------+-----------------------------+ | ``value`` | :py:attr:`~matlab2cpp.Node.value` | Node value | +-------------+-------------------------------------+-----------------------------+ In addition will number keys (in string format) represents the node children's ``node.str`` in order. Returns: dict: dictionary with all properties and references to other assosiated nodes. Example: >>> var = matlab2cpp.collection.Var(None, name="A", value="B", line=1, cur=0, code="C") >>> props = var.properties() >>> for key in sorted(props): ... print("{:<8}: {!r}".format(key, props[key])) backend : 'unknown' class : 'Var' code : 'C' cur : 0 line : 1 name : 'A' pointer : 0 ret : '' str : '' suggest : 'TYPE' type : 'TYPE' value : 'B' """ prop = self.prop.copy() for key in self.prop: if prop[key] is None and hasattr(self, key): prop[key] = getattr(self, key) I = len(self.children) for i in range(I): prop[str(i)] = prop["-"+str(I-i)] = self[i].prop["str"] return prop def auxiliary(self, type=None, convert=False): """ Create a auxiliary variable and rearange nodes to but current node on its own line before. Args: type (str, None): If provided, auxiliary variable type will be converted convert (bool): If true, add an extra function call ``conv_to`` to convert datatype in Armadillo. Example: Many statements that works inline in Matlab, must be done on multiple lines in C++. Take for example the statement ``[1,2]+3``. In C++, the rowvec ``[1,2]`` must first be initialized and converted into ``rowvec`` before arithmetics can be used:: >>> print(matlab2cpp.qscript("[1,2]+3")) sword __aux_irowvec_1 [] = {1, 2} ; _aux_irowvec_1 = irowvec(__aux_irowvec_1, 2, false) ; _aux_irowvec_1+3 ; The difference in tree structure is as follows: >>> print(matlab2cpp.qtree("[1,2]", core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Matrix matrix irowvec 1 2| | | Vector matrix irowvec 1 2| | | | Int int int 1 4| | | | Int int int >>> print(matlab2cpp.qtree("[1,2]+3", core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Assign matrix int 1 1| | Var irowvec irowvec _aux_irowvec_1 1 1| | Matrix matrix irowvec 1 2| | | Vector matrix irowvec 1 2| | | | Int int int 1 4| | | | Int int int 1 1| Statement code_block TYPE 1 1| | Plus expression irowvec 1 1| | | Var unknown irowvec _aux_irowvec_1 1 7| | | Int int int """ return backend.auxillary(self, type, convert) def resize(self): """ Resize function. Very similar to the function :py:func:`~matlab2cpp.Node.auxiliary`, but specific for reshaping cubes into slices. Might need to be rewritten. """ backend.resize(self) def include(self, name, **kws): """ Include library in the header of the file. These include:: +--------------+--------------------------+ | Name | Description | +==============+==========================+ | ``SPlot`` | Local SPlot library | +--------------+--------------------------+ | ``m2cpp`` | Local M2cpp library | +--------------+--------------------------+ | ``arma`` | Global Armadillo library | +--------------+--------------------------+ | ``iostream`` | Global iostream library | +--------------+--------------------------+ Args: name (str): Name of header to include **kws (str, optional): Optional args for header. Mostly not in use. """ backend.include(self, name, **kws) def warning(self, msg): """ Add a warning to the log file. Args: msg (str): Content of the warning See also: :py:func:`~matlab2cpp.Node.error` """ backend.error(self, msg, True) def error(self, msg): """ Add an error to the log file. Args: msg (str): Content of the error Example: >>> print(matlab2cpp.qlog(" a")) Error in class Var on line 1: a ^ unknown data type """ backend.error(self, msg, False) def create_declare(self): """ Investigate if the current node is declared (either in Params, Declares or in Structs), and create such a node if non exists in Declares. The declared variable's datatype will be the same as current node. Returns: Node : the (newly) declared node """ backend.create_declare(self) def suggest_datatype(self): """ Try to figure out from context, what the datatype should be for current node. Returns: (tuple): Suggestion on the form ``(dim, mem)`` """ return backend.suggest_datatype(self) def wall_clock(self): """ Prepare for the use of ``tic`` and ``toc`` functionality in code. Does nothing if called before. """ return backend.wall_clock(self) def __getitem__(self, index): """ Retrieve node child. Args: index (int): Get node child by positional order index (str): Get first instance with `node.name==index` index (Node): Get first instance with `node.name==index.name` index (slice): Get sublist of `node.children` Examples: >>> node = collection.Var(None, "a") >>> node["b"] Traceback (most recent call last): ... IndexError: node child "b" not found >>> node[0] Traceback (most recent call last): ... IndexError: index of Var out of range (0) >>> node[()] Traceback (most recent call last): ... TypeError: index of Var must be in (int, str, slice, Node), not tuple """ i = index # shorter if isinstance(i, Node): i = i.name if isinstance(i, str): if i not in self.names: raise IndexError("node child \"%s\" not found" % i) i = self.names.index(i) if isinstance(i, int): if len(self) <= i: raise IndexError( "index of %s out of range (%d)" % (self.cls, i)) return self.children[i] if isinstance(i, slice): return self.children[i] raise TypeError( "index of %s must be in (int, str, slice, Node), not %s" \ % (self.cls, i.__class__.__name__)) def __contains__(self, i): """ Test if (Node with) name ``i`` is contained in code. """ if isinstance(i, str): return i in self.names return i.name in self.names def __setitem__(self, key, val): self.prop[key] = val def __len__(self): return len(self.children) def __str__(self): return self.prop["str"] def __add__(self, val): return str(self)+str(val) def __radd__(self, val): return str(val)+str(val) def __iter__(self): return self.children.__iter__() def append(self, node): node.children.append(node) def pop(self, index): return self.children.pop(index) def flatten(self, ordered=False, reverse=False, inverse=False): r""" Return a list of all nodes | Structure: | A | | B | | | D | | | E | | C | | | F | | | G | | Sorted [o]rdered, [r]everse and [i]nverse: | | ori | ___ : A B D E C F G | o__ : A B C D E F G | _r_ : A C G F B E D | __i : D E B F G C A | or_ : A C B G F E D | o_i : D E F G B C A | _ri : E D B G F C A | ori : G F E D C B A Args: node (Node): Root node to start from ordered (bool): If True, make sure the nodes are hierarcically ordered. reverse (bool): If True, children are itterated in reverse order. inverse (bool): If True, tree is itterated in reverse order. Return: list: All nodes in a flatten list. """ return backend.flatten(self, ordered, reverse, inverse) def plotting(self): """ Prepare the code for plotting functionality. """ return backend.plotting(self) if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/node/m2cpp.py ================================================ code = r"""#ifndef MCONVERT_H #define MCONVERT_H #include using namespace arma; namespace m2cpp { template inline arma::Col scol(eT a) { return arma::Col(&a, 1, true); } template inline arma::Row srow(eT a) { return arma::Row(&a, 1, true); } template inline arma::Mat smat(eT a) { return arma::Mat(&a, 1, 1, true); } inline arma::uvec span(int a, int b) { arma::uvec s; int n = b - a; if (n < 0) return s; s.set_size(n + 1); for (int ii = 0; ii <= n; ii++) s(ii) = ii + a; return s; } template inline T span(int a, int b) { T s; int n = b - a; if (n < 0) return s; s.set_size(n + 1); for (int ii = 0; ii <= n; ii++) s(ii) = ii + a; return s; } inline arma::uvec span(int a, int step, int b) { arma::uvec s; int n = (b - a + step) / step; if (n < 0) { return s; } s.set_size(n); for (int ii = 0; ii < n; ii++) { s(ii) = step * ii + a; } return s; } template inline T span(int a, int step, int b) { T s; int n = (b - a + step) / step; if (n < 0) { return s; } s.set_size(n); for (int ii = 0; ii < n; ii++) { s(ii) = step * ii + a; } return s; } template inline arma::cx_mat fft(arma::Mat X, int dim) { if (dim == 1) return arma::fft(X); else return arma::strans(arma::fft(arma::strans(X))); } template inline arma::cx_mat fft(arma::Mat X, int n, int dim) { if (dim == 1) return arma::fft(X, n); else return arma::strans(arma::fft(arma::strans(X), n)); } inline arma::cx_mat ifft(arma::cx_mat X, int dim) { if (dim == 1) X = arma::ifft(X); else X = arma::strans(arma::ifft(arma::strans(X))); return X; } inline arma::cx_mat ifft(arma::cx_mat X, int n, int dim) { if (dim == 1) X = arma::ifft(X, n); else X = arma::strans(arma::ifft(arma::strans(X), n)); return X; } //template inline rowvec fspan(double a, double step, double b) { //arma::Col s; rowvec s; int n = (int) ((b - a) / step); if (n < 0) return s; //s.set_size(n + 1); s.set_size(n+1); for (int ii = 0; ii <= n; ii++) s(ii) = step * ii + a; return s; } inline int nextpow2(int n) { n = abs(n); int p = 0; int tmp = 1; while (tmp < n) { tmp *= 2; p++; } return p; } template inline T1 square(T1 a) { return a*a; } template inline arma::Mat hankel(const T1& c_, const T2& r_) { typedef typename T1::elem_type eT; int nc = r_.n_elem; int nr = c_.n_elem; const arma::Col c((eT*)c_.memptr(), nr, 0); const arma::Row r((eT*)r_.memptr(), nc, 0); if (r[0] != c[0]) { //("hankel: differing diagonal element. Using the column one"); } arma::Mat retval(nr, nc); for (int i = 1; i <= std::min(nr, nc); i++) { retval.submat(1 - 1, i - 1, nr - i + 1 - 1, i - 1) = c.rows(i - 1, nr - 1); } int tmp = 1; if (nc <= nr) { tmp = nr - nc + 2; } for (int i = nr; i >= tmp; i--) { retval.submat(i - 1, 2 + nr - i - 1, i - 1, nc - 1) = r.cols(2 - 1, nc - nr + i - 1); } return retval; } template inline arma::uword length(const T1& A) { return A.n_elem; } /* template inline arma::Mat convmtx(const T& v, int m) { arma::Mat out = zeros(v.n_elem + m - 1, m); arma::Col aux((typename T::elem_type*)v.memptr(), v.n_elem); for (int ii = 0; ii < m; ii++) { out.submat(ii, ii, ii + v.n_elem - 1, ii) = v; } if (v.n_rows == 1) out = out.t(); return out; } */ template inline arma::Mat convmtx(const T& v, int m) { arma::Mat out = zeros >(v.n_elem + m - 1, m); arma::Col aux((typename T::elem_type*)v.memptr(), v.n_elem); if (v.n_rows == 1) { for (int ii = 0; ii < m; ii++) { out.submat(ii, ii, ii + v.n_elem - 1, ii) = aux; } } else { for (int ii = 0; ii < m; ii++) { out.submat(ii, ii, ii + v.n_elem - 1, ii) = v; } } if (v.n_rows == 1) out = out.t(); return out; } template inline typename arma::enable_if2< arma::is_real::value, typename arma::Mat >::result conv2(const arma::Mat& A, const arma::Mat& B) { uword n = A.n_rows + B.n_rows - 1; uword m = A.n_rows + B.n_rows - 1; arma::Mat out = arma::real(arma::ifft2(fft2(A, n, m) % fft2(B, n, m))); return out; } template inline typename arma::enable_if2 < arma::is_complex::value || arma::is_complex::value, arma::Mat::result > > >::result conv2(const arma::Mat& A, const arma::Mat& B) { uword n = A.n_rows + B.n_rows - 1; uword m = A.n_rows + B.n_rows - 1; arma::Mat out = arma::ifft2(fft2(A, n, m) % fft2(B, n, m)); return out; } template inline eT fix(const eT a) { return a > eT(0) ? floor(a) : ceil(a); } template inline void intersect(arma::Col& C, arma::uvec& ia, arma::uvec& ib, const T& a, const T& b) { typedef typename T::elem_type eT; arma::uvec sa = arma::sort_index(a); arma::uvec sb = arma::sort_index(b); std::vector C_; std::vector ia_, ib_; int na = int(a.n_elem); int nb = int(b.n_elem); int ja = 0, jb = 0; for (;;) { arma::uword sja = sa(ja); arma::uword sjb = sb(jb); eT ca = a(sja); eT cb = b(sjb); if (ca > cb) { ja++; } else if (cb > ca) { jb++; } else { C_.push_back(ca); ia_.push_back(sja); ib_.push_back(sjb); while (++ja < na && a(sa(ja)) == ca) {} while (++jb < nb && b(sb(jb)) == cb) {} if (ja == na || jb == nb) break; } } ia = arma::uvec(ia_) + 1; ib = arma::uvec(ib_) + 1; C = arma::Col(C_); } template inline void intersect(arma::Col& C, const T& a, const T& b) { arma::uvec dum0, dum1; intersect(C, dum0, dum1, a, b); } template inline void unique_rows_core(Tr& C, Tr& a_sorted, arma::uvec& ia, const T& a) { typedef typename T::elem_type eT; int ic_cur = 0; auto cmp = [&a, &ic_cur](const int k, const int l) -> bool { return a(k, ic_cur) < a(l, ic_cur); }; int nr = int(a.n_rows); int nc = int(a.n_cols); arma::Col ac0(const_cast(a.colptr(0)), nr, false); arma::uvec ord = arma::sort_index(a.col(0)); std::vector ia0, ia1; std::vector* ia_ = &ia0; std::vector* ia_prev_ = &ia1; ia_->push_back(0); for (int ii = 1; ii < nr; ii++) { if (a(ord(ii), 0) != a(ord(ii - 1))) { ia_->push_back(ii); } } ia_->push_back(nr); for (int ic = 1; ic < nc; ic++) { ic_cur = ic; int ir = 0, ir_prev = 0; std::swap(ia_prev_, ia_); int na = int(ia_prev_->size()); ia_->clear(); for (int ii = 0; ii < na - 1; ii++) { ia_->push_back((*ia_prev_)[ii]); int l = (*ia_prev_)[ii], u = (*ia_prev_)[ii + 1]; std::sort(&ord(l), &(ord(u - 1)) + 1, cmp); for (int jj = l + 1; jj < u; jj++) { if (a(ord(jj - 1), ic) != a(ord(jj), ic)) { ia_->push_back(jj); } } } ia_->push_back(nr); } ia = arma::uvec(*ia_); int na = int(ia.n_elem); C.set_size(na - 1, a.n_cols); for (int ii = 0; ii < na - 1; ii++) { C.row(ii) = a.row(ord(ia(ii))); } a_sorted.set_size(nr, nc); for (int ir = 0; ir < nr; ir++) { a_sorted.row(ir) = a.row(ord(ir)); } } template inline T sortrows(const T& a) { typedef typename T::elem_type eT; arma::uvec dum0; arma::Mat dum1; arma::Mat ret; unique_rows_core(dum1, ret, dum0, a); return ret; } template inline void unique_rows(T& C, const T& a) { arma::uvec dum; unique_rows(C, dum, a); } template inline T unique_rows(const T& a) { T ret; unique_rows(ret, a); return ret; } template inline int isempty(const T& a) { return a.n_elem == 0; } template inline void unique(T& a, const T& b, const char* m) { T tmp; const T& in = b; if (&a == &b) { tmp = b; in = tmp; } if (strcmp(m, "rows") == 0) { unique_rows(a, tmp); } else { fprintf(stderr, "m2pp::unique(): Unrecognized option %s\n", m); } } static arma::wall_clock timer_; inline double tic() { timer_.tic(); return timer_.toc(); } inline double toc() { return timer_.toc(); } inline double toc(double start) { return timer_.toc() - start; } } #endif """ ================================================ FILE: src/matlab2cpp/node/reference.py ================================================ """ Each node has a set of attributes that allows for quick access to properties and other node of interest. For example, if `node` has a name, it can be referred to by `node.name`. Another example is to access the parent node by `node.parent`. Note that, if a reference does not exist, the node itself will be returned. """ groups = [ "Assign", "Assigns", "Branch", "For", "Func", "Main", "Set", "Cset", "Fset", "Nset", "Sset", "Get", "Cget", "Fget", "Nget", "Sget", "Statement", "Switch", "Tryblock", "Matrix", "While", "Block", "Node", "Transpose", "Ctranspose", ] nondeclares = ("Program", "Project", "Include", "Includes", "Struct", "Structs") structvars = ("Fvar", "Fget", "Fset", "Nget", "Nset", "Sget", "Sset") class Property_reference(object): "general property node" def __init__(self, name, default=None): self.name = name def __get__(self, instance, owner): return instance.prop[self.name] def __set__(self, instance, value): instance.prop[self.name] = value class Recursive_property_reference(object): "recursive property node" def __init__(self, name): self.name = name def __get__(self, instance, owner): a = instance.prop[self.name] if not (a is None): return a assert not (instance is instance.parent) a = Recursive_property_reference.__get__(self, instance.parent, owner) instance.prop[self.name] = a return a def __set__(self, instance, value): instance.prop[self.name] = value class File_reference(object): def __get__(self, instance, owner): if hasattr(instance, "_file"): return instance._file if instance.cls == "Program": file_name = instance.name else: file_name = instance.program.name instance._file = file_name return file_name class Line_reference(object): def __get__(self, instance, owner): if hasattr(instance, "_line"): return instance._line if instance.cls == "Project": line = 0 elif instance.cls == "Funcs": line = 1 else: pline = instance.parent.line pcur = instance.parent.cur cur = instance.cur if pcur == cur: line = pline else: line = pline + instance.program.code.count("\n", pcur, cur) instance._line = line return line class Group_reference(object): def __get__(self, instance, owner): if hasattr(instance, "_group"): return instance._group if instance.parent.cls in groups: group = instance.parent else: group = instance.parent.group instance._group = group return group class Func_reference(object): def __get__(self, instance, owner): if hasattr(instance, "_func"): return instance._func if instance.cls in ("Func", "Main", "Program"): func = instance else: func = instance.parent.func instance._func = func return func class Program_reference(object): def __get__(self, instance, owner): if hasattr(instance, "_program"): return instance._program if instance.cls == "Program": program = instance else: program = instance.parent.program instance._program = program return program class Project_reference(object): def __get__(self, instance, owner): if hasattr(instance, "_project"): return instance._project if instance.cls == "Project": project = instance else: project = instance.parent.project instance._project = project return project class Names(object): def __get__(self, instance, owner): return [i.prop["name"] for i in instance.children] class Declare_reference(object): def __get__(self, instance, owner): if hasattr(instance, "_declare"): return instance._declare if instance.cls in nondeclares: return instance if instance.cls in structvars or\ instance.backend in ("structs", "struct"): if instance.cls in ("Nget", "Nset"): if instance[0].cls == "String": value = instance[0]["value"] else: return instance else: value = instance.value if instance not in instance.program[3]: return instance struct = instance.program[3][instance] if value not in struct.names: return instance out = struct[struct.names.index(value)] instance._declare = out return out elif instance.parent.cls in "Struct": return instance else: if instance in instance.func[0]: out = instance.func[0][instance] instance._declare = out return out if instance in instance.func[2]: out = instance.func[2][instance] instance._declare = out return out return instance ================================================ FILE: src/matlab2cpp/parser.py ================================================ """Create parser for m2cpp.""" import argparse from textwrap import dedent import glob import matlab2cpp HELP_DESCRIPTION = """\ *** Matlab2cpp *** The toolbox frontend of the Matlab2cpp library. Use this to try to do automatic and semi-automatic translation from Matlab source file to C++. The program will create files with the same name as the input, but with various extra extensions. Scripts will receive the extension `.cpp`, headers and modules `.hpp`. A file containing data type and header information will be stored in a `.py` file. Any errors will be stored in `.log`. """ def matlab_file_completer(prefix, **kws): """Complete files with matlab extensions.""" return glob.glob("{}*.m".format(prefix)) def create_parser(): """Create argument parser.""" parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=dedent(HELP_DESCRIPTION)) parser.add_argument( "filename", help="File containing valid Matlab code." ).completer = matlab_file_completer parser.add_argument( "-o", '--original', action="store_true", help=( "Include original Matlab code line as comment before the " "C++ translation of the code line"), ) parser.add_argument( "-c", '--comments', action="store_true", help=( "Include Matlab comments in the generated C++ files."), ) parser.add_argument( "-s", '--suggest', action="store_true", help=( "Automatically populate the `.py` file with datatype " "with suggestions if possible."), ) parser.add_argument( "-S", '--matlab-suggest', action="store_true", help=( "Creates a folder m2cpp_temp. In the folder the matlab file(s) to " "be translated are also put. These matlab file(s) are slightly " "modified so that they output data-type information of the " "variables to file(s). This output can then be used to set the " "datatypes for the translation."), ) parser.add_argument( "-r", '--reset', action="store_true", help=( "Ignore the content of `.py` and make a fresh translation."), ) parser.add_argument( "-t", '--tree', action="store_true", help=( "Print the underlying node tree. Each line in the output " "represents a node and is formated as follows: \n\n" "` `\n\n" "The indentation represents the tree structure."), ) parser.add_argument( "-T", "--tree-full", action="store_true", help=( "Same as -t, but the full node tree, but include meta-nodes."), ) parser.add_argument( "-d", '--disp', action="store_true", help=( "Print out the progress of the translation process."), ) parser.add_argument( "-p", "--paths_file", type=str, dest="paths_file", help=( "Flag and paths_file (-p path_to_pathsfile). m2cpp will look for " "matlab files in the location specified in the paths_file"), ) parser.add_argument( "-omp", '--enable-omp', action="store_true", help=( "OpenMP code is inserted for Parfor and loops marked with the " "pragma %%#PARFOR (in Matlab code) when this flag is set."), ) parser.add_argument( "-tbb", '--enable-tbb', action="store_true", help=( "TBB code is inserted for Parfor and loops marked with the " "pragma %%#PARFOR (in Matlab code) when this flag is set."), ) parser.add_argument( "-ref", '--reference', action="store_true", help=( 'For the generated C++ code, function input parameters are ' '"copied by value" as default. With this flag some input ' 'parameters in the generated code can be const references. ' 'There can be some performance advantage of using const ' 'references instead of "copied by value". Note that Matlab ' '"copies by value". The Matlab code you try to translate to ' 'C++ code could try read as well as write to this input variable. ' "The code generator doesn't perform an analysis to detect this " 'and then "copy by value" for this variable.'), ) parser.add_argument( "-l", '--line', type=int, dest="line", help=( "Only display code related to code line number ``."), ) parser.add_argument( "-n", '--nargin', action="store_true", help=( "Don't remove if and switch branches which use nargin variable."), ) return parser ================================================ FILE: src/matlab2cpp/pyplot.py ================================================ code = r""" /* * SPlot.h * * Created on: 19. aug. 2010 * Author: nordmoen */ #ifndef SPLOT_H_ #define SPLOT_H_ #include //DGRIM #include "matlib.hpp" #include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include //DGRIM #include "armadillo/armadillo" class PyEngine { static void _initialize() { static bool initialized = false; if (!initialized) { Py_SetProgramName((char*)"splot"); Py_Initialize(); _import_array(); initialized = true; } } public: // Wrapper class which takes both row/col vectors, as well as initializer lists template class arma_vec { public: const std::vector v; arma_vec() : v() {} arma_vec(const std::initializer_list &c) : v(c) {} arma_vec(const std::vector &vec) : v(vec) {} arma_vec(const arma::Col &vec) : v(vec.begin(), vec.end()) {} arma_vec(const arma::Row &vec) : v(vec.begin(), vec.end()) {} arma_vec(const arma::subview_col &vec) : arma_vec(arma::Col{vec}) {} arma_vec(const arma::subview_row &vec) : arma_vec(arma::Row{vec}) {} template arma_vec(const arma::eOp, U> &vec) : arma_vec(arma::Col{vec}) {} template arma_vec(const arma::eOp, U> &vec) : arma_vec(arma::Row{vec}) {} }; // Wrapper class to limit arma::Mat implicit conversions (from string, for example). // For parameter-passing use only, since it borrows reference in some cases. template class arma_mat { const arma::Mat _m; public: const arma::Mat &m; arma_mat(const arma::Mat &mat) : m(mat) {} arma_mat(const arma::subview &view) : _m(view), m(_m) {} template arma_mat(const arma::eOp, U> &op) : _m(op), m(_m) {} }; class py_obj { friend class PyEngine; PyObject *obj; template static int npy_typenum(); template static PyObject *create(const std::initializer_list &in_dims, const InputIt &data, int flags=0) { std::vector dims; for (auto it=in_dims.begin(); it!=in_dims.end(); ++it) dims.push_back((npy_intp)*it); PyObject *obj = PyArray_NewFromDescr(&PyArray_Type, PyArray_DescrFromType(npy_typenum()), dims.size(), (npy_intp*)dims.data(), /*strides*/nullptr, /*data*/nullptr, flags, /*obj*/nullptr); std::copy_n(data, PyArray_Size(obj), (NPY_T*)PyArray_DATA((PyArrayObject*)obj)); return obj; } explicit py_obj(PyObject *o, bool steal_reference) : obj(o) { if (!steal_reference) Py_XINCREF(obj); } public: /* Basic constructors */ py_obj() : obj(nullptr) {} py_obj(const py_obj &other) : obj(other.obj) { Py_XINCREF(obj); } py_obj(const void *); // Trigger link error, to avoid implicit cast to bool /* Construct from primitive types, strings */ py_obj(bool const& b) : obj(PyBool_FromLong(b)) {} py_obj(int const& i) : obj(PyInt_FromLong(i)) {} py_obj(double const& d) : obj(PyFloat_FromDouble(d)) {} py_obj(const char* const& s) : obj(PyString_FromString(s)) {} py_obj(std::string const& s) : obj(PyString_FromString(s.c_str())) {} /* Construct from various sequences of double/float/int type */ py_obj(std::initializer_list c) : obj(create({c.size()}, c.begin())) {} py_obj(std::vector const& vec) : obj(create({vec.size()}, vec.begin())) {} py_obj(arma_vec const& vec) : py_obj(vec.v) {} py_obj(arma_mat const& mat) : obj(create({mat.m.n_rows, mat.m.n_cols}, mat.m.begin(), NPY_ARRAY_F_CONTIGUOUS)) {} py_obj(std::initializer_list c) : obj(create({c.size()}, c.begin())) {} py_obj(std::vector const& vec) : obj(create({vec.size()}, vec.begin())) {} py_obj(arma_vec const& vec) : py_obj(vec.v) {} py_obj(arma_mat const& mat) : obj(create({mat.m.n_rows, mat.m.n_cols}, mat.m.begin(), NPY_ARRAY_F_CONTIGUOUS)) {} py_obj(std::initializer_list c) : obj(create({c.size()}, c.begin())) {} py_obj(std::vector const& vec) : obj(create({vec.size()}, vec.begin())) {} py_obj(arma_vec const& vec) : py_obj(vec.v) {} py_obj(arma_mat const& mat) : obj(create({mat.m.n_rows, mat.m.n_cols}, mat.m.begin(), NPY_ARRAY_F_CONTIGUOUS)) {} py_obj(std::initializer_list c) : obj(create({c.size()}, c.begin())) {} py_obj(std::vector const& vec) : obj(create({vec.size()}, vec.begin())) {} py_obj(arma_vec const& vec) : py_obj(vec.v) {} py_obj(arma_mat const& mat) : obj(create({mat.m.n_rows, mat.m.n_cols}, mat.m.begin(), NPY_ARRAY_F_CONTIGUOUS)) {} py_obj(std::initializer_list c) : obj(create({c.size()}, c.begin())) {} py_obj(std::vector const& vec) : obj(create({vec.size()}, vec.begin())) {} py_obj(arma_vec const& vec) : py_obj(vec.v) {} py_obj(arma_mat const& mat) : obj(create({mat.m.n_rows, mat.m.n_cols}, mat.m.begin(), NPY_ARRAY_F_CONTIGUOUS)) {} py_obj(std::initializer_list c) : obj(create({c.size()}, c.begin())) {} py_obj(std::vector const& vec) : obj(create({vec.size()}, vec.begin())) {} py_obj(arma_vec const& vec) : py_obj(vec.v) {} py_obj(arma_mat const& mat) : obj(create({mat.m.n_rows, mat.m.n_cols}, mat.m.begin(), NPY_ARRAY_F_CONTIGUOUS)) {} /* Destructor */ ~py_obj() { Py_XDECREF(obj); } /* Assignment and equality operators */ py_obj &operator=(const py_obj &other) { Py_XDECREF(obj); obj = other.obj; Py_XINCREF(obj); return *this; } bool operator==(const py_obj &other) const { return obj==other.obj; } bool valid() const { return (obj!=nullptr); } private: /* Cast to PyObject */ operator PyObject*() { return obj; } }; static py_obj None() { return py_obj(Py_None, false); } typedef std::vector args_t; typedef std::map kwargs_t; protected: PyObject *main_module; py_obj py_call_object(py_obj object, const char *func, const args_t &args={}, const kwargs_t &kwargs={}) { py_obj pyFunc(PyObject_GetAttrString(object, func), true); if (!pyFunc.valid()) { std::cerr << "No such method: " << func << std::endl; return py_obj(nullptr, true); } py_obj pyArgs(PyTuple_New(args.size()), true); for (size_t i=0; i 1: self.setFontSize(self.pt - 1) def wheelEvent(self, event): if event.modifiers() == QtCore.Qt.ControlModifier: delta = event.delta() if qt_backend==4 else event.angleDelta().y() if delta > 1: self.zoom_in() else: self.zoom_out() else: QtWidgets.QTableView.wheelEvent(self, event) def closeEvent(self, event): del TableView._views[self] QtWidgets.QTableView.closeEvent(self, event) def table(mat, title=None): if QtWidgets.QApplication.instance() is None: import sys table.app = QtWidgets.QApplication(sys.argv) table.seq += 1 if title is None: title = 'Table %d '%table.seq title += ' (%dx%d %s)'%(mat.shape+(mat.dtype.name,)) model = TableModel(mat) view = TableView() view.setMonospaceFont() if np.iscomplexobj(mat): view.chars *= 2 view.setAttribute(QtCore.Qt.WA_DeleteOnClose) view.setModel(model) view.setFontSize(8) view.resize(600, 400) view.setWindowTitle(title) view.show() table.seq = 0 )"; } else { splot_py = static_cast(std::stringstream() << in.rdbuf()).str(); } py_code(splot_py.c_str()); } py_obj figure(const py_obj &figno) { return py_call("figure", {figno}); } py_obj hold(bool onoff) { return py_call("hold", {onoff}); } py_obj clf() { return py_call("clf"); } py_obj cla() { return py_call("cla"); } py_obj show(bool block=true) { return py_call("show", {block}); } py_obj xlabel(const char *label) { return py_call("xlabel", {label}); } py_obj ylabel(const char *label) { return py_call("ylabel", {label}); } py_obj title(const char *title) { return py_call("title", {title}); } py_obj plot(const arma_vec &x, const arma_vec &y, const kwargs_t &kwargs) { return py_call("plot", {x, y}, kwargs); } py_obj plot(const arma_vec &x, const arma_vec &y, const std::string& spec={}) { return py_call("plot", {x, y, spec}); } py_obj plot(const arma_vec &x1, const arma_vec &y1, const arma_vec &x2, const arma_vec &y2, const std::string& spec2={}) { return py_call("plot", {x1, y1, std::string(), x2, y2, spec2}); } py_obj plot(const arma_vec &x1, const arma_vec &y1, const std::string& spec1, const arma_vec &x2, const arma_vec &y2, const std::string& spec2={}) { return py_call("plot", {x1, y1, spec1, x2, y2, spec2}); } py_obj imshow(const arma_mat &A, const kwargs_t &kwargs={}) { kwargs_t new_kwargs(kwargs); new_kwargs.emplace("aspect", "auto"); new_kwargs.emplace("interpolation", "nearest"); //new_kwargs.emplace("clim", py_obj({A.min(), A.max()})); return py_call("imshow", {A}, new_kwargs); } py_obj imagesc(const arma_mat &A, const kwargs_t &kwargs={}) { return imshow(A, kwargs); } py_obj imagesc(const arma_vec &x, const arma_vec &y, const arma_mat &A, const kwargs_t &kwargs={}) { if (x.v.empty() && y.v.empty()) return imagesc(A, kwargs); if ((x.v.size() != 2 && x.v.size() != A.m.n_cols) || (y.v.size() != 2 && y.v.size() != A.m.n_rows)) return error("imagesc: length of x/y bounds must be 2 or matrix dimensions"); kwargs_t new_kwargs(kwargs); new_kwargs["extent"] = {x.v[0], x.v[x.v.size()-1], y.v[0], y.v[y.v.size()-1]}; return imagesc(A, new_kwargs); } py_obj imagesc(const arma_mat &A, const arma_vec &clims, const kwargs_t &kwargs={}) { return imagesc(arma_vec(), arma_vec(), A, clims, kwargs); } py_obj imagesc(const arma_vec &x, const arma_vec &y, const arma_mat &A, const arma_vec &clims, const kwargs_t &kwargs={}) { if (clims.v.empty()) return imagesc(x, y, A, kwargs); if (clims.v.size() != 2) return error("imagesc: length of c bounds must be 2"); kwargs_t new_kwargs(kwargs); new_kwargs["clim"] = clims; return imagesc(x, y, A, new_kwargs); } py_obj wigb(const arma_mat &A, double scale=1.0, const arma_vec &x={}, const arma_vec &z={}, double amx=0.0, const kwargs_t &kwargs={}) { return py_call("wigb", {A, scale, x, z, amx}, kwargs); } py_obj colorbar() { return py_call("colorbar"); } py_obj colorbar(py_obj mappable) { return py_call("colorbar", {mappable}); } py_obj colorbar(py_obj mappable, py_obj ax) { return py_call("colorbar", {mappable, ax}); } py_obj xlim(double xmin, double xmax) { return py_call("xlim", {xmin, xmax}); } py_obj ylim(double ymin, double ymax) { return py_call("ylim", {ymin, ymax}); } py_obj caxis(double cmin, double cmax) { return py_call("clim", {cmin, cmax}); } py_obj axis(double xmin, double xmax, double ymin, double ymax) { return py_call("axis", {{xmin, xmax, ymin, ymax}}); } py_obj grid(const kwargs_t &kwargs={}) { return py_call("grid", {}, kwargs); } py_obj subplot(int xyi, const kwargs_t &kwargs={}) { return py_call("subplot", {xyi}, kwargs); } py_obj subplot(int x, int y, int i, const kwargs_t &kwargs={}) { return py_call("subplot", {x, y, i}, kwargs); } py_obj colormap(py_obj fig, const std::string &name, int levels=64) { const size_t start = name.find('('); if (start != std::string::npos) { const size_t end = name.find(')'); if (end != std::string::npos && end>start) { const std::string name_part(name.substr(start)); const int levels_part = atoi(name.substr(start+1, end).c_str()); return colormap(fig, name_part, levels_part); } } py_obj cmap = py_call("get_cmap", {(name=="default" ? "jet" : name), levels}); return py_call_object(fig, "set_cmap", {cmap}); } py_obj colormap(const std::string &name, int levels=64) { return colormap(py_call("gci"), name, levels); } py_obj colormap(const py_obj &fig, const arma_mat &segments) { py_obj cmap = py_call_object(py_get_module("matplotlib.colors"), "ListedColormap", {segments}); return py_call_object(fig, "set_cmap", {cmap}); } py_obj colormap(const arma_mat &segments) { return colormap(py_call("gci"), segments); } void table(const arma_mat &mat, const py_obj &title=None()) { py_call("table", {mat, title}); } void table(const arma_mat &mat, const py_obj &title=None()) { py_call("table", {mat, title}); } }; template <> int PyEngine::py_obj::npy_typenum() { return NPY_DOUBLE; } template <> int PyEngine::py_obj::npy_typenum() { return NPY_FLOAT; } template <> int PyEngine::py_obj::npy_typenum() { return NPY_INT32; } template <> int PyEngine::py_obj::npy_typenum() { return NPY_UINT32; } template <> int PyEngine::py_obj::npy_typenum() { return NPY_INT64; } template <> int PyEngine::py_obj::npy_typenum() { return NPY_UINT64; } template <> int PyEngine::py_obj::npy_typenum() { return NPY_COMPLEX64; } template <> int PyEngine::py_obj::npy_typenum() { return NPY_COMPLEX128; } #endif /* SPLOT_H_ */ """ ================================================ FILE: src/matlab2cpp/qfunctions.py ================================================ """ For simplest use of the module, these function works as an alternative frontend to the ``mconvert`` script. +--------------------------------+-----------------------------------------+ | Function | Description | +================================+=========================================+ | :py:func:`~matlab2cpp.build` | Build token tree representation of code | +--------------------------------+-----------------------------------------+ | :py:func:`~matlab2cpp.qcpp` | Create content of `.cpp` file | +--------------------------------+-----------------------------------------+ | :py:func:`~matlab2cpp.qhpp` | Create content of `.hpp` file | +--------------------------------+-----------------------------------------+ | :py:func:`~matlab2cpp.qpy` | Create content of supplement `.py` file | +--------------------------------+-----------------------------------------+ | :py:func:`~matlab2cpp.qlog` | Create content of `.log` file | +--------------------------------+-----------------------------------------+ | :py:func:`~matlab2cpp.qscript` | Create script translation | +--------------------------------+-----------------------------------------+ | :py:func:`~matlab2cpp.qtree` | Create summary of node tree | +--------------------------------+-----------------------------------------+ """ __all__ = ["build", "qcpp", "qhpp", "qpy", "qlog", "qtree", "qscript"] def build( code, disp=False, retall=False, suggest=True, comments=True, vtypes=None, **kws ): """ Build a token tree out of Matlab code. This function is used by the other quick-functions as the first step in code translation. The function also handles syntax errors in the Matlab code. It will highlight the line it crashed on and explain as far as it can why it crashed. Args: code (str): Code to be interpreted disp (bool): If true, print out diagnostic information while interpreting. retall (bool): If true, return full token tree instead of only code related. suggest (bool): If true, suggestion engine will be used to fill in datatypes. comments (bool): If true, comments will be striped away from the solution. vtypes (dict): Verbatim translations added to tree before process. **kws: Additional arguments passed to :py:obj:`~matlab2cpp.Builder`. Returns: Builder,Node: The tree constructor if `retall` is true, else the root node for code. Example:: >>> from matlab2cpp.tree import Builder >>> builder = matlab2cpp.build("a=4", retall=True) >>> print(isinstance(builder, Builder)) True >>> node = matlab2cpp.build("a=4", retall=False) >>> print(isinstance(node, collection.Node)) True >>> print(matlab2cpp.build("a**b")) Traceback (most recent call last): ... SyntaxError: File: unamed, line 1 in Matlab code: a**b ^ Expected: expression start See also: :py:func:`~matlab2cpp.qtree`, :py:class:`~matlab2cpp.Builder`, :py:class:`~matlab2cpp.Node` """ from . import supplement, tree code = code + "\n\n\n\n" if vtypes: code = supplement.verbatim.set(vtypes, code) builder = tree.builder.Builder(disp=disp, comments=comments, **kws) builder.load("unamed", code) builder.configure(2*suggest) if retall: return builder if builder[0][1][0].name == "main": out = builder[0][1][0][3] return out return builder[0][1] def qcpp(code, suggest=True, **kws): """ Quick code translation of matlab script to C++ executable. For Matlab modules, code that only consists of functions, will be placed in the :py:func:`~matlab2cpp.qhpp`. In most cases, the two functions must be used together to create valid runnable code. Args: code (str, Node, Builder): A string or tree representation of Matlab code. suggest (bool): If true, use the suggest engine to guess data types. **kws: Additional arguments passed to :py:obj:`~matlab2cpp.Builder`. Returns: str: Best estimate of script. If code is a module, return an empty string. Example:: >>> code = "a = 4; b = 5.; c = 'abc'" >>> print(matlab2cpp.qcpp(code, suggest=False)) #include using namespace arma ; int main(int argc, char** argv) { TYPE a, b, c ; a = 4 ; b = 5. ; c = "abc" ; return 0 ; } >>> print(matlab2cpp.qcpp(code, suggest=True)) #include using namespace arma ; int main(int argc, char** argv) { double b ; int a ; std::string c ; a = 4 ; b = 5. ; c = "abc" ; return 0 ; } >>> build = matlab2cpp.build(code, retall=True) >>> print(matlab2cpp.qcpp(build) == matlab2cpp.qcpp(code)) True See also: :py:func:`~matlab2cpp.qscript`, :py:func:`~matlab2cpp.qhpp`, :py:obj:`~matlab2cpp.Builder` """ from . import tree if isinstance(code, str): tree_ = build(code, suggest=suggest, retall=True, **kws)[0] else: tree_ = code if isinstance(tree_, tree.builder.Builder): tree_ = tree_[0] tree_ = tree_.program if not tree_.str: tree_.translate() includes, funcs, inlines, structs, headers, log = tree_.program out = "" if funcs and funcs[0].name == "main": if includes.str: out += includes.str + "\n\n" if len(headers) > 1: out += headers.str + "\n\n" if structs.str: out += structs.str + "\n\n" if funcs.str: out += funcs.str + "\n\n" out = out.replace("__percent__", "%") out = out[:-2] from .rules._program import add_indenting, number_fix, strip out = strip(out) out = number_fix(out) out = add_indenting(out) return out def qhpp(code, suggest=False): """ Quick module translation of Matlab module to C++ library. If the code is a script, executable part of the code will be placed in :py:func:`~matlab2cpp.qcpp`. Args: code (str, Node, Builder): A string or tree representation of Matlab code. suggest (bool): If true, use the suggest engine to guess data types. **kws: Additional arguments passed to :py:obj:`~matlab2cpp.Builder`. Returns: str: C++ code of module. Example:: >>> code = "function y=f(x); y=x+1; end; function g(); f(4)" >>> print(matlab2cpp.qhpp(code)) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; TYPE f(TYPE x) ; void g() ; TYPE f(TYPE x) { TYPE y ; y = x+1 ; return y ; } void g() { f(4) ; } #endif >>> print(matlab2cpp.qhpp(code, suggest=True)) #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; int f(int x) ; void g() ; int f(int x) { int y ; y = x+1 ; return y ; } void g() { f(4) ; } #endif See also: :py:func:`~matlab2cpp.qcpp`, :py:class:`~matlab2cpp.Builder` """ from . import tree if isinstance(code, str): tree_ = build(code, suggest=suggest, retall=True)[0] else: tree_ = code if isinstance(tree_, tree.builder.Builder): tree_ = tree_[0] tree_ = tree_.program if not tree_.str: tree_.translate() includes, funcs, inlines, structs, headers, log = tree_ out = "" if funcs and funcs[0].name == "main": return out if funcs and funcs[0].name != "main": name = funcs[0].name + "_M_HPP" name = name.upper() name.replace(".", "_") out = "#ifndef " + name + "\n#define " + name + "\n\n" if includes.str: out += includes.str + "\n\n" if len(headers) > 1: out += headers.str + "\n\n" if structs.str: out += structs.str + "\n\n" if funcs.str: out += funcs.str + "\n\n" out = out[:-2] if funcs and funcs[0].name != "main": out += "\n#endif" out = out.replace("__percent__", "%") from .rules._program import add_indenting, number_fix, strip out = strip(out) out = number_fix(out) out = add_indenting(out) return out def qpy(code, suggest=True, prefix=False): """ Create annotation string for the supplement file containing datatypes for the various variables in various scopes. Args: code (str, Builder, Node): Representation of the node tree. suggest (bool): Use the suggestion engine if appropriate. prefix (bool): include a helpful comment in the beginning of the string. Returns: str: Supplement string Example:: >>> code = "a = 4; b = 5.; c = 'abc'" >>> print(matlab2cpp.qpy(code, suggest=False)) functions = { "main" : { "a" : "", # int "b" : "", # double "c" : "", # string }, } includes = [ '#include ', 'using namespace arma ;', ] >>> print(matlab2cpp.qpy(code, suggest=True)) functions = { "main" : { "a" : "int", "b" : "double", "c" : "string", }, } includes = [ '#include ', 'using namespace arma ;', ] See also: :py:mod:`~matlab2cpp.supplement`, :py:mod:`~matlab2cpp.datatype` """ from . import supplement, tree if isinstance(code, str): tree_ = build(code, suggest=suggest, retall=True)[0] tree_.translate() else: tree_ = code if isinstance(tree_, tree.builder.Builder): tree_ = tree_[0] if tree_.cls != "Program": raise KeyError( "Argument code should be code string, Builder or Program-node") ftypes = supplement.functions.get(tree_) stypes = supplement.structs.get(tree_) itypes = supplement.includes.get(tree_) itypes = [i for i in itypes if supplement.includes.write_to_includes(i)] vtypes = supplement.verbatim.get(tree_) suggestions = supplement.suggests.get(tree_) #print("ITYPASDASDA") #itypes = ["#include \"" + itype.split(os.path.sep)[-1] if ".hpp" in itype else itype for itype in itypes] #print(itypes) #print(".........;;;;;;;;-----") #itypes = [itype.split(os.path.sep)[-1] if ".hpp" in itype else itype for itype in itypes] out = supplement.str_variables(ftypes, stypes, itypes, suggestions, prefix, vtypes) out = out.replace("__percent__", "%") return out def qlog(code, suggest=False, **kws): """ Retrieve all errors and warnings generated through the code translation and summarize them into a string. Each entry uses four lines. For example: :: Error in class Var on line 1: function f(x) ^ unknown data type First line indicate at what node and line-number the error occured. The second and third prints the Matlab-code line in question with an indicator to where the code failed. The last line is the error or warning message generated. Args: code (str, Builder, Node): Representation of the node tree. suggest (bool): Use suggestion engine where appropriate. **kws: Additional arguments passed to :py:obj:`~matlab2cpp.Builder`. Returns: str: A string representation of the log Example:: >>> print(matlab2cpp.qlog("function f(x); x=4")) Error in class Var on line 1: function f(x); x=4 ^ unknown data type Error in class Var on line 1: function f(x); x=4 ^ unknown data type See alse: :py:func:`~matlab2cpp.Node.error`, :py:func:`~matlab2cpp.Node.warning` """ from . import tree if isinstance(code, str): tree_ = build(code, suggest=suggest, retall=True)[0] else: tree_ = code if isinstance(tree_, tree.builder.Builder): tree_ = tree_[0] if tree_.cls != "Program": raise KeyError( "Argument code should be code string, Builder or Program-node") tree_.translate() out = tree_[5].str out = out.replace("__percent__", "%") return out def qtree(code, suggest=False, core=False): """ Summarize the node tree with relevant information, where each line represents a node. Each line will typically look as follows:: 1 10 | | | Var unknown TYPE y The line can be interpreted as follows: +--------+-------------------------------+------------------------------------+ | Column | Description | Object | +========+===============================+====================================+ | 1 | Matlab code line number | :py:obj:`~matlab2cpp.Node.line` | +--------+-------------------------------+------------------------------------+ | 2 | Matlab code cursor number | :py:obj:`~matlab2cpp.Node.cur` | +--------+-------------------------------+------------------------------------+ | 3 | The node categorization type | :py:class:`~matlab2cpp.Node.cls` | +--------+-------------------------------+------------------------------------+ | 4 | The rule used for translation | :py:obj:`~matlab2cpp.Node.backend` | +--------+-------------------------------+------------------------------------+ | 5 | The data type of the node | :py:mod:`~matlab2cpp.Node.type` | +--------+-------------------------------+------------------------------------+ | 6 | Name of the node (if any) | :py:obj:`~matlab2cpp.Node.name` | +--------+-------------------------------+------------------------------------+ The vertical bars represents branches. The right most bar on each line points upwards towards its node parent. Args: code (str, Builder, Node): Representation of the node tree. suggest (bool): Use suggestion engine where appropriate. core (bool): Unly display nodes generated from Matlab code directly. **kws: Additional arguments passed to :py:obj:`~matlab2cpp.Builder`. Returns: str: A summary of the node tree. Example:: >>> print(matlab2cpp.qtree("function y=f(x); y=x+4")) # doctest: +NORMALIZE_WHITESPACE Program program TYPE unamed | Includes program TYPE | | Include program TYPE #include | | Include program TYPE using namespace arma ; 1 1| Funcs program TYPE unamed 1 1| | Func func_return TYPE f 1 1| | | Declares func_return TYPE 1 1| | | | Var unknown TYPE y 1 1| | | Returns func_return TYPE 1 10| | | | Var unknown TYPE y 1 13| | | Params func_return TYPE 1 14| | | | Var unknown TYPE x 1 16| | | Block code_block TYPE 1 18| | | | Assign expression TYPE 1 18| | | | | Var unknown TYPE y 1 20| | | | | Plus expression TYPE 1 20| | | | | | Var unknown TYPE x 1 22| | | | | | Int int int | Inlines program TYPE unamed | Structs program TYPE unamed | Headers program TYPE unamed | | Header program TYPE f | Log program TYPE unamed | | Error program TYPE Var:0 | | Error program TYPE Var:9 | | Error program TYPE Var:13 | | Error program TYPE Var:17 | | Error program TYPE Var:19 | | Error program TYPE Plus:19 See also: :py:mod:`matlab2cpp.tree`, :py:mod:`matlab2cpp.node` """ from . import tree if isinstance(code, str): tree_ = build(code, suggest=suggest, retall=True)[0] tree_.translate() else: tree_ = code if isinstance(tree_, tree.builder.Builder): tree_ = tree_[0] if core: if tree_.cls == "Program": if tree_[1] and tree_[1][0].name == "main": tree_ = tree_[1][0][-1] else: tree_ = tree_[1] return tree_.summary() def qscript(code, suggest=True, ftypes={}, **kws): """ Perform a full translation (like :py:func:`~matlab2cpp.qcpp` and :py:func:`~matlab2cpp.qhpp`), but only focus on the object of interest. If for example code is provided, then only the code part of the translation will be include, without any wrappers. It will be as close to a one-to-one translation as you can get. If a node tree is provided, current node position will be source of translation. Args: code (str, Builder, Node): Representation of the node tree. suggest (bool): Use suggestion engine where appropriate. **kws: Additional arguments passed to :py:obj:`~matlab2cpp.Builder`. Returns: str: A code translation in C++. Example: >>> print(matlab2cpp.qscript("a = 4")) a = 4 ; """ from . import tree if isinstance(code, str): tree_ = build(code, suggest=suggest, retall=True, **kws)[0] else: tree_ = code if isinstance(tree_, tree.builder.Builder): tree_ = tree_[0] if ftypes: tree_.ftypes = ftypes tree_.translate() out = "" if tree_.cls == "Program": if tree_[1] and tree_[1][0].name == "main": out = tree_[1][0][-1].str else: out = tree_[1].str else: out = tree_.str out = out.replace("__percent__", "%") from .rules._program import add_indenting, number_fix, strip out = strip(out) out = number_fix(out) out = add_indenting(out) return out ================================================ FILE: src/matlab2cpp/rules/__init__.py ================================================ """ .. _rules: Datatype driven rules have the same name as datatypes reference in :py:mod:`~matlab2cpp.datatype`. They are as follows: +-----------+----------------------------------------+------------------+ | Datatype | Rule | Description | +===========+========================================+==================+ | cell | :py:mod:`~matlab2cpp.rules._cell` | Cell structure | +-----------+----------------------------------------+------------------+ | char | :py:mod:`~matlab2cpp.rules._char` | Word character | +-----------+----------------------------------------+------------------+ | cube | :py:mod:`~matlab2cpp.rules._cube` | Armadillo cube | +-----------+----------------------------------------+------------------+ | cx_cube | :py:mod:`~matlab2cpp.rules._cx_cube` | Armadillo cube | +-----------+----------------------------------------+------------------+ | cx_double | :py:mod:`~matlab2cpp.rules._cx_double` | Scalar complex | +-----------+----------------------------------------+------------------+ | cx_mat | :py:mod:`~matlab2cpp.rules._cx_mat` | Armadillo matrix | +-----------+----------------------------------------+------------------+ | cx_rowvec | :py:mod:`~matlab2cpp.rules._cx_rowvec` | Armadillo rowvec | +-----------+----------------------------------------+------------------+ | cx_vec | :py:mod:`~matlab2cpp.rules._cx_vec` | Armadillo colvec | +-----------+----------------------------------------+------------------+ | double | :py:mod:`~matlab2cpp.rules._double` | Scalar double | +-----------+----------------------------------------+------------------+ | fcube | :py:mod:`~matlab2cpp.rules._fcube` | Armadillo cube | +-----------+----------------------------------------+------------------+ | float | :py:mod:`~matlab2cpp.rules._float` | Scalar float | +-----------+----------------------------------------+------------------+ | fmat | :py:mod:`~matlab2cpp.rules._fmat` | Armadillo matrix | +-----------+----------------------------------------+------------------+ | frowvec | :py:mod:`~matlab2cpp.rules._frowvec` | Armadillo rowvec | +-----------+----------------------------------------+------------------+ | fvec | :py:mod:`~matlab2cpp.rules._fvec` | Armadillo colvec | +-----------+----------------------------------------+------------------+ | icube | :py:mod:`~matlab2cpp.rules._icube` | Armadillo cube | +-----------+----------------------------------------+------------------+ | imat | :py:mod:`~matlab2cpp.rules._imat` | Armadillo matrix | +-----------+----------------------------------------+------------------+ | int | :py:mod:`~matlab2cpp.rules._int` | Scalar integer | +-----------+----------------------------------------+------------------+ | irowvec | :py:mod:`~matlab2cpp.rules._irowvec` | Armadillo rowvec | +-----------+----------------------------------------+------------------+ | ivec | :py:mod:`~matlab2cpp.rules._ivec` | Armadillo colvec | +-----------+----------------------------------------+------------------+ | mat | :py:mod:`~matlab2cpp.rules._mat` | Armadillo matrix | +-----------+----------------------------------------+------------------+ | rowvec | :py:mod:`~matlab2cpp.rules._rowvec` | Armadillo rowvec | +-----------+----------------------------------------+------------------+ | string | :py:mod:`~matlab2cpp.rules._string` | Character string | +-----------+----------------------------------------+------------------+ | struct | :py:mod:`~matlab2cpp.rules._struct` | Struct | +-----------+----------------------------------------+------------------+ | structs | :py:mod:`~matlab2cpp.rules._structs` | Array of structs | +-----------+----------------------------------------+------------------+ | ucube | :py:mod:`~matlab2cpp.rules._ucube` | Armadillo cube | +-----------+----------------------------------------+------------------+ | umat | :py:mod:`~matlab2cpp.rules._umat` | Armadillo matrix | +-----------+----------------------------------------+------------------+ | urowvec | :py:mod:`~matlab2cpp.rules._urowvec` | Armadillo rowvec | +-----------+----------------------------------------+------------------+ | uvec | :py:mod:`~matlab2cpp.rules._uvec` | Armadillo colvec | +-----------+----------------------------------------+------------------+ | uword | :py:mod:`~matlab2cpp.rules._uword` | Scalar uword | +-----------+----------------------------------------+------------------+ | vec | :py:mod:`~matlab2cpp.rules._vec` | Armadillo colvec | +-----------+----------------------------------------+------------------+ These basic types are then glued together through the following: +-------------------------------------------+---------------------------------------+ | Rule | Description | +===========================================+=======================================+ | :py:mod:`~matlab2cpp.rules._code_block` | Branches, loops etc. | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._expression` | Operators and special characters | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._func_lambda` | Anonymous functions | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._func_return` | Functions with one return value | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._func_returns` | Functions with multiple return values | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._matrix` | Matrix constructor | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._program` | Program postprocessing | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._reserved` | Reserved names from Matlab library | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._unknown` | Structures with unknown origin | +-------------------------------------------+---------------------------------------+ | :py:mod:`~matlab2cpp.rules._verbatim` | Special verbatim translations | +-------------------------------------------+---------------------------------------+ """ import glob import os import matlab2cpp sep = os.path.sep for name in glob.glob(os.path.dirname(__file__)+os.path.sep+"*.py"): name = name.split(sep)[-1] if name != "__init__": exec("from . import %s" % name[:-3]) from ._reserved import reserved if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/_cell.py ================================================ from .variables import * def Cell(node): # cells must stand on own line if node.parent.cls not in ("Assign", "Assigns"): node.auxiliary("cell") return "{", ",", "}" def Assign(node): if node.name == 'varargin': out = "%(0)s = va_arg(varargin, " + node[0].type + ") ;" else: out = "%(0)s.clear() ;" # append to cell, one by one for elem in node[1]: out = out + "\n%(0)s.push_back(" + str(elem) + ") ;" return out ================================================ FILE: src/matlab2cpp/rules/_char.py ================================================ from .assign import Assign from .variables import * ================================================ FILE: src/matlab2cpp/rules/_code_block.py ================================================ """ This module contains all the codeblock related nodes. Each node can then here be nested on top of each other. They are static in the sense that there only exists one copy, unaffected by type and have the backend fixd to `code_block`. """ import os import matlab2cpp from . import parallel def Statement(node): """ Stand-alone codeline without assignment etc. Args: node (Statement): Current position in node-tree Return: str : Translation of current node. Children: Expression Expression: Right hand side of expression Examples: >>> print(matlab2cpp.qscript("'text'")) "text" ; >>> print(matlab2cpp.qscript("123")) 123 ; >>> print(matlab2cpp.qscript("[1,2]")) {1, 2} ; >>> print(matlab2cpp.qscript("a")) a ; >>> print(matlab2cpp.qscript("f()")) f() ; """ return "%(0)s ;" def While(node): """ While-loop Args: node (While): Current position in node-tree Return: str : Translation of current node. Children: Expression Block Expression: Loop condition Block: Loop content Examples: >>> print(matlab2cpp.qscript("while 1, f()")) while (1) { f() ; } """ node.error("While-loops are currently not supported.") return "while (%(0)s)\n{\n%(1)s\n}" def Branch(node): """ Root of if/then/else branch Args: node (Branch): Current position in node-tree Return: str : Translation of current node. Children: If Elif* Else? If: If block Elif: Elseif block Else: Else block Examples: >>> print(matlab2cpp.qscript("if a, b; elseif c, d; else e")) if (a) { b ; } else if (c) { d ; } else { e ; } """ return "", "\n", "" def If(node): """ If in if/then/else branch Args: node (If): Current position in node-tree Return: str : Translation of current node. Children: Expression Block Expression: Branch condition Block: Code to be evaluated give condition Examples: >>> print(matlab2cpp.qscript("if a, b")) if (a) { b ; } >>> print(matlab2cpp.qscript("if a, end")) if (a) { // Empty block } """ return """if (%(0)s) { %(1)s }""" def Elif(node): """ Elseif in if/then/else branch Args: node (Elif): Current position in node-tree Return: str : Translation of current node. Children: Expression Block Expression: Branch condition Block: Code to be evaluated give condition Examples: >>> print(matlab2cpp.qscript("if a, b; elseif c, d")) if (a) { b ; } else if (c) { d ; } >>> print(matlab2cpp.qscript("if a, b; elseif c, end")) if (a) { b ; } else if (c) { // Empty block } """ return """else if (%(0)s) { %(1)s }""" def Else(node): """ Else in if/then/else branch Args: node (Else): Current position in node-tree Return: str : Translation of current node. Children: Block Block: Code to be evaluated give condition Examples: >>> print(matlab2cpp.qscript("if a, b; else c")) if (a) { b ; } else { c ; } >>> print(matlab2cpp.qscript("if a, b; else; end")) if (a) { b ; } else { // Empty block } """ return """else { %(0)s }""" def Switch(node): """ Root of switch/case branch Args: node (Switch): Current position in node-tree Return: str : Translation of current node. Children: Expression Case+ Otherwise? Expression: Test-expression Case: Case-block Otherwise: Otherwise-block Examples: >>> print(matlab2cpp.qscript("a=1; switch a; case b; c; otherwise; d")) a = 1 ; if (b == a) { c ; } else { d ; } """ if node[0].cls == "Var": out = "" # create switch variable else: node.type = node[0].type out = "%(type)s _var_%(type)s = %(0)s ;\n" return out + "\n".join(map(str, node[1:])) def Case(node): """ Case in switch/case Args: node (Case): Current position in node-tree Return: str : Translation of current node. Children: Expression Block Expression: Condition Block: Code to be evaluated give condition Example: >>> print(matlab2cpp.qscript("switch 1; case b; c")) int _var_int = 1 ; if (b == _var_int) { c ; } >>> print(matlab2cpp.qscript("a=1; switch a; case b; c;")) a = 1 ; if (b == a) { c ; } """ # first in row if node is node.parent[1]: out = "if (%(0)s == " else: out = "else if (%(0)s == " # define name if node.parent[0].cls == "Var": out = out + node.parent[0].name else: node.type = node.parent[0].type out += "_var_%(type)s" out = out + ")\n{\n%(1)s\n}" return out def Otherwise(node): """ Otherwise in switch/case Args: node (Otherwise): Current position in node-tree Return: str : Translation of current node. Children: Block Block: Code to be evaluated give condition Example: >>> print(matlab2cpp.qscript("switch 1; case a; b; otherwise; c")) int _var_int = 1 ; if (a == _var_int) { b ; } else { c ; } """ return "else\n{\n%(0)s\n}" def Tryblock(node): """ Root of try/catch Args: node (Tryblock): Current position in node-tree Return: str : Translation of current node. Children: Try Catch+ Try: Try-block Catch: Catch-block Examples: >>> print(matlab2cpp.qscript("try; a; catch; b")) try { a ; } catch (...) { b ; } """ node.error("Try-statement are currently not supported.") return "", "\n", "" def Try(node): """Try Args: node (Try): Current position in node-tree Return: str : Translation of current node. Children: Block Block : Try content """ return "try\n{\n", "", "\n}" def Catch(node): """ Catch-block in Try/Catch Args: node (Catch): Current position in node-tree Return: str : Translation of current node. Children: Block Block : Catch content """ name = node.name if not name: return "catch (...)\n{\n", "", "\n}" if name[0] != "@": return "catch ("+node.type()+" "+name+")\n{\n", "", "\n}" return "catch (...)\n{\n", "", "\n}" def Block(node): """ Codeblock Args: node (Block): Current position in node-tree Return: str : Translation of current node. Children: Codeline* Codeline : Sub-block, statement or assigments Examples: >>> print(matlab2cpp.qscript("a; if b; c; end; d")) a ; if (b) { c ; } d ; """ if not len(node): return "// Empty block" out = str(node[0]) for child in node[1:]: if child.cls == "Ecomment": out = out + " " + str(child) else: out = out + "\n" + str(child) return out def Assigns(node): """ Multiple assignment Args: node (Assigns): Current position in node-tree Return: str : Translation of current node. Children: Lhs Lhs+ Expression Lhs: Left hand side of assignment Expression: Right hand side of assignment Examples: >>> print(matlab2cpp.qscript("[a,b,c] = d")) [a, b, c] = d ; >>> print(matlab2cpp.qscript("[a,b,c] = [1,2,3]")) sword __aux_irowvec_1 [] = {1, 2, 3} ; _aux_irowvec_1 = irowvec(__aux_irowvec_1, 3, false) ; a = _aux_irowvec_1(0) ; b = _aux_irowvec_1(1) ; c = _aux_irowvec_1(2) ; """ # left-hand-side not a variable -> create auxillary variable that is if node[-1].cls != "Var": node[-1].auxiliary() # split into multiple lines out = "" for i in range(len(node[:-1])): i = str(i) out += "%(" +i+ ")s = " +str(node[-1])+ "(" +i+ ") ;\n" out = out[:-1] return out def Parfor(node): """ Parfor-loop Args: node (For): Current position in node-tree Return: str : Translation of current node. Children: Var Expression Block Var: Variable running the loop Expression: Container for loop (special handle for Colon) Block: Content to loop over Examples: >>> print(matlab2cpp.qscript("parfor i=1:10; a")) for (i=1; i<=10; i++) { a ; } >>> print(matlab2cpp.qscript("parfor i=1:2:10; a")) for (i=1; i<=10; i+=2) { a ; } >>> print(matlab2cpp.qscript("parfor i=a; b")) for (int _i=0; _i: if len(range) == 2: start, stop = range step = "1" # :: elif len(range) == 3: start, step, stop = range start, step, stop = map(str, [start, step, stop]) # return if omp: node.include("omp") out = parallel.omp(node, start, stop, step) elif tbb: node.include("tbb") #windows if os.name == 'nt': node.include("no_min_max") out = parallel.tbb(node, start, stop, step) return out else: out = "for (%(0)s=" + start + \ "; %(0)s<=" + stop + "; %(0)s" # special case for '+= 1' if step == "1": out += "++" else: out += "+=" + step out += ")\n{\n%(2)s\n}" #if tbb: # out += "\n});" return out # default return """for (int _%(0)s=0; _%(0)s>> print(matlab2cpp.qscript("for i=1:10; a")) for (i=1; i<=10; i++) { a ; } >>> print(matlab2cpp.qscript("for i=1:2:10; a")) for (i=1; i<=10; i+=2) { a ; } >>> print(matlab2cpp.qscript("for i=a; b")) for (int _i=0; _i: if len(range) == 2: start, stop = range step = "1" # :: elif len(range) == 3: start, step, stop = range start, step, stop = map(str, [start, step, stop]) if omp and parallel_loop: node.include("omp") out = parallel.omp(node, start, stop, step) elif tbb and parallel_loop: node.include("tbb") #windows if os.name == 'nt': node.include("no_min_max") out = parallel.tbb(node, start, stop, step) return out else: out = "for (%(0)s=" + start + \ "; %(0)s<=" + stop + "; %(0)s" # special case for '+= 1' if step == "1": out += "++" else: out += "+=" + step out += ")\n{\n%(2)s\n}" return out # i = [1, 2, 3, 4] if len(node) == 3: if node[1].dim in [1, 2]: return """for (auto %(0)s : %(1)s) { %(2)s } """ # default return """for (int _%(0)s=0; _%(0)s>> print(matlab2cpp.qscript("function f(); %{ comment %}")) void f() { /* comment */ } """ return "/*%(value)s*/" def Lcomment(node): """ Line comment Args: node (Lcomment): Current position in node-tree Return: str : Translation of current node. Examples: >>> print(matlab2cpp.qscript("function f(); % comment")) void f() { // comment } """ return "//%(value)s" def Ecomment(node): """ End comment Args: node (Ecomment): Current position in node-tree Return: str : Translation of current node. Examples: >>> print(matlab2cpp.qscript("a % comment")) a ; // comment """ return "//%(value)s" if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/_cube.py ================================================ from .assign import Assign from .variables import * from .cube import Get, Set, Resize Declare = "cube = %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_cx_cube.py ================================================ from .assign import Assign from .variables import * from .cube import Get, Set, Resize Declare = "cx_cube = %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_cx_double.py ================================================ from .assign import Assign from .variables import * Declare = "cx_double %(name)s ;" Imag = "cx_double(0, %(value)s)" ================================================ FILE: src/matlab2cpp/rules/_cx_mat.py ================================================ from .assign import Assign from .variables import * from .mat import Get, Set Declare = "cx_mat %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_cx_rowvec.py ================================================ from .assign import Assign from .variables import * from .rowvec import Get, Set Declare = "cx_rowvec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_cx_vec.py ================================================ from .assign import Assign from .variables import * from .vec import Get, Set Declare = "cx_vec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_double.py ================================================ from .assign import Assign from .variables import * Declare = "double %(name)s ;" Float = "%(value)s" ================================================ FILE: src/matlab2cpp/rules/_expression.py ================================================ import matlab2cpp from .assign import Assign def Paren(node): """Parenthesis surounding expression. Examples: >>> print(matlab2cpp.qscript("(1+2)*(3-4)")) (1+2)*(3-4) ; """ node.type = node[0].type return "(%(0)s)" def End(node): """The 'end' statement indicating not end of block, but end-of-range. Examples: >>> print(matlab2cpp.qscript("x = zeros(2,2,2); x(end, end, end)")) x = arma::zeros(2, 2, 2) ; x(x.n_rows-1, x.n_cols-1, x.n_slices-1) ; """ # find context for what end refers to pnode = node while pnode.parent.cls not in \ ("Get", "Cget", "Nget", "Fget", "Sget", "Set", "Cset", "Nset", "Fset", "Sset", "Block"): pnode = pnode.parent # end statement only makes sense in certain contexts if pnode.cls == "Block": node.error("Superfluous end-statement") return "end" index = pnode.parent.children.index(pnode) name = pnode = pnode.parent.name if len(node.group) == 1: if node.group.dim == 1: return name + ".n_rows" if node.group.dim == 2: return name + ".n_cols" # what end is referring to if index == 0: return name + ".n_rows" elif index == 1: return name + ".n_cols" elif index == 2: return name + ".n_slices" else: node.error("end statement in arg>3") Break = "break" def Return(node): """Return statement Examples: >>> print(matlab2cpp.qscript("function f(); return")) void f() { return ; } >>> print(matlab2cpp.qscript("function y=f(); return; y=1")) int f() { int y ; return y ; y = 1 ; return y ; } >>> print(matlab2cpp.qscript("function [y,z]=f(); return; y=1; z=2")) void f(int& y, int& z) { return ; y = 1 ; z = 2 ; } """ func = node.func if func.backend == "func_returns": return "return" if func.backend == "func_lambda": return "return _retval" return_value = func[1][0].name return "return " + return_value # simple operators def Mul(node): """(Matrix-)multiplication Examples: >>> print(matlab2cpp.qscript("a = [1,2,3]; b = [4;5;6]; c = a*b")) sword _a [] = {1, 2, 3} ; a = irowvec(_a, 3, false) ; sword _b [] = {4, 5, 6} ; b = ivec(_b, 3, false) ; c = arma::as_scalar(a*b) ; """ c_flag = False if not node[0].num: return "", "*", "" if len(node) == 2 and node.dim == 0: if node[0].backend == "reserved" and node[0].name == "i" and node[1].mem < 4: node.value = node[1].value return "cx_double(0, %(1)s)" elif node[1].backend == "reserved" and node[1].name == "i" and node[0].mem < 4: node.value = node[0].value return "cx_double(0, %(0)s)" dim = node[0].dim #mem = max(node[0].mem, 2) mem = 0 for n in node: mem = max(mem, n.mem) if node.mem == 4 and node[0].dim == 0 and node[0].mem != 4: out = "cx_double(%(0)s)" else: out = "%(0)s" index = 1 for child in node[1:]: sVal = str(index) index += 1 # not numerical if not child.num: return "", "*", "" if dim == 0: dim = child.dim if node.mem == 4 and child.dim == 0 and child.mem != 4: c_flag = True if dim == 1: if child.dim == 0: #pass dim = 1 elif child.dim == 1: child.error("multiplication shape mismatch, colvec*colvec") elif child.dim == 2: #pass dim = 3 elif child.dim == 3: child.error("multiplication shape mismatch, colvec*matrix") elif child.dim == 4: child.error("multiplication shape mismatch, colvec*cube") elif dim == 2: if child.dim == 0: #pass dim = 2 elif child.dim == 1: out = "arma::as_scalar(" + out + "*" + "%(" + sVal + ")s" + ")" #pass dim = 0 continue elif child.dim == 2: child.error("multiplication shape mismatch, rowvec*rowvec") elif child.dim == 3: #pass dim = 3 elif dim == 3: if child.dim == 0: #pass dim = 3 elif child.dim == 1: #pass dim = 1 elif child.dim == 2: child.error("multiplication shape mismatch, matrix*rowvec") elif child.dim == 3: #pass dim = 3 if c_flag: out = out + "*" + "(cx_double) %(" + sVal + ")s" else: out = out + "*" + "%(" + sVal + ")s" #mem = max(mem, child.mem) node.type = (dim, mem) #return "", "*", "" return out def Elmul(node): """Element multiplication Examples: >>> print(matlab2cpp.qscript("a = [1,2,3]; b = [4,5,6]; c = a.*b")) sword _a [] = {1, 2, 3} ; a = irowvec(_a, 3, false) ; sword _b [] = {4, 5, 6} ; b = irowvec(_b, 3, false) ; c = a%b ; """ # unknown input if node.type == "TYPE": return "", "__percent__", "" # not numerical if not node.num: node.error("non-numerical multiplication %s" % str([n.type for n in node])) return "", "*", "" # scalar multiplication, lhs or rhs of elmul is scalar if node.dim == 0 or node[0].dim == 0 or node[1].dim == 0: return "", "*", "" # Sclar's multiplication in Armadillo '%' needs special handle because of # interpolation in python return "", "__percent__", "" def Plus(node): """Addition Examples: >>> print(matlab2cpp.qscript("a = [1,2,3]; b = [4,5,6]; c = a+b")) sword _a [] = {1, 2, 3} ; a = irowvec(_a, 3, false) ; sword _b [] = {4, 5, 6} ; b = irowvec(_b, 3, false) ; c = a+b ; """ # non-numerical addition if not node.num: node.error("non-numerical addition %s" % str([n.type for n in node])) if node.mem == 4 and node.dim == 0: out = [] for child in node: if child.mem < 4: out.append("cx_double(" + str(child) + ", 0)") else: out.append(str(child)) return "+".join(out) return "", "+", "" def Minus(node): """Subtraction Examples: >>> print(matlab2cpp.qscript("a = [1,2,3]; b = [4,5,6]; c = a-b")) sword _a [] = {1, 2, 3} ; a = irowvec(_a, 3, false) ; sword _b [] = {4, 5, 6} ; b = irowvec(_b, 3, false) ; c = a-b ; """ return "", "-", "" Gt = "", ">", "" Ge = "", ">=", "" Lt = "", "<", "" Le = "", "<=", "" Ne = "", "!=", "" Eq = "", "==", "" Band = "", " && ", "" Land = "", " && ", "" Bor = "", " || ", "" Lor = "", " || ", "" def Elementdivision(node): """Element wise division """ # unknown input if node.type == "TYPE": # default to assume everything scalar out = str(node[0]) for child in node[1:]: # force to be float if int in divisor if child.cls == "Int": out = out + "/" + str(child) + ".0" else: out = out + "/" + str(child) return out out = str(node[0]) # I commented out the the code below # force float output mem = node[0].mem if mem<2: mem = 2 mem = max(node[0].mem, node[1].mem) # I think node will always have length 2, # thus for child in node[1:] should be same as child = node[1:] # but why fix whats not broken? commented out mem = max(...) and node.mem = mem for child in node[1:]: # convert ints to floats if child.cls == "Int": out = out + "/" + str(child) + ".0" # avoid int/uword division, or int/int division elif mem < 2: out = out + "*1.0/" + str(child) else: out = out + "/" + str(child) #mem = max(mem, child.mem) #node.mem = mem return out def Leftelementdivision(node): """Left element wise division """ # unknown input if node.type == "TYPE": return "", "\\", "" # iterate backwards out = str(node[-1]) # force float output mem = node[-1].mem if mem<2: mem = 2 for child in node[-2::-1]: # avoid explicit integer division if child.cls == "Int": out = str(child) + ".0/" + out # avoid implicit integer division if child.mem < 2 and mem < 2: out = str(child) + "*1.0/" + out else: out = str(child) + "/" + out #mem = max(mem, child.mem) #node.mem = mem return out def Matrixdivision(node): # start with first element ... out = str(node[0]) mem = node[0].mem or 2 dim = node[0].dim or 0 # everything scalar -> use element division if {n.dim for n in node} == {0}: return Elementdivision(node) else: # ... iterate over the others for child in node[1:]: # matrix handle if child.dim == 3: out = "arma::solve(" + str(child) + ".t(), " + out + ".t(), solve_opts::fast).t()" # integer handle elif child.cls == "Int": out = out + "/" + str(child) + ".0" # avoid integer division elif child.mem < 2 and mem < 2: out = out + "*1.0/" + str(child) elif child.type == "int" and mem == 4: out = out + "/" + "double(" + str(child) + ")" else: out = out + "/" + str(child) # track memory output #mem = max(mem, child.mem) # assert if division legal in matlab if dim == 0: pass #dim = child.dim elif dim == 1: if child.dim == 0: pass #dim = 1 elif child.dim == 1: node.error("Matrix division error 'colvec\\colvec'") elif child.dim == 2: pass #dim = 3 elif child.dim == 3: node.error("Matrix division error 'colvec\\matrix'") elif child.dim == 3: node.error("Matrix division error 'colvec\\cube'") elif dim == 2: if child.dim == 0: pass #dim = 2 elif child.dim == 1: pass #dim = 0 elif child.dim == 2: node.error("Matrix division error 'rowvec\\rowvec'") elif child.dim == 3: pass #dim = 2 elif child.dim == 4: pass #dim = 3 elif dim == 3: if child.dim == 0: pass #dim = 3 elif child.dim == 1: pass #dim = 1 elif child.dim == 2: node.error("Matrix division error 'matrix\\rowvec'") elif child.dim == 3: pass #dim = 3 elif child.dim == 4: pass #dim = 4 #if not (dim is None) and not (mem is None): # node.type = (dim, mem) return out def Leftmatrixdivision(node): """Left operator matrix devision """ # unknown input if node.type == "TYPE": return "", "\\", "" # start with first node ... out = str(node[0]) mem = node[0].mem dim = node[0].dim # everything scalar -> use left element division if {n.dim for n in node} == {0}: return Leftelementdivision(node) else: # ... iterate forwards for child in node[1:]: # classical array inversion if child.dim > 0: out = "arma::solve(" + out + ", " + str(child) + ", solve_opts::fast)" # avoid integer division # backwords since left division is reverse elif child.mem < 2 and mem < 2: out = "(" + out + ")*1.0/" + str(child) #mem = 2 # backwords since left division is reverse else: out = "(" + out + ")/" + str(child) # out = str(child) + "/" + out #mem = max(mem, child.mem) # assert division as legal if dim == 0: dim = node.dim elif dim == 1: if node.dim == 0: pass #dim = 1 elif node.dim == 1: node.error("Matrix division error 'colvec\\colvec'") elif node.dim == 2: pass #dim = 3 elif node.dim == 3: node.error("Matrix division error 'colvec\\matrix'") elif node.dim == 3: node.error("Matrix division error 'colvec\\cube'") elif dim == 2: if node.dim == 0: pass #dim = 2 elif node.dim == 1: pass #dim = 0 elif node.dim == 2: node.error("Matrix division error 'rowvec\\rowvec'") elif node.dim == 3: pass #dim = 2 elif node.dim == 4: pass #dim = 3 elif dim == 3: if node.dim == 0: pass #dim = 3 elif node.dim == 1: pass #dim = 1 elif node.dim == 2: node.error("Matrix division error 'matrix\\rowvec'") elif node.dim == 3: pass #dim = 3 elif node.dim == 4: pass #dim = 4 #node.type = (dim, mem) return out def Exp(node): """Exponent """ out = str(node[0]) for child in node[1:]: out = "pow(" + str(out) + ", " + str(child) + ")" return out def Elexp(node): """Elementwise exponent """ node.type = node[0].type out = str(node[0]) if len(node) == 2: exponent = str(node[1]) if exponent == "2": if(node[0].dim == 0): return "m2cpp::square(" + out + ")" else: return "arma::square(" + out + ")" for child in node[1:]: out = "arma::pow(" + str(out) + ", " + str(child) + ")" return out def All(node): """All ':' element """ arg = node.parent.name # is first arg if len(node.parent) > 0 and node.parent[0] is node: arg += ".n_rows" # is second arg elif len(node.parent) > 1 and node.parent[1] is node: arg += ".n_cols" # is third arg elif len(node.parent) > 2 and node.parent[2] is node: arg += ".n_slices" else: return "span::all" return "m2cpp::span(0, " + arg + "-1)" Neg = "-", "", "" #Not = "not ", "", "" Not = "!", "", "" def Transpose(node): """(Simple) transpose >>> print(matlab2cpp.qscript("a = [1,2,3]; b = a.'")) sword _a [] = {1, 2, 3} ; a = irowvec(_a, 3, false) ; b = arma::strans(a) ; """ # unknown datatype if not node.num: return "arma::strans(%(0)s)" """ # colvec -> rowvec if node[0].dim == 1: node.dim = 2 # rowvec -> colvec elif node[0].dim == 2: node.dim = 1 """ # not complex type #if node.mem < 4: # return "arma::strans(", "", ")" return "arma::strans(", "", ")" def Ctranspose(node): """Complex transpose >>> print(matlab2cpp.qscript("a = [1,2,3]; b = a'")) sword _a [] = {1, 2, 3} ; a = irowvec(_a, 3, false) ; b = arma::trans(a) ; """ # unknown input if not node.num: return "arma::trans(", "", ")" """ # colvec -> rowvec if node[0].dim == 1: node.dim = 2 # rowvec -> colvec elif node[0].dim == 2: node.dim = 1 """ return "arma::trans(", "", ")" def Colon(node): """Colon (as operator) Examples: >>> print(matlab2cpp.qscript("a = 1:10; b = 1:10:2")) a = m2cpp::fspan(1, 1, 10) ; b = m2cpp::fspan(1, 10, 2) ; >>> print(matlab2cpp.qscript("a = [1,2,3]; a(1:2:2)")) sword _a [] = {1, 2, 3} ; a = irowvec(_a, 3, false) ; arma::strans(a(m2cpp::span(0, 2, 1))) ; """ # context: array argument (must always be uvec) if node.group.cls in ("Get", "Cget", "Nget", "Fget", "Sget", "Set", "Cset", "Nset", "Fset", "Sset") and node.parent.num: #node.type = "uvec" node.include("m2cpp") # two arguments, use Armadillo span from:to if len(node) == 2: if node.parent.cls in ("Get", "Cget", "Nget", "Fget", "Sget", "Set", "Cset", "Nset", "Fset", "Sset") and node.parent.num and node.parent.backend != "reserved": if node.dim in (1, 2): return "arma::span(%(0)s-1, %(1)s-1)" if node.group.backend == "reserved": node.type = 'rowvec' return "m2cpp::fspan(%(0)s, 1, %(1)s)" return "m2cpp::span<%(type)s>(%(0)s-1, %(1)s-1)" # three arguments, not supported in Armadillo elif len(node) == 3: if node.group.backend == "reserved": node.type = 'rowvec' return "m2cpp::fspan(%(0)s, %(1)s, %(2)s)" return "m2cpp::span<%(type)s>(%(0)s-1, %(1)s, %(2)s-1)" else: return "m2cpp::span<%(type)s>(", ", ", ")" else: # context: matrix concatination #if node.group.cls in ("Matrix",) and node.group.num: # node.type = "rowvec" # context: pass to function #elif node.parent.cls in ("Get", "Cget", "Nget", "Fget", "Sget", # "Set", "Cset", "Nset", "Fset", "Sset"): # node.type = "rowvec" # context: assignment #elif node.group.cls in ("Assign",) and node.group[0].num: #Below the span is set to have the same type as LHS #Had to change here ass well or there will be an strans, #LHS have rowvec, while node.type (RHS) have vec dim #node.type = node.group[0].type # node.type = "rowvec" #print(node.group[0].mem) #node.mem = node.group[0].mem #else: # node.type = "rowvec" #include mconvert.h, which contain namespace m2cpp node.include("m2cpp") # : if len(node) == 2: if node.group.cls == "Assign": node.type = 'rowvec' return "m2cpp::fspan" + "(%(0)s, 1, %(1)s)" return "m2cpp::fspan" + "(%(0)s, 1, %(1)s)" # :: elif len(node) == 3: node.type = 'rowvec' args = "(%(0)s, %(1)s, %(2)s)" #return "m2cpp::span<" + node.type + ">" + args #return "arma::strans(arma::linspace(%(0)s, %(2)s, (%(2)s%(1)s))" return "m2cpp::fspan" + args #Sets template type to LHS: # ex, ti is type vec: ti = (m2cpp::span(0, 1, nt-1))*dt ; #should probably change the if statement above, context: assignment #if node.group.cls == "Assign": #print(node.group) #print(node.group.cls) #print(node.group[0].type) #print("\n\n\n") # return "m2cpp::span<" + node.group[0].type + ">" +args return "m2cpp::span<" + node.type + ">(", ", ", ")" if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/_fcube.py ================================================ from .assign import Assign from .variables import * from .cube import Get, Set, Resize Declare = "fcube = %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_float.py ================================================ from .assign import Assign from .variables import * Declare = "float %(name)s ;" Float = "%(value)s" ================================================ FILE: src/matlab2cpp/rules/_fmat.py ================================================ from .assign import Assign from .variables import * from .mat import Get, Set Declare = "fmat %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_frowvec.py ================================================ from .assign import Assign from .variables import * from .rowvec import Get, Set Declare = "frowvec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_func_lambda.py ================================================ """ Anonymous/Lambda Functions """ import matlab2cpp from .function import type_string from .assign import Assign def Get(node): """Function call of an lambda function Contains: Expression* Examples: >>> print(matlab2cpp.qscript("x = 4; f = @() x; y = f()")) x = 4 ; f = [x] () {x ; } ; y = f() ; """ return "%(name)s(", ", ", ")" Var = "%(name)s" def Lambda(node): """Lambda function statement During construction, builder creates a full function for lambda expression. These functions are available through node.reference. Property: name (of function) Examples: >>> print(matlab2cpp.qscript("f = @() 4")) f = [] () {4 ; } ; """ # lambda function are created as full functions, but referenced to be # written inline lfunc = node.reference ldeclares, lreturns, lparams, lblock = lfunc lnames = lparams.names + ldeclares.names expr = lblock[0][1] # location for where lambda is created func = node.func declares, returns, params, block = func out = "" # declare list in lambda function for declare in declares: if declare not in ldeclares: continue name = declare.name out += ", " + name # translate again where datatypes updated expr.translate() lfunc.translate() # return string out = "[" + out[2:] + "] " out += "(" + str(lparams) + ") {" + str(expr) + " ; }" return out Returns = "" def Params(node): """Parameter structure in lambda functions Contains: Var* Examples: >>> print(matlab2cpp.qscript("f = @(x,y,z) x+y+z; f(1,2.,'3')")) f = [] (int x, double y, std::string z) {x+y+z ; } ; f(1, 2., "3") ; """ return ", ".join(["%s %s" % (type_string(n), n.name) for n in node]) def Declares(node): """Variable scope in lambda function If variables Examples: >>> print(matlab2cpp.qscript("x = 4; f = @() x+2")) x = 4 ; f = [x] () {x+2 ; } ; """ # handle in Lambda return "" # Actual lambda function is hidden Func = "" if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/_func_return.py ================================================ """ Functions with single return Nodes ----- Func : Function definition Contains: Declares, Returns, Params, Block Property: name Returns : Function return variables Contains: Var, ... Params : Function parameter variables Get : Function call Example: "y(4)" Contains: Gets Property: name Var : Function call hidden as variable Example "y" Contains: nothing """ import matlab2cpp from .function import type_string from .variables import Get from .assign import Assign def Var(node): """ Function call as variable Writing a function as if a variable, is equivalent to calling the function without arguments. property: name (of variable) Examples: >>> print(matlab2cpp.qscript("function y=f(); y=1; end; function g(); f")) int f() { int y ; y = 1 ; return y ; } void g() { f() ; } """ # push the job over to Get return Get(node) Returns = "", "" """single return value are used verbatim""" def Params(node): """ Parameters in functions with one return Adds type prefix. Contains: Var* Examples: >>> code = "function y=f(a,b,c,d,e); y=1" >>> from matlab2cpp.tree import Builder >>> builder = Builder() >>> builder.load("unamed", code) >>> builder[0].ftypes = {"f":{"a": "int", "b":"double", "c":"cx_mat", ... "d":"func_lambda", "e":"struct", "y":"int"}} >>> print(matlab2cpp.qscript(builder)) int f(int a, double b, cx_mat c, std::function d, _E e) { int y ; y = 1 ; return y ; } """ out = "" # if -ref, -reference flag option if node.project.builder.reference: out += ", ".join(["const " + type_string(child) + "& " + child.name if child.dim > 0 else type_string(child) + " " + child.name for child in node]) else: out = ", ".join([type_string(child) + " " + child.name for child in node]) return out def Declares(node): """ Declarations in the beginning of function Contains: Var* Examples: >>> print(matlab2cpp.qscript("function d=f(); a=1; b.c='2'; d.e(1)=[4,5]")) _D f() { _B b ; _D d ; int a ; a = 1 ; b.c = "2" ; sword _d [] = {4, 5} ; d.e[0] = irowvec(_d, 2, false) ; return d ; } """ if not node: return "" returns = node.parent[1] declares = {} # {"int" : ["a", "b"]} -> int a, b ; structs = {} # {"_A" : "a"} -> _A a; # fill declares and structs for child in node[:]: type = type_string(child) if type not in declares: declares[type] = [] declares[type].append(child) if child.type == "structs": structs[child.name] = child # create output out = "" keys = sorted(declares.keys()) for key in keys: val = sorted(declares[key], key=lambda x: x.name) # datatype out += "\n" + key + " " # all variables with that type for v in val: out += str(v) if v.name in structs: structs_ = node.program[3] struct = structs_[structs_.names.index(v.name)] size = struct[struct.names.index("_size")].value out += "[%s]" % size out += ", " out = out[:-2] + " ;" return out[1:] def Func(node): """ Function declaration Contains: Declares Returns Params Block Property: name (of function) Examples: >>> print(matlab2cpp.qscript("function y=f(); y=1")) int f() { int y ; y = 1 ; return y ; } """ # type information is in params and declare, not return retname = node[1][0].name if retname in node[0].names: retval = node[0][node[0].names.index(retname)] if retname in node[2].names: retval = node[2][node[1].names.index(retname)] rettype = type_string(retval) # empty code_block function with return statement if len(node[-1]) == 0: return rettype + """ %(name)s(%(2)s) { return %(1)s }""" # function ends with a return statement if node[-1][-1] and node[-1][-1][-1].cls == "Return": return rettype + """ %(name)s(%(2)s) { %(0)s %(3)s }""" return rettype + """ %(name)s(%(2)s) { %(0)s %(3)s return %(1)s ; }""" def Main(node): """ Main function Contains: Declares Returns Params Block Property: name (of function) Examples: >>> print(matlab2cpp.qcpp("4")) #include using namespace arma ; int main(int argc, char** argv) { 4 ; return 0 ; } """ # has variables to declare if node[0]: return """int main(int argc, char** argv) { %(0)s %(3)s return 0 ; }""" return """int main(int argc, char** argv) { %(3)s return 0 ; }""" ================================================ FILE: src/matlab2cpp/rules/_func_returns.py ================================================ """ Functions with multiple returns """ import matlab2cpp from .function import type_string from .variables import Get def Func(node): """Function declaration Contains: Declares Returns Params Block Property: name (of function) Examples: >>> print(matlab2cpp.qscript("function f()")) void f() { // Empty block } """ if len(node[1]) and len(node[2]): if len(node[0])>len(node[1]): return """void %(name)s(%(2)s, %(1)s) { %(0)s %(3)s }""" return """void %(name)s(%(2)s, %(1)s) { %(3)s }""" # one of returns or params are missing if len(node[0])>len(node[1]): return """void %(name)s(%(2)s%(1)s) { %(0)s %(3)s }""" return """void %(name)s(%(2)s%(1)s) { %(3)s }""" def Var(node): """Function call as variable Writing a function as if a variable, is equivalent to calling the function without arguments. Property: name (of variable) Examples: >>> print(matlab2cpp.qscript("function f(); end; function g(); f")) void f() { // Empty block } void g() { f() ; } """ # push the job over to Get return Get(node) def Assign(node): """The function have multiple returns, but only one lhs return. """ return Assigns(node) def Assigns(node): """ Assignment where rhs is a function call and lhs are multiple returns. Concatenates return variables after the parameters, if any. Property: name (of function) Contains: Expression Expression+ Get Examples: >>> print(matlab2cpp.qscript('''function [a,b]=f(c,d); a=c; b=d ... function g(); [a,b] = f(1,2.)''')) void f(int c, double d, int& a, double& b) { a = c ; b = d ; } void g() { double b ; int a ; f(1, 2., a, b) ; } """ # existence of parameters in function call if node[-1]: params = [s.str for s in node[-1]] params = ", ".join(params) + ", " else: params = "" returns = [s.str for s in node[:-1]] returns = ", ".join(returns) return "%(name)s(" + params + returns + ") ;" def Returns(node): """Return value in function definition with zero or multiple returns Adds type prefix and '&' (since they are to be placed in parameters) Contains: Return* Examples: >>> print(matlab2cpp.qscript("function [a,b]=f(); a=1, b=2.")) void f(int& a, double& b) { a = 1 ; b = 2. ; } """ out = "" for child in node[:]: out += ", " + type_string(child) + "& " + str(child) return out[2:] def Params(node): """Parameters in function definition with zero or multiple returns Adds type prefix. Contains: Var* Examples: >>> ftypes = {"f": {"a":"int", "b":"double"}} >>> print(matlab2cpp.qscript("function f(a,b)", ftypes=ftypes)) void f(int a, double b) { // Empty block } """ # Create list of parameters out = "" # if -ref, -reference flag option if node.project.builder.reference: out += ", ".join(["const " + type_string(child) + "& " + child.name if child.dim > 0 else type_string(child) + " " + child.name for child in node]) return out else: out = ", ".join([type_string(child) + " " + child.name for child in node]) return out def Declares(node): """Variables declared on the top of the function Contains: Declare* Examples: >>> print(matlab2cpp.qscript("function f(); a=1; b.c='2'; d.e(1)=[4,5]")) void f() { _B b ; _D d ; int a ; a = 1 ; b.c = "2" ; sword _d [] = {4, 5} ; d.e[0] = irowvec(_d, 2, false) ; } """ # nothing to declare if not node: return "" returns = node.parent[1] declares = {} # {"int" : ["a", "b"]} -> int a, b ; structs = {} # {"_A" : "a"} -> _A a; # fill declares and structs for child in node[:]: # return values in multi-returns are declared as parameter if child.name in returns: continue type = type_string(child) if type not in declares: declares[type] = [] declares[type].append(child) if child.type == "structs": structs[child.name] = child # create output out = "" keys = sorted(declares.keys()) for key in keys: val = sorted(declares[key], key=lambda x: x.name) # datatype out += "\n" + key + " " # all variables with that type for v in val: out += str(v) if v.name in structs: structs_ = node.program[3] struct = structs_[structs_.names.index(v.name)] size = struct[struct.names.index("_size")].value out += "[%s]" % size out += ", " out = out[:-2] + " ;" return out[1:] if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/_fvec.py ================================================ from .assign import Assign from .variables import * from .vec import Get, Set Declare = "fvec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_icube.py ================================================ from .assign import Assign from .variables import * from .cube import Get, Set, Resize Declare = "icube = %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_imat.py ================================================ from .assign import Assign from .variables import * from .mat import Get, Set Declare = "imat %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_int.py ================================================ from .assign import Assign from .variables import * # Declare = "int %(name)s ;" Int = "%(value)s" ================================================ FILE: src/matlab2cpp/rules/_irowvec.py ================================================ from .assign import Assign from .variables import * from .rowvec import Get, Set Declare = "irowvec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_ivec.py ================================================ from .assign import Assign from .variables import * from .vec import Get, Set Declare = "ivec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_mat.py ================================================ from .assign import Assign from .variables import * from .mat import Get, Set Declare = "mat %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_matrix.py ================================================ """ Matrix declaration rules Nodes ----- Matrix : Matrix container Example: "[x;y]" Contains: Vector, ... Vector : (Column)-Vector container Contains: Expr, ... """ from ._code_block import Statement from . import assign, armadillo as arma def Vector(node): """A (row-)vector """ if node.value == "scalarsonly": return "", ", ", "" if node.type == "string": return "", " + ", "" nodes = [str(n) for n in node] #max mem in list and type list try: mem = max([int(n.mem) if n.num else -1 for n in node]) except: mem = -1 mem_type = ["uword", "sword", "float", "double", "cx_double"] #Join columns: a = [0, my_rowvec, b] for i in range(len(nodes)): # scalars must be converted first if node[i].value or node[i].dim == 0: # value=scalarsonly node[i].include("m2cpp") if mem != -1: nodes[i] = "m2cpp::srow<" + mem_type[mem] + ">(" + nodes[i] + ")" else: nodes[i] = "m2cpp::srow(" + nodes[i] + ")" if nodes: out = str(nodes[0]) for node_ in nodes[1:]: out = "arma::join_rows(%s, %s)" % (out, node_) return out return "" def Matrix(node): # ensure all vectors in matrix same length m = len(node[0]) if any([len(n)-m for n in node if n.value == "scalarsonly"]): shape = str([len(n) for n in node if n.value == "scalarsonly"]) node.error("shape missmatch %s" % shape) if node.type == "string": if len(node) > 1: return "", " + ", "" return "", " ", "" #clims in imagesc needs two {{ and }} for the python implementation if node.parent.name in ("imagesc", "wigb") and \ len(node) == 1 and len(node[0]) > 1: if all([n.dim == 0 for n in node[0]]): return "{{", ", ", "}}" #{ } around arma::join_rows() return "{", ", ", "}" # non-numerical elements in matrix if not node.num: if len(node[0]) == 1 and node[0][0].cls == "Colon": return "", ", ", "" return "{", ", ", "}" dims = {n.dim for n in node} # single vector with no content if len(node) == 1 and len(node[0]) == 0: return "{", ", ", "}" # everything on scalarsonly form elif all([n.value for n in node]): # Inline matrices are moved to own lines if node.parent.cls not in ("Assign", "Statement") and \ node.parent.backend != "reserved": if node.parent.cls in ("Get", "Set") and node.mem != 0: if node.parent.type == "TYPE": node.type = (node.dim, 3) else: node.type = (node.dim, 0) return str(node.auxiliary()) # if node.parent.cls in ("Assign", "Statement"): # node.parent.backend = "matrix" return "{", ", ", "}" # mix of scalars and colvecs, scalar and matrix, scalar and vec and matrix elif dims in ({0, 1}, {1}, {0, 3}, {0, 1, 3}): # make string of each vector in matrix nodes = [] for i in range(len(node)): #max mem in list and type list try: mem = max([int(n.mem) if n.num else -1 for n in node]) except: mem = -1 mem_type = ["uword", "sword", "float", "double", "cx_double"] # scalars must be converted first if node[i].value or node[i].dim == 0: # value=scalarsonly node[i].include("m2cpp") if mem != -1: nodes.append("m2cpp::scol<" + mem_type[mem] + ">(" + str(node[i]) + ")") else: nodes.append("m2cpp::scol(" + str(node[i]) + ")") else: nodes.append(str(node[i])) # mix of rowvecs and matrices, mix of columnvecs and matrices elif dims in ({2}, {3}, {2, 3}, {1, 3}): # make string of each vector in matrix nodes = [] for idx in range(len(node)): # decomposed vectors should be moved to own lines if node[idx].value: nodes.append(str(node[idx].auxiliary())) else: nodes.append(str(node[idx])) out = str(nodes[0]) for node_ in nodes[1:]: out = "arma::join_cols(%s, %s)" % (out, node_) if node.parent.name in ("imagesc", "wigb"): out = "{%s}" % out return out def Assign(node): #left-hand-side, right-hand-side lhs, rhs = node assert rhs.cls in ("Matrix", "Cell") # no content in matrix if len(rhs[0]) == 0: return "%(0)s.reset() ;" # non-numerical values in matrix or variable assign if not lhs.num or not rhs.num: return "%(0)s = %(1)s ;" # new local variable 'ctype' contains convertion type node.type = node["ctype"] = node[0].type dim = node.dim node.dim = 0 # decomposed matrix if rhs.value: type = node.type if type == "int": type = "sword" # scalar if rhs.dim == 0: return arma.scalar_assign(node) # colvec elif rhs.dim == 1: # save number of rows as 'rows' node["rows"] = len(node[1][0])*len(node[1]) if node.prop["ctype"] == "mat": return type + " _" + node[0].name + " [] = %(1)s ;\n"+\ "%(0)s = %(ctype)s(_" + node[0].name + ", %(rows)s, 1, false) ;" else: return type + " _" + node[0].name + " [] = %(1)s ;\n"+\ "%(0)s = %(ctype)s(_" + node[0].name + ", %(rows)s, false) ;" # use uniform initialization #return "%(0)s = %(1)s ;" # rowvec elif rhs.dim == 2: # save number of cols as 'cols' node["cols"] = len(node[1][0])*len(node[1]) if node.prop["ctype"] == "mat": return type + " _" + node[0].name + " [] = %(1)s ;\n"+\ "%(0)s = %(ctype)s(_" + node[0].name + ", 1, %(cols)s, false) ;" else: return type + " _" + node[0].name + " [] = %(1)s ;\n"+\ "%(0)s = %(ctype)s(_" + node[0].name + ", %(cols)s, false) ;" # use uniform initialization #return "%(0)s = %(1)s ;" # matrix elif rhs.dim == 3: # save number of rows and columns node["rows"] = len(node[1][0]) node["cols"] = len(node[1]) return type + " _" + node[0].name + " [] = %(1)s ;\n"+\ "%(0)s = arma::strans(%(ctype)s(_" + node[0].name + ", %(rows)s, %(cols)s, false)) ;" # use uniform initialization #my_list = node[1].children #my_list = ["{" + str(elem) + "}" for elem in my_list] #return "%(0)s = {" + ", ".join(my_list) + "} ;" assert False else: node.dim = dim return assign.Assign(node) Var = "%(name)s" def Cell(node): # move inline cells to own lines if node.parent.cls not in ("Assign", "Assigns"): node.auxiliary() return "{", ", ", "}" ================================================ FILE: src/matlab2cpp/rules/_program.py ================================================ import re import matlab2cpp from . import armadillo as arma from .function import type_string def add_indenting(text): """Add identing to text """ lines = text.split("\n") indent = 0 for i in range(len(lines)): line = lines[i] # Fix indentation and linesplit if line in ("}", "} ;") or line[:4] == "} //": indent -= 1 line = " "*indent + line elif line == "{": line = " "*indent + line indent += 1 else: line = " "*indent + line lines[i] = line text = "\n".join(lines) return text def number_fix(text): """Code has allot of '-1' statements in indexing. This code substitutes explicit number subtractions with the single index equivalent. Examples: >>> print(number_fix("a(8-1, 5-1)")) a(7, 4) """ # Cosmetic fix for p0,p1,p2 in set(re.findall(r"(([ ,(\[])(-?\d+)-1(?![*/]))", text)): val = int(p2)-1 val = p1+str(val) text = val.join(text.split(p0)) for p0,p1,p2 in \ set(re.findall(r"(([+\- ])(\d+)-1(?![*/]))", text)): if p1=="-": val = int(p2)+1 else: val = int(p2)-1 if val: val = p1+str(val) else: val = "" text = val.join(text.split(p0)) text = re.sub(r"\+-", r"-", text) return text def strip(text): """Remove trailing spaces and linefeeds. """ if not text: return text start = 0 while text[start] in "\n ": start += 1 end = 0 while text[end-1] in "\n ": end -= 1 if end: text = text[start:end] else: text = text[start:] return text def Project(node): return "" def Program(node): arma.include(node) return "" def Includes(node): return "", "\n", "" def Headers(node): return "", "\n", "" def Header(node): func = node.program[1][node.program[1].names.index(node.name)] if func.backend == "func_return": # if -ref, -reference flag option if node.project.builder.reference: code = func[1][0].type + " " + func.name + "(" +\ ", ".join(["const " + type_string(p) + "& " + p.name if p.dim > 0 else type_string(p) + " " + p.name for p in func[2]]) + ") ;" else: code = func[1][0].type + " " + func.name + "(" +\ ", ".join([type_string(p) + " " + p.name for p in func[2]]) + ") ;" elif func.backend == "func_returns" and not func[1]: # if -ref, -reference flag option if node.project.builder.reference: code = "void " + func.name + "(" +\ ", ".join(["const " + type_string(p) + "& " + p.name if p.dim > 0 else type_string(p) + " " + p.name for p in func[2]]) + ") ;" else: code = "void " + func.name + "(" +\ ", ".join([type_string(p) + " " + p.name for p in func[2]]) + ") ;" elif func.backend == "func_returns" and func[1]: # if -ref, -reference flag option if node.project.builder.reference: code = "void " + func.name + "(" +\ ", ".join(["const " + type_string(p) + "& " + p.name if p.dim > 0 else type_string(p) + " " + p.name for p in func[2]]) + ", " +\ ", ".join([type_string(p) + "& " + p.name for p in func[1]]) + ") ;" else: code = "void " + func.name + "(" +\ ", ".join([type_string(p) + " " + p.name for p in func[2]]) + ", " +\ ", ".join([type_string(p) + "& " + p.name for p in func[1]]) + ") ;" return code Include = "%(name)s" def Funcs(node): text = "\n\n".join(map(str, node[:])) # text = add_indenting(text) # text = number_fix(text) text = re.sub(r"\n *(\n *)+", r"\n\n", text) # text = strip(text) return text def Inlines(node): if not node: return "" text = "\n\n".join([n.name for n in node]) text = """#ifndef MCONVERT_H #define MCONVERT_H namespace m2cpp { """ + text + """ } #endif """ # text = add_indenting(text) return text def Inline(node): return "" Structs = "", "\n\n", "" def Log(node): return "", "\n\n", "" def Error(node): cls = node.name.split(":")[0] return 'Error in class ' + cls + ''' on line %(line)d: %(code)s ''' + " "*node.cur + "^\n%(value)s" def Warning(node): cls = node.name.split(":")[0] return 'Warning in class ' + cls + ''' on line %(line)d: %(code)s ''' + " "*node.cur + "^\n%(value)s" def Struct(node): name = "_"+node.name.capitalize() out = "struct " + name + "\n{" declares = {} for child in node[:]: type = child.type if type == "func_lambda": type == "std::function" if type == "structs": continue if type not in declares: declares[type] = [] declares[type].append(child) for key, val in declares.items(): out = out + "\n" + key + " " + ", ".join([str(v) for v in val]) + " ;" out = out + "\n} ;" # out = add_indenting(out) return out if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/_reserved.py ================================================ """ Reserved translation rules See :py:attr:`rules.reserved ` for a collection of set of the various reserved words implemented into matlab2cpp. """ import matlab2cpp # List of function names that should be handled by reserved.py: reserved = { "and", "or", "not", "all", "any", "isequal", "false", "true", "pi", "inf", "Inf", "nan", "NaN", "eps", "exp", "log", "log2", "log10", "power", "floor", "ceil", "fix", "cos", "acos", "cosh", "acosh", "sin", "asin", "sinh", "asinh", "mod", "eye", "fliplr", "flipud", "length", "max", "min", "size", "chol", "trace", "transpose", "ctranspose", "abs", "sqrt", "nextpow2", "fft", "ifft", "fft2", "ifft2", "hankel", "zeros", "ones", "round", "return", "rand", "qr", "clear", "close", "clc", "clf", "more", "format", "_conv_to", "_reshape", "reshape", "interp1", "linspace", "varargin", "sum", "cumsum", "conj", "real", "imag", "tic", "toc", "diag", "tril", "triu", "disp", "fprintf", "error", "convmtx", "conv2", "figure", "clf", "cla", "show", "xlabel", "ylabel", "hold", "load", "title", "plot", "imshow", "imagesc", "wigb", "colorbar", "xlim", "ylim", "caxis", "axis", "grid", "subplot", "colormap", "_splot", "logspace", "find", "unique", "intersect", "isempty", "sortrows", } # Common attribute from .assign import Assign #Assign = "%(0)s = %(1)s ;" def Var(node): return "%(name)s" Var_pi = "datum::pi" Var_true = "1" Var_false = "0" Var_inf = "datum::inf" Var_Inf = "datum::inf" Var_nan = "datum::nan" Var_NaN = "datum::nan" Var_eps = "datum::eps" def Get_NaN(node): return "arma::zeros(", ", ", ") * datum::nan" def conv_to(str, type): return "arma::conv_to<" + type + ">::from(" + str + ")" def Assign_elemwise_(node): if node[0].type != node[1].type: if node[0].dim == 0 and node[1].dim == 0: return "%(0)s = " + node[0].type + "(%(1)s) ;" if node[0].dim == 0 and node[1].dim > 0: if node[0].mem != node[1].mem: return "%(0)s = " + node[0].type + "(arma::as_scalar(%(1)s)) ;" return "%(0)s = arma::as_scalar(%(1)s) ;" if node[0].mem != node[1].mem: return "%(0)s = arma::conv_to<" + node[0].type + ">::from(%(1)s) ;" return "%(0)s = %(1)s ;" def Get_exp(node): node.type = node[0].type # scalar done through std if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::exp(", ", ", ")" return "arma::exp(", ", ", ")" def Get_log(node): node.type = node[0].type # scalar done through std if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::log(", ", ", ")" return "arma::log(", ", ", ")" def Get_log2(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::log(%(0)s)/std::log(2)" #seems like it is a C++11 feature #return "std::log2(", ", ", ")" return "arma::log2(", ", ", ")" def Get_log10(node) : node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::log10(", ", ", ")" return "arma::log10(", ", ", ")" def Get_power(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::pow(", ", ", ")" return "arma::pow(", ", ", ")" def Get_floor(node): # unknown input #if node[0].type == "TYPE": # return "floor(", ", ", ")" node.type = node[0].type # scalar done through std if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::floor(", ", ", ")" return "arma::floor(", ", ", ")" def Get_ceil(node): node.type = node[0].type # scalar done through std if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::ceil(%(0)s)" return "arma::ceil(%(0)s)" def Get_round(node): node.type = node[0].type assert len(node)<3 # number of decimals to retain if len(node) == 2: decimals = str(node[1]) else: decimals = "0" # int and uword do not have decimals if node[0].mem < 2: return "%(0)s" # hack to cut-off for scalars if node[0].dim == 0: node.include("cmath") if decimals == "0": return "std::round(%(0)s)" return "std::round(%(0)s*std::pow(10, %(1)s))*std::pow(10, -%(1)s)" # hack for cut-off for array-type if decimals == "0": return "arma::round(%(0)s)" return "arma::round(%(0)s*std::pow(10, %(1)s))*std::pow(10, -%(1)s)" def Get_fix(node): node.type = node[0].type if node[0].mem < 2: return "%(0)s" if node[0].dim == 0 and node[0].mem != 4: node.include("mconvert") return "m2cpp::fix(%(0)s)" return "arma::trunc(%(0)s)" def Assign_fix(node): return Assign_elemwise_(node) def Get_cos(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::cos(", ", ", ")" return "arma::cos(", ", ", ")" def Get_acos(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::acos(", ", ", ")" return "arma::acos(", ", ", ")" def Get_cosh(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::cosh(", ", ", ")" return "arma::cosh(", ", ", ")" def Get_acosh(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") #is a C++11 feature return "std::acosh(", ", ", ")" return "arma::acosh(", ", ", ")" def Get_sin(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::sin(", ", ", ")" return "arma::sin(", ", ", ")" def Get_asin(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::asin", ", ", ")" return "arma::asin(", ", ", ")" def Get_sinh(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::sinh", ", ", ")" return "arma::sinh(", ", ", ")" def Get_asinh(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: node.include("cmath") return "std::asinh", ", ", ")" return "arma::asinh(", ", ", ")" # Special handle of 'i'-variable """ removed from reserved: "i", def Var_i(node): return "cx_double(0, 1)" """ def Get_mod(node): node.type = node[0].type if node[0].dim == 0 and node[0].mem != 4: return "", " __percent__ ", "" return "mod(", ", ", ")" def Get_abs(node): node.type = node[0].type if len(node) and node[0].dim == 0: if node[0].mem == 4: #cx_double node.include("m2cpp") return "abs(m2cpp::smat(%(0)s))" node.include("cmath") return "std::abs(", ", ", ")" return "abs(", ", ", ")" def Get_sqrt(node): node.type = node[0].type #if len(node) > 0 ... if len(node) and node[0].cls == "Neg" and len(node[0]) == 1: return "cx_double(0, " + node[0][0].str + ")" return "sqrt(", ", ", ")" def Get_and(node): return "(", "*", ")" def Get_or(node): return "("+"+".join(["%(" + i +")s*%(" + i + ")s" \ for i in range(len(node))])+")" def Get_not(node): assert len(node) == 1 if not node[0].num: return "not(%(0)s)" return "(%(0)s == 0)" def Get_any(node): if not node[0].num: return "any(", ", ", ")" # unknown datatype if node[0].dim == 0: return "%(0)s" return "any(", ", ", ")" def Get_all(node): # unkonwn datatype if not node[0].num: return "all(", ", ", ")" # scalar value if node[0].dim == 0: return "%(0)s" return "all(", ", ", ")" def Get_isequal(node): return "%(0)s == %(1)s" def Var_return(node): # multi-return does not return a value if node.func.backend == "func_returns": return "return" return "return " + str(node.func[0]) def Get_size(node): # unknown input if node[0].type == "TYPE" or node.parent.cls == "Assigns": return "size(", ", ", ")" var = str(node[0]) # multiple args if len(node) > 1: # determine ax from second arg arg2 = node[1].value if arg2 == "1": return var+".n_rows" if arg2 == "2": return var+".n_cols" if arg2 == "3": return var+".n_slices" # colvec or rowvec elif node[0].dim in (1,2): #if node.parent.backend == "reserved" and\ # node.parent.name in ("min", "max"): # return var+".n_elem" #if node[0].dim == 1: # return "%(0)s.n_elem, 1" #elif node[0].dim == 2: # return "1, %(0)s.n_elem" #return var+".n_elem" if node.parent.cls == "Get": if node.parent.backend in ("reserved", "func_return", "func_returns", "func_lambda", "unknown"): return "%(0)s.n_rows, %(0)s.n_cols" # inline calls moved to own line if node.parent.cls not in ("Statement", "Assign"): return str(node.auxiliary()) return "{%(0)s.n_rows, %(0)s.n_cols}" # matrix (returns two values) elif node[0].dim == 3: if node.parent.cls == "Get": if node.parent.backend in ("reserved", "func_return", "func_returns", "func_lambda", "unknown"): return "%(0)s.n_rows, %(0)s.n_cols" return "%(0)s.n_rows-1, %(0)s.n_cols" # inline calls moved to own line if node.parent.cls not in ("Statement", "Assign"): return str(node.auxiliary()) return "{%(0)s.n_rows, %(0)s.n_cols}" # cube (return three values) elif node[0].dim == 4: if node.parent.cls == "Get": if node.parent.backend in ("reserved", "func_return", "func_returns", "func_lambda", "unknown"): return "%(0)s.n_rows, %(0)s.n_cols, %(0)s.n_slices" return "%(0)s.n_rows-1, %(0)s.n_cols-1, %(0)s.n_slices" # inline calls moved to own line if node.parent.cls not in ("Statement", "Assign"): return str(node.auxiliary()) return "{%(0)s.n_rows, %(0)s.n_cols, %(0)s.n_slices}" return "size(", ", ", ")" def Assign_size(node): num = node[-1][0].dim == 3 and "2" or "2" if len(node[-1]) == 2 and (node[-1][1].str == "1" or node[-1][1].str == "2"): return "%(0)s = %(1)s ;" else: return "uword _%(0)s [] = %(1)s ;\n"+\ "%(0)s = urowvec(_%(0)s, " + num + ", false) ;" def Assigns_size(node): val = node[-1][0] # multi-assigned size to own line if val.cls != "Var": val = val.auxiliary() val = str(val) # suggest some types for matrix out = "" if node[0].str != '~': out += "%(0)s = " + val + ".n_rows;\n" if node[1].str != '~': out += "%(1)s = " + val + ".n_cols;\n" if len(node) == 4 and node[2] != '~': out += "%(2)s = " + val + ".n_slices;\n" #if len(node)==3: # return # return "%(0)s = " +val+ ".n_rows ;\n%(1)s = " +val+ ".n_cols ;" if not (len(node) == 3 or len(node) == 4): raise NotImplementedError return out # suggest some types for cube #if len(node)==4: # return "%(0)s = "+val+".n_rows ;\n"+\ # "%(1)s = "+val+".n_cols ;\n"+\ # "%(2)s = "+val+".n_slices ;" def Get_chol(node): return "", ", ", "" def Assign_chol(node): lhs, rhs = node my_list = [] for n in rhs: my_list.append(n.str) my_string = ", ".join(my_list) return "%(0)s = chol(" + my_string + ") ;" def Assigns_chol(node): lhs = node[:-1] rhs = node[-1] if len(lhs) == 2: rhs_string = lhs[0].str + ", " + rhs.str # flip p bool value, to get same value as matlab ret_bool = lhs[1].str + " = !" + lhs[1].str + " ;" return lhs[1].str + " = chol(" + rhs_string + ") ;\n" + ret_bool # Default out. If more lhs values, then write new if statement lhs_list = [n.str for n in lhs] rhs_list = [n.str for n in rhs] lhs_string = ", ".join(lhs_list) rhs_string = ", ".join(rhs_list) return "[" + lhs_string + "]" + " = chol(" + rhs_string + ") ;" def Get_unique(node): node.include("m2cpp") return "", ", ", "" def Assign_unique(node): node.include("m2cpp") args = ", ".join([n.str for n in node]) return "m2cpp::unique(" + args + ");" def Assigns_unique(node): node.include("m2cpp") args = ", ".join([n.str for n in node]) return "m2cpp::unique(" + args + ");" def Get_sortrows(node): node.include("m2cpp") args = ", ".join([n.str for n in node]) return "m2cpp::sortrows(" + args + ");" def Get_intersect(node): node.include("m2cpp") return "", ", ", "" def Assign_intersect(node): node.include("m2cpp") lhs, rhs = node my_list = [] for n in rhs: my_list.append(n.str) my_string = ", ".join(my_list) return "%(0)s = m2cpp::intersect(" + my_string + ") ;" def Assigns_intersect(node): node.include("m2cpp") rhs = ", ".join([n.str for n in node]) return "m2cpp::intersect(" + rhs + ");" def Get_length(node): # array-type uses n_elem if node.cls == "Var": return "%(0)s.n_elem" node.include("m2cpp") return "m2cpp::length(%(0)s)" def Get_isempty(node): node.include("m2cpp") return "m2cpp::isempty(%(0)s)" def Get_min(node): # non.numerical input if not all([n.num for n in node]): return "min(", ", ", ")" # everything scalar if all([(n.dim < 1) for n in node]): if any([n.mem == 4 for n in node]): node.include("m2cpp") nodes = map(str, node) nodes = ["m2cpp::smat(" + name + ")" for name in nodes] arg = ", ".join(nodes) return "arma::min(" + arg + ")" node.include("algorithm") return "std::min(", ", ", ")" # single arg if len(node) == 1: return "arma::min(%(0)s)" # two args if len(node) == 2: if node[0].dim and node[1].dim: return "arma::min(%(0)s, %(1)s)" if node[0].dim==0: return "arma::clamp(%(1)s, %(1)s.min(), %(0)s)" if node[1].dim==0: return "arma::clamp(%(0)s, %(0)s.min(), %(1)s)" # three args if len(node) == 3: if node[2].dim == 0: # assues third arg is int and sets axis val = node[2].value if val == "1": return "arma::min(%(0)s, 1)" elif val == "2": return "arma::min(%(0)s, 0)" return "arma::min(%(0)s, %(2)s-1)" assert False def Assigns_min(node): assert len(node) == 3 var = node[2][0] # non-numerical assignment if not var.num: return "[", ", ", "] = min(", ") ;" # multi-assignmens on own line if var.cls != "Var": var = var.auxiliary() var = str(var) if node[0].str != '~': return "%(0)s = " + var + ".min(%(1)s) ;" else: return var + ".min(%(1)s);" def Get_max(node): # non.numerical input if not all([n.num for n in node]): return "max(", ", ", ")" # everything scalar if all([(n.dim<1) for n in node]): if any([n.mem == 4 for n in node]): node.include("m2cpp") nodes = map(str, node) nodes = ["m2cpp::smat(" + name + ")" for name in nodes] arg = ", ".join(nodes) return "arma::max(" + arg + ")" # single element, uword (returned from size(a)) #if len(node) == 1 and node[0].dim == 0: # return "%(0)s" node.include("algorithm") return "std::max(", ", ", ")" # number of args is ... if len(node) == 1: return "arma::max(%(0)s)" if len(node) == 2: if node[0].dim and node[1].dim: return "arma::max(%(0)s, %(1)s)" if node[0].dim==0: return "arma::clamp(%(1)s, %(0)s, %(1)s.max())" if node[1].dim==0: return "arma::clamp(%(0)s, %(1)s, %(0)s.max())" if len(node) == 3: if node[2].dim == 0: # thrid argument sets axis to take max over val = node[2]["value"] if val == "1": return "arma::max(%(0)s, 1)" elif val == "2": return "arma::max(%(0)s, 0)" return "arma::max(%(0)s, %(2)s-1)" assert False def Assigns_max(node): assert len(node) == 3 # right hand side of assignment var = node[-1] # non-numerical input if not var.num: return "[", ", ", "] = max(", ") ;" # multi-assign max on own line if var.cls != "Var": var = var.auxiliary() var = str(var) if node[0].str != '~': return "%(0)s = " + var + ".max(%(1)s);" else: var + ".max(%(1)s);" Var_eye = "1" def Get_eye(node): # not numerical input if not node[0].num: return "eye(", ", ", ")" # single argument constructor if len(node) == 1: if node[0].dim == 0: return "arma::eye<%(type)s>(%(0)s, %(0)s)" return "arma::eye<%(type)s>(%(0)s(0), %(0)s(1))" # double arguments if len(node) == 2: return "arma::eye<%(type)s>(%(0)s, %(1)s)" raise NotImplementedError def Get_trace(node): return "arma::trace(", ", ", ")" def Get_transpose(node): """Simple transpose """ return "arma::strans(%(0)s)" def Get_ctranspose(node): """Complex transpose """ return "arma::trans(%(0)s)" def Get_fliplr(node): return "arma::fliplr(%(0)s)" def Get_flipud(node): return "arma::flipud(%(0)s)" def Get_ones(node): # one argument if len(node) == 1: #size as argument: zeros(size(A)) if node[0].backend == "reserved" and node[0].name == "size": #Take the type of the LHS, normally it is the other way around if node.parent.cls == "Assign" and node.parent[0] != node: out = "arma::ones<" + node.parent[0].type + ">(" return out, ", ", ")" #return "arma::ones<%(type)s>(", ", ", ")" #arg input is a scalar if node[0].num and node[0].dim == 0: #dim is matrix if node.dim == 3: return "arma::ones<%(type)s>(%(0)s, %(0)s)" # arg input is vector if node[0].num and node[0].dim in (1,2): # non-trivial variables moved out to own line if node[0].cls != "Var": node[0].auxiliary() # indexing arg as input if node.dim in (1,2): return "arma::ones<%(type)s>(%(0)s(0))" if node.dim == 3: return "arma::ones<%(type)s>(%(0)s(0), %(0)s(1))" if node.dim == 4: return "arma::ones<%(type)s>(%(0)s(0), %(0)s(1), %(0)s(2))" # two args where one vector and other "1" handled specially elif len(node) == 2: if node.dim == 3: return "arma::ones<%(type)s>(%(0)s, %(1)s)" if node.dim in (1,2): if node[0].cls == "Int" and node[0].value == "1": return "arma::ones<%(type)s>(%(1)s)" if node[1].cls == "Int" and node[1].value == "1": return "arma::ones<%(type)s>(%(0)s)" return "arma::ones<%(type)s>(", ", ", ")" def Get_zeros(node): # one argument if len(node) == 1: #size as argument: zeros(size(A)) if node[0].backend == "reserved" and node[0].name == "size": #Take the type of the LHS, normally it is the other way around if node.parent.cls == "Assign" and node.parent[0] != node: out = "arma::zeros<" + node.parent[0].type + ">(" return out, ", ", ")" #return "arma::zeros<%(type)s>(", ", ", ")" #arg input is a scalar if node[0].num and node[0].dim == 0: #dim is matrix if node.dim == 3: return "arma::zeros<%(type)s>(%(0)s, %(0)s)" # arg input is vector if node[0].num and node[0].dim in (1,2): # non-trivial variables moved out to own line if node[0].cls != "Var": node[0].auxiliary() # indexing arg as input if vector if node.dim in (1,2): return "arma::zeros<%(type)s>(%(0)s(0))" if node.dim == 3: return "arma::zeros<%(type)s>(%(0)s(0), %(0)s(1))" if node.dim == 4: return "arma::zeros<%(type)s>(%(0)s(0), %(0)s(1), %(0)s(2))" # double argument creates colvec/rowvec/matrix depending on context elif len(node) == 2: if node.dim == 3: return "arma::zeros<%(type)s>(%(0)s, %(1)s)" if node.dim in (1,2): # use colvec if first index is '1' if node[0].cls == "Int" and node[0].value == "1": return "arma::zeros<%(type)s>(%(1)s)" # use rowvec if second index is '1' elif node[1].cls == "Int" and node[1].value == "1": return "arma::zeros<%(type)s>(%(0)s)" return "arma::zeros<%(type)s>(", ", ", ")" def Get_qr(node): return "", ", ", "" def Assign_qr(node): return "arma::qr(", ", ", ")" def Assigns_qr(node): return "arma::qr(", ", ", ")" def Var_rand(node): return "arma::randu<" + node.type + ">(1)" def Get_rand(node): return "arma::randu<" + node.type + ">(", ", ", ")" def Var_clear(node): #index = node.parent.children.index(node) #print(node.parent.parent.names) #del node.parent.parent.children[0] #print(node.parent.summary()) #return "" return "// clear" def Get_clear(node): return "// clear(", ", ", ")" def Var_close(node): return "// close" def Get_close(node): return "// close(", ", ", ")" def Var_clc(node): return "// clc" def Get_clc(node): return "// clc" def Var_clf(node): return "// clf" def Get_clf(node): return "// clf" def Var_more(node): return "// more" def Get_more(node): return "// more" def Var_format(node): return "// format" def Get_format(node): return "// format" def Get__conv_to(node): return "conv_to<%(type)s>::from(%(0)s)" def Get__reshape(node): return "%(value)s(", ", ", ")" def Get_reshape(node): return "reshape(", ", ", ")" def Get_nextpow2(node): node.include("m2cpp") return "m2cpp::nextpow2(", ", ", ")" def Assign_fft(node): if node[0].mem == 3: return "%(0)s = arma::conv_to<" + node[0].type + ">::from(%(1)s) ;" return "%(0)s = %(1)s ;" def Assign_ifft(node): return Assign_fft(node) def Get_fft(node): if node[0].mem != None: node.type = node[0].type # arma & matlab fft same for n_args in (1,2) if len(node) in (1, 2): return "arma::fft(", ", ", ")" elif len(node) == 3: if node[0].dim in (1,2): return "arma::fft(%(0)s, %(1)s)" if node[1].cls == "Matrix": node.include("m2cpp") return "m2cpp::fft<" + node[0].type + ">(%(0)s, %(2)s)" else: node.include("m2cpp") return "m2cpp::fft<" + node[0].type + ">(", ", ", ")" else: node.error("Number of args in 'fft' should be between 1 and 3") return "arma::fft(", ", ", ")" def Get_ifft(node): if node[0].mem != None: node.type = node[0].type # unknown input if not node.num: return "arma::ifft(", ", ", ")" if len(node) == 1: return "arma::ifft<%(type)s>(%(0)s)" elif len(node) == 2: return "arma::ifft<%(type)s>(%(0)s, %(1)s)" elif len(node) == 3: if node[0].dim in (1, 2): return "arma::ifft<%(type)s>(%(0)s, %(1)s)" if node[1].cls == "Matrix": node.include("m2cpp") return "m2cpp::ifft<%(type)s>(%(0)s, %(2)s)" else: node.include("m2cpp") return "m2cpp::ifft<%(type)s>(", ", ", ")" else: node.error("Number of args in 'ifft' should be between 1 and 3") if node[0].mem != 4: node.warning("Argument datatype of 'ifft' should be complex") return "arma::ifft(", ", ", ")" def Get_fft2(node): if node[0].mem != None: node.type = node[0].type return "arma::fft2(", ", ", ")" def Get_ifft2(node): if node[0].mem != None: node.type = node[0].type return "arma::ifft2(", ", ", ")" def Assign_fft2(node): return Assign_fft(node) def Assign_ifft2(node): return Assign_fft(node) def Get_hankel(node): node.include("m2cpp") return "m2cpp::hankel(", ", ", ")" def Get_interp1(node): if node.parent.cls == "Assign": out = "arma::interp1(%(0)s, %(1)s, %(2)s, " + node.parent[0].str if node[-1].type == "string": out = out + ", %(3)s)" else: out = out + ")" else: out = "arma::interp1(", ", ", ")" return out def Assign_interp1(node): return "%(1)s ;" def Get_linspace(node): return "arma::linspace<%(type)s>(", ", ", ")" def Get_sum(node): """ Summation function. Examples: >>> print(matlab2cpp.qscript("a=[1,2]; b = sum(a)", suggest=True)) sword _a [] = {1, 2} ; a = irowvec(_a, 2, false) ; b = int(arma::as_scalar(arma::sum(a))) ; >>> print(matlab2cpp.qscript("a=[1.5;2]; b = sum(a)", suggest=True)) double _a [] = {1.5, 2} ; a = vec(_a, 2, false) ; b = double(arma::as_scalar(arma::sum(a))) ; >>> print(matlab2cpp.qscript("a=[-1,2;3,4]; b = sum(a, 1)", suggest=True)) sword _a [] = {-1, 2, 3, 4} ; a = arma::strans(imat(_a, 2, 2, false)) ; b = arma::sum(arma:vectorize(a), 0) ; >>> print(matlab2cpp.qscript("a=[1., 2.; 3., 4.]; b = sum(a(:))", suggest=True)) double _a [] = {1., 2., 3., 4.} ; a = arma::strans(mat(_a, 2, 2, false)) ; b = double(arma::as_scalar(arma::sum(arma:vectorize(a(span(0, a.n_rows-1)))))) ; >>> print(matlab2cpp.qscript("a=rand(9, 9, 9); b = sum(a(:))", suggest=True)) a = arma::randu(9, 9, 9) ; b = double(arma::as_scalar(arma::sum(arma:vectorize(a(span(0, a.n_rows-1)))))) ; """ arg = node[0] # unknown input if not arg.num or arg.dim == 0: node.error("sum over non-array") return "arma::sum(", ", ", ")" if arg.dim > 2: arg = "arma:vectorize(%(0)s)" else: arg = "%(0)s" # second argument should be dim, matlab uses dim 1/2, and armadillo 0/1 if len(node) == 1: node.dim = 0 out = "%(type)s(arma::as_scalar(arma::sum(" + arg + ")))" elif len(node) == 2: out = "arma::sum(" + arg + ", %(1)s-1)" else: out = "arma::sum(", ", ", ")" return out def Get_cumsum(node): if len(node) < 2: return "cumsum(", ", ", ")" elif len(node) == 2: return "cumsum(%(0)s, %(1)s-1)" else: return "cumsum(", ", ", ")" def Get_conj(node): if node.dim == 0: if node.mem == 4: node.include("complex") return "std::conj(", ", ", ")" else: return "%(0)s" return "arma::conj(", ", ", ")" def Get_imag(node): return "arma::imag(", ", ", ")" def Get_real(node): # output always real return "arma::real(", ", ", ")" def Var_tic(node): return Get_tic(node) def Get_tic(node): node.wall_clock() node.type = 'double' return "m2cpp::tic()" def Assign_tic(node): node.wall_clock() node[0].type = 'double' return "%(0)s = m2cpp::tic();" def Var_toc(node): return Get_toc(node) def Get_toc(node): node.wall_clock() arg = ", ".join([n.str for n in node]) if node.parent.cls != "Statement": return "m2cpp::toc(" + arg + ")" node.include("iostream") return 'std::cout << "Elapsed time = " << m2cpp::toc(' + arg + ') << std::endl' def Get_diag(node): if node.dim == 3: return "diagmat(", ", ", ")" return "diagvec(", ", ", ")" def Get_tril(node): return "trimatl(", ", ", ")" def Get_triu(node): return "trimatu(", ", ", ")" def Var_disp(node): return "// disp" def Get_disp(node): node.include("iostream") if len(node) == 1: arg = node[0] if not arg.num or arg.dim == 0: return "std::cout << %(0)s << std::endl" else: return "%(0)s.print()" else: node.error("disp should take one argument") return "std::cout << ", "<< ", " << std::endl" def Get_fprintf(node): """ Matlab's fprintf can write to file and screen... this is translated to printf (so only printing to screen) will give error if trying to write to file. """ node.include("cstdio") return "std::printf(", ", ", ")" def Get_error(node): node.include("iostream") return "std::cerr << ", "<< ", " << std::endl" def Get_convmtx(node): node.include("m2cpp") return "m2cpp::convmtx(", ", ", ")" def Get_conv2(node): node.include("m2cpp") return "m2cpp::conv2<%(type)s>(", ", ", ")" #return "m2cpp::conv2<" + node[0].type + "," + node[1].type + ">(", ", ", ")" #SPlot reserved words def Get_figure(node): node.plotting() return "_plot.figure(", ", ", ")" def Var_hold(node): return Get_hold(node) def Get_hold(node): node.plotting() if node and node[0].cls == "String": if node[0].value == "on": return "_plot.hold(1)" if node[0].value == "off": return "_plot.hold(0)" node.error('argument must either be "on" or "off"') return "_plot.hold(", ", ", ")" node.error("hold toggle not supported") return "_plot.hold(", ", ", ")" def Var_load(node): return Get_load(node) def Get_load(node): out = "load " + node.code if len(node) == 1: if node[0].cls == "String": name = str(node[0].value).split(".")[0] out = name + ".load(%(0)s)" else: out = "%(0)s.load(\"" + node.value + "\")" return out def Var_clf(node): return Get_clf(node) def Get_clf(node): node.plotting() return "_plot.clf(", ", ", ")" def Var_cla(node): return Get_cla(node) def Get_cla(node): node.plotting() return "_plot.cla(", ", ", ")" def Get_show(node): node.plotting() return "_plot.show(", ", ", ")" def Get_xlabel(node): node.plotting() return "_plot.xlabel(", ", ", ")" def Get_ylabel(node): node.plotting() return "_plot.ylabel(", ", ", ")" def Get_title(node): node.plotting() return "_plot.title(", ", ", ")" def Get_plot(node): node.plotting() if len(node) > 2: state = True num_children = len(node) cur_child = 0 out = "" while state: #if cur_child > 0 and cur_child < num_children: # out += "\n" #elif cur_child >= num_children: # break out += "_plot.plot(" #add next two childen if (cur_child+2) <= num_children: out += "%(" + str(cur_child) + ")s, " cur_child += 1 out += "%(" + str(cur_child) + ")s" cur_child += 1 #test if linespec if cur_child < num_children and node[cur_child].type == "string": out += ", %(" + str(cur_child) + ")s" cur_child += 1 elif cur_child < num_children and node[cur_child].type != "string": out += "" elif cur_child >= num_children: out += "" #add next two children if (cur_child+2) <= num_children: out += ", %(" + str(cur_child) + ")s, " cur_child += 1 out += "%(" + str(cur_child) + ")s" cur_child += 1 #test if linespec if cur_child < num_children and node[cur_child].type == "string": out += ", %(" + str(cur_child) + ")s" cur_child += 1 elif cur_child < num_children and node[cur_child].type != "string": out += "" elif cur_child >= num_children: out += "" if (cur_child+1) <= num_children: out += ") ;\n" else: out += ")" state = False return out return "_plot.plot(", ", ", ")" def Get_imshow(node): node.plotting() return "_plot.imshow(", ", ", ")" def Get_imagesc(node): node.plotting() return "_plot.imagesc(", ", ", ")" def Get_wigb(node): node.plotting() return "_plot.wigb(", ", ", ")" def Var_colorbar(node): return Get_colorbar(node) def Get_colorbar(node): node.plotting() return "_plot.colorbar(", ", ", ")" def Get_xlim(node): """ Examples: >>> print(matlab2cpp.qscript("xlim(0.0, 3.14)")) _plot.xlim(0.0, 3.14) ; _plot.show() ; >>> print(matlab2cpp.qscript("xlim([0.0, 3.14])")) _plot.xlim(0.0, 3.14) ; _plot.show() ; """ node.plotting() if len(node) == 1: arg = node[0] if arg.cls == "Matrix" and len(arg[0]) == 2: a,b = arg[0] return "_plot.xlim(" + str(a) + ", " + str(b) + ")" elif arg.cls != "Matrix" and arg.num and arg.dim>0: name1 = arg.name + "(0)" name2 = arg.name + "(1)" if arg.mem not in (2,3): name1 = "static_cast(" + name1 + ")" name2 = "static_cast(" + name2 + ")" return "_plot.xlim(" + name1 + ", " + name2 + ")" node.error("argument array type") return "_plot.xlim(", ", ", ")" def Get_ylim(node): """ Examples: >>> print(matlab2cpp.qscript("ylim(0.5,.7)")) _plot.ylim(0.5, 0.7) ; _plot.show() ; >>> print(matlab2cpp.qscript("ylim([0.5,.7])")) _plot.ylim(0.5, 0.7) ; _plot.show() ; """ node.plotting() if len(node) == 1: arg = node[0] if arg.cls == "Matrix" and len(arg[0]) == 2: a,b = arg[0] return "_plot.ylim(" + str(a) + ", " + str(b) + ")" elif arg.cls != "Matrix" and arg.num and arg.dim>0: name1 = arg.name + "(0)" name2 = arg.name + "(1)" if arg.mem not in (2,3): name1 = "static_cast(" + name1 + ")" name2 = "static_cast(" + name2 + ")" return "_plot.ylim(" + name1 + ", " + name2 + ")" node.error("argument array type") return "_plot.ylim(", ", ", ")" def Get_caxis(node): """ >>> print(matlab2cpp.qscript("caxis(0, 3)")) _plot.caxis(0, 3) ; _plot.show() ; >>> print(matlab2cpp.qscript("caxis([0, 3])")) _plot.caxis(0, 3) ; _plot.show() ; """ node.plotting() if len(node) == 1: arg = node[0] if arg.cls == "Matrix" and len(arg[0]) == 2: a,b = arg[0] return "_plot.caxis(" + str(a) + ", " + str(b) + ")" elif arg.cls != "Matrix" and arg.num and arg.dim>0: name1 = arg.name + "(0)" name2 = arg.name + "(1)" if arg.mem not in (2,3): name1 = "static_cast(" + name1 + ")" name2 = "static_cast(" + name2 + ")" return "_plot.caxis(" + name1 + ", " + name2 + ")" node.error("argument array type") return "_plot.caxis(", ", ", ")" def Get_axis(node): """ >>> print(matlab2cpp.qscript("axis(0, 3, -2, 4)")) _plot.axis(0, 3, -2, 4) ; _plot.show() ; >>> print(matlab2cpp.qscript("axis([0, 3, -2, 4])")) _plot.axis(0, 3, -2, 4) ; _plot.show() ; """ node.plotting() if len(node) == 1: arg = node[0] if arg.cls == "Matrix" and len(arg[0]) == 4: a,b,c,d = arg[0] return "_plot.axis(" + str(a) + ", " + str(b) + ", " + str(c) + ", " + str(d) + ")" elif arg.cls != "Matrix" and arg.num and arg.dim>0: name1 = arg.name + "(0)"; name2 = arg.name + "(1)" name3 = arg.name + "(2)"; name4 = arg.name + "(3)" if arg.mem not in (2,3): name1 = "static_cast(" + name1 + ")" name2 = "static_cast(" + name2 + ")" name3 = "static_cast(" + name3 + ")" name4 = "static_cast(" + name4 + ")" return "_plot.axis(" + name1 + ", " + name2 + ", " + name3 + ", " + name4 + ")" node.error("argument array type") return "_plot.axis(", ", ", ")" def Var_grid(node): return Get_grid(node) def Get_grid(node): node.plotting() if node and node[0].cls == "String": if node[0].value == "on": return "_plot.grid({{\"b\", \"on\"}})" if node[0].value == "off": return "_plot.grid({{\"b\", \"off\"}})" node.error('argument must either be "on" or "off"') return "_plot.grid(", ", ", ")" return "_plot.grid(", ", ", ")" def Get_subplot(node): node.plotting() return "_plot.subplot(", ", ", ")" def Get_colormap(node): node.plotting() if len(node) == 1: arg = node[0] if len(arg) == 0: name = str(arg)[:-2] + "(1)" return "_plot.colormap(" + name + ")" return "_plot.colormap(", ", ", ")" def Get__splot(node): return "_plot.show()" def Get_logspace(node): if len(node) == 2: return "logspace<%(type)s>(%(0)s, %(1)s, 50)" if len(node) == 3: return "logspace<%(type)s>(%(0)s, %(1)s, %(2)s)" return "logspace<%(type)s>(", ", ", ")" def Get_find(node): return "find(", ", ", ") + 1" if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/_rowvec.py ================================================ from .assign import Assign from .variables import * from .rowvec import Get, Set Declare = "rowvec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_size_t.py ================================================ from .assign import Assign from .variables import * Size_t = "%(value)s" ================================================ FILE: src/matlab2cpp/rules/_string.py ================================================ from .assign import Assign from .variables import * Declare = "string %(name)s ;" def String(node): if node.name or node.parent.backend != "matrix": return '"%(value)s"' else: return 'std::string("%(value)s")' ================================================ FILE: src/matlab2cpp/rules/_struct.py ================================================ from .assign import Assign from .variables import * #def Fvar(node): #defined in variables.py # return "%(name)s.%(value)s" Declare = "struct %(name)s" def Matrix(node): return "", ", ", "" ================================================ FILE: src/matlab2cpp/rules/_structs.py ================================================ #from assign import Assign from .variables import * import matlab2cpp Declare = "struct %(name)s" def Counter(node): return "%(name)s = %(value)s" #def Fvar(node): #defined in variables.py # return "%(name)s.%(value)s" def Fget(node): pass def Fset(node): return "%(name)s.%(value)s[", ", ", "-1]" def Matrix(node): if node.backend == "structs": if node[0].cls == "Vector": if len(node[0]) == 1: return "%(0)s" return "[", ", ", "]" """def Assign(node): lhs, rhs = node print('here') # assign a my_var = [a.val], a is a structs, my_var should be a vec if node[1].cls == "Matrix" and node[1].backend == "structs": element = rhs[0][0] if element.backend == "structs": size = rhs.str var = lhs.name name = element.name value = element.value declares = node.func[0] if "_i" not in declares: declare = matlab2cpp.collection.Var(declares, "_i") declare.type = "int" declare.backend = "int" declares.translate() string = var + ".resize(" + size + ") ;\n" +\ "for (_i=0; _i<" + size + "; ++_i)\n "+\ var + "[_i] = " + name + "[_i]." + value + " ;" return string return "%(0)s = %(1)s""""" ================================================ FILE: src/matlab2cpp/rules/_ucube.py ================================================ from .assign import Assign from .variables import * from .cube import Get, Set, Resize Declare = "ucube = %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_umat.py ================================================ from .assign import Assign from .variables import * from .mat import Get, Set Declare = "umat %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_unknown.py ================================================ from .assign import Assign from .variables import * from . import armadillo as arma Declare = "TYPE %(name)s ;" def Assigns(node): lhs = map(str, node[:-1]) lhs = "[" + ", ".join(lhs) + "]" rhs = str(node[-1]) return lhs + " = " + rhs + " ;" def Matrix(node): return "", ", ", "" def Get(node): if len(node) == 2: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) # All + All if node[0].cls == node[1].cls == "All": return "%(name)s" # All + ... if node[0].cls == "All": # All + uvec if dim1: return "%(name)s.cols(" + arg1 + ")" # All + scalar return "%(name)s.col(" + arg1 + ")" # ... + All elif node[1].cls == "All": # uvec + All if dim0: return "%(name)s.rows(" + arg0 + ")" # scalar + All return "%(name)s.row(" + arg0 + ")" return "%(name)s(", ", ", ")" def Set(node): node.error("unknown data type") # Double argument if len(node) == 2: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) # All + All if node[0].cls == node[1].cls == "All": return "%(name)s" # All + ... if node[0].cls == "All": # All + uvec if dim1: return "%(name)s.cols(" + arg1 + ")" # All + scalar return "%(name)s.col(" + arg1 + ")" # ... + All elif node[1].cls == "All": # uvec + All if dim0: return "%(name)s.rows(" + arg0 + ")" # scalar + All return "%(name)s.row(" + arg0 + ")" # scalar + uvec if dim0 == 0 and dim1 > 0: #arg0 = "m2cpp::asuvec(" + arg0 + ")" return "%(name)s.col(" + arg0 + ").rows(" + arg1 + ")" # uvec + scalar elif dim0 > 0 and dim1 == 0: #arg1 = "m2cpp::asuvec(" + arg1 + ")" return "%(name)s.row(" + arg0 + ").cols(" + arg1 + ")" return "%(name)s(" + arg0 + ", " + arg1 + ")" return "%(name)s(", ", ", ")" ================================================ FILE: src/matlab2cpp/rules/_urowvec.py ================================================ from .assign import Assign from .variables import * from .rowvec import Get, Set Declare = "urowvec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_uvec.py ================================================ from .assign import Assign from .variables import * from .vec import Get, Set Declare = "uvec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_uword.py ================================================ from .assign import Assign from .variables import * Declare = "uword %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_vec.py ================================================ from .assign import Assign from .variables import * from .vec import Get, Set Declare = "vec %(name)s ;" ================================================ FILE: src/matlab2cpp/rules/_verbatim.py ================================================ Verbatim = "// %(name)s\n%(value)s" ================================================ FILE: src/matlab2cpp/rules/armadillo.py ================================================ """ filler for Armadillo """ import matlab2cpp def configure_arg(node, index): """ Configure an argument of an vector, matrix or cube. Args: node (Get, Set): Current possition in node-tree index (int): argument index (starting from 0) Returns: tuple: A string representation of argument and an index (-1,0,1) indicating if the argument was unknown, scalar or a vector, respectively. Examples: >>> print(matlab2cpp.qscript('x=[1,2]; x(:)')) sword _x [] = {1, 2} ; x = irowvec(_x, 2, false) ; x(span(0, x.n_rows-1)) ; >>> print(matlab2cpp.qscript('x=[1,2]; x(1)')) sword _x [] = {1, 2} ; x = irowvec(_x, 2, false) ; x(0) ; >>> print(matlab2cpp.qscript('x=[1,2]; x([1,2])')) sword _x [] = {1, 2} ; x = irowvec(_x, 2, false) ; uword __aux_urowvec_1 [] = {1, 2} ; _aux_urowvec_1 = urowvec(__aux_urowvec_1, 2, false) ; x(arma::strans(_aux_urowvec_1)-1) ; >>> print(matlab2cpp.qscript('x=[1,2]; x([1,2;2,1])')) sword _x [] = {1, 2} ; x = irowvec(_x, 2, false) ; uword __aux_umat_1 [] = {1, 2, 2, 1} ; _aux_umat_1 = arma::strans(umat(__aux_umat_1, 2, 2, false)) ; x(_aux_umat_1-1) ; >>> print(matlab2cpp.qscript("x=[1,2]; x(x')")) sword _x [] = {1, 2} ; x = irowvec(_x, 2, false) ; x(arma::trans(x)-1) ; """ out = "%(" + str(index) + ")s" # the full range ':' if node.cls == "All": arg = node.parent.name # first axis if index == 0: # axis and dim does not match for colvec and rowvec if node.parent.dim == 1: arg += ".n_cols" else: arg += ".n_rows" # second axis elif index == 1: arg += ".n_cols" # third axis elif index == 2: arg += ".n_slices" return "span(0, " + arg + "-1)", 1 # undefined type elif node.type == "TYPE": return out, -1 # float point scalar elif node.mem > 1 and node.dim == 0: out = "(uword) " + out #Float or double, vec or rowvec """ elif node.mem not in (0, 4) and node.dim in (1, 2): out = "arma::conv_to::from(" + out + ")" """ # rowvec (-> colvec) elif node.dim == 2: out = "arma::strans(" + out + ")" # scalar done verbatim if node.dim == 0: if node.cls == "Int": out = str(int(node.value)-1) elif node.cls == "Float": out = str(float(node.value)-1) else: out = out + "-1" dim = 0 # matrices and cubes elif node.dim > 2: dim = node.dim out = out + "-1" else: dim = 1 if len(node) > 0 and node[0].cls == "Paren": pass elif node.cls not in ["Colon", "Paren"]: out = out + "-1" return out, dim def scalar_assign(node): """ convert scalar to various array types """ # left-hand-side and right-hand-side lhs, rhs = node if lhs.mem < rhs.mem: node.warning("Type reduction from %s to %s" % (rhs.type, lhs.type)) # matrix suround are ignored if rhs.cls == "Matrix": rhs = str(rhs[0][0]) else: rhs = "%(1)s" if lhs.cls == "Set": return "%(0)s.fill(" + rhs + ") ;" #Earlier stuff, replaced with code right above """ if lhs.dim == 0: pass # as colvec elif lhs.dim == 1: node.include("m2cpp") rhs = "m2cpp::scol(" + rhs + ")" # as rowvec elif lhs.dim == 2: node.include("m2cpp") rhs = "m2cpp::srow(" + rhs + ")" # as matrix elif lhs.dim == 3: node.include("m2cpp") rhs = "m2cpp::smat(" + rhs + ")" # as cube elif lhs.dim == 4: #scube is not implemented in mconvert.h, namespace m2cpp at the moment node.include("m2cpp") rhs = "m2cpp::scube(" + rhs + ")" """ return "%(0)s = " + rhs + " ;" def include(node): """Add armadillo to header""" program = node.program includes = program[0] arma = "#include " namespace = "using namespace arma ;" if arma in includes and namespace in includes: return if arma not in includes: include = matlab2cpp.collection.Include(includes, arma, value=includes.value) include.backend = "program" if namespace not in includes: include = matlab2cpp.collection.Include(includes, namespace, value=includes.value) include.backend = "program" includes.translate() if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/assign.py ================================================ import matlab2cpp from . import armadillo as arma def Assign(node): """ Assignment (General case) Args: node (Assign): Current position in node-tree. Returns: str : Translation of current node. Examples: >>> print(matlab2cpp.qscript("a = b")) a = b ; >>> print(matlab2cpp.qscript("a=[1,2]; b=[1;2]; a=b")) sword _a [] = {1, 2} ; a = irowvec(_a, 2, false) ; sword _b [] = {1, 2} ; b = ivec(_b, 2, false) ; a = arma::strans(b) ; >>> print(matlab2cpp.qscript("a=[1,2,2,1]; b=[2,1;1,2]; a=b")) sword _a [] = {1, 2, 2, 1} ; a = irowvec(_a, 4, false) ; sword _b [] = {2, 1, 1, 2} ; b = arma::strans(imat(_b, 2, 2, false)) ; a = b ; """ # left-hand-side and right-hand-side lhs, rhs = node # unknown datatype if "TYPE" in (lhs.type, rhs.type) or lhs.type == rhs.type: return "%(0)s = %(1)s ;" # numerical if lhs.num and rhs.num: # mismatch between colvec and rowvec, do transpose if (lhs.dim == 2 and rhs.dim == 1) or\ (lhs.dim == 1 and rhs.dim == 2): #or lhs.mem != rhs.mem: #out = "arma::conv_to<" + lhs.type + ">::from(%(1)s)" out = "arma::strans(%(1)s)" else: out = "%(1)s" # both scalar if lhs.dim == 0 and rhs.dim == 0: if lhs.mem >= rhs.mem: out = "" + lhs.type + "(" + out + ")" else: node.warning("Type reduction from %s to %s" %\ (rhs.type, lhs.type)) # fill array with scalar value elif lhs.dim > 0 and rhs.dim == 0: return arma.scalar_assign(node) # dimensions that works just fine #elif lhs.dim in (1,2) and rhs.dim in (3, 4): # pass elif lhs.dim == 0 and rhs.dim > 0: out = lhs.type + "(arma::as_scalar(%(1)s))" # Added this elif to handle assignment of: complex type = non_complex type elif lhs.mem > rhs.mem: if lhs.dim > 0 and rhs.dim > 0: out = "conv_to<" + lhs.type + ">::from(%(1)s)" elif lhs.dim == 0 and rhs.dim == 0: if lhs.mem == 4: out = "" + lhs.type + "" + "(%(1)s)" # all the ways things are wrong elif lhs.dim > 0 and rhs.dim > 0: if lhs.mem >= rhs.mem: node.warning("Possible size incompatibility "+\ "%s and %s" % (lhs.type, rhs.type)) else: node.warning("Type reduction and possible size "+\ "incompatible %s and %s" % (lhs.type, rhs.type)) else: node.error("Types incompatible %s and %s" % (lhs.type, rhs.type)) else: node.error("Types incompatible %s and %s" % (lhs.type, rhs.type)) out = "%(1)s" out = "%(0)s = " + out + " ;" return out if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/cube.py ================================================ from .variables import * from . import armadillo as arma def Get(node): # number of argument not legal for cube if len(node) not in (1,2,3): if not len(node): node.error("Zero arguments in a cube call") else: node.error("More than three arguments in a cube call") return "%(name)s(", "-1, ", "-1)" # Single argument if len(node) == 1: arg, dim = arma.configure_arg(node[0], 0) # unknown input if dim == -1: return "%(name)s(%(0)s-1)" # scalar input #if dim == 0: # node.dim = 0 return "%(name)s(" + arg + ")" # Double argument elif len(node) == 2: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) # unkonwn input if -1 in (dim0, dim1): return "%(name)s(", "-1, ", "-1)" # Configure dimensions #if dim0: # if dim1: # node.dim = 3 # else: # node.dim = 1 #else: # if dim1: # node.dim = 2 # else: # node.dim = 0 node = node.resize() # matlab to armadillo fix for cubes return "_%(name)s(" + arg0 + ", " + arg1 + ")" # Triple argument elif len(node) == 3: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) arg2, dim2 = arma.configure_arg(node[2], 2) # unknown arguments if -1 in (dim0, dim1, dim2): return "%(name)s(", "-1, ", "-1)" # Configure dimensions #if dim0: # if dim1: # if dim2: # node.dim = 4#cube # else: # node.dim = 3#matrix # else: # if dim2: # node.dim = 3#matrix # else: # node.dim = 1#colvec #else: # if dim1: # if dim2: # node.dim = 3#matrix # else: # node.dim = 1#colvec # else: # if dim2: # node.dim = 1#colvec # else: # node.dim = 0#scalar return "%(name)s(" + arg0 + ", " + arg1 + ", " + arg2 + ")" def Set(node): if len(node) not in (1,2,3): if not len(node): node.error("Zero arguments in a cube set") else: node.error("More than three arguments in a cube set") return "%(name)s(", "-1, ", "-1)" # Single argument if len(node) == 1: arg, dim = arma.configure_arg(node[0], 0) # unknown arguments if dim == -1: return "%(name)s(%(0)s-1)" # if scalar arg, set node as scalar #if dim == 0: # node.dim = 0 return "%(name)s(" + arg + ")" # Double argument elif len(node) == 2: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) # unknown args if -1 in (dim0, dim1): return "%(name)s(", "-1, ", "-1)" node = node.resize() # matlab to armadillo fix for cubes # Configure dimensions #if dim0: # if dim1: # node.dim = 3#matrix # else: # node.dim = 1#colvec #else: # if dim1: # node.dim = 2#rowvec # else: # node.dim = 0#scalar return "%(name)s(" + arg0 + ", " + arg1 + ", 1)" # triple argument elif len(node) == 3: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) arg2, dim2 = arma.configure_arg(node[2], 2) # unkown input if -1 in (dim0, dim1, dim2): return "%(name)s(", ", ", ")" # Configure dimensions #if dim0: # if dim1: # if dim2: # node.dim = 4#cube # else: # node.dim = 3#matrix # else: # if dim2: # node.dim = 3#matrix # else: # node.dim = 1#colvec #else: # if dim1: # if dim2: # node.dim = 3#matrix # else: # node.dim = 1#colvec # else: # if dim2: # node.dim = 1#colvec # else: # node.dim = 0#scaler return "%(name)s(" + arg0 + ", " + arg1 + ", " + arg2 + ")" def Resize(node): """Special resizing of cube such that properly tranlsation between matlab and armadillo.""" return "%(type)s_%(name)s(%(name)s.memptr(), %(name)s.n_rows, " +\ "%(name)s.n_cols*%(name)s.n_slices, false) ;" ================================================ FILE: src/matlab2cpp/rules/function.py ================================================ import matlab2cpp def type_string(node): """ Determine string represnentation of type. Outside scalars and armadillo, the datatype name and their declaration do not match. This function converts simple datatype declaration and translate them to equivalent C++ declarations. +-----------------+-----------------------+ | Input | Output | +=================+=======================+ | numerical types | node.type | +-----------------+-----------------------+ | struct, structs | struct container name | +-----------------+-----------------------+ | func_lambda | std::function<...> | +-----------------+-----------------------+ | string | std::string | +-----------------+-----------------------+ Args: node (Node): location in tree Returns: str: String representation of node type """ # lambda-function if node.type == "func_lambda": # link to actual lambda-function func = None if hasattr(node.declare, "reference"): func = node.declare.reference elif "_"+node.name in node.program[1].names: func = node.program[1]["_"+node.name] if not (func is None): # no returns in lambda if len(func[1]) == 0: ret = "void" prm = ", ".join([p.type for p in func[2]]) # single return elif len(func[1]) == 1: ret = func[1][0].type prm = ", ".join([p.type for p in func[2]]) # multiple return else: ret = "void" prm = ", ".join([p.type for p in func[2][:]+func[1][:]]) return "std::function<" + ret + "(" + prm + ")>" else: node.warning("lambda function content not found") return "std::function" # struct scalar and array type elif node.type in ("struct", "structs"): declare = node.declare if declare.parent.cls == "Struct": declare = declare.parent return "_" + declare.name.capitalize() elif node.type == "string": return "std::string" return node.type if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/rules/mat.py ================================================ from . import armadillo as arma def Get(node): """ Statement Get (a) a() Assign Var (b) Get (a) b = a() """ # number of args not correct if len(node) not in (1,2): if not len(node): node.error("Zero arguments in a matrix call") else: node.error("More than two arguments in a matrix call") return "%(name)s(", "-1, ", "-1)" # Single argument if len(node) == 1: arg, dim = arma.configure_arg(node[0], 0) # unknown input if dim == -1: return "%(name)s(%(0)s-1)" # scalar begets scalar #if dim == 0: # node.dim = 0 return "%(name)s(" + arg + ")" # Double argument elif len(node) == 2: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) # unknown input if -1 in (dim0, dim1): return "%(name)s(", "-1, ", "-1)" # Configure dimensions #if dim0: # if dim1: # node.dim = 3#matrix # else: # node.dim = 1#colvec #else: # if dim1: # node.dim = 2#rowvec # else: # node.dim = 0#scalar # All + All if node[0].cls == node[1].cls == "All": return "%(name)s" # All + ... if node[0].cls == "All": # All + uvec if dim1: return "%(name)s.cols(" + arg1 + ")" # All + scalar return "%(name)s.col(" + arg1 + ")" # ... + All elif node[1].cls == "All": # uvec + All if dim0: return "%(name)s.rows(" + arg0 + ")" # scalar + All return "%(name)s.row(" + arg0 + ")" # scalar + uvec if dim0 == 0 and dim1 > 0: index = node[1].str.index('(') return "%(name)s(m2cpp::span(" + arg0 + ", " + arg0 + ")" + ", " \ + "m2cpp::span" + node[1].str[index:] + ")" #return "%(name)s.col(" + arg0 + ").rows(" + arg1 + ")" # uvec + scalar elif dim0 > 0 and dim1 == 0: return "%(name)s(" + arg0 + ", m2cpp::span(" + arg1 + ", " + arg1 + "))" #index = node[0].str.index('(') #return "%(name)s(" + "m2cpp::span" + node[0].str[index:] + ", m2cpp::span(" + arg1 + ", " + arg1 + "))" #return "%(name)s.row(" + arg0 + ").cols(" + arg1 + ")" # uvec + uvec if dim0 > 0 and dim1 > 0: a0 = node[0].str.replace("arma::span", "m2cpp::span") a1 = node[1].str.replace("arma::span", "m2cpp::span") return "%(name)s(" + a0 + ", " + a1 + ")" return "%(name)s(" + arg0 + ", " + arg1 + ")" def Set(node): """ Assign Set (a) Var (n) Var (b) a(n) = b """ # wrong number of argumets if len(node) not in (1,2): if not len(node): node.error("Zero arguments in a matrix set") else: node.error("More than two arguments in a matrix set") return "%(name)s(", "-1, ", "-1)" # Single argument if len(node) == 1: arg, dim = arma.configure_arg(node[0], 0) # scalar arg is scalar #if dim == 0: # node.dim = 0 # unknown datatype if dim == -1: return "%(name)s(", "-1, ", "-1)" return "%(name)s(" + arg + ")" # Double argument elif len(node) == 2: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) # unknown datatype if -1 in (dim0, dim1): return "%(name)s(", "-1, ", "-1)" # Configure dimensions #if dim0: # if dim1: # node.dim = 3#matrix # else: # node.dim = 1#colvec #else: # if dim1: # node.dim = 2#rowvec # else: # node.dim = 0#scalar # All + All if node[0].cls == node[1].cls == "All": return "%(name)s" # All + ... if node[0].cls == "All": # All + uvec if dim1: return "%(name)s.cols(" + arg1 + ")" # All + scalar return "%(name)s.col(" + arg1 + ")" # ... + All elif node[1].cls == "All": # uvec + All if dim0: return "%(name)s.rows(" + arg0 + ")" # scalar + All return "%(name)s.row(" + arg0 + ")" # scalar + uvec if dim0 == 0 and dim1 > 0: index = node[1].str.index('(') return "%(name)s(m2cpp::span(" + arg0 + ", " + arg0 + ")" + ", " \ + "m2cpp::span" + node[1].str[index:] + ")" #return "%(name)s.col(" + arg0 + ").rows(" + arg1 + ")" # uvec + scalar elif dim0 > 0 and dim1 == 0: index = node[0].str.index('(') return "%(name)s(" + "m2cpp::span" + node[0].str[index:] + \ ", m2cpp::span(" + arg1 + ", " + arg1 + "))" #return "%(name)s.row(" + arg0 + ").cols(" + arg1 + ")" # uvec + uvec if dim0 > 0 and dim1 > 0: a0 = node[0].str.replace("arma::span", "m2cpp::span") a1 = node[1].str.replace("arma::span", "m2cpp::span") return "%(name)s(" + a0 + ", " + a1 + ")" return "%(name)s(" + arg0 + ", " + arg1 + ")" ================================================ FILE: src/matlab2cpp/rules/parallel.py ================================================ def variable_lists(node): nodes = node.flatten(ordered=False, reverse=False, inverse=False) #store some variable names, in private or shared assigned_var = [] type_info = [] #get iterator name iterator_name = node[0].name for n in nodes: if n.cls == "Assign": #index = n.parent.children.index(n) #lhs var of the assignment if n[0].cls == "Var": if n[0].name not in assigned_var: assigned_var.append(n[0].name) type_info.append(n[0].type) """ if n[0].cls == "Set": var_name = n[0].name #subnodes to Set #index = n.parent.children.index(n) #subnodes = n.parent[index].flatten(ordered=False, reverse=False, inverse=False) subnodes = n[0].flatten(ordered=False, reverse=False, inverse=False) for subnode in subnodes[1:]: if subnode.name and subnode.name == iterator_name: shared_variable.append(var_name) #print(subnode.name) """ #multiple return from function are assigned to vars if n.cls == "Assigns": #and n.backend == "func_returns": for sub_node in n: if sub_node.cls == "Var": if sub_node.name not in assigned_var: assigned_var.append(sub_node.name) type_info.append(sub_node.type) #get the iteration variable in the for loop if n.cls == "Var" and n.parent.cls == "For": if n.name not in assigned_var: assigned_var.append(n.name) type_info.append(n.type) #shared_variable = list(set(shared_variable)) #print(shared_variable) #for n in nodes: # if (n.cls == "Var" or n.cls == "Get") and n.backend != "reserved" and n.name \ # not in [shared_variable, node[0].name]: # private_variable.append(n.name) #private_variable = list(set(private_variable)) #return private_variable, shared_variable, assigned_var, type_info return assigned_var, type_info def omp(node, start, stop, step): assigned_var, type_info = variable_lists(node) #out = "#pragma omp parallel for\nfor (%(0)s=" + start + \ # "; %(0)s<=" + stop + "; %(0)s" temp_str = "" if len(assigned_var) > 1: temp_str = ", ".join(assigned_var[1:]) temp_str = "firstprivate(" + temp_str + ")" out = "#pragma omp parallel for " + temp_str + "\nfor (%(0)s=" + start + \ "; %(0)s<=" + stop + "; %(0)s" return out def tbb(node, start, stop, step): assigned_var, type_info = variable_lists(node) any_vec_or_mat = False for var, type in zip(assigned_var, type_info): if type not in ["uword", "int", "float", "double"]: any_vec_or_mat = True #tbb.counter += 1 out = "{\n" #str_val = str(tbb.counter) if any_vec_or_mat: declare_struct = "struct tbb_var_struct" + "\n{" for var, type in zip(assigned_var, type_info): if type not in ["uword", "int", "float", "double"]: declare_struct += "\n" + type + " " + var + ";" declare_struct += "\n} " + ";\n" declare_struct += "tbb::combinable tbb_per_thread_data" + " ;\n" out += declare_struct #for var, type in zip(assigned_var, type_info): # out += "tbb::enumerable_thread_specific<" + type + "> " + "_" + var + " = " + var + " ;\n" out += "tbb::parallel_for(tbb::blocked_range(" + start + ", " + stop + "+1" + \ "),\n" + "[&]" + "(const tbb::blocked_range& _range) \n{\n" #assign to local L, x, y for var, type in zip(assigned_var, type_info): if type in ["uword", "int", "float", "double"]: out += type + " " + var + ";\n" if any_vec_or_mat: out += "struct tbb_var_struct" + " tbb_struct_vars = tbb_per_thread_data" + ".local() ;\n" for var, type in zip(assigned_var, type_info): if type not in ["uword", "int", "float", "double"]: out += type + "& " + var + " = " + "tbb_struct_vars." + var + ";\n" #for var, type in zip(assigned_var, type_info): # out += type + "& " + var + " = _" + var + ".local() ;\n" out += "for (" + "%(0)s = _range.begin(); %(0)s != _range.end(); %(0)s" # special case for '+= 1' if step == "1": out += "++" else: out += "+=" + step out += ")\n{\n%(2)s\n}" out += "\n}\n);\n" out += "}" return out #tbb.counter = 0 ================================================ FILE: src/matlab2cpp/rules/rowvec.py ================================================ from . import armadillo as arma def Get(node): if len(node) != 1: if not len(node): node.error("Zero arguments in a rowvec call") return "%(name)s()" elif len(node) == 2 and node[0].cls == "Int" and node[0].value == "1": node_ = node[1] #special case hh = h0F(:,ones(1,M)) with h0F rowvec, and ones elif len(node) == 2 and node[0].name == "ones" or \ node[1].name == "ones": out = "%(name)s(" if node[0].name == "ones": out = out + "%(0)s-1, " #return "%(name)s(%(0)s-1, %(1)s)" else: out = out + "%(0)s, " if node[1].name == "ones": out = out + "%(1)s-1)" #return "%(name)s(%(0)s, %(1)s-1)" else: out = out + "%(1)s)" return out else: node.error("More than one arguments in a rowvec call") return "%(name)s(", "-1, ", "-1)" else: node_ = node[0] arg, dim = arma.configure_arg(node_, 0) if dim == -1: return "%(name)s(", "-1, ", "-1)" #if dim == 0: # node.dim = 0 # a(uvec array) or a(1:2:5) if (node[0].type == "uvec" and node[0].cls == "Var") or \ node[0].cls == "Colon" and len(node[0]) == 3: return "arma::strans(%(name)s(" + arg + "))" return "%(name)s(" + arg + ")" def Set(node): if len(node) != 1: if not len(node): node.error("Zero arguments in a rowvec call") return "%(name)s()" elif len(node) == 2 and node[0].cls == "Int" and node[0].value == "1": node_ = node[1] else: node.error("More than one arguments in a rowvec call") return "%(name)s(", "-1, ", "-1)" else: node_ = node[0] arg, dim = arma.configure_arg(node_, 0) #if dim == 0: # node.dim = 0 if dim == -1: return "%(name)s(", "-1, ", "-1)" return "%(name)s(" + arg + ")" ================================================ FILE: src/matlab2cpp/rules/variables.py ================================================ #import matlab2cpp def Var(node): if node.type == "TYPE": node.error("unknown data type") return "%(name)s" def Fvar(node): if node.backend == "structs": if node.parent.cls == "Vector": if len(node.parent) == 1 and len(node.parent.parent) == 1: size = node.declare.parent["_size"] return size.value return "%(name)s[0].%(value)s" return "%(name)s.%(value)s" def Cvar(node): "a{b}" #if node.type == "TYPE": # node.declare.type = "cell" if not node.type == "cell" or node.type == "varargin": node.error("Behaves like cell, not %s" % node.type) return "%(name)s{", "}{", "}" def Set(node): if node.type == "TYPE": node.error("unknown data type") elif node.num and node.mem == 0: node.error("scalar are not iterable") return "%(name)s(", ", ", ")" def Cset(node): "a{b}(c) = d" if node.type == "TYPE": node.declare.type = "cell" elif node.type != "cell": node.error("Behaves like cell, not %s" % node.type) n_fields = node["n_fields"] out = "%(name)s{%(" out = out + ")s}{%(".join(map(str, range(n_fields))) out = out + ")s}(%(" out = out + ")s, %(".join(map(str, range(n_fields, len(node)))) out = out + ")s)" return out def Sset(node): node.pointer = 0 if len(node) == 1 and node[0].cls == "Int": return "%(name)s[" + str(int(node[0].value)-1) + "].%(value)s" return "%(name)s[", ", ", "-1].%(value)s" def Nset(node): return "%(name)s.(", ", ", ")" def Get(node): return "%(name)s(", ", ", ")" def Cget(node): "a{b}(c)" if node.type == "TYPE": node.declare.type = "cell" elif node.type != "cell": node.error("Behaves like cell, not %s" % node.type) n_fields = node["n_fields"] out = "%(name)s{%(" out = out + ")s}{%(".join(map(str, range(n_fields))) out = out + ")s}(%(" out = out + ")s, %(".join(map(str, range(n_fields, len(node)))) out = out + ")s)" return out def Fget(node): node.pointer = 0 if len(node) == 1 and node[0].cls == "Int": return "%(name)s[" + str(int(node[0].value)-1) + "].%(value)s" return "%(name)s.%(value)s(", ", ", ")" def Sget(node): return "%(name)s[", ", ", "-1].%(value)s" def Nget(node): return "%(name)s.(", ", ", ")" """ def Assign(node): lhs, rhs = node # assign a my_var = [a.val], a is a structs, my_var should be a vec if node[1].cls == "Matrix" and (node[1].backend == "struct" or node[1].backend == "unknown"): element = rhs[0][0] if element.backend == "struct" or element.backend == "unknown": var = lhs.name name = element.name value = element.value declares = node.func[0] if "_i" not in declares: declare = matlab2cpp.collection.Var(declares, "_i") declare.type = "int" declare.backend = "int" declares.translate() for var in declares: index = var.parent.children.index(var) if var.name == name: del var.parent.children[index] for program in node.project: structs = program[3] for struct in structs: index = struct.parent.children.index(struct) if struct.name == name: del struct.parent.children[index] string = var + ".set_size(" + name + ".size()" + ") ;\n" +\ "for (_i=0; _i<" + var + ".n_elem" + "; ++_i)\n " +\ var + "(_i) = " + name + "[_i]." + value + " ;" return string return "%(0)s = %(1)s;" """ ================================================ FILE: src/matlab2cpp/rules/vec.py ================================================ from .variables import * from . import armadillo as arma def Get(node): if len(node) != 1: if not len(node): node.error("Zero arguments in a vec call") return "%(name)s()" elif len(node) == 2 and node[1].cls == "Int" and node[1].value == "1": pass #special case hh = h0F(:,ones(1,M)) with h0F vec, and ones elif len(node) == 2 and node[0].name == "ones" or \ node[1].name == "ones": out = "%(name)s(" if node[0].name == "ones": out = out + "%(0)s-1, " #return "%(name)s(%(0)s-1, %(1)s)" else: out = out + "%(0)s, " if node[1].name == "ones": out = out + "%(1)s-1)" #return "%(name)s(%(0)s, %(1)s-1)" else: out = out + "%(1)s)" return out else: node.error("More than one arguments in a vec call") return "%(name)s(", ", ", ")" #if len(node) == 1: arg, dim = arma.configure_arg(node[0], 0) if dim == -1: return "%(name)s(", "-1, ", "-1)" #if dim == 0: # node.dim = 0 return "%(name)s(" + arg + ")" """ elif len(node) == 2: arg0, dim0 = arma.configure_arg(node[0], 0) arg1, dim1 = arma.configure_arg(node[1], 1) if dim0 == -1: return "%(name)s(", "-1, ", "-1)" elif dim1 == -1: return "%(name)s(", "-1, ", "-1)" #if dim == 0: # node.dim = 0 return "%(name)s(" + arg0 + ", " + arg1 + ")" """ def Set(node): if len(node) != 1: if not len(node): node.error("Zero arguments in a vec call") return "%(name)s()" elif len(node) == 2 and node[1].cls == "Int" and node[1].value == "1": pass else: node.error("More than one arguments in a vec call") return "%(name)s(", "-1, ", "-1)" arg, dim = arma.configure_arg(node[0], 0) if dim == -1: return "%(name)s(", "-1, ", "-1)" #if dim == 0: # node.dim = 0 return "%(name)s(" + arg + ")" ================================================ FILE: src/matlab2cpp/setpaths.py ================================================ import os from . import tree def multiple_folder_paths(setpath_file): builder = tree.builder.Builder() folder_paths = [] if os.path.isfile(setpath_file): f = open(setpath_file, "rU") code = f.read() f.close() else: return folder_paths builder.load("paths_file", code) builder.configure() #Get code_block code_block = builder.project[0][1][0][3] #save variables that are assigned of type string variables = {} folder_paths = [] #each node in code_block is basically one line of code for node in code_block: #Assignments node if node.cls == "Assign": subnodes = node[1].flatten(ordered=False, reverse=False, inverse=False) str_tmp = '' for subnode in subnodes: if subnode.cls == "String": str_tmp += subnode.value elif subnode.cls == "Var" and subnode.type == "string": str_tmp += variables[subnode.name] variables[node[0].name] = str_tmp #Statement node if node.cls == "Statement" and node[0].cls == "Get" and node[0].name in {"path", "addpath"}: subnodes = node[0].flatten(ordered=False, reverse=False, inverse=False) str_tmp = '' for subnode in subnodes: if subnode.cls == "String": str_tmp += subnode.value if subnode.cls == "Var" and subnode.type == "string": str_tmp += variables[subnode.name] folder_paths.append(str_tmp) #remove separator if it is at the end of the string folder_paths = [path.rstrip(os.path.sep) for path in folder_paths] return folder_paths ================================================ FILE: src/matlab2cpp/supplement/__init__.py ================================================ """ """ PREFIX = """# encoding: utf-8 # # Supplement file # # Valid inputs: # # uword int float double cx_double # uvec ivec fvec vec cx_vec # urowvec irowvec frowvec rowvec cx_rowvec # umat imat fmat mat cx_mat # ucube icube fcube cube cx_cube # # char string struct structs func_lambda """ from .functions import Ftypes from .suggests import Sstypes from .structs import Stypes from .includes import Itypes from .verbatim import Vtypes def str_variables(types_f={}, types_s={}, types_i=[], suggest={}, prefix=True, types_v={}): """ Convert a nested dictionary for types, suggestions and structs and use them to create a suppliment text ready to be saved. Kwargs: types_f (dict): Function variables datatypes types_s (dict): Struct variables datatypes types_i (list): Includes in header types_v (dict): Verbatim translations suggest (dict): Suggested datatypes for types_f and types_s prefix (bool): True if the type explaination should be included Returns: str String representation of suppliment file Example: >>> types_f = {"f" : {"a":"int"}, "g" : {"b":""}} >>> types_s = {"c" : {"d":""}} >>> types_i = ["#include "] >>> suggest = {"g" : {"b":"float"}, "c" : {"d":"vec"}} >>> print(str_variables(types_f, types_s, types_i, suggest, prefix=False)) functions = { "f" : { "a" : "int", }, "g" : { "b" : "", # float }, } structs = { "c" : { "d" : "", # vec }, } includes = [ '#include ', ] """ if prefix: out = PREFIX else: out = "" if types_f: if prefix: out += "\n" out += "functions = {\n" keys = sorted(types_f.keys()) for name in keys: out += ' "%s" : {\n' % (name) types = types_f[name] keys2 = sorted(types.keys()) l = max([len(k) for k in keys2]+[0])+4 for key in keys2: val = types[key] sug = suggest.get(name, {}).get(key, "") if key[:1] == "_": continue elif val: out += " "*(l-len(key)) + '"%s" : "%s",\n' % (key, val) elif sug: out += " "*(l-len(key)) + '"%s" : "", # %s\n' % (key, sug) else: out += " "*(l-len(key)) + '"%s" : "",\n' % (key) out += " },\n" out += "}" if types_s: if types_f or prefix: out += "\n" out += "structs = {\n" keys = sorted(types_s.keys()) for name in keys: out += ' "%s" : {\n' % (name) types = types_s[name] keys2 = sorted(types.keys()) l = max([len(k) for k in keys2])+4 for key in keys2: val = types[key] sug = suggest.get(name, {}).get(key, "") if key[-5:] == "_size": val = val or 100 out += " "*(l-len(key)) + '"%s" : %s,\n' % (key, val) elif val: out += " "*(l-len(key)) + '"%s" : "%s",\n' % (key, val) elif sug: out += " "*(l-len(key)) + '"%s" : "", # %s\n' % (key, sug) else: out += " "*(l-len(key)) + '"%s" : "",\n' % (key) out += " },\n" out += "}" if types_i: if prefix or types_f or types_s: out += "\n" out += "includes = [\n" for key in types_i: if key: out += " '" + key + "',\n" out += "]" if types_v: if types_f or prefix or types_s or types_i: out += "\n" out += "verbatims = {\n" keys = sorted(types_v.keys()) l = max([len(k) for k in keys])+2 for key in keys: val = types_v[key] if "\n" in val: out += " "*(l-len(key)) + '"%s" : """%s""",\n' % (key, val) else: out += " "*(l-len(key)) + '"%s" : "%s",\n' % (key, val) out += "}" return out ================================================ FILE: src/matlab2cpp/supplement/functions.py ================================================ """ """ def set(node, types): funcs = node.program[1] # Functions for name in types.keys(): if name in funcs.names: types_ = types[name] func = funcs[funcs.names.index(name)] declares, returns, params = func[:3] for key in types_.keys(): if key in declares.names: if key in returns.names: var = returns[returns.names.index(key)] var.type = types_[key] var = declares[declares.names.index(key)] var.type = types_[key] elif key in params.names: var = params[params.names.index(key)] var.type = types_[key] def get(node): funcs = node.program[1] types = {} for func in funcs: types[func.name] = types_ = {} declares, params = func[0], func[2] for var in declares[:]+params[:]: type = var.type if type == "TYPE": type = "" types_[var.name] = type if not type: type = var.prop["suggest"] if type == "TYPE": type = "" return types class Ftypes(object): "Access to function types from program node" def __get__(self, instance, owner): return get(instance) def __set__(self, instance, value): set(instance, value) if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/supplement/includes.py ================================================ """ """ import matlab2cpp def set(node, types): includes = node.program[0] # Includes for key in types: #print(includes.names) if key not in includes.names: matlab2cpp.collection.Include(includes, key) def get(node): includes = node.program[0] types_i = [] for include in includes: types_i.append(include.name) return types_i def write_to_includes(include_string): write = True not_to_include = ['#include "SPlot.h"', '#include ', '#define NOMINMAX', 'include "mconvert.h"'] if include_string in not_to_include: write = False return write class Itypes(object): def __get__(self, instance, owner): return get(instance) def __set__(self, instance, value): set(instance, value) if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/supplement/structs.py ================================================ """ """ def set(node, types): from .. import collection structs = node.program[3] # Structs for name in types.keys(): if name in structs.names: types_ = types[name] struct = structs[structs.names.index(name)] for key in types_.keys(): if key in struct.names: var = struct[struct.names.index(key)] if var.cls == "Counter": var.value = str(types_[key]) else: var.type = types_[key] else: var = collection.Declare( struct, key, backend="struct", type=types_[key]) def get(node): structs = node.program[3] types_s = {} for struct in structs: types_s[struct.prop["name"]] = types = {} for var in struct: type = var.prop["type"] if type == "TYPE": type = "" if type == "structs": type = var.prop["value"] types[var.name] = type return types_s class Stypes(object): def __get__(self, instance, owner): return get(instance) def __set__(self, instance, value): set(instance, value) ================================================ FILE: src/matlab2cpp/supplement/suggests.py ================================================ def get(node): funcs = node.program[1] structs = node.program[3] suggest = {} for func in funcs: suggest[func.name] = suggest_ = {} declares, params = func[0], func[2] for var in declares[:]+params[:]: type = var.prop["type"] if type == "TYPE": type = "" if not type: type = var.prop["suggest"] if type == "TYPE": type = "" if type: suggest_[var.name] = type for struct in structs: suggest[struct.name] = suggest_ = {} for var in struct: type = var.prop["type"] if type == "TYPE": type = "" if not type: type = var.prop["suggest"] if type == "TYPE": type = "" if type: suggest_[var.name] = type return suggest class Sstypes(object): def __get__(self, instance, owner): return get(instance) def __set__(self, instance, value): raise AttributeError("Suggestions not to be set manually") ================================================ FILE: src/matlab2cpp/supplement/verbatim.py ================================================ import re set_ = set def set(D, code): for key, value in D.items(): findterm = r'.*' + re.escape(key) + r'.*' keys = set_(re.findall(findterm, code)) value = '___' + value.replace('\n', '___') value = value.replace('\\', '//') for key_ in keys: findterm_ = re.escape(key_) value_ = "___" + key_ + value code = re.sub(findterm_, value_, code) return code #find nodes that contain verbatim def get(node): nodes = node.flatten() D = {} for node in nodes: if node.cls == "Verbatim": D[node.name] = node.value return D class Vtypes(object): def __get__(self, instance, owner): return get(instance) ================================================ FILE: src/matlab2cpp/tree/__init__.py ================================================ """ Parsing of Matlab code is solely done through the :py:class:`~matlab2cpp.Builder` class. It contains three main use methods: :py:func:`~matlab2cpp.Builder.load`, :py:func:`~matlab2cpp.Builder.configure` and :py:func:`~matlab2cpp.Builder.translate`. In addition there are a collection of method with names starting with ``create_`` that creates various structures of the node tree. In addition to :py:class:`~matlab2cpp.Builder` there are submodules with support function for modules. Constructor help functions are as follows: +--------------------------------------+--------------------------------------+ | Module | Description | +======================================+======================================+ | :py:mod:`matlab2cpp.tree.assign` | Support functions for variable | | | assignments | +--------------------------------------+--------------------------------------+ | :py:mod:`matlab2cpp.tree.branches` | Support functions for if-tests, | | | loops, try-blocks | +--------------------------------------+--------------------------------------+ | :py:mod:`matlab2cpp.tree.codeblock` | Support functions for filling in | | | codeblock content | +--------------------------------------+--------------------------------------+ | :py:mod:`matlab2cpp.tree.expression` | Support functions for filling in | | | expression content | +--------------------------------------+--------------------------------------+ | :py:mod:`matlab2cpp.tree.functions` | Support functions for constructing | | | Functions, both explicit and lambda, | | | and program content | +--------------------------------------+--------------------------------------+ | :py:mod:`matlab2cpp.tree.misc` | Miscelenious support functions | +--------------------------------------+--------------------------------------+ | :py:mod:`matlab2cpp.tree.variables` | Support functions for constructing | | | various variables | +--------------------------------------+--------------------------------------+ In addition a collectio of genereal purpose modules are available: +-------------------------------------+---------------------------------------+ | Module | Description | +=====================================+=======================================+ | :py:mod:`matlab2cpp.tree.constants` | A collection of usefull constants | | | used by various interpretation rules | +-------------------------------------+---------------------------------------+ | :py:mod:`matlab2cpp.tree.findend` | Look-ahead functions for finding the | | | end of various code structures | +-------------------------------------+---------------------------------------+ | :py:mod:`matlab2cpp.tree.identify` | Look-ahead functions for identifying | | | ambigous contexts | +-------------------------------------+---------------------------------------+ | :py:mod:`matlab2cpp.tree.iterate` | Support functions for segmentation of | | | lists | +-------------------------------------+---------------------------------------+ """ from .builder import Builder __all__ = ("Builder",) ================================================ FILE: src/matlab2cpp/tree/assign.py ================================================ """ Support functions for identifying assignments. +-------------------------------------------+----------------------------------+ | Function | Description | +===========================================+==================================+ | :py:func:`~matlab2cpp.tree.assign.single` | Assignment with single return | +-------------------------------------------+----------------------------------+ | :py:func:`~matlab2cpp.tree.assign.multi` | Assignment with multiple returns | +-------------------------------------------+----------------------------------+ """ from __future__ import print_function from .. import collection from . import findend, constants as c, identify, iterate def multi(self, parent, cur, eq_loc): """ Assignment with multiple return Args: self (Builder): Code constructor. parent (Node): Parent node cur (int): Current position in code eq_loc (int): position of the assignment marker ('='-sign) Returns: int: Index to end of assignment Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "[a,b] = c") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Assigns assign.multi '[a,b] = c' 1 Var variables.assign 'a' 3 Var variables.assign 'b' 8 Expression expression.create 'c' 8 Var variables.variable 'c' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Assigns unknown TYPE c 1 2| | Var unknown TYPE a 1 4| | Var unknown TYPE b 1 9| | Var unknown TYPE c """ if self.code[cur] != "[": self.syntaxerror(cur, "multi-assign start") if self.code[eq_loc] != "=": self.syntaxerror(cur, "assignment sign (=)") j = eq_loc+1 while self.code[j] in " \t.": if self.code[j] == ".": j = findend.dots(self, j)+1 else: j += 1 end = findend.expression(self, j) if self.disp: print("%4d Assigns " % cur, end=" ") print("%-20s" % "assign.multi", end=" ") print(repr(self.code[cur:end+1])) if identify.space_delimited(self, cur): l = iterate.space_list(self, cur) else: l = iterate.comma_list(self, cur) if len(l[0]) == 1: return self.create_assign(parent, l[0][0][0], eq_loc) assigns = collection.Assigns(parent, cur=cur, code=self.code[cur:end+1]) for vector in l: for start, stop in vector: self.create_assign_variable(assigns, start, end=stop) cur = eq_loc + 1 while self.code[cur] in " \t": cur += 1 cur_ = self.create_expression(assigns, cur) assigns.name = assigns[-1].name return cur_ def single(self, parent, cur, eq_loc): """ Assignment with single return. Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code eq_loc (int): position of the assignment marker ('='-sign) Returns: int: Index to end of assignment Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "a=b") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Assign assign.single 'a=b' 0 Var variables.assign 'a' 2 Expression expression.create 'b' 2 Var variables.variable 'b' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Assign unknown TYPE b 1 1| | Var unknown TYPE a 1 3| | Var unknown TYPE b """ if self.code[cur] not in c.letters: self.syntaxerror(cur, "assignment start") if self.code[eq_loc] != "=": self.syntaxerror(cur, "assignment indicator (=)") j = eq_loc+1 while self.code[j] in " \t": j += 1 end = findend.expression(self, j) if self.disp: print("%4d Assign " % cur, end="") print("%-20s" % "assign.single", end="") print(repr(self.code[cur:end+1])) assign = collection.Assign(parent, cur=cur, code=self.code[cur:end+1]) cur = self.create_assign_variable(assign, cur, eq_loc) cur += 1 while self.code[cur] in " \t": cur += 1 if self.code[cur] == "]": cur += 1 while self.code[cur] in " \t": cur += 1 if self.code[cur] != "=": self.syntaxerror(cur, "assignment indicator (=)") k = cur+1 while self.code[k] in " \t": k += 1 self.create_expression(assign, k, end) assign.name = assign[-1].name if len(assign) != 2: self.syntaxerror(k, "single assign when multi-assign") return end if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/tree/branches.py ================================================ """ Iterpretors related to branches, loops and try. +------------------------------------------------+-----------------------+ | Function | Description | +================================================+=======================+ | :py:func:`~matlab2cpp.tree.branches.trybranch` | Try-catch block | +------------------------------------------------+-----------------------+ | :py:func:`~matlab2cpp.tree.branches.switch` | Switch-case branch | +------------------------------------------------+-----------------------+ | :py:func:`~matlab2cpp.tree.branches.whileloop` | While loop | +------------------------------------------------+-----------------------+ | :py:func:`~matlab2cpp.tree.branches.forloop` | For loop | +------------------------------------------------+-----------------------+ | :py:func:`~matlab2cpp.tree.branches.ifbranch` | If-ifelse-else branch | +------------------------------------------------+-----------------------+ """ from __future__ import print_function import matlab2cpp from . import constants as c, findend def trybranch(self, parent, cur): ''' Try-catch block Args: self (Builder): Code constructor. parent (Node): Parent node cur (int): Current position in code Returns: int: Index to end of block Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", ... """try ... a ... catch ... b""") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Try branches.trybranch 'try' 6 Codeblock codeblock.codeblock 6 Statement codeblock.codeblock 'a' 6 Expression expression.create 'a' 6 Var variables.variable 'a' 16 Codeblock codeblock.codeblock 16 Statement codeblock.codeblock 'b' 16 Expression expression.create 'b' 16 Var variables.variable 'b' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Tryblock code_block TYPE 1 1| | Try code_block TYPE 2 7| | | Block code_block TYPE 2 7| | | | Statement code_block TYPE 2 7| | | | | Var unknown TYPE a 3 9| | Catch code_block TYPE 4 17| | | Block code_block TYPE 4 17| | | | Statement code_block TYPE 4 17| | | | | Var unknown TYPE b ''' if self.code[cur:cur+3] != "try" or self.code[cur+3] not in c.k_end: self.syntaxerror(cur, "start of try-block") if self.disp: print("%4d Try " % cur, end="") print("%-20s" % "branches.trybranch", end="") print(repr(self.code[cur:cur+3])) start = cur tryblock = matlab2cpp.collection.Tryblock(parent, cur=cur) trybranch = matlab2cpp.collection.Try(tryblock) cur += 3 while self.code[cur] in " \t\n,;": cur += 1 cur = self.create_codeblock(trybranch, cur) trybranch.code = self.code[start:cur] if self.code[cur:cur+5] != "catch" or self.code[cur+5] not in c.k_end: self.syntaxerror(cur, "start of catch-block") catch_ = matlab2cpp.collection.Catch(tryblock, cur=cur) start_ = cur cur += 5 while self.code[cur] in " \t\n,;": cur += 1 cur = self.create_codeblock(catch_, cur) catch_.code = self.code[start_:cur] tryblock.code = self.code[start:cur] return cur def switch(self, parent, cur): ''' Switch-case branch Args: self (Builder): Code constructor. parent (Node): Parent node cur (int): Current position in code Returns: int: Index to end of codeblock Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", ... """switch a ... case b ... c ... case d ... d ... end""") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Switch branches.switch 'switch a' 7 Expression expression.create 'a' 7 Var variables.variable 'a' 9 Case branches.switch 'case b' 14 Expression expression.create 'b' 14 Var variables.variable 'b' 18 Codeblock codeblock.codeblock 18 Statement codeblock.codeblock 'c' 18 Expression expression.create 'c' 18 Var variables.variable 'c' 20 Case branches.switch 'case d' 25 Expression expression.create 'd' 25 Var variables.variable 'd' 29 Codeblock codeblock.codeblock 29 Statement codeblock.codeblock 'd' 29 Expression expression.create 'd' 29 Var variables.variable 'd' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Switch code_block TYPE 1 8| | Var unknown TYPE a 2 10| | Case code_block TYPE 2 15| | | Var unknown TYPE b 3 19| | | Block code_block TYPE 3 19| | | | Statement code_block TYPE 3 19| | | | | Var unknown TYPE c 4 21| | Case code_block TYPE 4 26| | | Var unknown TYPE d 5 30| | | Block code_block TYPE 5 30| | | | Statement code_block TYPE 5 30| | | | | Var unknown TYPE d ''' if not (self.code[cur:cur+6] == "switch" and\ self.code[cur+6] in " \t("): self.syntaxerror(cur, "start of switch branch") k = cur+6 while self.code[k] in " \t": k += 1 end = findend.expression(self, k) if self.disp: print("%4d Switch " % cur, end="") print("%-20s" % "branches.switch", end="") print(repr(self.code[cur:end+1])) switch = matlab2cpp.collection.Switch(parent, cur=cur) self.create_expression(switch, k, end) k = end+1 while self.code[k] in " \t\n;,": k += 1 while self.code[k:k+4] == "case" and self.code[k+4] in " \t(": cur = k k += 4 while self.code[k] in " \t": k += 1 end = findend.expression(self, k) if self.disp: print("%4d Case " % cur, end="") print("%-20s" % "branches.switch", end="") print(repr(self.code[cur:end+1])) case = matlab2cpp.collection.Case(switch, cur=cur) cur = self.create_expression(case, k, end) k = cur+1 while self.code[k] in " \t;,\n": k += 1 k = self.create_codeblock(case, k) if self.code[k:k+9] == "otherwise" and self.code[k+9] in " \t(,;\n": cur = k if self.disp: print("%4d Otherwise " % cur, end="") print("%-20s" % "branches.switch", end="") print(repr(self.code[cur:cur+10])) otherwise = matlab2cpp.collection.Otherwise(switch, cur=cur) k += 9 while self.code[k] in " \t\n;,": k += 1 k = self.create_codeblock(otherwise, k) return k def whileloop(self, parent, cur): ''' While loop Args: self (Builder): Code constructor. parent (Node): Parent node cur (int): Current position in code Returns: int: Index to end of code block Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", ... """while a ... b ... end""") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 While branches.whileloop 'while a' 6 Expression expression.create 'a' 6 Var variables.variable 'a' 10 Codeblock codeblock.codeblock 10 Statement codeblock.codeblock 'b' 10 Expression expression.create 'b' 10 Var variables.variable 'b' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| While code_block TYPE 1 7| | Var unknown TYPE a 2 11| | Block code_block TYPE 2 11| | | Statement code_block TYPE 2 11| | | | Var unknown TYPE b ''' if self.code[cur:cur+5] != "while" and self.code[cur+5] not in c.k_end: self.syntaxerror(cur, "start of while-loop") start = cur k = cur+5 while self.code[k] in " \t": k += 1 end = findend.expression(self, k) if self.disp: print("%4d While " % cur, end="") print("%-20s" % "branches.whileloop", end="") print(repr(self.code[cur:end+1])) whileloop = matlab2cpp.collection.While(parent, cur=cur, code=self.code[cur:end+1]) if self.code[k] == "(": k += 1 while self.code[k] in " \t": k += 1 cur = self.create_expression(whileloop, k) cur += 1 cur += 1 while self.code[cur] in " \t": cur += 1 end = self.create_codeblock(whileloop, cur) #whileloop.code = self.code[start:end+1] return end def parforloop(self, parent, cur): if self.code[cur:cur+6] != "parfor": self.syntaxerror(cur, "parfor loop start") start = cur if self.disp: print("%4d Parfor " % cur, end="") print(repr(self.code[cur:self.code.find("\n", cur)]), end="") print("branches.parforloop") parfor_loop = matlab2cpp.collection.Parfor(parent, cur=cur, code=self.code[cur:self.code.find("\n", cur)]) cur = cur+6 while self.code[cur] in "( \t": cur += 1 cur = self.create_variable(parfor_loop, cur) if parfor_loop.project.builder.enable_tbb: parfor_loop[0].type = "uword" else: parfor_loop[0].create_declare() parfor_loop[0].suggest = "int" cur += 1 while self.code[cur] in " \t": cur += 1 if self.code[cur] != "=": self.syntaxerror(cur, "for-loop assignment (=)") cur += 1 while self.code[cur] in " \t": cur += 1 cur = self.create_expression(parfor_loop, cur) cur += 1 while self.code[cur] in ") \t": cur += 1 if self.code[cur] == ",": cur += 1 while self.code[cur] in " \t\n;": cur += 1 end = self.create_codeblock(parfor_loop, cur) #parfor_loop.code = self.code[start:end] return end def forloop(self, parent, cur): ''' For loop Args: self (Builder): Code constructor. parent (Node): Parent node cur (int): Current position in code Returns: int: Index to end of code block Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", ... """for a = b ... c ... end""") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 For 'for a = b' branches.forloop 4 Var variables.variable 'a' 8 Expression expression.create 'b' 8 Var variables.variable 'b' 12 Codeblock codeblock.codeblock 12 Statement codeblock.codeblock 'c' 12 Expression expression.create 'c' 12 Var variables.variable 'c' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| For code_block TYPE 1 5| | Var unknown (int) a 1 9| | Var unknown TYPE b 2 13| | Block code_block TYPE 2 13| | | Statement code_block TYPE 2 13| | | | Var unknown TYPE c ''' if self.code[cur:cur+3] != "for": self.syntaxerror(cur, "for loop start") start = cur if self.disp: print("%4d For " % cur, end=" ") print(repr(self.code[cur:self.code.find("\n", cur)]), end=" ") print("branches.forloop") for_loop = matlab2cpp.collection.For(parent, cur=cur, code=self.code[cur:self.code.find("\n", cur)]) cur = cur+3 while self.code[cur] in "( \t": cur += 1 cur = self.create_variable(for_loop, cur) index = for_loop.parent.children.index(for_loop) tbb = for_loop.parent.children[index - 1].cls if tbb == "Tbb_for": for_loop[0].create_declare() for_loop[0].suggest = "uword" else: for_loop[0].create_declare() for_loop[0].suggest = "int" cur += 1 while self.code[cur] in " \t": cur += 1 if self.code[cur] != "=": self.syntaxerror(cur, "for-loop assignment (=)") cur += 1 while self.code[cur] in " \t": cur += 1 cur = self.create_expression(for_loop, cur) cur += 1 while self.code[cur] in ") \t": cur += 1 if self.code[cur] == ",": cur += 1 while self.code[cur] in " \t\n;": cur += 1 end = self.create_codeblock(for_loop, cur) #for_loop.code = self.code[start:end] return end def ifbranch(self, parent, start): ''' If-ifelse-else branch Args: self (Builder): Code constructor. parent (Node): Parent node cur (int): Current position in code Returns: int: Index to end of code block Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", ... """if a ... b ... elseif c ... d ... end""") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 If branches.ifbranch 'if a' 3 Expression expression.create 'a' 3 Var variables.variable 'a' 4 Codeblock codeblock.codeblock 7 Statement codeblock.codeblock 'b' 7 Expression expression.create 'b' 7 Var variables.variable 'b' 9 Else if branches.ifbranch 'elseif c' 16 Expression expression.create 'c' 16 Var variables.variable 'c' 17 Codeblock codeblock.codeblock 20 Statement codeblock.codeblock 'd' 20 Expression expression.create 'd' 20 Var variables.variable 'd' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Branch code_block TYPE 1 4| | If code_block TYPE 1 4| | | Var unknown TYPE a 1 5| | | Block code_block TYPE 2 8| | | | Statement code_block TYPE 2 8| | | | | Var unknown TYPE b 3 10| | Elif code_block TYPE 3 17| | | Var unknown TYPE c 3 18| | | Block code_block TYPE 4 21| | | | Statement code_block TYPE 4 21| | | | | Var unknown TYPE d ''' if self.code[start:start+2] != "if" or self.code[start+2] not in c.k_end: self.syntaxerror(start, "if branch start") branch = matlab2cpp.collection.Branch(parent, cur=start) cur = start cur += 2 while self.code[cur] in " \t": cur += 1 if self.code[cur] not in c.e_start: self.syntaxerror(cur, "expression start") end = findend.expression(self, cur) if self.disp: print("%4d If " % (start), end="") print("%-20s" % "branches.ifbranch", end="") print(repr(self.code[start:end+1])) node = matlab2cpp.collection.If(branch, cur=cur, code=self.code[start:end+1]) # this code below gives error if the if statement is: if (a > 1) && (a < 6) #because it digs down recursevly in (a > 1) and not the whole statement #if self.code[cur] == "(": # cur += 1 # while self.code[cur] in " \t": # cur += 1 self.create_expression(node, cur) cur = end+1 end = self.create_codeblock(node, cur) #node.code = self.code[cur:end] cur = end while self.code[cur:cur+6] == "elseif" and self.code[cur+6] in c.k_end: #node.code = self.code[start:cur] start = cur cur += 6 while self.code[cur] in " \t": cur += 1 end = findend.expression(self, cur) if self.disp: print("%4d Else if " % (start), end="") print("%-20s" % "branches.ifbranch", end="") print(repr(self.code[start:end+1])) node = matlab2cpp.collection.Elif(branch, cur=start, code=self.code[start:end+1]) #if self.code[cur] == "(": # cur += 1 # while self.code[cur] in " \t": # cur += 1 self.create_expression(node, cur) cur = end+1 cur = end = self.create_codeblock(node, cur) cur = end #node.code = self.code[start:cur] if self.code[cur:cur+4] == "else" and self.code[cur+4] in c.k_end: start = cur cur += 4 if self.disp: print("%4d Else " % (start), end="") print("%-20s" % "branches.ifbranch", end="") print(repr(self.code[start:start+5])) node = matlab2cpp.collection.Else(branch, cur=start) end = self.create_codeblock(node, cur) #node.code = self.code[start:end+1] branch.code = self.code[start:end+1] return end if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/tree/builder.py ================================================ from __future__ import print_function from .. import collection, rules, configure class Builder(object): """ Convert Matlab-code to a tree of nodes. Given that one or more Matlab programs are loaded, each one can be accessed through indexing the Builder instance. For example:: >>> from matlab2cpp.tree import Builder >>> builder = Builder() >>> builder.load("prg1.m", "function y=prg1(x); y=x") >>> builder.load("prg2.m", "prg1(4)") >>> builder.configure(suggest=True) >>> builder.translate() >>> prg1, prg2 = builder >>> print(prg1.cls, prg1.name) Program prg1.m >>> print(prg2.cls, prg2.name) Program prg2.m Programs that are loaded, configured and translated, can be converted into C++ code through the front end functions in :py:mod:`matlab2cpp.qfunctions`:: >>> print(matlab2cpp.qhpp(prg1)) #ifndef PRG1_M_HPP #define PRG1_M_HPP #include using namespace arma ; int prg1(int x) { int y ; y = x ; return y ; } #endif >>> print(matlab2cpp.qcpp(prg2)) #include using namespace arma ; int main(int argc, char** argv) { prg1(4) ; return 0 ; } """ def __init__(self, disp=False, comments=True, original=False, enable_omp=False, enable_tbb=False, reference=False, **kws): """ Args: disp (bool): Verbose output while loading code comments (bool): Include comments in the code interpretation **kws: Optional arguments are passed to :py:mod:`matlab2cpp.rules` """ self.disp = disp self.comments = comments self.original = original self.project = collection.Project() self.project.kws = kws self.project.builder = self self.enable_omp = enable_omp self.enable_tbb = enable_tbb self.reference = reference self.configured = False def __getitem__(self, index): """ Get root node for a program through indexing builder[index] <=> Builder.__getitem__(builder, index) Args: index (int): Loaded order Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder() >>> builder.load("prg1.m", "function y=prg1(x); y=x") >>> builder.load("prg2.m", "prg1(4)") >>> prg1 = builder[0] >>> prg2 = builder[1] """ return self.project[index] def __str__(self): """ Summary of all node trees Same as :py:func:`matlab2cpp.Node.summary`, but for the whole project. str(builder) <=> Builder.__str__(builder) Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder() >>> print(builder) # doctest: +NORMALIZE_WHITESPACE Project unknown TYPE See also: :py:func:`matlab2cpp.Node.summary` """ return self.project.summary() def load(self, name, code): """ Load a Matlab code into the node tree. The code is inserted into the attribute `self.code` and initiate the :py:func:`matlab2cpp.Builder.create_program`, which evoces various other ``create_*`` methods. Each method creates nodes and/or pushes the job over to other create methods. Args: name (str): Name of program (usually valid filename). code (str): Matlab code to be loaded Raises: SyntaxError: Error in the Matlab code. Example:: >>> from matlab2cpp.tree import Builder >>> builder = Builder() >>> builder.load("unnamed.m", "") >>> print(builder) # doctest: +NORMALIZE_WHITESPACE Project unknown TYPE | Program unknown TYPE unnamed.m | | Includes unknown TYPE 1 1| | Funcs unknown TYPE unnamed.m | | Inlines unknown TYPE unnamed.m | | Structs unknown TYPE unnamed.m | | Headers unknown TYPE unnamed.m | | Log unknown TYPE unnamed.m """ assert isinstance(name, str) assert isinstance(code, str) if self.disp: print("loading", name) #Replace ... [stuff] \n with ... [stuff] \n " " l = 0 while l != -1: #str.find returns -1 if not found l = code.find("...", l) if l != -1: m = code.find("\n", l) if m != -1: #m = m+1 code = code[:m+1] + " " + code[m+1:] l = m self.code = code + "\n\n\n" self.create_program(name) index = self.project.names.index(name) program = self.project[index] nodes = program.flatten(False, True, False) # Find if some names should be reserved unassigned = {} for node in nodes[::-1]: if node.cls not in ("Var", "Fvar", "Cvar", "Set", "Cset", "Sset", "Fset", "Nset", "Get", "Cget", "Fget", "Nget", "Sget"): continue if node.name not in unassigned: unassigned[node.name] = True if node.parent.cls in ("Params", "Declares"): unassigned[node.name] = False unassigned = [k for k,v in unassigned.items() if v] reserved = set([]) for i in range(len(unassigned)-1, -1, -1): if unassigned[i] in rules._reserved.reserved: reserved.add(unassigned.pop(i)) for node in nodes[::-1]: if node.name in reserved: node.backend = "reserved" program.unassigned = unassigned def get_unknowns(self, index=-1): """ Get unknown variables and function calls names in a program. Args: index (int, str): Either loading index or the name of the program. Returns: list: strings of the names of the unknown variables and calls. Example: >>> builder = Builder(); builder.load("prg.m", "a;b;c") >>> print(sorted(builder.get_unknowns())) ['a', 'b', 'c'] """ if isinstance(index, str): index = self.project.names.index(index) assert isinstance(index, int) return self.project[index].unassigned def configure(self, suggest=True, **kws): """ Configure node tree with datatypes. Args: suggest (bool): Uses suggestion engine to fill in types Example:: >>> from matlab2cpp.tree import Builder >>> builder = Builder() >>> builder.load("unnamed.m", "a=1; b=2.; c='c'") >>> print(builder) # doctest: +NORMALIZE_WHITESPACE Project unknown TYPE | Program unknown TYPE unnamed.m | | Includes unknown TYPE 1 1| | Funcs unknown TYPE unnamed.m 1 1| | | Main unknown TYPE main 1 1| | | | Declares unknown TYPE 1 1| | | | | Var unknown TYPE a 1 1| | | | | Var unknown TYPE b 1 1| | | | | Var unknown TYPE c 1 1| | | | Returns unknown TYPE 1 1| | | | Params unknown TYPE 1 1| | | | Block unknown TYPE 1 1| | | | | Assign unknown TYPE 1 1| | | | | | Var unknown TYPE a 1 3| | | | | | Int unknown TYPE 1 6| | | | | Assign unknown TYPE 1 6| | | | | | Var unknown TYPE b 1 8| | | | | | Float unknown TYPE 1 12| | | | | Assign unknown TYPE 1 12| | | | | | Var unknown TYPE c 1 14| | | | | | String unknown TYPE | | Inlines unknown TYPE unnamed.m | | Structs unknown TYPE unnamed.m | | Headers unknown TYPE unnamed.m | | Log unknown TYPE unnamed.m >>> builder.configure(suggest=True) >>> print(builder) # doctest: +NORMALIZE_WHITESPACE Project program TYPE | Program program TYPE unnamed.m | | Includes program TYPE 1 1| | Funcs program TYPE unnamed.m 1 1| | | Main func_return TYPE main 1 1| | | | Declares func_return TYPE 1 1| | | | | Var int int a 1 1| | | | | Var double double b 1 1| | | | | Var string string c 1 1| | | | Returns func_return TYPE 1 1| | | | Params func_return TYPE 1 1| | | | Block code_block TYPE 1 1| | | | | Assign int int 1 1| | | | | | Var int int a 1 3| | | | | | Int int int 1 6| | | | | Assign double double 1 6| | | | | | Var double double b 1 8| | | | | | Float double double 1 12| | | | | Assign string string 1 12| | | | | | Var string string c 1 14| | | | | | String string string | | Inlines program TYPE unnamed.m | | Structs program TYPE unnamed.m | | Headers program TYPE unnamed.m | | Log program TYPE unnamed.m Raises: RuntimeError: Method can only be run once. """ if self.configured: raise RuntimeError("configure can only be run once") self.configured = True configure.configure(self, suggest, **kws) def translate(self): """ Perform translation on all nodes in all programs in builder. Also runs configure if not done already. See also: :py:mod:`matlab2cpp.rules` """ if not self.configured: self.configure() for program in self.project: program.translate() def syntaxerror(self, cur, text): """ Raise an SyntaxError related to the Matlab code. Called from various ``create_*`` methods when code is invalid. Args: cur (int): Current location in the Matlab code text (str): The related rational presented to the user Raises: SyntaxError: Error in the Matlab code. Example:: >>> from matlab2cpp.tree import Builder >>> builder = Builder() >>> prg = builder.load("unnamed.m", "0123456789") >>> builder.syntaxerror(7, "example of error") Traceback (most recent call last): ... SyntaxError: File: unnamed.m, line 1 in Matlab code: 0123456789 ^ Expected: example of error """ start = cur-1 while start > 0 and self.code[start] != "\n": start -= 1 end = cur+1 while end < len(self.code) and self.code[end] != "\n": end += 1 out = "File: %s, line %d in Matlab code:\n" % (self.project[-1].name, self.code.count("\n", 0, cur)+1) out += self.code[start:end] + "\n" + " "*(cur-start) + "^\n" out += "Expected: " + text raise SyntaxError(out) def create_program(self, name): """ Create program meta variables and initiates to fill them | Structure: | Program | | Includes | | Funcs | | Inlines | | Structs | | Headers | | Log Args: name (str): filename of program Returns: int: position in program when scanning is complete. See also: :py:func:`matlab2cpp.tree.functions.program` """ from . import functions assert isinstance(name, (int, str)) return functions.program(self, name) def create_function(self, parent, cur): """ Create function (not main) | Structure: | Func | | Declares | | Returns | | Params | | Args: parent (Funcs): Reference to parent node cur (int): position where function is identified Returns: int: position where function ends See also: :py:func:`matlab2cpp.tree.functions.function` """ from . import functions assert isinstance(parent, collection.Funcs) return functions.function(self, parent, cur) def create_main(self, parent, cur): """ Create main function | Structure: | Main | | Declares | | Returns | | Params | | Args: parent (Funcs): Reference to parent node cur (int): position where main function is identified Returns: int: position where main function ends See also: :py:func:`matlab2cpp.tree.functions.main` """ from . import functions assert isinstance(parent, collection.Funcs) return functions.main(self, parent, cur) def create_lambda_assign(self, parent, cur, eq_loc): """ Create assignments involving lambda functions | Structure: | Assign | | | | Args: parent (Block): Reference to parent node cur (int): position where Lambda assignment is identified eq_loc (int): position of assignment equal sign Returns: int: position where Lambda assignment ends See also: :py:func:`matlab2cpp.tree.functions.lambda_assign` """ from . import functions assert isinstance(parent, collection.Block) return functions.lambda_assign(self, parent, cur, eq_loc) def create_lambda_func(self, parent, cur): """ Create lambda function | Structure (function part): | Func | | Declares | | Returns | | | Var (_retval) | | Params | | Block | | | Assign | | | | Var (_retval) | | | | | | Structure (lambda part): | Lambda Args: parent (Assign): Reference to parent node cur (int): position where Lambda function is identified Returns: int: position where Lambda function ends See also: :py:func:`matlab2cpp.tree.functions.lambda_func` """ from . import functions assert isinstance(parent, collection.Assign) return functions.lambda_func(self, parent, cur) def create_codeblock(self, parent, cur): """ Create codeblock Block | Structure: | Assign|Assigns|Bcomment|Ecomment|Lcomment|Statement `Statements` are handled locally and evoces Legal parents: Case, Catch, Elif, Else, For, Func, If, Main, Otherwise, Switch, Try, While Args: parent (Node): Reference to parent node cur (int): position where codeblock is identified Returns: int: position where codeblock ends See also: :py:func:`matlab2cpp.tree.codeblock.codeblock` """ from . import codeblock pnames = ["Case", "Catch", "Elif", "Else", "Parfor", "For", "Func", "If", "Main", "Otherwise", "Switch", "Try", "While"] pnodes = [getattr(collection, name) for name in pnames] ppart = [isinstance(parent, node) for node in pnodes] if not any(ppart): raise AssertionError( "parent of Block: %s not valid group parent\n%s" %\ (parent.cls, str(pnames))) return codeblock.codeblock(self, parent, cur) def create_assigns(self, parent, cur, eq_loc): """ Create assignment with multiple returns | Structure: | Assigns | | | | Get|Var Args: parent (Block): Reference to parent node cur (int): position where assignments is identified eq_loc (int): position of assignment equal sign Returns: int: position where assignments ends See also: :py:func:`matlab2cpp.tree.assign.multi` """ from . import assign assert isinstance(parent, collection.Block) return assign.multi(self, parent, cur, eq_loc) def create_assign(self, parent, cur, eq_loc): """ Create assignment with single return | Structure: | Assign | | | | Get|Var Args: parent (Block): Reference to parent node cur (int): position where assignment is identified eq_loc (int): position of assignment equal sign Returns: int: position where assignment ends See also: :py:func:`matlab2cpp.tree.assign.single` """ from . import assign assert isinstance(parent, collection.Block) return assign.single(self, parent, cur, eq_loc) def create_parfor(self, parent, cur): """ Create parfor-loop | Structure: | Parfor | | | | | | Args: parent (Block): Reference to parent node cur (int): position where for-loop is identified Returns: int: position where for-loop ends """ from . import branches assert isinstance(parent, collection.Block) return branches.parforloop(self, parent, cur) def create_for(self, parent, cur): """ Create For-loop | Structure: | For | | | | | | Args: parent (Block): Reference to parent node cur (int): position where for-loop is identified Returns: int: position where for-loop ends See also: :py:func:`matlab2cpp.tree.branches.forloop` """ from . import branches assert isinstance(parent, collection.Block) return branches.forloop(self, parent, cur) def create_if(self, parent, cur): """ Create if-branch | Structure (main): | Branch | | If | | | | | | | | * | | ? | | Structure (else if): | Elif | | | | | | Structure (else): | Else | | Args: parent (Block): Reference to parent node cur (int): position where if-branch is identified Returns: int: position where if-branch ends See also: :py:func:`matlab2cpp.tree.branches.ifbranch` """ from . import branches assert isinstance(parent, collection.Block) return branches.ifbranch(self, parent, cur) def create_while(self, parent, cur): """ Create while-loop | Structure: | While | | | | Args: parent (Block): Reference to parent node cur (int): position where while-loop is identified Returns: int: position where while-loop ends See also: :py:func:`matlab2cpp.tree.branches.whileloop` """ from . import branches assert isinstance(parent, collection.Block) return branches.whileloop(self, parent, cur) def create_switch(self, parent, cur): """ Create switch-branch | Structure (main): | Switch | | | | + | | ? | | Structure (case): | Case | | | | | | Structure (otherwise): | Otherwise | | Args: parent (Block): Reference to parent node cur (int): position where switch is identified Returns: int: position where switch ends See also: :py:func:`matlab2cpp.tree.branches.switch` """ from . import branches assert isinstance(parent, collection.Block) return branches.switch(self, parent, cur) def create_try(self, parent, cur): """ Create try-block | Structure: | Tryblock | | Try | | | | | Catch | | | Args: parent (Block): Reference to parent node cur (int): position where try-block is identified Returns: int: position where try-block ends See also: :py:func:`matlab2cpp.tree.branches.trybranch` """ from . import branches assert isinstance(parent, collection.Block) return branches.trybranch(self, parent, cur) def create_cell(self, parent, cur): """ Create cell-structure (expression) | Structure: | Cell | | + Args: parent (Node): Reference to parent node cur (int): position where cell is identified Returns: int: position where cell ends See also: :py:func:`matlab2cpp.tree.misc.cell` """ from . import misc return misc.cell(self, parent, cur) def create_pragma_parfor(self, parent, cur): from . import misc assert isinstance(parent, collection.Block) return misc.pragma_for(self, parent, cur) def create_comment(self, parent, cur): """ Create comment | Structure: | Bcomment|Ecomment|Lcomment Args: parent (Block): Reference to parent node cur (int): position where comment is identified Returns: int: position where comment ends See also: :py:func:`matlab2cpp.tree.misc.comment` """ from . import misc assert isinstance(parent, collection.Block) return misc.comment(self, parent, cur) def create_verbatim(self, parent, cur): """ Create verbatim translation A manual overrides switch provided by the user to perform translations. | Structure: | Verbatim Args: parent (Block): Reference to parent node cur (int): position where verbatim is identified Returns: int: position where verbatim ends See also: :py:func:`matlab2cpp.tree.misc.verbatim` """ from . import misc return misc.verbatim(self, parent, cur) def create_string(self, parent, cur): """ Create string (Expression) | Structure: | String Args: parent (Node): Reference to parent node cur (int): position where string is identified Returns: int: position where string ends See also: :py:func:`matlab2cpp.tree.misc.string` """ from . import misc return misc.string(self, parent, cur) def create_list(self, parent, cur): """ Create list of expressions | Structure: | * Args: parent (Node): Reference to parent node cur (int): position where list is identified Returns: int: position where list ends See also: :py:func:`matlab2cpp.tree.misc.list` """ from . import misc return misc.list(self, parent, cur) def create_matrix(self, parent, cur): """ Create matrix (Expression) | Structure (main): | Matrix | | * | | Structure (vector): | Vector | | * Args: parent (Node): Reference to parent node cur (int): position where matrix is identified Returns: int: position where matrix ends See also: :py:func:`matlab2cpp.tree.misc.matrix` """ from . import misc return misc.matrix(self, parent, cur) def create_number(self, parent, cur): """ Create number (Expression) | Structure: | Int|Float|Imag Args: parent (Node): Reference to parent node cur (int): position where number is identified Returns: int: position where number ends See also: :py:func:`matlab2cpp.tree.misc.number` """ from . import misc return misc.number(self, parent, cur) def create_reserved(self, parent, cur): """ Create Matlab reserved keywords. Some words like "hold", "grid" and "clear", behaves differently than regular Matlab. They take arguments after space, not in parenthesis. | Structure (main): | Get | | * | | Structure (string): | String Args: parent (Block): Reference to parent node cur (int): position where reserved statement is identified Returns: int: position where reserved statement ends See also: :py:func:`matlab2cpp.tree.misc.reserved` """ from . import misc assert isinstance(parent, collection.Block) return misc.reserved(self, parent, cur) def create_variable(self, parent, cur): """ Create left-hand-side variable (Expression) | Structure: | Cget|Cvar|Fget|Fvar|Get|Nget|Var|Sget|Svar | | ? Args: parent (Node): Reference to parent node cur (int): position where variable is identified Returns: int: position where variable ends See also: :py:func:`matlab2cpp.tree.variables.variable` """ from . import variables return variables.variable(self, parent, cur) def create_assign_variable(self, parent, cur, end=None): """ Create right-hand-side variable (Expression) | Structure: | Cset|Cvar|Fset|Fvar|Nset|Var|Set|Sset|Svar | | ? Args: parent (Node): Reference to parent node cur (int): position where variable is identified end (int, optional): position where variable ends Returns: int: position where variable ends See also: :py:func:`matlab2cpp.tree.variables.assign` """ from . import variables return variables.assign(self, parent, cur, end) def create_expression(self, parent, cur, end=None): """ Create expression Main engine for creating expression. Args: parent (Node): Reference to parent node cur (int): position where expression is identified end (int, optional): position where expression ends Returns: int: position where expression ends See also: :py:func:`matlab2cpp.tree.expression.create` """ from . import expression return expression.create(self, parent, cur, end) ================================================ FILE: src/matlab2cpp/tree/codeblock.py ================================================ """The main codeblock loop""" from __future__ import print_function import matlab2cpp from . import findend, constants as c def codeblock(self, parent, start): ''' If-ifelse-else branch Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code Returns: int: Index to end of codeblock Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "a; 'b'; 3") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock 'a' 0 Expression expression.create 'a' 0 Var variables.variable 'a' 3 Statement codeblock.codeblock "'b'" 3 String misc.string "'b'" 8 Statement codeblock.codeblock '3' 8 Expression expression.create '3' 8 Int misc.number '3' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Var unknown TYPE a 1 4| Statement code_block TYPE 1 4| | String string string 1 9| Statement code_block TYPE 1 9| | Int int int ''' cur = start block = matlab2cpp.collection.Block(parent, cur=cur) if self.disp: print("%4d Codeblock " % cur, end="") print("%-20s" % "codeblock.codeblock") is_end_terminated = False while True: #print(self.code[cur:cur+5]) if self.code[cur] in " \t;": pass elif self.code[cur] == "\n": if len(self.code)-cur < 3: break #%#PARFOR token elif self.code[cur:cur+8] == "%#PARFOR": cur = self.create_pragma_parfor(block, cur) elif self.code[cur] == "%": cur = self.create_comment(block, cur) elif self.code[cur:cur+3] == "___": cur = self.create_verbatim(block, cur) elif self.code[cur] == "[": # Divide between statement and assignment eq_loc = findend.matrix(self, cur)+1 while self.code[eq_loc] in " \t": eq_loc += 1 if self.code[eq_loc] == "=" and self.code[eq_loc+1] != "=": cur = self.create_assigns(block, cur, eq_loc) else: statement = matlab2cpp.collection.Statement(block, cur=cur) end = findend.expression(self, cur) if self.disp: print("%4d Statement " % cur, end="") print("%-20s" % "codeblock.codeblock", end="") print(repr(self.code[cur:end+1])) statement.code = self.code[cur:end+1] cur = self.create_expression( statement, cur, end=end) elif self.code[cur] == "'": end = findend.string(self, cur) if self.disp: print("%4d Statement " % cur, end="") print("%-20s" % "codeblock.codeblock", end="") print(repr(self.code[cur:end+1])) statement = matlab2cpp.collection.Statement(block, cur=cur, code=self.code[cur:end+1]) cur = self.create_string(statement, cur) elif self.code[cur:cur+4] == "case" and self.code[cur+4] in c.k_end: break elif self.code[cur:cur+5] == "catch" and self.code[cur+5] in c.k_end: break elif self.code[cur:cur+3] == "end" and self.code[cur+3] in c.k_end: cur += 3 is_end_terminated = True break elif self.code[cur:cur+4] == "else" and self.code[cur+4] in c.k_end: break elif self.code[cur:cur+6] == "elseif" and self.code[cur+6] in c.k_end: break elif self.code[cur:cur+6] == "parfor" and self.code[cur+6] in c.k_end: cur = self.create_parfor(block, cur) elif self.code[cur:cur+3] == "for" and self.code[cur+3] in c.k_end: cur = self.create_for(block, cur) elif self.code[cur:cur+8] == "function" and\ self.code[cur+8] in c.k_end + "[": cur -= 1 break elif self.code[cur:cur+2] == "if" and self.code[cur+2] in c.k_end: cur = self.create_if(block, cur) elif self.code[cur:cur+9] == "otherwise" and self.code[cur+9] in c.k_end: break elif self.code[cur:cur+6] == "switch" and self.code[cur+6] in c.k_end: cur = self.create_switch(block, cur) elif self.code[cur:cur+3] == "try" and self.code[cur+3] in c.k_end: cur = self.create_try(block, cur) elif self.code[cur:cur+5] == "while" and self.code[cur+5] in c.k_end: cur = self.create_while(block, cur) elif self.code[cur:cur+4] == "hold" and \ self.code[cur+4] not in c.letters+c.digits+"_": cur = self.create_reserved(block, cur) elif self.code[cur:cur+4] == "load" and \ self.code[cur+4] not in c.letters+c.digits+"_": cur = self.create_reserved(block, cur) elif self.code[cur:cur+4] == "disp" and \ self.code[cur+4] not in c.letters+c.digits+"_": cur = self.create_reserved(block, cur) elif self.code[cur:cur+4] == "grid" and \ self.code[cur+4] not in c.letters+c.digits+"_": cur = self.create_reserved(block, cur) elif self.code[cur] in c.e_start: j = findend.expression(self, cur) j += 1 while self.code[j] in " \t": j += 1 eq_loc = j if self.code[eq_loc] == "=": # and self.code[eq_loc+1] != "=": j = eq_loc +1 while self.code[j] in " \t": j += 1 if self.code[j] == "@": cur = self.create_lambda_assign(block, cur, eq_loc) else: cur = self.create_assign(block, cur, eq_loc) else: end = findend.expression(self, cur) if self.disp: print("%4d Statement " % cur, end="") print("%-20s" % "codeblock.codeblock", end="") print(repr(self.code[cur:end+1])) statement = matlab2cpp.collection.Statement(block, cur=cur, code=self.code[cur:end+1]) cur = self.create_expression(statement, cur, end=end) cur += 1 if len(self.code)-cur<3: break block.is_end_terminated = is_end_terminated block.code = self.code[start:cur+1] return cur if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/tree/constants.py ================================================ """ Matlab consists of various legal start and end characters depending on context. This module is a small collection of constants available to ensure that context is defined correctly. Attributes: e_start (str): characers allowed in expression start e_end (str): characers allowed to terminate expression l_start (str): characters allowed in list start l_end (str): characters allowed to terminate list prefixes (str): characters allowed as prefix univery operators postfix1 (str): characters allowed as postfix univary operators postfix2 (tuple): same as postfix1, but tuple of multi-char operators op1 (str): characters allowed as infix operators op2 (tuple): same as op1, but tuple of multi-char operators """ import string letters = string.ascii_letters digits = string.digits # start and end of expression e_start = letters + digits + "[({~-+:@.'" e_end = "%])},;\n" # start and end of lists l_start = "[({" l_end = "]})" # end of keyword k_end = " \t(,;\n%" prefixes = "-+~" postfix1 = "'" postfix2 = (".'",) # operators with one character op1 = r"^\/*+-:<>&|" # operators using two characters op2 = ( ".^", ".\\", "./", ".*", "<=", ">=", "==", "~=", "&&", "||") ================================================ FILE: src/matlab2cpp/tree/expression.py ================================================ """ Expression interpretor """ from __future__ import print_function from .. import collection RECIVER_OPERATORS = { "^": collection.Exp, ".^": collection.Elexp, "\\": collection.Leftmatrixdivision, ".\\": collection.Leftelementdivision, "/": collection.Matrixdivision, "./": collection.Elementdivision, "*": collection.Mul, ".*": collection.Elmul, "+": collection.Plus, "-": collection.Minus, ":": collection.Colon, "<": collection.Lt, "<=": collection.Le, ">": collection.Gt, ">=": collection.Ge, "==": collection.Eq, "~=": collection.Ne, "&": collection.Band, "|": collection.Bor, "&&": collection.Land, "||": collection.Lor, } def create(self, node, start, end=None, start_opr=None): """ Create expression in three steps: 1) In order, split into sub-expressions for each dividing operator 2) Address prefixes, postfixes, parenthesises, etc. 3) Identify the remaining singleton Args: self (Builder): Code constructor. node (Node): Reference to the parent node start (int): current possition in code end (int, optional): end of expression. Required for space-delimited expression. start_opr (str, optional): At which operator the recursive process is. (For internal use) Returns: int : index to end of the expression Examples:: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "a*b+c/d") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock 'a*b+c/d' 0 Expression expression.create 'a*b+c/d' 0 Expression expression.create 'a*b' 0 Expression expression.create 'a' 0 Var variables.variable 'a' 2 Expression expression.create 'b' 2 Var variables.variable 'b' 4 Expression expression.create 'c/d' 4 Expression expression.create 'c' 4 Var variables.variable 'c' 6 Expression expression.create 'd' 6 Var variables.variable 'd' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Plus expression TYPE 1 1| | | Mul expression TYPE 1 1| | | | Var unknown TYPE a 1 3| | | | Var unknown TYPE b 1 5| | | Matrixdivisionexpression TYPE 1 5| | | | Var unknown TYPE c 1 7| | | | Var unknown TYPE d """ from . import findend, identify, constants if self.code[start:start+3] == "...": start = findend.dots(self, start) start += 1 while self.code[start] in " \t": start += 1 if self.code[start] == ":": if self.disp: print("%4d Expression " % (start), end="") print("%-20s" % "expression.create", end="") print(repr(self.code[start:start+1])) print("%4d All " % (start), end="") print("%-20s" % "expression.create", end="") print(repr(self.code[start:start+1])) collection.All(node, cur=start, code=self.code[start]) return start if end is None: end = findend.expression(self, start) if self.disp: print("%4d Expression " % (start), end="") print("%-20s" % "expression.create", end="") print(repr(self.code[start:end+1])) if self.code[start] not in constants.e_start: self.syntaxerror(start, "expression start") operators = [ "||", "&&", "|", "&", "~=", "==", ">=", ">", "<=", "<", ":", "+", "-", ".*", "*", "./", "/", ".\\", "\\", ".^", "^"] if not (start_opr is None): operators = operators[operators.index(start_opr)+1:] for opr in operators: # Pre-screen if opr not in self.code[start:end+1]: continue starts = [start] last = start ends = [] k = start while True: if self.code[k] == "(": k = last = findend.paren(self, k) elif self.code[k] == "[": k = last = findend.matrix(self, k) elif self.code[k] == "{": k = last = findend.cell(self, k) elif self.code[k] == "'": if identify.string(self, k): k = last = findend.string(self, k) else: last = k elif opr == self.code[k:k+len(opr)]: if opr in "+-": # no prefixes and no (scientific) numbers if self.code[last] not in constants.letters+constants.digits+")]}" or\ self.code[k-1] in "dDeE" and self.code[k-2] in\ constants.digits+"." and self.code[k+1] in constants.digits: k += 1 continue k += len(opr)-1 while self.code[k+1] in " \t": k += 1 # no all-operator if opr == ":" and self.code[k+1] in ",;\n)]}": k += 1 continue starts.append(k+1) ends.append(last) elif self.code[k] in constants.letters+constants.digits+"_": last = k k += 1 if k >= end: ends.append(end) break if len(ends) > 1: operator = RECIVER_OPERATORS[opr] node = operator(node) node.cur = start node.code = self.code[starts[0]:ends[-1]+1] for s,e in zip(starts, ends): create(self, node, s, e, opr) return end # All operators removed at this point! END = end # Prefixes while self.code[start] in "-~": if self.code[start] == "-": node = collection.Neg(node, cur=start, code=self.code[start:end+1]) start += 1 if self.code[start] == "~": node = collection.Not(node, cur=start, code=self.code[start:end+1]) start += 1 while self.code[start] in " \t": start += 1 # Postfixes if self.code[end] == "'" and not self.code[start] == "'": if self.code[end-1] == ".": node = collection.Transpose(node, cur=start, code=self.code[start:end+1]) end -= 2 else: node = collection.Ctranspose(node, cur=start, code=self.code[start:end+1]) node.cur = start node.code = self.code[start:end+1] end -= 1 while self.code[end] in " \t": end -= 1 # Parenthesis if self.code[start] == "(": if self.code[end] != ")": self.syntaxerror(end, "parenthesis end") node = collection.Paren(node, cur=start, code=self.code[start:end+1]) start += 1 while self.code[start] in " \t": start += 1 end -= 1 while self.code[end] in " \t": end -= 1 return create(self, node, start, end) # Reserved keywords elif self.code[start:start+3] == "end" and self.code[start+3] in " +-:\t" + constants.e_end: node = collection.End(node, cur=start, code=self.code[start:start+3]) elif self.code[start:start+6] == "return" and self.code[start+6] in " ,;\n": node = collection.Return(node, cur=start, code=self.code[start:start+6]) elif self.code[start:start+5] == "break" and self.code[start+5] in " ,;\n": node = collection.Break(node, cur=start, code=self.code[start:start+5]) # Rest elif self.code[start] == "'": if self.code[end] != "'": self.syntaxerror(end, "string end") if "\n" in self.code[start:end]: self.syntaxerror(end, "non line-feed characters in string") collection.String(node, self.code[start+1:end], cur=start, code=self.code[start:end+1]) elif self.code[start] in constants.digits or\ self.code[start] == "." and self.code[start+1] in constants.digits: cur = self.create_number(node, start) elif self.code[start] == "[": cur = self.create_matrix(node, start) elif self.code[start] == "{": cur = self.create_cell(node, start) else: if self.code[start] not in constants.letters+"@": self.syntaxerror(start, "variable name") cur = self.create_variable(node, start) return END ================================================ FILE: src/matlab2cpp/tree/findend.py ================================================ """ Look-ahead routines to find end character. +------------------------------------------------------+------------------------+ | Function | Description | +======================================================+========================+ | :py:func:`~matlab2cpp.tree.findend.expression` | Find end of expression | | | (non-space delimited) | +------------------------------------------------------+------------------------+ | :py:func:`~matlab2cpp.tree.findend.expression_space` | Find end of expression | | | (space delimited) | +------------------------------------------------------+------------------------+ | :py:func:`~matlab2cpp.tree.findend.matrix` | Find end of matrix | | | construction | +------------------------------------------------------+------------------------+ | :py:func:`~matlab2cpp.tree.findend.string` | Find end of string | +------------------------------------------------------+------------------------+ | :py:func:`~matlab2cpp.tree.findend.comment` | Find end of comment | +------------------------------------------------------+------------------------+ | :py:func:`~matlab2cpp.tree.findend.dots` | Find continuation | | | after ellipse | +------------------------------------------------------+------------------------+ | :py:func:`~matlab2cpp.tree.findend.paren` | Find matching | | | parenthesis | +------------------------------------------------------+------------------------+ | :py:func:`~matlab2cpp.tree.findend.cell` | Find matching | | | cell-parenthesis | +------------------------------------------------------+------------------------+ """ from . import constants, identify def expression(self, start): """ Find end of expression (non-space delimited) Args: self (Builder): Code constructor start (int): current position in code Returns: int: index location of end of expression """ if self.code[start] not in constants.e_start: self.syntaxerror(start, "expression start") k = start while True: if self.code[k] == "(": k = paren(self, k) #k += 1 #break elif self.code[k] == "[": k = matrix(self, k) elif self.code[k] == "'" and identify.string(self, k): k = string(self, k) elif self.code[k] == "{": k = cell(self, k) #elif self.code[k:k+3] == "...": # k = dots(self, k) elif self.code[k] == "=": if self.code[k+1] == "=": k += 1 else: break elif self.code[k] in "><~": if self.code[k+1] == "=": k += 1 elif self.code[k:k+3] == "...": k = dots(self, k) elif self.code[k] in constants.e_end: break k += 1 k -= 1 while self.code[k] in " \t": k -= 1 return k def expression_space(self, start): """ Find end of expression (space delimited) Args: self (Builder): Code constructor start (int): current position in code Returns: int: index location of end of expression """ if self.code[start] not in constants.e_start: self.syntaxerror(start, "expression start") k = last = start while True: if self.code[k] == "(": k = last = paren(self, k) elif self.code[k] == "[": k = last = matrix(self, k) elif self.code[k] == "'": if identify.string(self, k): k = last = string(self, k) else: last = k elif self.code[k] == "{": k = last = cell(self, k) elif self.code[k:k+3] == "...": k = dots(self, k) elif self.code[k] == ";": return last elif self.code[k] == "=": if self.code[k+1] == "=": k += 1 else: return last elif self.code[k] in "><~": if self.code[k+1] == "=": k += 1 elif self.code[k] in "+-": while self.code[k+1] in " \t": k += 1 elif self.code[k] in " \t": if identify.space_delimiter(self, k): return last while self.code[k+1] in " \t+-~": k += 1 elif self.code[k] in constants.e_end: return last elif self.code[k] in constants.letters + constants.digits + "_@": while self.code[k+1] in constants.letters + constants.digits + "_@": k += 1 last = k k += 1 def matrix(self, start): """ Find end of matrix construction Args: self (Builder): Code constructor start (int): current position in code Returns: int: index location of end of matrix """ if self.code[start] != "[": self.syntaxerror(start, "matrix start ([)") k = start+1 if identify.space_delimited(self, start): # Ignore first string occurrence while self.code[k] in " \t": k += 1 if self.code[k] == "'": k = string(self, k)+1 while True: if self.code[k] == "[": k = matrix(self, k) elif self.code[k] == "]": return k elif self.code[k] == "%": k = comment(self, k) elif self.code[k] == "'" and identify.string(self, k): #and self.code[k-1] in constants.s_start: k = string(self, k) k += 1 else: while True: if self.code[k] == "[": k = matrix(self, k) elif self.code[k] == "]": return k elif self.code[k] == "%": k = comment(self, k) elif self.code[k] == "'" and identify.string(self, k): k = string(self, k) k += 1 def string(self, start): """ Find end of string Args: self (Builder): Code constructor start (int): current position in code Returns: int: index location of end of string """ if self.code[start] != "'": self.syntaxerror(start, "start of string (')") k = self.code.find("'", start+1) if k == -1: self.syntaxerror(start, "matching end of string (')") if self.code.find("\n", start, k) != -1: self.syntaxerror(start, "non line-feed character in string") return k def pragma_for(self,start): end = self.code.find("\n", start) #while self.code[end+1] == "%" # end = self.code.find("\n", start+1) if end <= -1: self.syntaxerror(start, "comment end") return end def tbb_for(self, start): end = self.code.find("\n", start) if end <= -1: self.syntaxerror(start, "command end") return end def comment(self, start): """ Find end of comment Args: self (Builder): Code constructor start (int): current position in code Returns: int: index location of end of comment """ if self.code[start] != "%": self.syntaxerror(start, "comment start") # block comment if self.code[start+1] == "{": eoc = self.code.find("%}", start+2) if eoc <= -1: self.syntaxerror(start, "matching end of comment block (%})") return eoc+1 # Line comment eoc = self.code.find("\n", start) if eoc <= -1: self.syntaxerror(start, "comment end") return eoc #should find the end of verbatim area def verbatim(self, start): """ Find end of verbatim Arg: self(Builder): Code constructor start (int): current position in code Returns: int: index location of end of verbatim """ if self.code[start:start+3] != "___": self.syntaxerror(start, "verbatim start") return self.code.find("\n", start)-1 def dots(self, start): """ Find continuation of expression after ellipse Args: self (Builder): Code constructor start (int): current position in code Returns: int: index location of end of ellipse """ if self.code[start:start+3] != "...": self.syntaxerror(start, "three dots (...)") k = self.code.find("\n", start) if k == -1: self.syntaxerror(start, "next line feed character") return k def paren(self, start): """ Find matching parenthesis Args: self (Builder): Code constructor start (int): current position in code Returns: int: index location of matching parenthesis """ if self.code[start] != "(": self.syntaxerror(start, "start parenthesis") k = start+1 while True: if self.code[k] == "%": self.syntaxerror(k, "no comments in parenthesis") elif self.code[k:k+3] == "...": k = dots(self, k) elif self.code[k] == "'" and identify.string(self, k): k = string(self, k) elif self.code[k] == "[": k = matrix(self, k) elif self.code[k] == "(": k = paren(self, k) elif self.code[k] == ")": return k k += 1 def cell(self, start): """ Find matching cell-parenthesis Args: self (Builder): Code constructor start (int): current position in code Returns: int: index location of matching cell-parenthesis """ if self.code[start] != "{": self.syntaxerror(start, "start of cell ({)") k = start while True: if self.code[k] == "%": self.syntaxerror(k, "no comment in cell group") elif self.code[k] == "'" and identify.string(self, k): k = string(self, k) elif self.code[k] == "(": k = paren(self, k) elif self.code[k] == "[": k = matrix(self, k) elif self.code[k] == "}": l = k+1 while self.code[l] in " \t": l += 1 if self.code[l] != "{": return k k = l k += 1 ================================================ FILE: src/matlab2cpp/tree/functions.py ================================================ """ Functions, programs and meta-nodes +----------------------------------------------------+-------------------------+ | Functions | Description | +====================================================+=========================+ | :py:func:`~matlab2cpp.tree.functions.program` | Program outer shell | +----------------------------------------------------+-------------------------+ | :py:func:`~matlab2cpp.tree.functions.function` | Explicit functions | +----------------------------------------------------+-------------------------+ | :py:func:`~matlab2cpp.tree.functions.main` | Main script | +----------------------------------------------------+-------------------------+ | :py:func:`~matlab2cpp.tree.functions.lambda_assign`| Anonymous function | | | assignment | +----------------------------------------------------+-------------------------+ | :py:func:`~matlab2cpp.tree.functions.lambda_func` | Anonymous function | | | content | +----------------------------------------------------+-------------------------+ """ from __future__ import print_function from . import constants, findend, iterate, identify from .. import collection def program(self, name): """ The outer shell of the program Args: self (Builder): Code constructor name (str): Name of the program Returns: Node: The root node of the constructed node tree Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unamed", "a") # doctest: +NORMALIZE_WHITESPACE loading unamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock 'a' 0 Expression expression.create 'a' 0 Var variables.variable 'a' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder)) # doctest: +NORMALIZE_WHITESPACE Program program TYPE unamed | Includes program TYPE 1 1| Funcs program TYPE unamed 1 1| | Main func_return TYPE main 1 1| | | Declares func_return TYPE 1 1| | | Returns func_return TYPE 1 1| | | Params func_return TYPE 1 1| | | Block code_block TYPE 1 1| | | | Statement code_block TYPE 1 1| | | | | Var unknown TYPE a | Inlines program TYPE unamed | Structs program TYPE unamed | Headers program TYPE unamed | Log program TYPE unamed """ if self.disp: print(" Program ", end="") print("functions.program") # Create intial nodes program = collection.Program(self.project, name=name, cur=0, code=self.code) includes = collection.Includes(program, value=name, code='') funcs = collection.Funcs(program, name=name) collection.Inlines(program, name=name) collection.Structs(program, name=name) collection.Headers(program, name=name) collection.Log(program, name=name) #includes.include("armadillo") #includes.include("mconvert") # Start processing cur = 0 while True: if self.code[cur] in " \t;\n": pass elif self.code[cur] == "%": cur = findend.comment(self, cur) elif self.code[cur:cur+8] == "function": cur = self.create_function(funcs, cur) else: self.create_main(funcs, cur) break if len(self.code)-cur<=2: break cur += 1 #includes.include("namespace_arma") return program def function(self, parent, cur): """ Explicit functions Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code Returns: int : Index to end of function Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "function f(); end") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Function functions.function 'function f()' 12 Codeblock codeblock.codeblock >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Funcs program TYPE unnamed 1 1| Func func_returns TYPE f 1 1| | Declares func_returns TYPE 1 1| | Returns func_returns TYPE 1 11| | Params func_returns TYPE 1 13| | Block code_block TYPE >>> builder = Builder(True) >>> builder.load("unnamed", "function y=f(x); y=x") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Function functions.function 'function y=f(x)' 0 Return 'y' 12 Param functions.function 'x' 15 Codeblock codeblock.codeblock 17 Assign assign.single 'y=x' 17 Var variables.assign 'y' 19 Expression expression.create 'x' 19 Var variables.variable 'x' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Funcs program TYPE unnamed 1 1| Func func_return TYPE f 1 1| | Declares func_return TYPE 1 1| | | Var unknown TYPE y 1 1| | Returns func_return TYPE 1 10| | | Var unknown TYPE y 1 13| | Params func_return TYPE 1 14| | | Var unknown TYPE x 1 16| | Block code_block TYPE 1 18| | | Assign unknown TYPE x 1 18| | | | Var unknown TYPE y 1 20| | | | Var unknown TYPE x """ if self.code[cur:cur+8] != "function": self.syntaxerror(cur, "function start") if self.code[cur+8] not in constants.k_end+"[": self.syntaxerror(cur, "function name or return values") START = cur k = cur + 8 while self.code[k] in " \t": k += 1 if self.code[k] not in constants.letters+"[": self.syntaxerror(k, "function name or return values") start = k k = findend.expression(self, k) end = k k += 1 while self.code[k] in " \t": k += 1 # with return values if self.code[k] == "=": k += 1 while self.code[k] in " \t.": if self.code[k:k+3] == "...": k = findend.dots(self, k)+1 else: k += 1 l = k if self.code[l] not in constants.letters: self.syntaxerror(l, "function name") while self.code[l+1] in constants.letters+constants.digits+"_": l += 1 m = l+1 while self.code[m] in " \t": m += 1 if self.code[m] == "(": m = findend.paren(self, m) else: m = l if self.disp: print("%4d Function " % cur, end="") print("%-20s" % "functions.function", end="") print(repr(self.code[START:m+1])) name = self.code[k:l+1] func = collection.Func(parent, name, cur=cur) collection.Declares(func, code="") returns = collection.Returns(func, code=self.code[start:end+1]) # multi-return if self.code[start] == "[": if identify.space_delimited(self, start): L = iterate.space_list(self, start) else: L = iterate.comma_list(self, start) end = START for array in L: for s,e in array: end = s if self.disp: print("%4d Return " % cur, end="") print("%-20s" % "functions.function", end="") print(repr(self.code[s:e+1])) if not any([a in constants.letters+constants.digits+"_@" \ for a in self.code[s:e+1]]): self.syntaxerror(s, "return value") collection.Var(returns, self.code[s:e+1], cur=s, code=self.code[s:e+1]) # single return else: end = findend.expression(self, start) if self.disp: print("%4d Return " % cur, end="") print(repr(self.code[start:end+1])) collection.Var(returns, self.code[start:end+1], cur=start, code=self.code[start:end+1]) cur = l+1 while self.code[cur] in " \t": cur += 1 # No returns else: m = k if self.code[m] == "(": m = findend.paren(self, m) else: m = end if self.disp: print("%4d Function " % cur, end="") print("%-20s" % "functions.function", end="") print(repr(self.code[START:m+1])) end = start+1 while self.code[end] in constants.letters+"_": end += 1 name = self.code[start:end] func = collection.Func(parent, name, cur=cur) collection.Declares(func) returns = collection.Returns(func) cur = end # Parameters params = collection.Params(func, cur=cur) if self.code[cur] == "(": end = findend.paren(self, cur) params.code = self.code[cur+1:end] L = iterate.comma_list(self, cur) for array in L: for s,e in array: if self.disp: print("%4d Param " % cur, end="") print("%-20s" % "functions.function", end="") print(repr(self.code[s:e+1])) var = collection.Var(params, self.code[s:e+1], cur=s, code=self.code[s:e+1]) cur = end cur += 1 cur = self.create_codeblock(func, cur) # Postfix for var in returns: var.create_declare() end = cur func.code = self.code[START:end+1] collection.Header(func.program[4], func.name) return cur def main(self, parent, cur): """ Main script Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code Returns: int : Index to end of script Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "a") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock 'a' 0 Expression expression.create 'a' 0 Var variables.variable 'a' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder)) # doctest: +NORMALIZE_WHITESPACE Program program TYPE unnamed | Includes program TYPE 1 1| Funcs program TYPE unnamed 1 1| | Main func_return TYPE main 1 1| | | Declares func_return TYPE 1 1| | | Returns func_return TYPE 1 1| | | Params func_return TYPE 1 1| | | Block code_block TYPE 1 1| | | | Statement code_block TYPE 1 1| | | | | Var unknown TYPE a | Inlines program TYPE unnamed | Structs program TYPE unnamed | Headers program TYPE unnamed | Log program TYPE unnamed """ if self.disp: print("%4d Main " % cur, end="") print("functions.main") func = collection.Main(parent) collection.Declares(func)#, backend="func_return") collection.Returns(func)#, backend="func_return") collection.Params(func)#, backend="func_return") return self.create_codeblock(func, cur) def lambda_assign(self, node, cur, eq_loc): """ Anonymous function constructor Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code eq_loc (int): location of assignment sign ('=') Returns: int : Index to end of function line Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "f = @(x) 2*x") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Assign 'f = @(x) 2*x' functions.lambda_assign 0 Var variables.assign 'f' 4 Lambda functions.lambda_func'@(x) 2*x' 6 Expression expression.create 'x' 6 Var variables.variable 'x' 9 Expression expression.create '2*x' 9 Expression expression.create '2' 9 Int misc.number '2' 11 Expression expression.create 'x' 11 Var variables.variable 'x' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder)) # doctest: +NORMALIZE_WHITESPACE Program program TYPE unnamed | Includes program TYPE 1 1| Funcs program TYPE unnamed 1 1| | Main func_return TYPE main 1 1| | | Declares func_return TYPE 1 1| | | | Var func_lambda TYPE f 1 1| | | Returns func_return TYPE 1 1| | | Params func_return TYPE 1 1| | | Block code_block TYPE 1 1| | | | Assign func_lambda func_lambda 1 1| | | | | Var func_lambda TYPE f 1 1| | | | | Lambda func_lambda func_lambda_f 1 5| | Func func_lambda TYPE _f 1 5| | | Declares func_lambda TYPE 1 5| | | | Var unknown TYPE _retval 1 5| | | Returns func_lambda TYPE 1 5| | | | Var unknown TYPE _retval 1 5| | | Params func_lambda TYPE 1 7| | | | Var unknown TYPE x 1 5| | | Block code_block TYPE 1 5| | | | Assign expression TYPE 1 5| | | | | Var unknown TYPE _retval 1 10| | | | | Mul expression TYPE 1 10| | | | | | Int int int 1 12| | | | | | Var unknown TYPE x | Inlines program TYPE unnamed | Structs program TYPE unnamed | Headers program TYPE unnamed | Log program TYPE unnamed """ if self.code[cur] not in constants.letters: self.syntaxerror(cur, "anonymous function name") if self.code[eq_loc] != "=": self.syntaxerror(cur, "anonymous function assignment (=)") if self.disp: print("%4d Assign " % cur, end="") print(repr(self.code[cur:self.code.find("\n", cur)]), end=" ") print("functions.lambda_assign") assign = collection.Assign(node, cur=cur)#, backend="func_lambda") self.create_assign_variable(assign, cur, eq_loc) k = eq_loc+1 while self.code[k] in " \t": k += 1 end = self.create_lambda_func(assign, k) assign.code = self.code[cur:end+1] return end def lambda_func(self, node, cur): """ Anonymous function content. Support function of `lambda_assign`. Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code Returns: int : Index to end of function line """ if self.code[cur] != "@": self.syntaxerror(cur, "anonymous function indicator (@)") end = cur +1 while self.code[end] in " \t": end += 1 if self.code[end] != "(": self.syntaxerror(end, "anonymous function argument list") end = findend.paren(self, end) end += 1 while self.code[end] in " \t": end += 1 end = findend.expression(self, end) if self.disp: print("%4d Lambda " % cur, end="") print("%-20s" % "functions.lambda_func", end="") print(repr(self.code[cur:end+1])) if node.cls == "Assign": name = node[0].name else: name = "lambda" funcs = node.program[1] name = "_%s" % (name) if name in funcs.names: i = 0 while name+"%d" % i in funcs.names: i += 1 name = name + "%d" % i func = collection.Func(funcs, name, cur=cur, code=self.code[cur:end+1]) declares = collection.Declares(func) returns = collection.Returns(func) params = collection.Params(func) k = cur+1 while self.code[k] in " \t": k += 1 if self.code[k] != "(": self.syntaxerror(k, "anonymous function argument list") cur = self.create_list(params, k) cur += 1 while self.code[cur] in " \t": cur += 1 block = collection.Block(func) assign = collection.Assign(block) var = collection.Var(assign, "_retval") cur = self.create_expression(assign, cur, end=end) for n in assign[1].flatten(): if (n.cls in ("Get", "Cget", "Var", "Fvar", "Fget", "Sget")) and n.name in node.func[0].names + node.func[2].names: n.create_declare() var = collection.Var(returns, "_retval") var.create_declare() lamb = collection.Lambda(node, name) lamb.reference = func return cur if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: src/matlab2cpp/tree/identify.py ================================================ """ Rutines for identifying code structure. +------------------------------------------------------+-----------------------+ | Function | Description | +======================================================+=======================+ | :py:func:`~matlab2cpp.tree.identify.space_delimiter` | Check if at expression| | | space-delimiter | +------------------------------------------------------+-----------------------+ | :py:func:`~matlab2cpp.tree.identify.string` | Check if at string | | | start | +------------------------------------------------------+-----------------------+ | :py:func:`~matlab2cpp.tree.identify.space_delimited` | Check if list is | | | space-delimited | +------------------------------------------------------+-----------------------+ """ from . import constants def space_delimiter(self, start): """ Check if mid-expression space-delimiter. This already assumes that position is in the middle of a space delimited list. Use `space_delimited` to check if a list is space or comma delimited. Args: self (Builder): Code constructor start (int): Current position in code Returns: bool: True if whitespace character classifies as a delimiter """ if self.code[start] not in " \t": self.syntaxerror(start, "space delimiter") k = start while True: if self.code[k:k+3] == "...": return False elif self.code[k] in " \t": pass elif self.code[k] in "+-~": if self.code[k+1] in " \t": return False elif self.code[k:k+2] in constants.op2: return False elif self.code[k] in constants.op1: return False else: return True k += 1 def string(self, k): """ Check if at string start Args: self (Builder): Code constructor k (int): Current position in code Returns: bool: True if character classifies as start of string. """ if self.code[k] != "'": self.syntaxerror(k, "start of string character (')") if self.code[k-1] == ".": return False j = k-1 while self.code[j] in " \t": j -= 1 if self.code[j] in constants.letters+constants.digits+")]}_": # special cases if self.code[j-3:j+1] == "case": return True return False return True def space_delimited(self, start): """ Check if list is space-delimited Args: self (Builder): Code constructor start (int): Current position in code Returns: bool: True if list consists of whitespace delimiters """ from . import findend if self.code[start] not in constants.l_start: self.syntaxerror(start, "list start") k = start+1 while self.code[k] in " \t": k += 1 if self.code[k] in constants.l_end: return False if self.code[k] not in constants.e_start: self.syntaxerror(k, "expression start") if self.code[k] == "'": k = findend.string(self, k)+1 while self.code[k] in " \t": k += 1 while True: if self.code[k] == "(": k = findend.paren(self, k) elif self.code[k] == "[": k = findend.matrix(self, k) elif self.code[k] == "{": k = findend.cell(self, k) elif self.code[k] == "'" and string(self, k): #k = findend.string(self, k) #if self.code[k-1] in constants.s_start: return True elif self.code[k:k+3] == "...": k = findend.dots(self, k) elif self.code[k] in " \t": if space_delimiter(self, k): return True while self.code[k+1] in " \t": k += 1 elif self.code[k] in constants.e_end: if self.code[k] == ",": return False elif self.code[k] in constants.l_end: return False elif self.code[k] != ";": return True elif self.code[k] == "\n": return True while self.code[k+1] in " \t": k += 1 elif self.code[k+1] in constants.letters + constants.digits + "_@": while self.code[k+1] in constants.letters + constants.digits + "_@": k += 1 k += 1 ================================================ FILE: src/matlab2cpp/tree/iterate.py ================================================ """ Rutines for iterating lists +------------------------------------------------+----------------------+ | Functions | Description | +================================================+======================+ | :py:func:`~matlab2cpp.tree.iterate.comma_list` | Iterate over a comma | | | separated list | +------------------------------------------------+----------------------+ | :py:func:`~matlab2cpp.tree.iterate.space_list` | Iterate over a space | | | delimited list | +------------------------------------------------+----------------------+ """ from . import constants as c, findend, identify def comma_list(self, start): """ Iterate over a comma separated list Args: self (Builder): Code constructor start (int): Current position in code Returns: list : A list of 2-tuples that represents index start and end for each expression in list """ if self.code[start] not in c.l_start: self.syntaxerror(start, "list start") k = start+1 while self.code[k] in " \t": k += 1 if self.code[k] in "]}": return [[]] out = [[]] count = False while True: if self.code[k:k+3] == "...": k = findend.dots(self, k) elif self.code[k] in c.e_start: if count: self.syntaxerror(k, "comma list indicator") count = True end = findend.expression(self, k) out[-1].append((k, end)) k = end elif self.code[k] == ",": if not count: self.syntaxerror(k, "comma list indicator") count = False elif self.code[k] == ";": if not count: self.syntaxerror(k, "comma list indicator") count = False out.append([]) elif self.code[k] in c.l_end: return out k += 1 def space_list(self, start): """ Iterate over a space delimited list Args: self (Builder): Code constructor start (int): Current position in code Returns: list : A list of 2-tuples that represents index start and end for each expression in list """ if self.code[start] not in c.l_start: self.syntaxerror(start, "start of list") k = start+1 while self.code[k] in " \t": k += 1 out = [[]] count = False dots = False while True: if self.code[k:k+3] == "...": k = findend.dots(self, k) dots = True if self.code[k] in ";\n": if not dots: out.append([]) dots = False count = False if self.code[k] == ";": # and self.code[k+1] == "\n": while self.code[k+1] in " \n": k += 1 if self.code[k] in c.e_start: if count: self.syntaxerror(k, "expression start") count = True end = findend.expression_space(self, k) out[-1].append((k, end)) k = end elif self.code[k] in c.l_end: return out elif self.code[k] in " \t": count = False k += 1 ================================================ FILE: src/matlab2cpp/tree/misc.py ================================================ """Interpretors that didn't fit other places""" from __future__ import print_function from .. import collection from . import constants as c, findend, expression, iterate, identify def number(self, node, start): """ Verbatim number Args: self (Builder): Code constructor node (Node): Parent node start (int): Current position in code Returns: int : End of number Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "42.") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock '42.' 0 Expression expression.create '42.' 0 Float misc.number '42.' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Float double double """ if not (self.code[start] in c.digits or\ self.code[start] == "." and self.code[start+1] in c.digits): self.syntaxerror(start, "number") k = start while self.code[k] in c.digits: k += 1 last = k-1 integer = True if self.code[k] == "." and \ (self.code[k+1:k+3] != ".." and self.code[k+1] not in "*/"): integer = False k += 1 while self.code[k] in c.digits: k += 1 last = k-1 if self.code[k] in "eEdD": exp = k k = k+1 if self.code[k] in "+-": k += 1 while self.code[k] in c.digits: k += 1 number = self.code[start:exp] + "e" + self.code[exp+1:k] last = k-1 if self.code[k] in "ij": k += 1 node = collection.Imag(node, number, cur=start, code=self.code[start:last+1]) if self.disp: print("%4d Imag " % (start), end="") print("%-20s" % "misc.number", end="") print(repr(self.code[start:last+1])) else: node = collection.Float(node, number, cur=start, code=self.code[start:last+1]) if self.disp: print("%4d Float " % (start), end="") print("%-20s" % "misc.number", end="") print(repr(self.code[start:last+1])) elif integer: number = self.code[start:k] if self.code[k] in "ij": node = collection.Imag(node, self.code[start:k], cur=start, code=self.code[start:last+1]) k += 1 if self.disp: print("%4d Imag " % (start), end="") print("%-20s" % "misc.number", end="") print(repr(self.code[start:last+1])) else: node = collection.Int(node, self.code[start:k], cur=start, code=self.code[start:last+1]) if self.disp: print("%4d Int " % (start), end="") print("%-20s" % "misc.number", end="") print(repr(self.code[start:last+1])) else: if self.code[k] in "ij": node = collection.Imag(node, self.code[start:k], cur=start, code=self.code[start:last+1]) k += 1 if self.disp: print("%4d Imag " % (start), end="") print("%-20s" % "misc.number", end="") print(repr(self.code[start:last+1])) else: node = collection.Float(node, self.code[start:k], cur=start, code=self.code[start:k]) if self.disp: print("%4d Float " % (start), end="") print("%-20s" % "misc.number", end="") print(repr(self.code[start:last+1])) return k-1 def string(self, parent, cur): """ Verbatim string Args: self (Builder): Code constructor parent (Node): Parent node start (int): Current position in code Returns: int : End of string Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "'abc'") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock "'abc'" 0 String misc.string "'abc'" >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | String string string """ end = findend.string(self, cur) if "\n" in self.code[cur:end]: self.syntaxerror(cur, "no line-feed character in string") collection.String(parent, self.code[cur+1:end], cur=cur, code=self.code[cur:end+1]) if self.disp: print("%4d String " % cur, end="") print("%-20s" % "misc.string", end="") print(repr(self.code[cur:end+1])) return end def list(self, parent, cur): """ A list (both comma or space delimited) Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code Returns: int : End of list Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "[2 -3]") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock '[2 -3]' 0 Expression expression.create '[2 -3]' 0 Matrix misc.matrix '[2 -3]' 1 Vector misc.matrix '2 -3' 1 Expression expression.create '2' 1 Int misc.number '2' 3 Expression expression.create '-3' 4 Int misc.number '3' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Matrix matrix irowvec 1 2| | | Vector matrix irowvec 1 2| | | | Int int int 1 4| | | | Neg expression int 1 5| | | | | Int int int """ if self.code[cur] not in "({": self.syntaxerror(cur, "start of list character") end = cur for vector in iterate.comma_list(self, cur): for start,end in vector: self.create_expression(parent, start, end) end += 1 while self.code[end] in " \t": end += 1 if self.code[end] not in ")}": self.syntaxerror(cur, "end of list character") return end def pragma_for(self, parent, cur): assert parent.cls == "Block" if self.code[cur:cur+8] != "%#PARFOR": self.syntaxerror(cur, "pragma_for") k = cur+8 end = findend.pragma_for(self, k) if self.disp: print("%4d Pragma_for " % cur, end="") print("%-20s" % "misc.pragma_for", end="") print(repr(self.code[cur:end])) collection.Pragma_for(parent, self.code[cur+1:end], cur=cur) return end def comment(self, parent, cur): """ Comments on any form Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code Returns: int : End of comment Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True, comments=True) >>> builder.load("unnamed", "4 % comment") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock '4' 0 Expression expression.create '4' 0 Int misc.number '4' 2 Comment misc.comment '% comment' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Int int int 1 3| Ecomment code_block TYPE """ assert parent.cls == "Block" if self.code[cur] != "%": self.syntaxerror(cur, "comment") end = findend.comment(self, cur) if not self.comments: return end if self.disp: print("%4d Comment " % cur, end="") print("%-20s" % "misc.comment", end="") print(repr(self.code[cur:end])) if self.code[cur+1] == "{": comment = collection.Bcomment(parent, self.code[cur+2:end-1], cur=cur) else: k = cur-1 while self.code[k] in " \t": k -= 1 if self.code[k] == "\n": comment = collection.Lcomment(parent, self.code[cur+1:end], cur=cur) else: comment = collection.Ecomment(parent, self.code[cur+1:end], cur=cur) comment.code = self.code[cur:end+1] return end def verbatim(self, parent, cur): """ Verbatim, indicated by _ Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code Returns: int : End of verbatim """ assert parent.cls == "Block" end = findend.verbatim(self, cur) if self.disp: print("%4d Verbatim " % cur, end="") print("%-20s" % "misc.verbatim", end="") print(repr(self.code[cur:end+1])) keys = self.code[cur:end+1].split("___") name = keys[1] value = "\n".join(keys[2:]) verbatim = collection.Verbatim( parent, name, value, cur=cur, code=self.code[cur:end+1]) return end def matrix(self, node, cur): """ Verbatim matrices Args: self (Builder): Code constructor node (Node): Parent node cur (int): Current position in code Returns: int : End of matrix Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "[[1 2] [3 4]]") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock '[[1 2] [3 4]]' 0 Expression expression.create '[[1 2] [3 4]]' 0 Matrix misc.matrix '[[1 2] [3 4]]' 1 Vector misc.matrix '[1 2] [3 4]' 1 Expression expression.create '[1 2]' 1 Matrix misc.matrix '[1 2]' 2 Vector misc.matrix '1 2' 2 Expression expression.create '1' 2 Int misc.number '1' 4 Expression expression.create '2' 4 Int misc.number '2' 7 Expression expression.create '[3 4]' 7 Matrix misc.matrix '[3 4]' 8 Vector misc.matrix '3 4' 8 Expression expression.create '3' 8 Int misc.number '3' 10 Expression expression.create '4' 10 Int misc.number '4' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Matrix matrix irowvec 1 2| | | Vector matrix irowvec 1 2| | | | Matrix matrix irowvec 1 3| | | | | Vector matrix irowvec 1 3| | | | | | Int int int 1 5| | | | | | Int int int 1 8| | | | Matrix matrix irowvec 1 9| | | | | Vector matrix irowvec 1 9| | | | | | Int int int 1 11| | | | | | Int int int """ if self.code[cur] != "[": self.syntaxerror(cur, "bracket start") end = findend.matrix(self, cur) if self.disp: print("%4d Matrix " % cur, end="") print("%-20s" % "misc.matrix", end="") print(repr(self.code[cur:end+1])) if identify.space_delimited(self, cur): L = iterate.space_list(self, cur) else: L = iterate.comma_list(self, cur) matrix = collection.Matrix(node, cur=cur, code=self.code[cur:end+1]) for array in L: if array: start = array[0][0] end = array[-1][-1] else: start = cur vector = collection.Vector(matrix, cur=start, code=self.code[start:end+1]) if self.disp: print("%4d Vector " % (start), end="") print("%-20s" % "misc.matrix", end="") print(repr(self.code[start:end+1])) for start,end in array: self.create_expression(vector, start, end) if not L: if self.disp: print("%4d Vector " % cur, end="") print("%-20s" % "misc.matrix", end="") print(repr("")) vector = collection.Vector(matrix, cur=cur, code="") return findend.matrix(self, cur) def cell(self, node, cur): """ Verbatim cells Args: self (Builder): Code constructor node (Node): Parent node cur (int): Current position in code Returns: int : End of cell Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "{1, 2}") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock '{1, 2}' 0 Expression expression.create '{1, 2}' 0 Cell misc.cell '{1, 2}' 1 Expression expression.create '1' 1 Int misc.number '1' 4 Expression expression.create '2' 4 Int misc.number '2' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Cell cell TYPE 1 2| | | Int int int 1 5| | | Int int int """ if self.code[cur] != "{": self.syntaxerror(cur, "curly braces") end = findend.cell(self, cur) if self.disp: print("%4d Cell " % cur, end="") print("%-20s" % "misc.cell", end="") print(repr(self.code[cur:end+1])) if identify.space_delimited(self, cur): L = iterate.space_list(self, cur) else: L = iterate.comma_list(self, cur) cell = collection.Cell(node, cur=cur, code=self.code[cur:end+1]) for array in L: if array: start = array[0][0] end = array[-1][-1] else: start = cur for start, end in array: self.create_expression(cell, start, end) return findend.cell(self, cur) def reserved(self, node, start): """Reserved keywords""" k = start l = k while self.code[l] not in ";\n": l += 1 newline = l if self.disp: print("%4d reserved " % k, end="") print("%-20s" % "misc.reserved", end="") print(repr(self.code[k:newline])) if self.code[k:k+4] == "disp": statement = collection.Statement(node, cur=start, code=self.code[start:newline]) l = k+4 while self.code[l] in " \t": l += 1 if self.code[l] == "(": return expression.create(self, statement, k) k += 4 while self.code[k] in " \t": k += 1 name = "" if self.code[k] == "\'": l = findend.string(self, k) name = str(self.code[k+1:l]) else: l = k while self.code[l] not in " \t\n": l += 1 name = str(self.code[k:l]) get = collection.Get(statement, name="disp", cur=start, value=self.code[k:l]) #name = str(self.code[k+1:l]) node = collection.String(get, name) #node.create_declare() while self.code[k] not in ";\n": k += 1 return k if self.code[k:k+4] == "load": statement = collection.Statement(node, cur=start, code=self.code[start:newline]) l = k+4 while self.code[l] in " \t": l += 1 if self.code[l] == "(": return expression.create(self, statement, k) k += 4 while self.code[k] in " \t\n'": k += 1 l = k while self.code[l] not in " \t\n": l += 1 get = collection.Get(statement, name="load", cur=start, value=self.code[k:l]) name = str(self.code[k:l]).split(".")[0] node = collection.Var(get, name) node.create_declare() while self.code[k] not in ";\n": k += 1 return k if self.code[k:k+4] == "hold": statement = collection.Statement(node, cur=start, code=self.code[start:newline]) l = k+4 while self.code[l] in " \t": l += 1 if self.code[l] == "(": return expression.create(self, statement, k) k += 4 while self.code[k] in " \t": k += 1 get = collection.Get(statement, name="hold", cur=start) if self.code[k:k+2] == "on" and (self.code[k+2] in c.k_end): collection.String(get, "on") return k+2 if self.code[k:k+3] == "off" and (self.code[k+3] in c.k_end): collection.String(get, "off") return k+3 if self.code[k:k+3] == "all" and (self.code[k+3] in c.k_end): collection.String(get, "all") return k+3 while self.code[k] not in ";\n": k += 1 return k if self.code[k:k+4] == "grid": statement = collection.Statement(node, cur=start, code=self.code[k:newline]) l = k+4 while self.code[l] in " \t": l += 1 if self.code[l] == "(": return expression.create(self, statement, k) k += 4 while self.code[k] in " \t": k += 1 get = collection.Get(statement, name="grid", cur=start) if self.code[k:k+2] == "on" and (self.code[k+2] in c.k_end): collection.String(get, "on", cur=k) return k+2 if self.code[k:k+3] == "off" and (self.code[k+3] in c.k_end): collection.String(get, "off", cur=k) return k+3 if self.code[k:k+5] == "minor" and (self.code[k+5] in c.k_end): collection.String(get, "minor", cur=k) return k+5 while self.code[k] not in ";\n": k += 1 return k if self.code[k:k+5] == "clear": pass ================================================ FILE: src/matlab2cpp/tree/suppliment.py ================================================ class Fbuilder(object): "Same as Ftypes, but placed in builder" def __get__(self, instance, owner): out = {} for program in instance: out.update(program.ftypes) return out def __set__(self, instance, value): for program in instance: program.ftypes = value class Ibuilder(object): "Same as Itypes, but placed in builder" def __get__(self, instance, owner): out = set() for program in instance: out = out.union(program.itypes) return out def __set__(self, instance, value): for program in instance: program.itypes = value class Sbuilder(object): "Same as Stypes, but placed in builder" def __get__(self, instance, owner): out = {} for program in instance: out.update(program.stypes) return out def __set__(self, instance, value): for program in instance: program.stypes = value class Vbuilder(object): "Same as Vtypes, but placed in builder" def __get__(self, instance, owner): out = {} for program in instance: out.update(program.vtypes) return out def __set__(self, instance, value): for program in instance: program.vtypes = value ================================================ FILE: src/matlab2cpp/tree/variables.py ================================================ """ Variable interpretor """ from __future__ import print_function from . import constants, findend from .. import collection def assign(self, parent, cur, end=None): """ Variable left side of an assignment Args: self (Builder): Code constructor parent (Node): Parent node cur (int): Current position in code Kwargs: end (int, optional): End of variable Returns: int : End of variable Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "a = 4") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Assign assign.single 'a = 4' 0 Var variables.assign 'a' 4 Expression expression.create '4' 4 Int misc.number '4' >>> builder.configure(suggest=False) >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Assign int int 1 1| | Var unknown (int) a 1 5| | Int int int """ if self.code[cur] not in constants.letters + '~': self.syntaxerror(cur, "assign variable name") k = cur+1 while self.code[k] in constants.letters+constants.digits+"_": k += 1 name = self.code[cur:k] last = k while self.code[k] in " \t": k += 1 # Get value of cell if self.code[k] == "{": end = findend.cell(self, k) end = end+1 while self.code[end] in " \t": end += 1 if self.code[end] == "(": end = findend.paren(self, end) node = collection.Cset(parent, name, cur=cur, code=self.code[cur:end+1]) if self.disp: print("%4d Cset " % cur, end="") print("%-20s" % "variables.assign", end="") print(repr(self.code[cur:end+1])) n_fields = 0 while self.code[k] == "{": #cur = self.iterate_cell(node, k) cur = findend.cell(self, k) #cur = iterate.comma_list(node, k) k = cur+1 while self.code[k] in " \t": k += 1 n_fields += 1 while self.code[k] in " \t": k += 1 if self.code[k] != "(": self.syntaxerror(k, "parenthesis start") cur = self.create_list(node, k) node["n_fields"] = n_fields node["n_args"] = len(node) - n_fields else: end = findend.cell(self, k) node = collection.Cvar(parent, name, cur=cur, code=self.code[cur:end+1]) if self.disp: print("%4d Cvar " % cur, end="") print("%-20s" % "variables.assign", end="") print(repr(self.code[cur:end+1])) num = 0 while self.code[k] == "{": cur = findend.cell(self, k) #cur = self.iterate_cell(node, k) #print(node.code) #print(k) #print("\n\n") #l = 0 #while node.code[l] in constants.letters+constants.digits+"_": # l += 1 #list = iterate.comma_list(node, l) #for tup in list: # collection.Var(node, node.code[tup[0]:tup[1]+1]) #print(list) #print("LISTAT\n\n") k = cur+1 while self.code[k] in " \t": k += 1 num += 1 # Set value of array elif self.code[k] == "(": end = findend.paren(self, k) if self.code[end+1] == "." and self.code[end+2] in constants.letters: start = end+2 end += 2 while self.code[end] in constants.letters+constants.digits+"_": end += 1 value = self.code[start:end] if self.disp: print("%4d Sset " % cur) print("%-20s" % "variables.assign", end="") print(repr(self.code[cur:end])) node = collection.Sset(parent, name, value, cur=cur, code=self.code[cur:end], pointer=1) last = self.create_list(node, k) cur = end-1 else: if self.disp: print("%4d Set " % cur) print("%-20s" % "variables.assign", end="") print(repr(self.code[cur:end+1])) node = collection.Set(parent, name, cur=cur, code=self.code[cur:end+1]) last = self.create_list(node, k) cur = last elif self.code[k] == ".": k += 1 # Fieldname of type "a.() = ..." if self.code[k] == "(": end = findend.paren(self, k) k += 1 while self.code[k] in " \t": k += 1 if self.disp: print("%4d Nset " % cur, end="") print("%-20s" % "variables.assign", end="") print(repr(self.code[cur:end+1])) node = collection.Nset(parent, name) node.cur = cur node.code = self.code[cur:end+1] cur = self.create_expression(node, cur) elif self.code[k] in constants.letters: j = k+1 while self.code[j] in constants.letters+constants.digits+"_.": j += 1 value = self.code[k:j] last = j-1 while self.code[j] in " \t": j += 1 # Fieldname of type "a.b(...) = ..." if self.code[j] == "(": end = findend.paren(self, j) if self.disp: print("%4d Fset " % cur, end="") print("%-20s" % "variables.assign", end="") print(repr(self.code[cur:end+1])) node = collection.Fset(parent, name, value=value, cur=cur, code=self.code[cur:end+1]) cur = self.create_list(node, j) # Fieldname of type "a.b = ..." else: if self.disp: print("%4d Fvar " % cur, end="") print("%-20s" % "variables.assign", end="") print(repr(self.code[cur:last+1])) node = collection.Fvar(parent, name, value=value, cur=cur, code=self.code[cur:last+1]) cur = last # Simple variable assignment else: if self.disp: print("%4d Var " % cur, end="") print("%-20s" % "variables.assign", end="") print(repr(self.code[cur:last])) node = collection.Var(parent, name, cur=cur, code=self.code[cur:last]) cur = last-1 if name != '~': node.create_declare() return cur def variable(self, parent, cur): """ Variable not on the left side of an assignment Args: self (Builder): Code constructor node (Node): Parent node cur (int): Current position in code Kwargs: end (int, optional): End of variable Returns: int : End of variable Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "a") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock 'a' 0 Expression expression.create 'a' 0 Var variables.variable 'a' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) #doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Var unknown TYPE a """ k = cur if self.code[k] == "@": k += 1 if self.code[k] not in constants.letters: self.syntaxerror(k, "variable name") k += 1 while self.code[k] in constants.letters+constants.digits+"_": k += 1 name = self.code[cur:k] last = k while self.code[k] in " \t": k += 1 # Get value of cell if self.code[k] == "{": end = findend.cell(self, k) end = end+1 while self.code[end] in " \t": end += 1 if self.code[end] == "(": end = findend.paren(self, end) node = collection.Cget(parent, name, cur=cur, code=self.code[cur:end+1]) if self.disp: print("%4d Cget " % cur, end="") print("%-20s" % "variables.variable", end="") print(repr(self.code[cur:end+1])) n_fields = 0 while self.code[k] == "{": cur = findend.cell(self, k) #cur = self.iterate_cell(node, k) #cur = iterate.comma_list(node, k) k = cur+1 while self.code[k] in " \t": k += 1 n_fields += 1 while self.code[k] in " \t": k += 1 if self.code[k] != "(": self.syntaxerror(k, "argument parenthesis") cur = self.create_list(node, k) node["n_fields"] = n_fields node["n_args"] = len(node) - n_fields else: end = findend.cell(self, k) node = collection.Cvar(parent, name, cur=cur, code=self.code[cur:end+1]) if self.disp: print("%4d Cvar " % cur, end="") print("%-20s" % "variables.variable", end="") print(repr(self.code[cur:end+1])) num = 0 while self.code[k] == "{": cur = cell_arg(self, node, k) k = cur+1 while self.code[k] in " \t": k += 1 num += 1 # Get value of array elif self.code[k] == "(": end = findend.paren(self, k) if self.code[end+1] == "." and self.code[end+2] in constants.letters: start = end+2 end += 2 while self.code[end] in constants.letters+constants.digits+"_": end += 1 value = self.code[start:end] if self.disp: print("%4d Sget " % cur, end="") print("%-20s" % "variables.variable", end="") print(repr(self.code[cur:end])) node = collection.Sget(parent, name, value, cur=cur, code=self.code[cur:end], pointer=1) last = self.create_list(node, k) cur = end else: if self.disp: print("%4d Get " % cur, end="") print("%-20s" % "variables.variable", end="") print(repr(self.code[cur:end+1])) node = collection.Get(parent, name, cur=cur, code=self.code[cur:end+1]) last = self.create_list(node, k) cur = last elif self.code[k] == "." and self.code[k+1] not in ".*/\\^'": k += 1 # Fieldname of type "a.(..)" if self.code[k] == "(": end = findend.paren(self, k) if self.disp: print("%4d Nget " % cur, end="") print("%-20s" % "variables.variable", end="") print(repr(self.code[cur:end+1])) k += 1 while self.code[k] in " \t": k += 1 node = collection.Nget(parent, name, cur=cur, code=self.code[cur:end+1]) cur = self.create_expression(node, k) elif self.code[k] in constants.letters: j = k+1 while self.code[j] in constants.letters+constants.digits+"_": j += 1 value = self.code[k:j] last = j while self.code[j] in " \t": j += 1 # Fieldname of type "a.b(...)" if self.code[j] == "(": end = findend.paren(self, j) if self.disp: print("%4d Fget " % cur, end="") print("%-20s" % "variables.variable", end="") print(repr(self.code[cur:end+1])) node = collection.Fget(parent, name, cur=cur, value=value, code=self.code[cur:end+1]) j += 1 while self.code[j] in " \t": j += 1 cur = self.create_expression(node, j) node.create_declare() # Fieldname of type "a.b" else: if self.disp: print("%4d Fvar " % cur, end="") print("%-20s" % "variables.variable", end="") print(repr(self.code[cur:last])) node = collection.Fvar(parent, name, value=value, cur=cur, code=self.code[cur:last]) cur = last-1 node.create_declare() # Simple variable else: if self.disp: print("%4d Var " % cur, end="") print("%-20s" % "variables.variable", end="") print(repr(self.code[cur:last])) node = collection.Var(parent, name, cur=cur, code=self.code[cur:last]) cur = last-1 while self.code[cur] in " \t": cur += 1 return cur def cell_arg(self, cset, cur): """ Argument of a cell call. Support function to `assign` and `variable`. Args: self (Builder): Code constructor cset (Node): Parent node cur (int): Current position in code Returns: int : End of argument Example: >>> from matlab2cpp.tree import Builder >>> builder = Builder(True) >>> builder.load("unnamed", "a{b}") # doctest: +NORMALIZE_WHITESPACE loading unnamed Program functions.program 0 Main functions.main 0 Codeblock codeblock.codeblock 0 Statement codeblock.codeblock 'a{b}' 0 Expression expression.create 'a{b}' 0 Cvar variables.variable 'a{b}' 2 Expression expression.create 'b' 2 Var variables.variable 'b' >>> builder.configure() >>> print(matlab2cpp.qtree(builder, core=True)) # doctest: +NORMALIZE_WHITESPACE 1 1Block code_block TYPE 1 1| Statement code_block TYPE 1 1| | Cvar cell TYPE a 1 3| | | Var unknown TYPE b """ if self.code[cur] != "{": self.syntaxerror(cur, "Curly start") cur = cur+1 while True: if self.code[cur] == "}": return cur elif self.code[cur] in constants.e_start: cur = self.create_expression(cset, cur) cur += 1 while self.code[cur] in " \t": cur += 1 return cur elif self.code[cur] == " ": pass cur += 1 if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: test/data/function_reference.hpp ================================================ #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; irowvec f(irowvec x) ; void g() ; irowvec f(irowvec x) { irowvec y ; y = x+2 ; return y ; } void g() { irowvec x, y ; sword _x [] = {1, 2, 3} ; x = irowvec(_x, 3, false) ; y = f(x) ; } #endif ================================================ FILE: test/data/function_reference.m ================================================ function y=f(x) y = x+2 end function g() x = [1,2,3] y = f(x) end ================================================ FILE: test/data/function_reference_2.hpp ================================================ #ifndef F_M_HPP #define F_M_HPP #include using namespace arma ; void f(irowvec a, ivec b, irowvec& y, ivec& z) ; void g() ; void f(irowvec a, ivec b, irowvec& y, ivec& z) { y = a+2 ; z = b-3 ; } void g() { irowvec a, y ; ivec b, z ; sword _a [] = {1, 2, 3} ; a = irowvec(_a, 3, false) ; sword _b [] = {4, 5, 6} ; b = ivec(_b, 3, false) ; f(a, b, y, z) ; } #endif ================================================ FILE: test/data/function_reference_2.m ================================================ function [y,z]=f(a,b) y = a+2 z = b-3 end function g() a = [1,2,3] b = [4;5;6] [y,z] = f(a,b) end ================================================ FILE: test/data/fx_decon.cpp ================================================ #ifndef FX_DECON_M_HPP #define FX_DECON_M_HPP #include #include "mconvert.h" #include using namespace arma ; mat fx_decon(mat DATA, double dt, int lf, double mu, double flow, int fhigh) ; void ar_modeling(cx_vec x, int lf, double mu, cx_vec& yf, cx_vec& yb) ; mat fx_decon(mat DATA, double dt, int lf, double mu, double flow, int fhigh) { cx_mat DATA_FX, DATA_FX_b, DATA_FX_f ; cx_vec aux_in, aux_out_b, aux_out_f ; int ihigh, ilow, k, nf, nt, ntraces ; mat DATA_b, DATA_f ; nt = DATA.n_rows; ntraces = DATA.n_cols; nf = pow(2, m2cpp::nextpow2(nt)) ; DATA_FX_f = arma::zeros(nf, ntraces) ; DATA_FX_b = arma::zeros(nf, ntraces) ; ilow = std::floor(flow*dt*nf)+1 ; if (ilow<1) { ilow = 1 ; } ihigh = std::floor(fhigh*dt*nf)+1 ; if (ihigh>std::floor(nf/2.0)+1) { ihigh = std::floor(nf/2.0)+1 ; } DATA_FX = m2cpp::fft(DATA, nf, 1) ; for (k=ilow; k<=ihigh; k++) { aux_in = arma::trans(DATA_FX.row(k-1)) ; ar_modeling(aux_in, lf, mu, aux_out_f, aux_out_b) ; DATA_FX_f.row(k-1) = arma::trans(aux_out_f) ; DATA_FX_b.row(k-1) = arma::trans(aux_out_b) ; } for (k=nf/2.0+2; k<=nf; k++) { DATA_FX_f.row(k-1) = arma::conj(DATA_FX_f.row(nf-k+1)) ; DATA_FX_b.row(k-1) = arma::conj(DATA_FX_b.row(nf-k+1)) ; } DATA_f = arma::real(m2cpp::ifft(DATA_FX_f, 1)) ; DATA_f = DATA_f.rows(arma::span(0, nt-1)) ; DATA_b = arma::real(m2cpp::ifft(DATA_FX_b, 1)) ; DATA_b = DATA_b.rows(arma::span(0, nt-1)) ; DATA_f = (DATA_f+DATA_b) ; DATA_f.cols(arma::span(lf, ntraces-lf-1)) = DATA_f.cols(arma::span(lf, ntraces-lf-1))/2.0 ; return DATA_f ; } void ar_modeling(cx_vec x, int lf, double mu, cx_vec& yf, cx_vec& yb) { cx_double beta ; cx_mat B, M, temp ; cx_vec C, R, ab, af, y ; uword nx ; nx = m2cpp::length(x) ; y = x(arma::span(0, nx-lf-1)) ; C = x(arma::span(1, nx-lf)) ; R = x(arma::span(nx-lf, nx-1)) ; M = m2cpp::hankel(C, R) ; B = arma::trans(M)*M ; beta = B(0, 0)*(cx_double) mu/100.0 ; ab = arma::solve((B+beta*arma::eye(lf, lf)), arma::trans(M), solve_opts::fast)*y ; temp = M*ab ; temp = arma::join_cols(temp, arma::zeros(lf, 1)) ; yb = temp ; y = x(arma::span(lf, nx-1)) ; C = x(arma::span(lf-1, nx-2)) ; R = arma::flipud(x(arma::span(0, lf-1))) ; M = toeplitz(C, R) ; B = arma::trans(M)*M ; beta = B(0, 0)*(cx_double) mu/100.0 ; af = arma::solve((B+beta*arma::eye(lf, lf)), arma::trans(M), solve_opts::fast)*y ; temp = M*af ; temp = arma::join_cols(arma::zeros(lf, 1), temp) ; yf = temp ; return ; } #endif ================================================ FILE: test/data/fx_decon.m ================================================ function [DATA_f] = fx_decon(DATA,dt,lf,mu,flow,fhigh); [nt,ntraces] = size(DATA); nf = 2^nextpow2(nt); DATA_FX_f = zeros(nf,ntraces); DATA_FX_b = zeros(nf,ntraces); ilow = floor(flow*dt*nf)+1; if ilow<1; ilow=1; end; ihigh = floor(fhigh*dt*nf)+1; if ihigh > floor(nf/2)+1; ihigh=floor(nf/2)+1; end DATA_FX = fft(DATA,nf,1); for k = ilow:ihigh; aux_in = DATA_FX(k,:)'; [aux_out_f,aux_out_b] = ar_modeling(aux_in,lf,mu); DATA_FX_f(k,:) = aux_out_f'; DATA_FX_b(k,:) = aux_out_b'; end; for k=nf/2+2:nf DATA_FX_f(k,:) = conj(DATA_FX_f(nf-k+2,:)); DATA_FX_b(k,:) = conj(DATA_FX_b(nf-k+2,:)); end DATA_f = real(ifft(DATA_FX_f,[],1)); DATA_f = DATA_f(1:nt,:); DATA_b = real(ifft(DATA_FX_b,[],1)); DATA_b = DATA_b(1:nt,:); DATA_f = (DATA_f + DATA_b); DATA_f(:,lf+1:ntraces-lf)= DATA_f(:,lf+1:ntraces-lf)/2; return function [yf,yb] = ar_modeling(x,lf,mu); nx = length(x); y = x(1:nx-lf,1); C = x(2:nx-lf+1,1); R = x(nx-lf+1:nx,1); M = hankel(C,R); B = M'*M; beta = B(1,1)*mu/100; ab = (B + beta*eye(lf))\M'*y; temp = M*ab; temp = [temp;zeros(lf,1)]; yb = temp; y = x(lf+1:nx,1); C = x(lf:nx-1,1); R = flipud(x(1:lf,1)); M = toeplitz(C,R); B = M'*M; beta = B(1,1)*mu/100; af = (B + beta*eye(lf))\M'*y; temp = M*af; temp = [zeros(lf,1);temp]; yf = temp; return ================================================ FILE: test/data/fx_decon.m.py ================================================ functions = { "ar_modeling" : { "B" : "cx_mat", "C" : "cx_vec", "M" : "cx_mat", "R" : "cx_vec", "ab" : "cx_vec", "af" : "cx_vec", "beta" : "cx_double", "lf" : "int", "mu" : "double", "nx" : "uword", "temp" : "cx_mat", "x" : "cx_vec", "y" : "cx_vec", "yb" : "cx_vec", "yf" : "cx_vec", }, "fx_decon" : { "DATA" : "mat", "DATA_FX" : "cx_mat", "DATA_FX_b" : "cx_mat", "DATA_FX_f" : "cx_mat", "DATA_b" : "mat", "DATA_f" : "mat", "aux_in" : "cx_vec", "aux_out_b" : "cx_vec", "aux_out_f" : "cx_vec", "dt" : "double", "fhigh" : "int", "flow" : "double", "ihigh" : "int", "ilow" : "int", "k" : "int", "lf" : "int", "mu" : "double", "nf" : "int", "nt" : "int", "ntraces" : "int", }, } includes = [ '#include ', 'using namespace arma ;', ] ================================================ FILE: test/data/simple_assignment.cpp ================================================ #include using namespace arma ; int main(int argc, char** argv) { double b ; int a ; irowvec d ; ivec e ; std::string c ; a = 1 ; b = 2. ; c = "3" ; sword _d [] = {4, 5} ; d = irowvec(_d, 2, false) ; sword _e [] = {6, 7} ; e = ivec(_e, 2, false) ; return 0 ; } ================================================ FILE: test/data/simple_assignment.m ================================================ a = 1 b = 2. c = '3' d = [4, 5] e = [6; 7] ================================================ FILE: test/test_conversion.py ================================================ """Test fx_decon script.""" import os def test_fx_decon(): """Test fx_decon script.""" os.system("m2cpp fx_decon.m -s > /dev/null") with open("fx_decon.cpp", "r") as src: reference_code = src.read().strip() with open("fx_decon.m.hpp", "r") as src: converted_code = "\n".join(src.read().split("\n")[2:]).strip() assert converted_code == reference_code ================================================ FILE: test/test_simple_assignment.py ================================================ """Test simple assignment.""" import pytest from matlab2cpp import qcpp, qhpp @pytest.fixture(params=[ "simple_assignment", ]) def cpp_filename(request): return request.param def test_cpp_executables(cpp_filename): """Test basic variable types.""" with open("%s.m" % cpp_filename) as src: source_code = src.read().strip() translation = qcpp(source_code, suggest=True) with open("%s.cpp" % cpp_filename) as src: cpp_reference = src.read().strip() assert translation == cpp_reference @pytest.fixture(params=[ "function_reference", "function_reference_2", ]) def hpp_filename(request): return request.param def test_hpp_executables(hpp_filename): """Test basic variable types.""" with open("%s.m" % hpp_filename) as src: source_code = src.read().strip() translation = qhpp(source_code, suggest=True) with open("%s.hpp" % hpp_filename) as src: hpp_reference = src.read().strip() assert translation == hpp_reference