├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE-parser ├── LICENSE-pd ├── LICENSE-xnor ├── Makefile ├── README.md ├── configure ├── examples.txt ├── process.rb └── src ├── CMakeLists.txt ├── jit_expr-help.pd ├── jit_expr.cpp ├── jit_expr_version.h.in ├── llvmcodegen ├── codegen.cc └── codegen.h ├── main.cc ├── parse ├── CMakeLists.txt ├── ast.cc ├── ast.h ├── driver.cc ├── driver.hh ├── parse.yy ├── scan.ll └── scanner.hh ├── print.cc └── print.h /.gitignore: -------------------------------------------------------------------------------- 1 | parser 2 | build/ 3 | core 4 | .gdb_history 5 | *.pd_* 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/pd.build"] 2 | path = src/pd.build 3 | url = https://github.com/pierreguillot/pd.build.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Minimum version of CMake to build this project 2 | cmake_minimum_required(VERSION 3.0) #below 3 has a bug in mac os for c++11 3 | 4 | 5 | if (POLICY CMP0025) 6 | cmake_policy(SET CMP0025 NEW) 7 | endif () 8 | 9 | project(jit_expr) 10 | 11 | #Defines subdirectory 12 | add_subdirectory(src/) 13 | -------------------------------------------------------------------------------- /LICENSE-parser: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Rémi Berson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE-pd: -------------------------------------------------------------------------------- 1 | This software is copyrighted by Miller Puckette and others. The following 2 | terms (the "Standard Improved BSD License") apply to all files associated with 3 | the software unless explicitly disclaimed in individual files: 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 3. The name of the author may not be used to endorse or promote 16 | products derived from this software without specific prior 17 | written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 23 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30 | THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /LICENSE-xnor: -------------------------------------------------------------------------------- 1 | This software is copyrighted by Alex Norman and others. The following 2 | terms (the "Standard Improved BSD License") apply to all files associated with 3 | the software unless explicitly disclaimed in individual files: 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 3. The name of the author may not be used to endorse or promote 16 | products derived from this software without specific prior 17 | written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 23 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30 | THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: pd 2 | 3 | clean: 4 | @rm -rf build 5 | 6 | pd: 7 | @make -C build 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JIT expr/expr~/fexpr~ 2 | === 3 | 4 | Just-in-time (JIT) clones of the [expr/expr~/fexpr~](http://yadegari.org/expr/expr.html) [pure data](http://puredata.info/) objects. 5 | 6 | Requirements 7 | --- 8 | 9 | * [cmake](https://cmake.org/) 3.0 or greater 10 | * [bison](https://www.gnu.org/software/bison/) tested with 3.0.4, didn't work with 2.3 on osX 11 | * [flex](https://github.com/westes/flex) tested with 2.6.0 and 2.6.4 12 | * [llvm](http://llvm.org/) 5.0 13 | * [pure-data](https://puredata.info/) headers 14 | * [pd.build](https://github.com/pierreguillot/pd.build) 15 | * it is already set up as a submodule, remember `git submodule init && git submodule update` 16 | 17 | Build 18 | --- 19 | 20 | ### Linux 21 | 22 | `./configure && make pd` 23 | 24 | ### Mac OsX 25 | 26 | The build in flex header is old, I needed to target the one I installed with homebrew which was installed in */usr/local/opt/flex/include/* or with macports which wav installed in */opt/local/include/* 27 | 28 | * make the cmake build directory: 29 | * `mkdir build/ && cd build` 30 | * let cmake know where your (homebrew) flex headers and m_pd.h are, update this to reflect your paths: 31 | * `cmake -DFLEX_INCLUDE_DIR=/usr/local/opt/flex/include -DCMAKE_CXX_FLAGS="-I/Applications/Pd-0.48-1.app/Contents/Resources/src/" ..` 32 | * 0.1.1 was built on a 10.10 machine with this command: 33 | * `cmake -DFLEX_INCLUDE_DIR=/opt/local/include -DCMAKE_CXX_FLAGS="-I/Applications/Pd*.app/Contents/Resources/src -I/opt/local/include -stdlib=libc++ -std=c++11" -DCMAKE_CXX_COMPILER=/opt/local/bin/clang++-mp-5.0 -DLLVM_DIR=/opt/local/libexec/llvm-5.0/lib/cmake/llvm/` 34 | 35 | ### Windows 36 | 37 | Could use some help here.. 38 | 39 | 40 | Install 41 | --- 42 | 43 | copy *build/jit_expr* to your appropriate *pd externals* directory 44 | 45 | for example: 46 | 47 | * Linux: `mkdir -p ~/.local/lib/pd/extra && cp -r build/jit_expr ~/.local/lib/pd/extra/` 48 | * MacOS: `cp -r build/jit_expr ~/Library/Pd/` 49 | * Windows: not sure.. *%AppData%\Pd* 50 | 51 | Notes 52 | --- 53 | 54 | [godbolt](https://godbolt.org/) can be useful for finding what llvm instruction to use. 55 | `-emit-llvm -S -O0` 56 | 57 | Acknowledgements 58 | --- 59 | 60 | * [Bison Flex C++ Template by Rémi Berson](https://github.com/remusao/Bison-Flex-CPP-template) was the starting point for the tokeniser and parser 61 | * some of the LLVM code is based on the [llvm tutorial](https://llvm.org/docs/tutorial/) 62 | * some of the expr pd code is based on the [original expr code](https://github.com/pure-data/pure-data) by [Shahrokh Yadegari](http://yadegari.org/) and [Miller Puckette](http://msp.ucsd.edu/software.html) 63 | 64 | TODO 65 | === 66 | 67 | * remove overloaded virtual warning disable, if we can fix it 68 | * convert more of the functions into LLVM code so it can be further optimized 69 | 70 | Thanks 71 | === 72 | 73 | * Thanks to **Marco Matteo Markidis** for help building the osx external for 10.10+ 74 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env sh 2 | 3 | echo "\033[33m -------------------------------------------\033[37m" 4 | echo "\033[33m| JIT_EXPR |\033[37m" 5 | echo "\033[33m| CONFIGURE |\033[37m" 6 | echo "\033[33m -------------------------------------------\033[37m" 7 | 8 | 9 | # Function that calls CMake with the corrects CFLAGS 10 | call_CMake() 11 | { 12 | cd build/ 13 | if [ "${1}" = "" ] ; then 14 | cmake ../ -G"Unix Makefiles" 15 | else 16 | cmake ../ -DCMAKE_BUILD_TYPE=Debug -G"Unix Makefiles" 17 | fi; 18 | 19 | # Checking for errors 20 | if [ "$?" != "0" ] ; then 21 | cd ../ 22 | rm -fr build/ 23 | echo 24 | echo "\033[31m<< An error occured while configuring >>\033[37m" 25 | echo "\033[31m<< Please, report this bug >>\033[37m" 26 | echo 27 | return "1" 28 | fi; 29 | 30 | cd ../ 31 | echo 32 | echo "\033[32m<< Project is now ready to be built >>\033[37m" 33 | echo "\033[32m << Run \`make\` to continue >>\033[37m" 34 | } 35 | 36 | 37 | # Function that builds CFLAGS according to arguments 38 | parse_opt() 39 | { 40 | DEBUG="" 41 | 42 | for i in $@; do 43 | case $i in 44 | "--with-debug" ) 45 | DEBUG="YES";; 46 | "--help" ) 47 | print_usage 48 | return;; 49 | * ) 50 | print_usage 51 | return;; 52 | esac 53 | done 54 | 55 | call_CMake "${DEBUG}" 56 | } 57 | 58 | 59 | # Print the Usage of the configure script 60 | print_usage() 61 | { 62 | echo 63 | echo "Configuring Parser project" 64 | echo "Usage: ./configure [--with-debug] [--help]" 65 | echo " All the arguments are optionnals but if --help is given" 66 | echo "the program exit, displaying this Usage." 67 | echo " If no argument is passed, Makefile will be generated with" 68 | echo "default CFLAGS and no debug mode." 69 | echo " You can pass as much arguments as you like." 70 | echo "" 71 | echo "--help - Display this Usage and exit program" 72 | echo "--with-debug - Generate Makefile that will build the" 73 | echo " project with debugs CFLAGS (-g)." 74 | echo 75 | } 76 | 77 | 78 | # 79 | # Check if build/ directory exists 80 | # 81 | if [ ! -e build ] ; then 82 | mkdir -m 750 build/ 83 | else 84 | rm -fr build/* 85 | fi; 86 | 87 | # 88 | # Get_opt 89 | # 90 | parse_opt $@ 91 | -------------------------------------------------------------------------------- /examples.txt: -------------------------------------------------------------------------------- 1 | count_1003 = $y2[-1] + 1 \; if(count_1003 > n_1003 \, 1 \, count_1003) 2 | -------------------------------------------------------------------------------- /process.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | f = File.read(ARGV[0]) 4 | 5 | r = /#X obj \d+ \d+ (f?expr~?) (.*?)(? n >= -vector 14 | size; 15 | #X text 79 535 and 'n' for $y# has to satisfy: -1 => n >= -vector size 16 | ; 17 | #X text 21 128 - [jit/expr]: used for evaluation of control data expressions 18 | , f 62; 19 | #X text 45 414 - $v#: an audio signal vector/block, f 59; 20 | #X text 45 312 - $i#: an integer, f 59; 21 | #X text 45 331 - $f#: a float, f 59; 22 | #X text 45 350 - $s#: a symbol (used to define arrays), f 59; 23 | #X obj 760 147 jit/expr $f1 + 1 \; $f1 - 1 \; $f1 * 2; 24 | #X floatatom 848 208 5 0 0 0 - - -, f 5; 25 | #N canvas 273 26 812 549 Arrays 0; 26 | #N canvas 0 50 450 300 (subpatch) 0; 27 | #X array array1 10 float 0; 28 | #X coords 0 10 9 0 220 130 1 0 0; 29 | #X restore 537 143 graph; 30 | #X floatatom 187 341 0 0 9 0 - - -; 31 | #X floatatom 187 398 0 0 0 0 - - -; 32 | #X obj 187 370 jit/expr $s2[$f1]; 33 | #X msg 275 342 symbol array1; 34 | #X obj 532 39 loadbang; 35 | #X floatatom 137 135 5 0 9 0 - - -, f 5; 36 | #X floatatom 137 211 5 0 0 0 - - -, f 5; 37 | #X obj 137 172 jit/expr array1[$f1]; 38 | #X msg 532 71 \; array1 0 1 4 2 8 5 6 1 4 2 8; 39 | #X text 30 87 For instance:; 40 | #X obj 275 313 loadbang; 41 | #X text 32 265 A symbol inlet - defined as $s# (# being the inlet number) 42 | - is used to receive array names.; 43 | #X floatatom 585 430 0 0 100 0 - - -; 44 | #X obj 585 311 vsl 20 100 0 10 0 0 empty empty empty 0 -9 0 10 -262144 45 | -1 -1 0 1; 46 | #X obj 585 461 jit/expr array1[1] = $f1; 47 | #X text 27 23 The jit/expr family of objects can read values from array 48 | (without interpolation) and also write to them. The syntax is arrayname[index]. 49 | ; 50 | #X text 263 165 input is the index to read from the array, f 21; 51 | #X text 206 461 You can set values to an array index with "="* as in: 52 | ; 53 | #X text 524 493 *a.k.a. "store function"; 54 | #X connect 1 0 3 0; 55 | #X connect 3 0 2 0; 56 | #X connect 4 0 3 1; 57 | #X connect 5 0 9 0; 58 | #X connect 6 0 8 0; 59 | #X connect 8 0 7 0; 60 | #X connect 11 0 4 0; 61 | #X connect 13 0 15 0; 62 | #X connect 14 0 13 0; 63 | #X restore 803 307 pd Arrays; 64 | #X text 556 182 Basic examples:; 65 | #X text 748 277 Further details:; 66 | #N canvas 2 72 1362 694 Dealing_with_"\$0" 0; 67 | #X obj 623 138 jit/expr $s2[$f1]; 68 | #X obj 711 107 symbol \$0-x; 69 | #X floatatom 623 112 5 0 99 0 - - -, f 5; 70 | #X floatatom 623 166 5 0 0 0 - - -, f 5; 71 | #X obj 711 84 loadbang; 72 | #N canvas 0 50 450 300 (subpatch) 0; 73 | #X array \$0-x 100 float 1; 74 | #A 0 -0.720016 -0.706683 -0.693349 -0.680016 -0.666682 -0.653348 -0.640015 75 | -0.625014 -0.610014 -0.595014 -0.580013 -0.565013 -0.550013 -0.535012 76 | -0.520012 -0.505012 -0.490011 -0.475011 -0.460011 -0.44501 -0.43001 77 | -0.415009 -0.400009 -0.385009 -0.370008 -0.355008 -0.340008 -0.325007 78 | -0.310007 -0.295007 -0.280006 -0.264006 -0.248006 -0.232005 -0.216005 79 | -0.200005 -0.184004 -0.168004 -0.152003 -0.136003 -0.120003 -0.106669 80 | -0.0933355 -0.0800018 -0.0666682 -0.0533346 -0.0400009 -0.0266673 -0.0133336 81 | -3.10441e-10 0.0133336 0.0266673 0.0400009 0.0533346 0.0666682 0.0800018 82 | 0.0933355 0.106669 0.120003 0.133336 0.14667 0.160004 0.173337 0.186671 83 | 0.200005 0.213338 0.226672 0.240005 0.253339 0.266673 0.280006 0.29715 84 | 0.314293 0.331436 0.348579 0.365723 0.382866 0.400009 0.413343 0.426676 85 | 0.44001 0.453344 0.466677 0.480011 0.500011 0.520012 0.540012 0.560013 86 | 0.570013 0.580013 0.590014 0.600014 0.620014 0.640015 0.660015 0.680016 87 | 0.680016 0.720016 0.720016 0.720016; 88 | #X coords 0 1 99 -1 200 150 1 0 0; 89 | #X restore 224 216 graph; 90 | #X text 22 139 Also \, the '-' character is used as the subtraction 91 | operator \, so '\$0-x' is interpreted as "Id number" minus 'x'.; 92 | #X obj 82 406 jit/expr \$0-x[$f1]; 93 | #X floatatom 82 372 5 0 99 0 - - -, f 5; 94 | #X floatatom 82 442 5 0 0 0 - - -, f 5; 95 | #X obj 767 406 value x\$0; 96 | #X obj 809 374 value y_\$0; 97 | #X obj 590 354 jit/expr x\$0 \; y_\$0; 98 | #X msg 767 376 1.3; 99 | #X msg 809 344 4.7; 100 | #X obj 767 304 loadbang; 101 | #X obj 590 323 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 102 | -1 -1; 103 | #X floatatom 650 403 5 0 0 0 - - -, f 5; 104 | #X floatatom 590 403 5 0 0 0 - - -, f 5; 105 | #X text 499 37 A solution for such issues is to send the table name 106 | as a symbol:; 107 | #X text 511 231 But a simpler practice is to just avoid these issues 108 | by having '\$0' after a symbol character \, such as:; 109 | #X text 21 27 '\$0' - the patch ID number used to force locality in 110 | Pd - may be used in the definition of arrays or variables names. But 111 | it is important to note the restrictions of adopting the C-style expression 112 | syntax \, where variables cannot start with a number \, so '\$0x' is 113 | an illegal character as Pd sees it as starting with a number. in this 114 | case \, the object won't load!; 115 | #X text 209 399 It loads \, but the symbol is interpreted as "\$0 - 116 | x" \, so it is not a valid symbol character and not a table name - 117 | see Pd window for error., f 40; 118 | #X text 690 166 <= works now; 119 | #X connect 0 0 3 0; 120 | #X connect 1 0 0 1; 121 | #X connect 2 0 0 0; 122 | #X connect 4 0 1 0; 123 | #X connect 7 0 9 0; 124 | #X connect 8 0 7 0; 125 | #X connect 12 0 18 0; 126 | #X connect 12 1 17 0; 127 | #X connect 13 0 10 0; 128 | #X connect 14 0 11 0; 129 | #X connect 15 0 13 0; 130 | #X connect 15 0 14 0; 131 | #X connect 16 0 12 0; 132 | #X restore 803 361 pd Dealing_with_"\$0"; 133 | #X text 21 212 - [jit/fexpr~]: used for evaluation of signal expressions 134 | on sample level data (i.e. \, filter design) - very cpu intensive! 135 | , f 62; 136 | #X text 21 259 Input variables types for [jit/expr] \, [jit/expr~] 137 | and [jit/fexpr~] are: integers \, floats and symbols \, specified as 138 | follows where the '#' stands for the inlet number:, f 62; 139 | #X text 21 377 The [jit/expr~] object has an exclusive input variable 140 | type for signal vectors \, defined as:, f 62; 141 | #X text 21 443 The [jit/fexpr~] object has exclusive variable types 142 | \, defined as:, f 62; 143 | #X text 21 155 - [jit/expr~]: used for evaluation of audio signal expression 144 | - on a vector by vector basis (vector/block size is defined by the 145 | [block~] or [switch~] objects)., f 62; 146 | #X text 510 33 These objects have several built in operators and functions. 147 | The syntax is quite similar to how expressions are written in C. A 148 | semicolon can be used to define and separate different expressions. 149 | If so \, an outlet is created for each expression and they're evaluated 150 | from right to left (or bottom to up) order:; 151 | #X text 533 408 see also:; 152 | #X obj 582 510 block~; 153 | #X obj 641 510 value; 154 | #N canvas 2 72 1362 694 All_functions 0; 155 | #N canvas 497 97 374 326 Arithmetic-operators 0; 156 | #X obj 57 90 jit/expr $f1 + 4; 157 | #X floatatom 57 63 5 0 0 0 - - -, f 5; 158 | #X floatatom 57 118 5 0 0 0 - - -, f 5; 159 | #X floatatom 208 63 5 0 0 0 - - -, f 5; 160 | #X floatatom 208 118 5 0 0 0 - - -, f 5; 161 | #X obj 208 90 jit/expr $f1 * 4; 162 | #X floatatom 57 197 5 0 0 0 - - -, f 5; 163 | #X floatatom 57 252 5 0 0 0 - - -, f 5; 164 | #X floatatom 208 197 5 0 0 0 - - -, f 5; 165 | #X floatatom 208 252 5 0 0 0 - - -, f 5; 166 | #X obj 57 224 jit/expr $f1 - 1; 167 | #X obj 208 224 jit/expr $f1 / 10; 168 | #X text 57 34 Add; 169 | #X text 206 34 Multiply; 170 | #X text 206 174 Divide; 171 | #X text 56 174 Subtract; 172 | #X connect 0 0 2 0; 173 | #X connect 1 0 0 0; 174 | #X connect 3 0 5 0; 175 | #X connect 5 0 4 0; 176 | #X connect 6 0 10 0; 177 | #X connect 8 0 11 0; 178 | #X connect 10 0 7 0; 179 | #X connect 11 0 9 0; 180 | #X restore 147 131 pd Arithmetic-operators; 181 | #N canvas 513 126 621 391 Bitwise-operators 0; 182 | #X floatatom 47 218 5 0 0 0 - - -, f 5; 183 | #X obj 47 188 jit/expr ~ $f1; 184 | #X floatatom 44 103 5 0 0 0 - - -, f 5; 185 | #X obj 44 73 jit/expr $f1 & 3; 186 | #X floatatom 44 43 5 0 0 0 - - -, f 5; 187 | #X text 104 41 Bitwise And; 188 | #X floatatom 347 98 5 0 0 0 - - -, f 5; 189 | #X floatatom 347 38 5 0 0 0 - - -, f 5; 190 | #X obj 347 68 jit/expr $f1 << 1; 191 | #X floatatom 349 207 5 0 0 0 - - -, f 5; 192 | #X floatatom 349 147 5 0 0 0 - - -, f 5; 193 | #X text 404 147 Bitshift Right; 194 | #X text 413 36 Bitshift Left; 195 | #X obj 349 177 jit/expr $f1 >> 2; 196 | #X floatatom 353 320 5 0 0 0 - - -, f 5; 197 | #X floatatom 353 260 5 0 0 0 - - -, f 5; 198 | #X obj 353 290 jit/expr $f1 ^ 1; 199 | #X text 409 260 Bitwise Exclusive Or; 200 | #X floatatom 52 324 5 0 0 0 - - -, f 5; 201 | #X floatatom 52 264 5 0 0 0 - - -, f 5; 202 | #X text 114 256 Bitwise Or; 203 | #X obj 52 294 jit/expr $f1 | 3; 204 | #X floatatom 47 158 5 0 0 0 - - -, f 5; 205 | #X text 97 155 Bitwise Not; 206 | #X text 141 187 (one's complement); 207 | #X connect 1 0 0 0; 208 | #X connect 3 0 2 0; 209 | #X connect 4 0 3 0; 210 | #X connect 7 0 8 0; 211 | #X connect 8 0 6 0; 212 | #X connect 10 0 13 0; 213 | #X connect 13 0 9 0; 214 | #X connect 15 0 16 0; 215 | #X connect 16 0 14 0; 216 | #X connect 19 0 21 0; 217 | #X connect 21 0 18 0; 218 | #X connect 22 0 1 0; 219 | #X restore 147 162 pd Bitwise-operators; 220 | #N canvas 444 131 689 317 Comparison-operators 0; 221 | #X floatatom 48 55 5 0 0 0 - - -, f 5; 222 | #X floatatom 48 110 5 0 0 0 - - -, f 5; 223 | #X floatatom 199 54 5 0 0 0 - - -, f 5; 224 | #X floatatom 199 109 5 0 0 0 - - -, f 5; 225 | #X floatatom 55 194 5 0 0 0 - - -, f 5; 226 | #X floatatom 55 249 5 0 0 0 - - -, f 5; 227 | #X floatatom 351 58 5 0 0 0 - - -, f 5; 228 | #X floatatom 351 113 5 0 0 0 - - -, f 5; 229 | #X obj 48 82 jit/expr $f1 > 4; 230 | #X obj 199 81 jit/expr $f1 < 4; 231 | #X obj 55 221 jit/expr $f1 >= 1; 232 | #X text 207 26 Less than; 233 | #X text 48 26 Greater than; 234 | #X text 50 148 Greater than; 235 | #X text 57 166 or equal; 236 | #X floatatom 203 195 5 0 0 0 - - -, f 5; 237 | #X floatatom 203 250 5 0 0 0 - - -, f 5; 238 | #X text 206 167 or equal; 239 | #X text 202 149 Less than; 240 | #X obj 203 222 jit/expr $f1 <= 10; 241 | #X text 356 25 Equal; 242 | #X obj 351 85 jit/expr $f1 == 2; 243 | #X floatatom 497 58 5 0 0 0 - - -, f 5; 244 | #X floatatom 497 113 5 0 0 0 - - -, f 5; 245 | #X text 502 25 Not Equal; 246 | #X obj 497 85 jit/expr $f1 != 2; 247 | #X floatatom 360 191 5 0 0 0 - - -, f 5; 248 | #X floatatom 360 246 5 0 0 0 - - -, f 5; 249 | #X text 365 158 Logical And; 250 | #X obj 360 218 jit/expr $f1 && 1; 251 | #X floatatom 498 191 5 0 0 0 - - -, f 5; 252 | #X floatatom 498 246 5 0 0 0 - - -, f 5; 253 | #X text 503 158 Logical Or; 254 | #X obj 498 218 jit/expr $f1 || 0; 255 | #X connect 0 0 8 0; 256 | #X connect 2 0 9 0; 257 | #X connect 4 0 10 0; 258 | #X connect 6 0 21 0; 259 | #X connect 8 0 1 0; 260 | #X connect 9 0 3 0; 261 | #X connect 10 0 5 0; 262 | #X connect 15 0 19 0; 263 | #X connect 19 0 16 0; 264 | #X connect 21 0 7 0; 265 | #X connect 22 0 25 0; 266 | #X connect 25 0 23 0; 267 | #X connect 26 0 29 0; 268 | #X connect 29 0 27 0; 269 | #X connect 30 0 33 0; 270 | #X connect 33 0 31 0; 271 | #X restore 147 193 pd Comparison-operators; 272 | #N canvas 458 88 472 464 If-function 0; 273 | #X floatatom 82 53 5 0 0 0 - - -, f 5; 274 | #X floatatom 82 112 5 0 0 0 - - -, f 5; 275 | #X floatatom 87 218 5 0 0 0 - - -, f 5; 276 | #X floatatom 87 277 5 0 0 0 - - -, f 5; 277 | #X text 142 213 if(condition \, true \, false); 278 | #X obj 87 246 jit/expr if($f1 > 0 \, $f1 * 10 \, $f1 - 1); 279 | #X floatatom 59 346 5 0 0 0 - - -, f 5; 280 | #X floatatom 59 405 5 0 0 0 - - -, f 5; 281 | #X obj 59 374 jit/expr if($f1 > 10 \, 10 \, if($f1 < -10 \, -10 \, 282 | $f1)); 283 | #X text 118 404 <= clips at -10 and 10; 284 | #X obj 82 81 jit/expr if($f1 >= 0 \, 1 \, -1); 285 | #X text 53 21 Syntax: if(condition \, true \, false); 286 | #X text 143 325 You can also nest an if function as a true/false expression: 287 | , f 32; 288 | #X text 39 163 It accepts expressions as true/false as well:; 289 | #X connect 0 0 10 0; 290 | #X connect 2 0 5 0; 291 | #X connect 5 0 3 0; 292 | #X connect 6 0 8 0; 293 | #X connect 8 0 7 0; 294 | #X connect 10 0 1 0; 295 | #X restore 147 226 pd If-function; 296 | #N canvas 444 91 491 376 Random-function 0; 297 | #X floatatom 107 206 5 0 0 0 - - -, f 5; 298 | #X obj 107 175 jit/expr random(10 \, 20); 299 | #X obj 107 147 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 300 | -1 -1; 301 | #X text 133 142 random values from 10 to 19; 302 | #X floatatom 108 306 5 0 0 0 - - -, f 5; 303 | #X obj 108 247 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 304 | -1 -1; 305 | #X obj 108 275 jit/expr random(-100 \, 101); 306 | #X text 134 242 random values from -100 to 100; 307 | #X text 36 32 The random function generates random numbers within a 308 | given range \, specified by the two arguments. The range is from the 309 | first argument to the second argument minus one., f 51; 310 | #X connect 1 0 0 0; 311 | #X connect 2 0 1 0; 312 | #X connect 5 0 6 0; 313 | #X connect 6 0 4 0; 314 | #X restore 147 257 pd Random-function; 315 | #N canvas 437 97 594 434 Table-functions 0; 316 | #X obj 63 73 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 317 | -1; 318 | #X floatatom 63 163 0 0 0 0 - - -; 319 | #X floatatom 145 164 0 0 0 0 - - -; 320 | #X floatatom 228 164 0 0 0 0 - - -; 321 | #X text 28 101 Sum; 322 | #X text 28 119 sum; 323 | #X text 20 136 size; 324 | #N canvas 0 50 450 300 (subpatch) 0; 325 | #X array table 100 float 1; 326 | #A 0 -0.714272 -0.678559 -0.642845 -0.607131 -0.571417 -0.52856 -0.485703 327 | -0.428561 -0.371419 -0.314277 -0.257134 -0.228563 -0.114279 -0.0857077 328 | -0.0571365 -0.0285654 5.72205e-06 0.0285769 0.0428624 0.057148 0.0857191 329 | 0.11429 0.142861 0.171432 0.171432 0.200004 0.200004 0.200004 0.228575 330 | 0.228575 0.228575 0.228575 0.228575 0.228575 0.228575 0.200004 0.0857191 331 | 5.72205e-06 -0.228563 -0.885699 -0.885699 -0.885699 -0.885699 -0.857128 332 | -0.842843 -0.828557 -0.771415 -0.73332 -0.695225 -0.65713 -0.599988 333 | -0.542846 -0.485703 -0.419038 -0.352372 -0.257134 -0.171421 -0.114279 334 | 5.72205e-06 0.0285769 0.142861 0.200004 0.228575 0.271431 0.314288 335 | 0.342859 0.400002 0.428573 0.485715 0.485715 0.514286 0.514286 0.514286 336 | 0.514286 0.514286 0.485715 0.457144 0.428573 0.428573 0.400002 0.342859 337 | 0.328574 0.314288 0.285717 0.285717 0.257146 0.257146 0.257146 0.257146 338 | 0.257146 0.257146 0.257146 0.285717 0.314288 0.342859 0.342859 0.342859 339 | 0.342859 0.342859 0.342859; 340 | #X coords 0 1 99 -1 200 140 1 0 0; 341 | #X restore 344 94 graph; 342 | #X text 96 276 Note that the syntax requires that the table name is 343 | inside quotes., f 51; 344 | #X obj 295 347 jit/expr size("$s1") \;; 345 | #X obj 63 102 jit/expr Sum("table" \, 3 \, 9) \; sum("table") \; size("table") 346 | ; 347 | #X msg 295 317 symbol table; 348 | #X floatatom 295 380 5 0 0 0 - - -, f 5; 349 | #X text 64 187 Sums a range of indexes, f 8; 350 | #X text 146 189 Sums all indexes, f 8; 351 | #X text 225 189 Gives array size, f 5; 352 | #X text 114 26 There are 3 functions that operate on arrays: Sum \, 353 | sum and size., f 45; 354 | #X connect 0 0 10 0; 355 | #X connect 9 0 12 0; 356 | #X connect 10 0 1 0; 357 | #X connect 10 1 2 0; 358 | #X connect 10 2 3 0; 359 | #X connect 11 0 9 0; 360 | #X restore 147 326 pd Table-functions; 361 | #N canvas 576 55 618 571 Power-functions 0; 362 | #X floatatom 74 60 5 0 0 0 - - -, f 5; 363 | #X floatatom 74 119 5 0 0 0 - - -, f 5; 364 | #X floatatom 74 151 5 0 0 0 - - -, f 5; 365 | #X floatatom 74 208 5 0 0 0 - - -, f 5; 366 | #X floatatom 71 337 5 0 0 0 - - -, f 5; 367 | #X floatatom 71 409 0 0 0 0 - - -; 368 | #X floatatom 338 103 5 0 0 0 - - -, f 5; 369 | #X floatatom 426 101 5 0 0 0 - - -, f 5; 370 | #X floatatom 338 31 5 0 0 0 - - -, f 5; 371 | #X floatatom 340 414 5 0 0 0 - - -, f 5; 372 | #X floatatom 340 484 0 0 0 0 - - -; 373 | #X floatatom 74 513 5 0 0 0 - - -, f 5; 374 | #X obj 74 88 jit/expr pow($f1 \, 2); 375 | #X text 42 88 pow; 376 | #X text 33 178 sqrt; 377 | #X text 41 364 exp; 378 | #X floatatom 338 276 5 0 0 0 - - -, f 5; 379 | #X floatatom 338 224 5 0 0 0 - - -, f 5; 380 | #X obj 338 56 jit/expr ln($f1) \; log($f1); 381 | #X obj 74 484 jit/expr fact($f1); 382 | #X msg 74 451 3; 383 | #X floatatom 435 482 0 0 0 0 - - -; 384 | #X text 304 442 erf; 385 | #X text 296 458 erfc; 386 | #X floatatom 166 408 0 0 0 0 - - -; 387 | #X floatatom 339 187 5 0 0 0 - - -, f 5; 388 | #X floatatom 339 135 5 0 0 0 - - -, f 5; 389 | #X obj 339 160 jit/expr log1p($f1); 390 | #X obj 338 249 jit/expr log10($f1); 391 | #X floatatom 341 375 5 0 0 0 - - -, f 5; 392 | #X floatatom 341 323 5 0 0 0 - - -, f 5; 393 | #X obj 341 348 jit/expr ldexp($f1 \, 3); 394 | #X text 310 57 ln; 395 | #X text 302 70 log; 396 | #X text 284 250 log10; 397 | #X text 287 159 log1p; 398 | #X text 290 348 ldexp; 399 | #X text 36 482 fact; 400 | #X obj 71 365 jit/expr exp($f1) \; expm1($f1); 401 | #X text 24 382 expm1; 402 | #X floatatom 73 239 5 0 0 0 - - -, f 5; 403 | #X floatatom 73 296 5 0 0 0 - - -, f 5; 404 | #X text 33 268 cbrt; 405 | #X obj 74 179 jit/expr sqrt($f1); 406 | #X obj 73 267 jit/expr cbrt($f1); 407 | #X text 183 382 exp - 1; 408 | #X text 267 507 error function; 409 | #X text 434 505 complementary error function, f 14; 410 | #X obj 340 442 jit/expr erf($f1) \; erfc($f1); 411 | #X text 76 33 Raise to the power of; 412 | #X text 141 148 Square root; 413 | #X text 150 239 Cube root; 414 | #X text 133 324 Exp: 'e' raised to the power of, f 15; 415 | #X text 110 451 Factorial; 416 | #X text 385 29 Natural log; 417 | #X text 397 133 Natural log of input + 1; 418 | #X text 385 221 Log base 10; 419 | #X text 396 307 Multiply by integral power of 2, f 20; 420 | #X text 400 409 Error functions; 421 | #X text 124 54 second argument; 422 | #X connect 0 0 12 0; 423 | #X connect 2 0 43 0; 424 | #X connect 4 0 38 0; 425 | #X connect 8 0 18 0; 426 | #X connect 9 0 48 0; 427 | #X connect 12 0 1 0; 428 | #X connect 17 0 28 0; 429 | #X connect 18 0 6 0; 430 | #X connect 18 1 7 0; 431 | #X connect 19 0 11 0; 432 | #X connect 20 0 19 0; 433 | #X connect 26 0 27 0; 434 | #X connect 27 0 25 0; 435 | #X connect 28 0 16 0; 436 | #X connect 30 0 31 0; 437 | #X connect 31 0 29 0; 438 | #X connect 38 0 5 0; 439 | #X connect 38 1 24 0; 440 | #X connect 40 0 44 0; 441 | #X connect 43 0 3 0; 442 | #X connect 44 0 41 0; 443 | #X connect 48 0 10 0; 444 | #X connect 48 1 21 0; 445 | #X restore 147 359 pd Power-functions; 446 | #N canvas 411 157 793 422 Trigonometric-functions 0; 447 | #X floatatom 73 34 5 0 0 0 - - -, f 5; 448 | #X floatatom 73 93 5 0 0 0 - - -, f 5; 449 | #X floatatom 73 125 5 0 0 0 - - -, f 5; 450 | #X floatatom 73 182 5 0 0 0 - - -, f 5; 451 | #X floatatom 72 213 5 0 0 0 - - -, f 5; 452 | #X floatatom 72 270 5 0 0 0 - - -, f 5; 453 | #X obj 73 62 jit/expr sin($f1); 454 | #X text 38 62 sin; 455 | #X text 35 152 cos; 456 | #X obj 73 153 jit/expr cos($f1); 457 | #X text 35 242 tan; 458 | #X obj 72 241 jit/expr tan($f1); 459 | #X floatatom 273 34 5 0 0 0 - - -, f 5; 460 | #X floatatom 273 93 5 0 0 0 - - -, f 5; 461 | #X floatatom 273 125 5 0 0 0 - - -, f 5; 462 | #X floatatom 273 182 5 0 0 0 - - -, f 5; 463 | #X text 231 62 asin; 464 | #X text 228 152 acos; 465 | #X obj 273 62 jit/expr asin($f1); 466 | #X obj 273 153 jit/expr acos($f1); 467 | #X text 122 32 sine; 468 | #X text 124 125 cosine; 469 | #X text 123 213 tangent; 470 | #X floatatom 274 209 5 0 0 0 - - -, f 5; 471 | #X floatatom 274 266 5 0 0 0 - - -, f 5; 472 | #X obj 274 237 jit/expr tan($f1); 473 | #X text 230 238 atan; 474 | #X text 340 32 arc sine; 475 | #X text 335 125 arc cosine; 476 | #X text 335 209 arc tangent; 477 | #X floatatom 506 33 5 0 0 0 - - -, f 5; 478 | #X floatatom 506 92 5 0 0 0 - - -, f 5; 479 | #X floatatom 506 124 5 0 0 0 - - -, f 5; 480 | #X floatatom 506 181 5 0 0 0 - - -, f 5; 481 | #X floatatom 507 208 5 0 0 0 - - -, f 5; 482 | #X floatatom 507 265 5 0 0 0 - - -, f 5; 483 | #X text 454 61 asinh; 484 | #X text 451 151 acosh; 485 | #X text 453 237 atanh; 486 | #X text 630 46 inverse hyperbolic sine, f 10; 487 | #X obj 506 61 jit/expr asinh($f1); 488 | #X obj 506 152 jit/expr acosh($f1); 489 | #X obj 507 236 jit/expr atanh($f1); 490 | #X text 631 137 inverse hyperbolic cosine, f 10; 491 | #X text 632 225 inverse hyperbolic tangent, f 10; 492 | #X floatatom 243 319 5 0 0 0 - - -, f 5; 493 | #X floatatom 243 376 5 0 0 0 - - -, f 5; 494 | #X text 192 348 atan2; 495 | #X obj 243 347 jit/expr atan2($f1 \, $f2); 496 | #X floatatom 380 319 5 0 0 0 - - -, f 5; 497 | #X text 405 347 arc tangent of 2 variables; 498 | #X connect 0 0 6 0; 499 | #X connect 2 0 9 0; 500 | #X connect 4 0 11 0; 501 | #X connect 6 0 1 0; 502 | #X connect 9 0 3 0; 503 | #X connect 11 0 5 0; 504 | #X connect 12 0 18 0; 505 | #X connect 14 0 19 0; 506 | #X connect 18 0 13 0; 507 | #X connect 19 0 15 0; 508 | #X connect 23 0 25 0; 509 | #X connect 25 0 24 0; 510 | #X connect 30 0 40 0; 511 | #X connect 32 0 41 0; 512 | #X connect 34 0 42 0; 513 | #X connect 40 0 31 0; 514 | #X connect 41 0 33 0; 515 | #X connect 42 0 35 0; 516 | #X connect 45 0 48 0; 517 | #X connect 48 0 46 0; 518 | #X connect 49 0 48 1; 519 | #X restore 147 392 pd Trigonometric-functions; 520 | #N canvas 2 72 1362 694 Other-functions 0; 521 | #X floatatom 93 24 5 0 0 0 - - -, f 5; 522 | #X floatatom 93 97 5 0 0 0 - - -, f 5; 523 | #X text 202 52 convert to int; 524 | #X floatatom 88 145 5 0 0 0 - - -, f 5; 525 | #X floatatom 88 235 5 0 0 0 - - -, f 5; 526 | #X text 58 51 int; 527 | #X text 46 176 rint; 528 | #X text 139 137 round a float to a nearby integer, f 17; 529 | #X text 6 209 nearbyint; 530 | #X text 141 289 convert to float; 531 | #X floatatom 89 365 5 0 0 0 - - -, f 5; 532 | #X floatatom 233 363 5 0 0 0 - - -, f 5; 533 | #X floatatom 89 293 5 0 0 0 - - -, f 5; 534 | #X text 37 323 float; 535 | #X floatatom 421 30 5 0 0 0 - - -, f 5; 536 | #X floatatom 421 106 5 0 0 0 - - -, f 5; 537 | #X floatatom 537 103 5 0 0 0 - - -, f 5; 538 | #X text 386 75 min; 539 | #X text 386 58 max; 540 | #X obj 421 58 jit/expr max($f1 \, 0) \; min($f1 \, 0); 541 | #X text 473 28 maximum / minimum; 542 | #X obj 420 173 jit/expr copysign($f1 \, $f2); 543 | #X floatatom 578 150 5 0 0 0 - - -, f 5; 544 | #X floatatom 420 148 5 0 0 0 - - -, f 5; 545 | #X text 353 174 copysign; 546 | #X floatatom 420 202 5 0 0 0 - - -, f 5; 547 | #X text 466 201 copy sign of a number; 548 | #X obj 89 318 jit/expr float($i1) / 10 \; float(5) / 10 \;; 549 | #X floatatom 732 162 5 0 0 0 - - -, f 5; 550 | #X floatatom 841 161 5 0 0 0 - - -, f 5; 551 | #X obj 732 117 jit/expr isinf($f1) \; finite($f1); 552 | #X text 683 116 isinf; 553 | #X text 675 134 finite; 554 | #X text 860 115 "is infinite" and "is finite", f 15; 555 | #X floatatom 732 85 5 0 0 0 - - -, f 5; 556 | #X msg 772 27 2; 557 | #X obj 732 52 pow 1e+10; 558 | #X msg 732 25 1; 559 | #X floatatom 740 345 5 0 0 0 - - -, f 5; 560 | #X floatatom 740 283 5 0 0 0 - - -, f 5; 561 | #X obj 740 313 jit/expr isnan($f1); 562 | #X text 688 314 isnan; 563 | #X text 924 288 "is Nan" (not a number), f 8; 564 | #X text 372 296 modf; 565 | #X floatatom 413 253 5 0 0 0 - - -, f 5; 566 | #X floatatom 413 322 5 0 0 0 - - -, f 5; 567 | #X text 365 280 imodf; 568 | #X obj 93 52 jit/expr int($f1) \; trunc($f1); 569 | #X floatatom 188 97 5 0 0 0 - - -, f 5; 570 | #X text 48 64 trunc; 571 | #X text 202 67 truncate a float; 572 | #X floatatom 139 235 5 0 0 0 - - -, f 5; 573 | #X text 38 193 round; 574 | #X floatatom 190 235 5 0 0 0 - - -, f 5; 575 | #X obj 88 176 jit/expr rint($f1) \; round($f1) \; nearbyint($f1); 576 | #X text 471 400 float remainder functions, f 15; 577 | #X floatatom 410 424 5 0 0 0 - - -, f 5; 578 | #X floatatom 410 495 5 0 0 0 - - -, f 5; 579 | #X floatatom 547 494 5 0 0 0 - - -, f 5; 580 | #X floatatom 91 419 5 0 0 0 - - -, f 5; 581 | #X floatatom 91 492 5 0 0 0 - - -, f 5; 582 | #X floatatom 193 492 5 0 0 0 - - -, f 5; 583 | #X text 143 417 ceil / floor; 584 | #X text 47 447 ceil; 585 | #X text 39 465 floor; 586 | #X obj 91 447 jit/expr ceil($f1) \; floor($f1); 587 | #X obj 413 279 jit/expr imodf($f1) \; modf($f1); 588 | #X text 397 343 get signed integer, f 10; 589 | #X text 507 345 get signed fractional, f 11; 590 | #X floatatom 522 322 5 0 0 0 - - -, f 5; 591 | #X floatatom 771 398 5 0 0 0 - - -, f 5; 592 | #X obj 771 435 jit/expr abs($f1); 593 | #X text 739 435 abs; 594 | #X floatatom 771 472 5 0 0 0 - - -, f 5; 595 | #X text 819 397 absolute value; 596 | #X text 369 452 fmod; 597 | #X text 329 466 remainder; 598 | #X obj 740 223 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 599 | -1 -1; 600 | #X obj 410 449 jit/expr fmod($f1 \, 4.1) \; remainder($f1 \, 4.1); 601 | #X msg 410 400 7; 602 | #X obj 740 249 jit/expr log(-1.); 603 | #X connect 0 0 47 0; 604 | #X connect 3 0 54 0; 605 | #X connect 12 0 27 0; 606 | #X connect 14 0 19 0; 607 | #X connect 19 0 15 0; 608 | #X connect 19 1 16 0; 609 | #X connect 21 0 25 0; 610 | #X connect 22 0 21 1; 611 | #X connect 23 0 21 0; 612 | #X connect 27 0 10 0; 613 | #X connect 27 1 11 0; 614 | #X connect 30 0 28 0; 615 | #X connect 30 1 29 0; 616 | #X connect 34 0 30 0; 617 | #X connect 35 0 36 0; 618 | #X connect 36 0 34 0; 619 | #X connect 37 0 36 0; 620 | #X connect 39 0 40 0; 621 | #X connect 40 0 38 0; 622 | #X connect 44 0 66 0; 623 | #X connect 47 0 1 0; 624 | #X connect 47 1 48 0; 625 | #X connect 54 0 4 0; 626 | #X connect 54 1 51 0; 627 | #X connect 54 2 53 0; 628 | #X connect 56 0 78 0; 629 | #X connect 59 0 65 0; 630 | #X connect 65 0 60 0; 631 | #X connect 65 1 61 0; 632 | #X connect 66 0 45 0; 633 | #X connect 66 1 69 0; 634 | #X connect 70 0 71 0; 635 | #X connect 71 0 73 0; 636 | #X connect 77 0 80 0; 637 | #X connect 78 0 57 0; 638 | #X connect 78 1 58 0; 639 | #X connect 79 0 56 0; 640 | #X connect 80 0 39 0; 641 | #X restore 147 424 pd Other-functions; 642 | #N canvas 332 23 592 281 Store-function 0; 643 | #X text 40 26 The store function is defined with "=" \, and it can 644 | store the result of any operation into a variable or table index.; 645 | #X obj 62 129 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 646 | -1; 647 | #X floatatom 62 211 5 0 0 0 - - -, f 5; 648 | #X floatatom 276 204 5 0 0 0 - - -, f 5; 649 | #X obj 62 158 jit/expr array2[inc - 1] = 1 / inc \; inc = inc + 1; 650 | #N canvas 0 22 450 278 (subpatch) 0; 651 | #X array array2 16 float 2; 652 | #X coords 0 1.1 16 -0.1 200 140 1 0 0; 653 | #X restore 351 101 graph; 654 | #X obj 129 119 v inc; 655 | #X msg 129 94 0; 656 | #X connect 1 0 4 0; 657 | #X connect 4 0 2 0; 658 | #X connect 4 1 3 0; 659 | #X connect 7 0 6 0; 660 | #X restore 147 291 pd Store-function; 661 | #X text 28 14 Here are all the operators and functions available for 662 | [jit/expr] \, [jit/expr~] and [jit/fexpr~] \, even though the given 663 | examples are only presented with [jit/expr]., f 52; 664 | #X text 28 71 These are organized in different groups. Click on the 665 | subpatches below to check each group:, f 52; 666 | #X restore 803 387 pd All_functions \; and operators; 667 | #N canvas 2 52 1362 714 [jit/expr] 0; 668 | #X obj 49 115 jit/expr 1; 669 | #X floatatom 47 248 0 0 0 0 - - -; 670 | #X floatatom 49 145 0 0 0 0 - - -; 671 | #X msg 49 87 bang; 672 | #X obj 150 115 jit/expr 2 + 3; 673 | #X msg 150 88 bang; 674 | #X floatatom 150 143 0 0 0 0 - - -; 675 | #X floatatom 47 309 0 0 0 0 - - -; 676 | #X floatatom 69 402 0 0 0 0 - - -; 677 | #X floatatom 69 478 0 0 0 0 - - -; 678 | #X floatatom 700 156 0 0 0 0 - - -; 679 | #X obj 700 128 jit/expr 8 / 3; 680 | #X floatatom 625 324 0 0 0 0 - - -; 681 | #X obj 700 102 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 682 | -1 -1; 683 | #X obj 625 256 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 684 | -1 -1; 685 | #X floatatom 566 505 0 0 0 0 - - -; 686 | #X obj 566 477 jit/expr $f1 / 3; 687 | #X floatatom 693 503 0 0 0 0 - - -; 688 | #X obj 693 475 jit/expr $i1 / 3; 689 | #X msg 693 434 8; 690 | #X obj 693 404 loadbang; 691 | #X text 17 27 Examples of [jit/expr] object:; 692 | #X text 277 111 bang evaluates the expression, f 17; 693 | #X obj 47 278 jit/expr ($f1 + 2) + $i2; 694 | #X floatatom 184 249 0 0 0 0 - - -; 695 | #X text 437 23 NOTE About integers:; 696 | #X floatatom 748 324 0 0 0 0 - - -; 697 | #X obj 625 280 jit/expr float(8) / 3 \; 8./3; 698 | #X text 121 400 use of trigonometric functions; 699 | #X obj 69 430 jit/expr cos(2 * 3.14159 * $f1 / 360) \; sin(2 * 3.14159 700 | * $f1 / 360); 701 | #X floatatom 304 478 0 0 0 0 - - -; 702 | #X text 465 53 Integer numbers in the [jit/expr] object are interpreted 703 | as integers \, not floats. Hence \, the result of the division below 704 | is also an integer.; 705 | #X text 466 193 You can use the "float" function to convert an integer 706 | to a float. Another workaround is to have a decimal point in a way 707 | Pd can't turn into an integer \, such as below in the bottom expression: 708 | ; 709 | #X text 222 242 '$f1' is a float from the 1st inlet \, '$i2' is an 710 | integer from the second inlet (float input values are truncated to 711 | integers), f 26; 712 | #X text 475 365 But there's no issue if you send an integer value to 713 | an inlet defined as a float.; 714 | #X msg 146 372 print; 715 | #X msg 207 89 print; 716 | #X text 279 145 print outputs the llvm assembly; 717 | #X connect 0 0 2 0; 718 | #X connect 1 0 23 0; 719 | #X connect 3 0 0 0; 720 | #X connect 4 0 6 0; 721 | #X connect 5 0 4 0; 722 | #X connect 8 0 29 0; 723 | #X connect 11 0 10 0; 724 | #X connect 13 0 11 0; 725 | #X connect 14 0 27 0; 726 | #X connect 16 0 15 0; 727 | #X connect 18 0 17 0; 728 | #X connect 19 0 16 0; 729 | #X connect 19 0 18 0; 730 | #X connect 20 0 19 0; 731 | #X connect 23 0 7 0; 732 | #X connect 24 0 23 1; 733 | #X connect 27 0 12 0; 734 | #X connect 27 1 26 0; 735 | #X connect 29 0 9 0; 736 | #X connect 29 1 30 0; 737 | #X connect 35 0 29 0; 738 | #X connect 36 0 4 0; 739 | #X restore 557 268 pd [jit/expr] Examples; 740 | #N canvas 2 72 1362 694 [jit/expr~] 0; 741 | #X floatatom 80 361 0 0 0 0 - - -; 742 | #X obj 80 468 dac~; 743 | #X text 125 361 frequency; 744 | #X floatatom 685 260 0 0 10 0 - - -; 745 | #X obj 478 329 tabsend~ a1; 746 | #N canvas 0 50 450 300 (subpatch) 0; 747 | #X array a1 64 float 0; 748 | #X coords 0 1 63 -1 200 140 1; 749 | #X restore 614 347 graph; 750 | #X obj 88 631 tabsend~ a2; 751 | #X obj 253 627 tabsend~ a3; 752 | #N canvas 0 50 450 300 (subpatch) 0; 753 | #X array a2 64 float 1; 754 | #A 0 0.426241 0.471417 0.509466 0.5395 0.560763 0.572675 0.574871 0.567133 755 | 0.549476 0.522103 0.485382 0.439915 0.386436 0.325852 0.259226 0.187714 756 | 0.112595 0.0352011 -0.0430835 -0.120859 -0.196732 -0.269351 -0.337427 757 | -0.39975 -0.455253 -0.502973 -0.542119 -0.572078 -0.592377 -0.602793 758 | -0.60326 -0.593903 -0.575082 -0.547297 -0.511266 -0.467854 -0.418052 759 | -0.363017 -0.303973 -0.242242 -0.179198 -0.116226 -0.0547288 0.0039332 760 | 0.0584543 0.107614 0.150295 0.185514 0.212438 0.23039 0.238884 0.237616 761 | 0.226482 0.205578 0.175178 0.135769 0.0880036 0.0327047 -0.0291521 762 | -0.0964589 -0.168 -0.242475 -0.318515 -0.394739; 763 | #X coords 0 1 63 -1 200 140 1; 764 | #X restore 395 520 graph; 765 | #N canvas 0 50 450 300 (subpatch) 0; 766 | #X array a3 64 float 0; 767 | #X coords 0 1 63 -1 200 140 1; 768 | #X restore 617 520 graph; 769 | #X obj 80 388 osc~ 440; 770 | #X obj 283 332 hsl 128 15 0 127 0 0 empty empty empty -2 -8 0 10 -262144 771 | -1 -1 0 1; 772 | #X floatatom 280 352 5 0 0 0 - - -, f 5; 773 | #X text 331 352 amplitude; 774 | #X obj 280 399 line~; 775 | #X obj 80 429 jit/expr~ $v1 * pow($v2 / 127 \, 4); 776 | #X obj 88 581 jit/expr~ $v1 * $v2 \; if ($v2 > 0 \, 0 \, $v1*$v2); 777 | #X obj 88 548 osc~ 440; 778 | #X obj 478 252 osc~ 440; 779 | #X msg 322 52 \; pd dsp 0; 780 | #X msg 229 52 \; pd dsp 1; 781 | #X text 227 30 audio on; 782 | #X text 320 29 audio off; 783 | #X obj 478 290 jit/expr~ max(min($v1 \, $f2) \, -$f2); 784 | #X obj 685 145 vsl 20 100 0 1 0 0 empty empty empty 0 -9 0 10 -262144 785 | -1 -1 0 1; 786 | #X text 460 187 Move the slider to change the limiter threshold (from 787 | 0-1)., f 29; 788 | #X text 64 29 make sure to turn on the audio for the [jit/expr~] examples 789 | ====>, f 23; 790 | #X obj 130 297 print~; 791 | #X msg 148 268 bang; 792 | #X floatatom 232 207 0 0 0 0 - - -; 793 | #X floatatom 130 182 0 0 0 0 - - -; 794 | #X obj 130 237 jit/expr~ $v1 + $f2; 795 | #X obj 130 209 sig~ 1; 796 | #X obj 253 548 osc~ 550; 797 | #X text 458 163 A simple limiter/clip example:; 798 | #X text 27 202 Examples:; 799 | #X text 468 37 NOTE: The vector/block size can be set with the [block~] 800 | or [switch~] objects \, and is 64 samples by default., f 43; 801 | #X msg 280 375 \$1 20; 802 | #X text 22 111 NOTE: The first inlet of [jit/expr~] needs to be of 803 | type '$v1' (cannot be '$f1' \, '$i1' or '$s1'). A float sent to a '$v#' 804 | inlet is promoted to a signal., f 54; 805 | #X connect 0 0 10 0; 806 | #X connect 3 0 23 1; 807 | #X connect 10 0 15 0; 808 | #X connect 11 0 12 0; 809 | #X connect 12 0 37 0; 810 | #X connect 14 0 15 1; 811 | #X connect 15 0 1 0; 812 | #X connect 15 0 1 1; 813 | #X connect 16 0 6 0; 814 | #X connect 16 1 7 0; 815 | #X connect 17 0 16 0; 816 | #X connect 18 0 23 0; 817 | #X connect 23 0 4 0; 818 | #X connect 24 0 3 0; 819 | #X connect 28 0 27 0; 820 | #X connect 29 0 31 1; 821 | #X connect 30 0 32 0; 822 | #X connect 31 0 27 0; 823 | #X connect 32 0 31 0; 824 | #X connect 33 0 16 1; 825 | #X connect 37 0 14 0; 826 | #X restore 549 296 pd [jit/expr~] Examples; 827 | #N canvas 2 72 1362 694 [jit/fexpr~] 0; 828 | #X msg 339 45 \; pd dsp 0; 829 | #X msg 246 45 \; pd dsp 1; 830 | #X text 244 23 audio on; 831 | #X text 337 22 audio off; 832 | #X text 41 387 - $x1: same as $x1[0] \, $x2: same as $x2[0] (and so 833 | on)., f 60; 834 | #X text 41 368 - $x: same as $x1[0]., f 60; 835 | #X text 41 406 - $y: same as $y1[-1]., f 60; 836 | #X text 41 426 - $y1: same as $y1[-1] \, $y2: same as $y2[-1] (and 837 | so on)., f 60; 838 | #X msg 112 538 start; 839 | #X msg 60 538 stop; 840 | #X obj 60 508 loadbang; 841 | #X msg 181 535 set 4000; 842 | #X obj 168 501 sig~ 0.001; 843 | #X obj 168 615 jit/fexpr~ $x1[0] + $y1[-1]; 844 | #X text 290 560 clears output buffer; 845 | #X obj 168 641 snapshot~; 846 | #X obj 32 593 metro 100; 847 | #X obj 32 571 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 848 | 1; 849 | #X floatatom 168 673 0 0 0 0 - - -; 850 | #X text 248 500 increment value; 851 | #X text 246 534 set last output value ($y1[-1]); 852 | #X text 506 233 - clear: clears all the previous input and output buffers 853 | , f 60; 854 | #X text 506 194 - clear x#: clears the previous values of the #th input 855 | , f 60; 856 | #X text 506 213 - clear y#: clears the previous values of the #th output 857 | , f 60; 858 | #X text 506 87 - set y# : sets the as many supplied values of 859 | the #th output (e.g. "set y3 1 2" sets $y3[-1] = 1 and $y3[-2] = 2). 860 | ; 861 | #X floatatom 767 549 0 0 0 0 - - -; 862 | #X msg 568 515 start; 863 | #X msg 522 515 stop; 864 | #X obj 522 482 loadbang; 865 | #X msg 728 511 0; 866 | #X obj 598 670 dac~; 867 | #X obj 623 624 *~ 0.1; 868 | #X obj 623 585 jit/fexpr~ $x1 + $x1[$f2]; 869 | #X msg 623 457 1102.5; 870 | #X floatatom 623 515 0 0 0 0 - - -; 871 | #X text 502 382 Simple FIR filter:, f 61; 872 | #X msg 807 511 -20; 873 | #X msg 767 511 -10; 874 | #X text 705 465 change values to filter the frequencies, f 23; 875 | #X text 506 131 - set : sets the first past values of each output 876 | (e.g. "set 0.1 2.2 0.4" sets y1[-1] = 0.1 \, y2[-1] = 2.2 \, and y3[-1] 877 | = 0.4).; 878 | #X text 506 42 - set x# : sets as many supplied values of the 879 | #th input (e.g. "set x2 1 2" sets $x2[0] = 1 and $x2[-1] = 2)., f 880 | 60; 881 | #X text 14 93 NOTES:; 882 | #X text 14 460 Simple accumulator example:; 883 | #X text 482 13 [jit/fexpr~] responds to the following methods:, f 884 | 63; 885 | #X text 62 234 - $y#[n]: output sample of outlet # indexed by 'n'; 886 | #X text 62 215 - $x#[n]: input sample of inlet # indexed by 'n', f 887 | 50; 888 | #X text 24 346 - There are shorthands as follows:; 889 | #X text 506 252 - stop: stops the computation of [jit/fexpr~] *, f 890 | 60; 891 | #X text 506 272 - start: starts the computation of [jit/fexpr~] *, 892 | f 60; 893 | #X text 835 615 more examples:; 894 | #N canvas 2 72 1362 694 Difference 0; 895 | #X obj 194 193 v pr; 896 | #X obj 341 193 v r; 897 | #X obj 249 195 v b; 898 | #X floatatom 194 164 5 0 0 0 - - -, f 5; 899 | #X floatatom 341 166 5 0 0 0 - - -, f 5; 900 | #X msg 194 139 10; 901 | #X floatatom 249 165 7 0 0 0 - - -, f 7; 902 | #X floatatom 399 166 5 0 0 0 - - -, f 5; 903 | #X obj 399 195 v dt; 904 | #X msg 341 137 18; 905 | #X msg 399 127 0.01; 906 | #X obj 79 400 dac~; 907 | #X obj 172 65 bng 25 250 50 0 empty empty empty 20 8 0 8 -262144 -1 908 | -1; 909 | #X obj 449 136 line; 910 | #X msg 449 110 0.01 \, 0.04 5000; 911 | #X obj 101 193 loadbang; 912 | #N canvas 0 50 450 300 (subpatch) 0; 913 | #X array X 64 float 0; 914 | #X coords 0 20 63 -20 200 140 1 0 0; 915 | #X restore 178 423 graph; 916 | #N canvas 0 50 450 300 (subpatch) 0; 917 | #X array Y 64 float 0; 918 | #X coords 0 20 63 -20 200 140 2 0 0; 919 | #X restore 418 421 graph; 920 | #N canvas 0 50 450 300 (subpatch) 0; 921 | #X array Z 64 float 0; 922 | #X coords 0 40 63 0 200 140 2 0 0; 923 | #X restore 655 420 graph; 924 | #X obj 249 136 jit/expr 8./3; 925 | #X text 577 289 $y1 -> $y1[-1] $y2 -> $y2[-1] $y3 -> $y3[-1], f 15 926 | ; 927 | #X obj 94 359 *~ 0.01; 928 | #X text 145 547 -20; 929 | #X text 152 416 20; 930 | #X text 381 547 -20; 931 | #X text 388 416 20; 932 | #X text 635 547 0; 933 | #X text 630 416 40; 934 | #X text 624 24 Lorenz Equations written with 3 state variables X \, 935 | Y \, and Z:, f 30; 936 | #X text 412 301 <= Note the shorthands:; 937 | #X text 670 67 dX/dt = pr * (Y - X); 938 | #X text 670 87 dY/dt = X(r - Z) - Y; 939 | #X text 670 107 dZ/dt = X*Y - bZ, f 20; 940 | #X obj 172 285 jit/fexpr~ $y1 + pr*($y2 - $y1) * dt \; $y2 + ($y1*(r 941 | - $y3) - $y2) * dt \; $y3 + ($y1*$y2 - b*$y3) * dt; 942 | #X obj 172 359 tabsend~ X; 943 | #X obj 286 359 tabsend~ Y; 944 | #X obj 400 360 tabsend~ Z; 945 | #X text 294 250 <= sets initial values of $y1[-1] \, $y2[-1] \, and 946 | $y3[-1]; 947 | #X msg 23 220 start \; pd dsp 1; 948 | #X text 108 51 bang to start =>, f 8; 949 | #X text 228 24 This is an example of how [jit/fexpr~] can be used to 950 | solve differential equations such as the lorenz equations., f 49; 951 | #X msg 101 218 stop; 952 | #X text 446 165 <= experiment with these parameter values. If you; 953 | #X text 470 183 hear a click and audio stops \, the system went unstable 954 | and you need to bang on the top again to reload the default values. 955 | , f 46; 956 | #X msg 172 251 set 1.2 2.3 4.4; 957 | #X connect 3 0 0 0; 958 | #X connect 4 0 1 0; 959 | #X connect 5 0 3 0; 960 | #X connect 6 0 2 0; 961 | #X connect 7 0 8 0; 962 | #X connect 9 0 4 0; 963 | #X connect 10 0 7 0; 964 | #X connect 12 0 5 0; 965 | #X connect 12 0 9 0; 966 | #X connect 12 0 10 0; 967 | #X connect 12 0 38 0; 968 | #X connect 12 0 19 0; 969 | #X connect 12 0 44 0; 970 | #X connect 13 0 7 0; 971 | #X connect 14 0 13 0; 972 | #X connect 15 0 41 0; 973 | #X connect 19 0 6 0; 974 | #X connect 21 0 11 0; 975 | #X connect 21 0 11 1; 976 | #X connect 33 0 21 0; 977 | #X connect 33 0 34 0; 978 | #X connect 33 1 35 0; 979 | #X connect 33 2 36 0; 980 | #X connect 38 0 33 0; 981 | #X connect 41 0 33 0; 982 | #X connect 44 0 33 0; 983 | #X restore 708 669 pd Difference equations (Lorenz); 984 | #X text 25 262 'n' goes from 0 to -vector size (defined by the [block~] 985 | or [switch~] objects). As such \, $x#[0] specifies the current sample 986 | input \, and $y#[-1] the last sample output (which is the minimum 'n' 987 | value \, for $y#).; 988 | #X text 79 24 make sure to turn on the audio for the [jit/fexpr~] examples 989 | ====>, f 23; 990 | #X text 540 311 * [jit/fexpr~] can be CPU expensive! By default \, 991 | [jit/fexpr~] is on when it is loaded \, but you can save CPU and control 992 | when it is on or off with the 'start' and 'stop' messages., f 54; 993 | #N canvas 695 185 491 366 Fractional 0; 994 | #X msg 120 179 start; 995 | #X msg 68 178 stop; 996 | #X obj 68 147 loadbang; 997 | #X obj 140 329 dac~; 998 | #X obj 151 288 *~ 0.1; 999 | #X obj 298 136 hsl 128 15 0 -10 0 0 empty empty empty -2 -8 0 10 -262144 1000 | -1 -1 0 1; 1001 | #X floatatom 295 162 0 -10 0 0 - - -; 1002 | #X obj 295 222 line~; 1003 | #X msg 295 192 \$1 100; 1004 | #X obj 151 253 jit/fexpr~ $x1 + $x1[$X2]; 1005 | #X obj 151 211 osc~ 2205; 1006 | #X text 194 116 fractional sample index offset, f 17; 1007 | #X text 25 16 When fractional index offset is used for either input 1008 | or output samples \, [jit/fexpr~] determines the value by linear interpolation. 1009 | , f 63; 1010 | #X text 25 57 In the following example \, you can continuously change 1011 | the sample input index from 0 to -10 (which filters the frequency of 1012 | 2205)., f 63; 1013 | #X connect 0 0 9 0; 1014 | #X connect 1 0 9 0; 1015 | #X connect 2 0 1 0; 1016 | #X connect 4 0 3 0; 1017 | #X connect 4 0 3 1; 1018 | #X connect 5 0 6 0; 1019 | #X connect 6 0 8 0; 1020 | #X connect 7 0 9 1; 1021 | #X connect 8 0 7 0; 1022 | #X connect 9 0 4 0; 1023 | #X connect 10 0 9 0; 1024 | #X restore 756 641 pd Fractional sample index; 1025 | #X text 555 404 -10 offset filters audio at frequency of 2205 Hz -20 1026 | offset filters audio at frequency of 1102.5 Hz, f 50; 1027 | #X msg 640 484 2205; 1028 | #X obj 623 546 osc~ 2205; 1029 | #X text 25 121 - The first inlet of [jit/fexpr~] only accepts signals 1030 | and needs to be '$x1' (cannot be '$f1' \, '$i1' or '$s1'). Other than 1031 | that \, a float sent to a '$v#' inlet is promoted to a signal.; 1032 | #X text 25 174 - [jit/fexpr~] does not understand '$v#' variables from 1033 | [jit/expr~] \, it has its special input and output variables defined 1034 | as:; 1035 | #X msg 187 560 clear y1; 1036 | #X msg 204 587 print; 1037 | #X text 261 589 prints the generated llvm assembly; 1038 | #X text 506 290 - print: prints the generated llvm assembly; 1039 | #X connect 8 0 13 0; 1040 | #X connect 9 0 13 0; 1041 | #X connect 10 0 9 0; 1042 | #X connect 10 0 17 0; 1043 | #X connect 11 0 13 0; 1044 | #X connect 12 0 13 0; 1045 | #X connect 13 0 15 0; 1046 | #X connect 15 0 18 0; 1047 | #X connect 16 0 15 0; 1048 | #X connect 17 0 16 0; 1049 | #X connect 25 0 32 1; 1050 | #X connect 26 0 32 0; 1051 | #X connect 27 0 32 0; 1052 | #X connect 28 0 27 0; 1053 | #X connect 29 0 25 0; 1054 | #X connect 31 0 30 0; 1055 | #X connect 31 0 30 1; 1056 | #X connect 32 0 31 0; 1057 | #X connect 33 0 34 0; 1058 | #X connect 34 0 57 0; 1059 | #X connect 36 0 25 0; 1060 | #X connect 37 0 25 0; 1061 | #X connect 56 0 34 0; 1062 | #X connect 57 0 32 0; 1063 | #X connect 60 0 13 0; 1064 | #X connect 61 0 13 0; 1065 | #X restore 541 324 pd [jit/fexpr~] Examples; 1066 | #X obj 804 238 print jit/expr; 1067 | #X obj 714 458 +; 1068 | #X obj 714 483 +~; 1069 | #X obj 714 433 >; 1070 | #X text 573 434 binary operators:; 1071 | #X text 545 458 arithmetic operators:; 1072 | #X text 580 483 audio operators:; 1073 | #X text 561 206 (click on the subpatches to open them), f 13; 1074 | #X obj 694 510 random; 1075 | #N canvas 175 30 885 622 [value] 0; 1076 | #X floatatom 596 202 5 0 0 0 - - -, f 5; 1077 | #X obj 596 137 until; 1078 | #X msg 719 138 0; 1079 | #X obj 719 168 v i; 1080 | #X obj 596 169 jit/expr i = i + 1; 1081 | #X msg 596 72 10; 1082 | #X obj 596 229 print; 1083 | #X obj 261 63 loadbang; 1084 | #X obj 221 176 value a; 1085 | #X obj 261 149 value b; 1086 | #X obj 304 123 value c; 1087 | #X obj 91 337 jit/expr (a + b) / c; 1088 | #X obj 91 310 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 1089 | -1; 1090 | #X floatatom 91 371 0 0 0 0 - - -; 1091 | #X obj 596 103 trigger float bang; 1092 | #X text 22 15 The jit/expr family of objects can access variables set 1093 | into [value] objects.; 1094 | #X obj 697 302 jit/expr i * 2 \; i = i + 1; 1095 | #X floatatom 697 348 5 0 0 0 - - -, f 5; 1096 | #X floatatom 771 349 5 0 0 0 - - -, f 5; 1097 | #X obj 697 267 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 1098 | -1 -1; 1099 | #X text 33 233 All you have to do is just use the variable name into 1100 | the expression:, f 27; 1101 | #X obj 90 422 jit/expr~ (a + b) / c; 1102 | #X obj 90 453 print~; 1103 | #X obj 62 421 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 1104 | -1; 1105 | #X msg 221 97 5; 1106 | #X msg 261 97 3; 1107 | #X msg 304 97 2.5; 1108 | #X obj 91 533 print~; 1109 | #X obj 63 501 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 1110 | -1; 1111 | #X obj 91 502 jit/fexpr~ (a + b) / c; 1112 | #X text 325 305 Expressions are output from right to left order in 1113 | [jit/expr] \, since they're evaluated from bottom to top. This means 1114 | you need to update a variable and call it in the above expression \, 1115 | check it =============>, f 51; 1116 | #X obj 729 478 v last; 1117 | #X obj 603 509 jit/fexpr~ last = $x1[-1] \; $x1[0] - last; 1118 | #X obj 754 564 print~; 1119 | #X obj 788 529 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 1120 | -1 -1; 1121 | #X obj 603 478 phasor~ 1000; 1122 | #X text 100 98 loading values into [value] objects =======>, f 16 1123 | ; 1124 | #X text 471 14 It is also possible to assign values to variables inside 1125 | the jit/expr family of objects with "=" ("store function")., f 54 1126 | ; 1127 | #X text 325 401 You cannot assign variables values in [jit/expr~] because 1128 | it deals with signal vectors and not single values. On the other hand 1129 | \, [jit/fexpr~] can assign values to variables \, but they're evaluated 1130 | in from top to bottom instead. This means you need to update a variable 1131 | and call it in the below expression \, check it:, f 68; 1132 | #X connect 0 0 6 0; 1133 | #X connect 1 0 4 0; 1134 | #X connect 2 0 3 0; 1135 | #X connect 4 0 0 0; 1136 | #X connect 5 0 14 0; 1137 | #X connect 7 0 25 0; 1138 | #X connect 7 0 26 0; 1139 | #X connect 7 0 24 0; 1140 | #X connect 11 0 13 0; 1141 | #X connect 12 0 11 0; 1142 | #X connect 14 0 1 0; 1143 | #X connect 14 1 2 0; 1144 | #X connect 16 0 17 0; 1145 | #X connect 16 1 18 0; 1146 | #X connect 19 0 16 0; 1147 | #X connect 21 0 22 0; 1148 | #X connect 23 0 22 0; 1149 | #X connect 24 0 8 0; 1150 | #X connect 25 0 9 0; 1151 | #X connect 26 0 10 0; 1152 | #X connect 28 0 27 0; 1153 | #X connect 29 0 27 0; 1154 | #X connect 32 1 33 0; 1155 | #X connect 34 0 33 0; 1156 | #X connect 35 0 32 0; 1157 | #X restore 803 333 pd [value]; 1158 | #X text 309 15 Version @jit_expr_VERSION_MAJOR@.@jit_expr_VERSION_MINOR@.@jit_expr_VERSION_PATCH@ 1159 | ; 1160 | #X text 796 517 helpfile modified from original expr help; 1161 | #X text 20 16 Expression evaluation family of objects:, f 62; 1162 | #X text 20 33 Just in time [JIT] compiled; 1163 | #X obj 811 491 jit/expr; 1164 | #X msg 811 462 version; 1165 | #X text 809 438 print the version; 1166 | #X text 21 93 Online original documentation: http://yadegari.org/jit/expr/jit/expr.html 1167 | , f 62; 1168 | #X text 19 50 Originally By Shahrokh Yadegari \,; 1169 | #X text 19 67 cloned and made JIT by Alex Norman; 1170 | #X obj 805 548 declare -lib jit_expr; 1171 | #X connect 3 0 15 0; 1172 | #X connect 4 0 34 0; 1173 | #X connect 5 0 34 0; 1174 | #X connect 15 0 4 0; 1175 | #X connect 15 1 5 0; 1176 | #X connect 15 2 16 0; 1177 | #X connect 16 0 34 0; 1178 | #X connect 49 0 48 0; 1179 | -------------------------------------------------------------------------------- /src/jit_expr.cpp: -------------------------------------------------------------------------------- 1 | //Copyright (c) Alex Norman, 2018. 2 | //see LICENSE-xnor 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "llvmcodegen/codegen.h" 15 | #include "parser.hh" 16 | #include "jit_expr_version.h" 17 | 18 | #if defined (_MSC_VER) // Visual studio 19 | #define thread_local __declspec( thread ) 20 | #elif defined (__GCC__) // GCC 21 | #define thread_local __thread 22 | #endif 23 | 24 | #include 25 | 26 | struct _jit_expr_proxy; 27 | struct _jit_expr; 28 | 29 | namespace ast = xnor::ast; 30 | 31 | namespace { 32 | enum class XnorExpr { 33 | CONTROL, 34 | VECTOR, 35 | SAMPLE 36 | }; 37 | 38 | struct cpp_expr { 39 | int dsp_buffer_size = 0; 40 | 41 | std::vector ins; 42 | std::vector outs; 43 | std::vector proxies; 44 | 45 | parse::Driver driver; 46 | xnor::LLVMCodeGenVisitor cv; 47 | 48 | xnor::LLVMCodeGenVisitor::function_t func; 49 | XnorExpr expr_type = XnorExpr::CONTROL; 50 | 51 | std::vector outfloat; 52 | std::vector outarg; 53 | std::vector infloats; 54 | std::vector symbol_inputs; 55 | std::map> saved_inputs; //pair is data and size of data in bytes 56 | std::map> saved_outputs; 57 | 58 | std::vector inarg; 59 | std::vector input_types; 60 | int signal_inputs = 0; //could just calc from input_types 61 | 62 | bool compute = true; 63 | std::string code_printout; 64 | 65 | //constructor 66 | cpp_expr(XnorExpr t) : expr_type(t) { }; 67 | ~cpp_expr() { 68 | free_io_buffers(); 69 | for (auto i: ins) 70 | inlet_free(i); 71 | ins.clear(); 72 | 73 | for (auto i: outs) 74 | outlet_free(i); 75 | outs.clear(); 76 | } 77 | 78 | void free_io_buffers() { 79 | for (auto& it : saved_inputs) { 80 | auto& p = it.second; 81 | if (p.second == 0) 82 | continue; 83 | freebytes((void *)p.first, p.second); 84 | p.first = nullptr; 85 | p.second = 0; 86 | } 87 | for (auto& it : saved_outputs) { 88 | auto& p = it.second; 89 | if (p.second == 0) 90 | continue; 91 | freebytes((void *)p.first, p.second); 92 | p.first = nullptr; 93 | p.second = 0; 94 | } 95 | } 96 | }; 97 | } 98 | 99 | extern "C" void *jit_expr_new(t_symbol *s, int argc, t_atom *argv); 100 | extern "C" void jit_expr_free(struct _jit_expr * x); 101 | extern "C" void jit_expr_start(struct _jit_expr * x); 102 | extern "C" void jit_expr_stop(struct _jit_expr * x); 103 | extern "C" void jit_expr_print(struct _jit_expr * x); 104 | extern "C" void jit_expr_version(struct _jit_expr * x); 105 | extern "C" void jit_expr_setup(void); 106 | extern "C" void jit_fexpr_tilde_set(struct _jit_expr *x, t_symbol *s, int argc, t_atom *argv); 107 | extern "C" void jit_fexpr_tilde_clear(struct _jit_expr *x, t_symbol *s, int argc, t_atom *argv); 108 | 109 | //functions called from generated code 110 | extern "C" float jit_expr_fact(float v); 111 | extern "C" float * jit_expr_table_value_ptr(t_symbol * name, float findex); 112 | extern "C" float jit_expr_table_size(t_symbol * name); 113 | extern "C" float jit_expr_table_sum(t_symbol * name, float start, float end); 114 | extern "C" float jit_expr_table_sum_all(t_symbol * name); 115 | extern "C" float jit_expr_max(float a, float b); 116 | extern "C" float jit_expr_min(float a, float b); 117 | extern "C" float jit_expr_random(float a, float b); 118 | extern "C" float jit_expr_imodf(float v); 119 | extern "C" float jit_expr_modf(float v); 120 | 121 | extern "C" float jit_expr_isnan(float v); 122 | extern "C" float jit_expr_isinf(float v); 123 | extern "C" float jit_expr_finite(float v); 124 | 125 | extern "C" float jit_expr_value_assign(t_symbol * name, float v); 126 | extern "C" float jit_expr_value_get(t_symbol * name); 127 | extern "C" float jit_expr_deref(float * v); 128 | 129 | extern "C" float jit_expr_array_read(float * array, float index, int array_length); 130 | 131 | static t_class *jit_expr_class; 132 | static t_class *jit_expr_proxy_class; 133 | static t_class *jit_expr_tilde_class; 134 | static t_class *jit_fexpr_tilde_class; 135 | 136 | typedef struct _jit_expr { 137 | t_object x_obj; 138 | std::shared_ptr cpp; 139 | float exp_f; /* control value to be transformed to signal */ 140 | } t_jit_expr; 141 | 142 | typedef struct _jit_expr_proxy { 143 | t_pd p_pd; 144 | unsigned int index; 145 | t_jit_expr *parent; 146 | } t_jit_expr_proxy; 147 | 148 | 149 | void *jit_expr_new(t_symbol *s, int argc, t_atom *argv) 150 | { 151 | //create the driver and code visitor 152 | t_jit_expr *x = NULL; 153 | 154 | if (strcmp("jit/expr~", s->s_name) == 0) { 155 | x = (t_jit_expr *)pd_new(jit_expr_tilde_class); 156 | x->cpp = std::make_shared(XnorExpr::VECTOR); 157 | } else if (strcmp("jit/fexpr~", s->s_name) == 0) { 158 | x = (t_jit_expr *)pd_new(jit_fexpr_tilde_class); 159 | x->cpp = std::make_shared(XnorExpr::SAMPLE); 160 | } else { 161 | if (strcmp("jit/expr", s->s_name) != 0) 162 | error("jit_expr_new: bad object name '%s'", s->s_name); 163 | x = (t_jit_expr *)pd_new(jit_expr_class); 164 | x->cpp = std::make_shared(XnorExpr::CONTROL); 165 | } 166 | 167 | //read in the arguments into a string 168 | char buf[1024]; 169 | std::string line; 170 | for (int i = 0; i < argc; i++) { 171 | atom_string(&argv[i], buf, 1024); 172 | line += " " + std::string(buf); 173 | } 174 | 175 | #if 0 176 | std::cout << line << std::endl; 177 | #endif 178 | 179 | //parse and setup 180 | try { 181 | //make sure there is more than just a space 182 | if (line.find_first_not_of(' ') == std::string::npos) { 183 | x->cpp->func = nullptr; 184 | } else { 185 | auto statements = x->cpp->driver.parse_string(line); 186 | x->cpp->func = x->cpp->cv.function(statements, x->cpp->code_printout); 187 | 188 | auto inputs = x->cpp->driver.inputs(); 189 | //we automatically have at least one input even if we're not using it 190 | if (inputs.size() == 0) { 191 | auto v = std::make_shared(x->cpp->expr_type == XnorExpr::CONTROL ? ast::Variable::VarType::FLOAT : ast::Variable::VarType::VECTOR, 0); 192 | inputs.push_back(v); 193 | } 194 | 195 | x->cpp->infloats.resize(inputs.size(), 0); 196 | x->cpp->symbol_inputs.resize(inputs.size(), nullptr); 197 | x->cpp->inarg.resize(inputs.size()); 198 | x->cpp->input_types.resize(inputs.size(), ast::Variable::VarType::FLOAT); //this will be overwritten when the variables are set up 199 | 200 | x->cpp->signal_inputs = 0; 201 | x->cpp->outarg.resize(statements.size()); 202 | 203 | switch (x->cpp->expr_type) { 204 | case XnorExpr::CONTROL: 205 | { 206 | if (inputs.size() >= 1 && 207 | inputs.at(0)->type() != ast::Variable::VarType::FLOAT && 208 | inputs.at(0)->type() != ast::Variable::VarType::INT && 209 | inputs.at(0)->type() != ast::Variable::VarType::SYMBOL) { 210 | error("the first inlet of jit/expr must be a float, int or symbol"); 211 | jit_expr_free(x); 212 | return NULL; 213 | } 214 | 215 | //there will always be at least one output 216 | x->cpp->outfloat.resize(statements.size()); 217 | 218 | for (size_t i = 0; i < x->cpp->outarg.size(); i++) { 219 | x->cpp->outarg[i] = &x->cpp->outfloat[i]; 220 | x->cpp->outs.push_back(outlet_new(&x->x_obj, &s_float)); 221 | } 222 | } 223 | break; 224 | case XnorExpr::VECTOR: 225 | { 226 | if (inputs.size() >= 1 && 227 | inputs.at(0)->type() != ast::Variable::VarType::VECTOR) { 228 | error("the first inlet of jit/expr~ must be a vector"); 229 | jit_expr_free(x); 230 | return NULL; 231 | } 232 | 233 | for (size_t i = 0; i < statements.size(); i++) 234 | x->cpp->outs.push_back(outlet_new(&x->x_obj, &s_signal)); 235 | } 236 | break; 237 | case XnorExpr::SAMPLE: 238 | { 239 | if (inputs.size() >= 1 && 240 | inputs.at(0)->type() != ast::Variable::VarType::INPUT && 241 | inputs.at(0)->type() != ast::Variable::VarType::VECTOR) { 242 | error("the first inlet of jit/fexpr~ must be a input sample or vector variable"); 243 | jit_expr_free(x); 244 | return NULL; 245 | } 246 | 247 | for (size_t i = 0; i < statements.size(); i++) { 248 | x->cpp->outs.push_back(outlet_new(&x->x_obj, &s_signal)); 249 | x->cpp->saved_outputs[i] = {nullptr, 0}; 250 | } 251 | } 252 | break; 253 | } 254 | 255 | for (size_t i = 0; i < inputs.size(); i++) { 256 | auto v = inputs.at(i); 257 | x->cpp->input_types[i] = v->type(); 258 | switch (v->type()) { 259 | case ast::Variable::VarType::FLOAT: 260 | case ast::Variable::VarType::INT: 261 | if (i != 0) { 262 | t_jit_expr_proxy *p = (t_jit_expr_proxy *)pd_new(jit_expr_proxy_class); 263 | p->index = v->input_index(); 264 | p->parent = x; 265 | x->cpp->proxies.push_back(p); 266 | x->cpp->ins.push_back(inlet_new(&x->x_obj, &p->p_pd, &s_float, &s_float)); 267 | } 268 | break; 269 | case ast::Variable::VarType::VECTOR: 270 | if (x->cpp->expr_type == XnorExpr::CONTROL) { 271 | error("cannot create vector inlet for jit/expr"); 272 | jit_expr_free(x); 273 | return NULL; 274 | } 275 | if (i != 0) 276 | x->cpp->ins.push_back(inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal)); 277 | x->cpp->signal_inputs += 1; 278 | x->cpp->saved_inputs[v->input_index()] = {nullptr, 0}; 279 | break; 280 | case ast::Variable::VarType::INPUT: 281 | if (x->cpp->expr_type != XnorExpr::SAMPLE) { 282 | error("input sample inlet only works for jit/fexpr~"); 283 | jit_expr_free(x); 284 | return NULL; 285 | } 286 | if (i != 0) 287 | x->cpp->ins.push_back(inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal)); 288 | x->cpp->signal_inputs += 1; 289 | x->cpp->saved_inputs[v->input_index()] = {nullptr, 0}; 290 | break; 291 | case ast::Variable::VarType::SYMBOL: 292 | if (i != 0) 293 | x->cpp->ins.push_back(symbolinlet_new(&x->x_obj, &x->cpp->symbol_inputs.at(i))); 294 | break; 295 | default: 296 | throw std::runtime_error("input type not handled " + std::to_string(v->input_index())); 297 | } 298 | } 299 | } 300 | } catch (std::runtime_error& e) { 301 | error("error parsing \"%s\" %s", line.c_str(), e.what()); 302 | jit_expr_free(x); 303 | return NULL; 304 | } 305 | 306 | return (void *)x; 307 | } 308 | 309 | void jit_expr_free(t_jit_expr * x) { 310 | if (x == NULL) 311 | return; 312 | x->cpp = nullptr; 313 | } 314 | 315 | void jit_expr_bang(t_jit_expr * x) { 316 | if (x->cpp->func == nullptr) 317 | return; 318 | 319 | //assign input values 320 | for (size_t i = 0; i < x->cpp->inarg.size(); i++) { 321 | auto t = x->cpp->input_types.at(i); 322 | switch (t) { 323 | case ast::Variable::VarType::FLOAT: 324 | case ast::Variable::VarType::INT: 325 | x->cpp->inarg.at(i).flt = x->cpp->infloats.at(i); 326 | break; 327 | case ast::Variable::VarType::SYMBOL: 328 | x->cpp->inarg.at(i).sym = x->cpp->symbol_inputs.at(i); 329 | break; 330 | default: 331 | error("unhandled input type"); 332 | break; 333 | } 334 | } 335 | 336 | //execute function 337 | x->cpp->func(&x->cpp->outarg.front(), &x->cpp->inarg.front(), 1); 338 | 339 | //output! 340 | for (unsigned int i = 0; i < x->cpp->outarg.size(); i++) 341 | outlet_float(x->cpp->outs.at(i), *(x->cpp->outarg.at(i))); 342 | } 343 | 344 | static void jit_expr_list(t_jit_expr *x, t_symbol * /*s*/, int argc, const t_atom *argv) { 345 | for (int i = 0; i < std::min(argc, (int)x->cpp->infloats.size()); i++) { 346 | auto t = x->cpp->input_types.at(i); 347 | if (argv[i].a_type == A_FLOAT && (t == ast::Variable::VarType::FLOAT || t == ast::Variable::VarType::INT)) { 348 | x->cpp->infloats.at(i) = argv[i].a_w.w_float; 349 | } else if (argv[i].a_type == A_SYMBOL && t == ast::Variable::VarType::SYMBOL) { 350 | x->cpp->symbol_inputs.at(i) = argv[i].a_w.w_symbol; 351 | } else { 352 | pd_error(x, "expr: type mismatch"); 353 | } 354 | } 355 | jit_expr_bang(x); 356 | } 357 | 358 | void jit_expr_proxy_float(t_jit_expr_proxy *p, t_floatarg f) { 359 | p->parent->cpp->infloats.at(p->index) = f; 360 | } 361 | 362 | static t_int *jit_expr_tilde_perform(t_int *w) { 363 | t_jit_expr *x = (t_jit_expr *)(w[1]); 364 | int n = std::min((int)(w[2]), x->cpp->dsp_buffer_size); 365 | 366 | int vector_index = 3; 367 | for (unsigned int i = 0; i < x->cpp->input_types.size(); i++) { 368 | switch (x->cpp->input_types.at(i)) { 369 | case ast::Variable::VarType::FLOAT: 370 | case ast::Variable::VarType::INT: 371 | x->cpp->inarg.at(i).flt = x->cpp->infloats.at(i); 372 | break; 373 | case ast::Variable::VarType::SYMBOL: 374 | x->cpp->inarg.at(i).sym = x->cpp->symbol_inputs.at(i); 375 | break; 376 | case ast::Variable::VarType::VECTOR: { 377 | //we make a copy of the input data and provide that as we might stomp on it 378 | //in our function because buffers get reused 379 | t_sample * in = (t_sample*)w[vector_index++]; 380 | t_sample * buf = x->cpp->saved_inputs.at(i).first; 381 | memcpy(buf, in, n * sizeof(t_sample)); //copy the new data in 382 | x->cpp->inarg.at(i).vec = buf; 383 | } 384 | break; 385 | case ast::Variable::VarType::INPUT: 386 | { 387 | t_sample * in = (t_sample*)w[vector_index++]; 388 | t_sample * buf = x->cpp->saved_inputs.at(i).first; 389 | memcpy(buf + n, buf, n * sizeof(t_sample)); //copy the old data forward 390 | memcpy(buf, in, n * sizeof(t_sample)); //copy the new data in 391 | x->cpp->inarg.at(i).vec = buf; 392 | } 393 | break; 394 | default: 395 | //XXX 396 | break; 397 | } 398 | } 399 | 400 | //if we're not computing then we just clear everything out 401 | if (!x->cpp->compute) { 402 | size_t vsize = x->cpp->dsp_buffer_size; 403 | for (unsigned int i = 0; i < x->cpp->outarg.size(); i++) { 404 | auto p = (t_sample *)w[vector_index++]; 405 | memset(p, 0, vsize * sizeof(t_sample)); 406 | } 407 | } else { 408 | if (x->cpp->expr_type == XnorExpr::SAMPLE) { 409 | //render to the saved buffers [which has some old needed data into it] 410 | for (unsigned int i = 0; i < x->cpp->outarg.size(); i++) { 411 | x->cpp->outarg.at(i) = x->cpp->saved_outputs.at(i).first; 412 | } 413 | x->cpp->func(&x->cpp->outarg.front(), &x->cpp->inarg.front(), n); 414 | 415 | //copy out the saved buffers 416 | for (unsigned int i = 0; i < x->cpp->outarg.size(); i++) { 417 | auto f = (t_sample *)w[vector_index++]; 418 | auto &p = x->cpp->saved_outputs.at(i); 419 | memcpy(f, p.first, p.second); 420 | } 421 | } else { 422 | for (unsigned int i = 0; i < x->cpp->outarg.size(); i++) { 423 | x->cpp->outarg.at(i) = (t_sample *)w[vector_index++]; 424 | } 425 | x->cpp->func(&x->cpp->outarg.front(), &x->cpp->inarg.front(), n); 426 | } 427 | } 428 | return w + vector_index; 429 | } 430 | 431 | //the external howto doc says: 432 | //The signals are arranged in the array in such way, that they are ordered in 433 | //a clockwise way in the graphical representation of the object 434 | //If both left and right in- and out-signals exist, this means: 435 | //First is the leftmost in-signal followed by the right in-signals; after the 436 | //right out-signals, finally there comes the leftmost out-signal. 437 | 438 | static void jit_expr_tilde_dsp(t_jit_expr *x, t_signal **sp) { 439 | if (x->cpp->func == nullptr) 440 | return; 441 | 442 | x->cpp->free_io_buffers(); 443 | 444 | //there is always at least one signal input 445 | int input_signals = x->cpp->signal_inputs; 446 | int output_signals = x->cpp->outarg.size(); 447 | 448 | int vecsize = input_signals + output_signals + 2; 449 | t_int ** vec = (t_int **)getbytes(sizeof(t_int) * vecsize); 450 | vec[0] = (t_int*)x; 451 | vec[1] = (t_int*)sp[0]->s_n; 452 | int vsize = x->cpp->dsp_buffer_size = sp[0]->s_n; 453 | 454 | //add the inputs 455 | int voffset = 2; 456 | int invbytes = vsize * sizeof(t_sample); 457 | for (int i = 0; i < input_signals; i++) { 458 | vec[i + voffset] = (t_int*)sp[i]->s_vec; 459 | //allocate saved buffers if we need them 460 | if (i < x->cpp->input_types.size()) { 461 | switch (x->cpp->input_types.at(i)) { 462 | case ast::Variable::VarType::VECTOR: 463 | x->cpp->saved_inputs.at(i).first = (t_sample*)getbytes(invbytes); 464 | x->cpp->saved_inputs.at(i).second = invbytes; 465 | break; 466 | case ast::Variable::VarType::INPUT: 467 | x->cpp->saved_inputs.at(i).first = (t_sample*)getbytes(invbytes * 2); //input buffers need access to last input as well 468 | x->cpp->saved_inputs.at(i).second = invbytes * 2; 469 | break; 470 | default: 471 | break; 472 | } 473 | } 474 | } 475 | 476 | //then the outputs 477 | voffset += input_signals; 478 | 479 | //save outputs if needed 480 | int outvbytes = x->cpp->expr_type == XnorExpr::SAMPLE ? (vsize * sizeof(t_sample)) : 0; 481 | for (int i = 0; i < output_signals; i++) { 482 | vec[i + voffset] = (t_int*)sp[i + input_signals]->s_vec; 483 | if (outvbytes) { 484 | x->cpp->saved_outputs.at(i).first = (t_sample*)getbytes(outvbytes); 485 | x->cpp->saved_outputs.at(i).second = outvbytes; 486 | } 487 | } 488 | 489 | dsp_addv(jit_expr_tilde_perform, vecsize, (t_int*)vec); 490 | freebytes(vec, sizeof(t_int) * vecsize); 491 | } 492 | 493 | void jit_expr_start(t_jit_expr *x) { x->cpp->compute = true; } 494 | void jit_expr_stop(t_jit_expr *x) { x->cpp->compute = false; } 495 | void jit_expr_print(t_jit_expr *x) { 496 | switch (x->cpp->expr_type) { 497 | case XnorExpr::CONTROL: 498 | post("jit/expr: "); 499 | break; 500 | case XnorExpr::VECTOR: 501 | post("jit/expr~: "); 502 | break; 503 | case XnorExpr::SAMPLE: 504 | post("jit/fexpr~: "); 505 | break; 506 | } 507 | std::stringstream ss(x->cpp->code_printout.c_str()); 508 | std::string out; 509 | while (std::getline(ss, out)) { 510 | poststring(out.c_str()); 511 | poststring("\n"); 512 | } 513 | } 514 | 515 | void jit_expr_version_post() { 516 | post("jit/expr,expr~,fexpr~ version %d.%d.%d", JIT_EXPR_VERSION_MAJOR, JIT_EXPR_VERSION_MINOR, JIT_EXPR_VERSION_PATCH); 517 | } 518 | 519 | void jit_expr_version(t_jit_expr * x) { 520 | jit_expr_version_post(); 521 | } 522 | 523 | void jit_expr_setup(void) { 524 | xnor::LLVMCodeGenVisitor::init(); 525 | jit_expr_version_post(); 526 | 527 | jit_expr_class = class_new(gensym("jit/expr"), 528 | (t_newmethod)jit_expr_new, 529 | (t_method)jit_expr_free, 530 | sizeof(t_jit_expr), 531 | 0, 532 | A_GIMME, 0); 533 | 534 | class_addlist(jit_expr_class, jit_expr_list); 535 | class_addbang(jit_expr_class, jit_expr_bang); 536 | class_addmethod(jit_expr_class, (t_method)jit_expr_version, gensym("version"), A_NULL); 537 | class_addmethod(jit_expr_class, (t_method)jit_expr_print, gensym("print"), A_NULL); 538 | class_sethelpsymbol(jit_expr_class, gensym("jit_expr")); 539 | 540 | jit_expr_proxy_class = class_new(gensym("jit_expr_proxy"), 541 | 0, 0, 542 | sizeof(t_jit_expr_proxy), 543 | CLASS_PD, 544 | A_NULL); 545 | class_addfloat(jit_expr_proxy_class, jit_expr_proxy_float); 546 | 547 | jit_expr_tilde_class = class_new(gensym("jit/expr~"), 548 | (t_newmethod)jit_expr_new, 549 | (t_method)jit_expr_free, 550 | sizeof(t_jit_expr), 551 | 0, 552 | A_GIMME, 0); 553 | class_addmethod(jit_expr_tilde_class, nullfn, gensym("signal"), A_NULL); 554 | CLASS_MAINSIGNALIN(jit_expr_tilde_class, t_jit_expr, exp_f); 555 | class_addmethod(jit_expr_tilde_class, (t_method)jit_expr_version, gensym("version"), A_NULL); 556 | class_addmethod(jit_expr_tilde_class, (t_method)jit_expr_tilde_dsp, gensym("dsp"), A_NULL); 557 | class_addmethod(jit_expr_tilde_class, (t_method)jit_expr_print, gensym("print"), A_NULL); 558 | class_sethelpsymbol(jit_expr_tilde_class, gensym("jit_expr")); 559 | 560 | jit_fexpr_tilde_class = class_new(gensym("jit/fexpr~"), 561 | (t_newmethod)jit_expr_new, 562 | (t_method)jit_expr_free, 563 | sizeof(t_jit_expr), 564 | 0, 565 | A_GIMME, 0); 566 | class_addmethod(jit_fexpr_tilde_class, nullfn, gensym("signal"), A_NULL); 567 | CLASS_MAINSIGNALIN(jit_fexpr_tilde_class, t_jit_expr, exp_f); 568 | class_addmethod(jit_fexpr_tilde_class, (t_method)jit_expr_version, gensym("version"), A_NULL); 569 | class_addmethod(jit_fexpr_tilde_class, (t_method)jit_expr_tilde_dsp, gensym("dsp"), A_NULL); 570 | class_addmethod(jit_fexpr_tilde_class, (t_method)jit_fexpr_tilde_set, gensym("set"), A_GIMME, 0); 571 | class_addmethod(jit_fexpr_tilde_class, (t_method)jit_fexpr_tilde_clear, gensym("clear"), A_GIMME, 0); 572 | class_addmethod(jit_fexpr_tilde_class, (t_method)jit_expr_start, gensym("start"), A_NULL); 573 | class_addmethod(jit_fexpr_tilde_class, (t_method)jit_expr_stop, gensym("stop"), A_NULL); 574 | class_addmethod(jit_fexpr_tilde_class, (t_method)jit_expr_print, gensym("print"), A_NULL); 575 | class_sethelpsymbol(jit_fexpr_tilde_class, gensym("jit_expr")); 576 | } 577 | 578 | //utility functions 579 | 580 | namespace { 581 | int facti(int i) { 582 | if (i <= 0) 583 | return 1; 584 | return i * facti(i - 1); 585 | } 586 | 587 | //adapted from max_ex_tab x_vexpr_if.c 588 | t_word * jit_get_table(t_symbol *name, int& sizeout) { 589 | t_garray * a; 590 | sizeout = 0; 591 | t_word *vec; 592 | if (!name || !(a = (t_garray *)pd_findbyclass(name, garray_class)) || !garray_getfloatwords(a, &sizeout, &vec)) { 593 | sizeout = 0; //in case it was altered? 594 | //XXX post error 595 | return nullptr; 596 | } 597 | return vec; 598 | } 599 | 600 | //if end < 0, end == size 601 | float jit_expr_table_sum_range(t_symbol * name, ssize_t start, ssize_t end) { 602 | int s = 0; 603 | t_word * vec = jit_get_table(name, s); 604 | if (!vec) 605 | return 0.0f; 606 | 607 | ssize_t size = s; 608 | 609 | start = std::min(std::max(start, static_cast(0)), size); 610 | if (end < 0) 611 | end = size; 612 | else 613 | end = std::min(std::max(end, static_cast(0)), size); 614 | 615 | float sum = 0; 616 | for (ssize_t i = start; i < end; i++) 617 | sum += vec[i].w_float; 618 | return sum; 619 | } 620 | } 621 | 622 | float jit_expr_fact(float v) { 623 | return static_cast(facti(static_cast(v))); 624 | } 625 | 626 | float * jit_expr_table_value_ptr(t_symbol * name, float findex) { 627 | if (!name) 628 | return nullptr; 629 | 630 | int size = 0; 631 | t_word * vec = jit_get_table(name, size); 632 | if (!vec || size <= 0) { 633 | return nullptr; 634 | } 635 | int index = std::min(std::max(0, static_cast(findex)), size - 1); 636 | return &(vec[index].w_float); 637 | } 638 | 639 | float jit_expr_table_size(t_symbol * name) { 640 | int size = 0; 641 | jit_get_table(name, size); 642 | return static_cast(size); 643 | } 644 | 645 | float jit_expr_table_sum(t_symbol * name, float fstart, float fend) { 646 | if (fstart > fend || fend < 0) 647 | return 0.0f; 648 | return jit_expr_table_sum_range(name, static_cast(fstart), static_cast(fend) + 1); 649 | } 650 | 651 | float jit_expr_table_sum_all(t_symbol * name) { 652 | return jit_expr_table_sum_range(name, 0, -1); 653 | } 654 | 655 | float jit_expr_max(float a, float b) { return std::max(a, b); } 656 | float jit_expr_min(float a, float b) { return std::min(a, b); } 657 | float jit_expr_random(float fstart, float fend) { 658 | int start = static_cast(fstart); 659 | int end = static_cast(fend - 1); 660 | if (start >= end) 661 | return 0; 662 | 663 | //https://stackoverflow.com/questions/21237905/how-do-i-generate-thread-safe-uniform-random-numbers 664 | static thread_local std::mt19937 generator; 665 | std::uniform_int_distribution distribution(start, end); 666 | return static_cast(distribution(generator)); 667 | } 668 | 669 | float jit_expr_imodf(float v) { 670 | return truncf(v); 671 | } 672 | 673 | float jit_expr_modf(float v) { 674 | return v - truncf(v); 675 | } 676 | 677 | float jit_expr_isnan(float v) { return std::isnan(v) ? 1 : 0; } 678 | 679 | float jit_expr_isinf(float v) { return std::isinf(v) ? 1 : 0; } 680 | 681 | float jit_expr_finite(float v) { return std::isfinite(v) ? 1 : 0; } 682 | 683 | float jit_expr_value_assign(t_symbol * name, float v) { 684 | if (name) 685 | value_setfloat(name, v); 686 | return v; 687 | } 688 | 689 | float jit_expr_value_get(t_symbol * name) { 690 | float v = 0; 691 | return (name && value_getfloat(name, &v) == 0) ? v : 0; 692 | } 693 | 694 | float jit_expr_deref(float * v) { 695 | return v != 0 ? *v : 0; 696 | } 697 | 698 | float jit_expr_array_read(float * array, float index, int array_length) { 699 | int i = static_cast(index); 700 | float off = index - static_cast(i); 701 | float v1 = array[i % array_length]; 702 | float v2 = array[(i + 1) % array_length]; 703 | return v2 * off + v1 * (1.0 - off); 704 | } 705 | 706 | 707 | /* 708 | * below based on : 709 | * "expr" was written by Shahrokh Yadegari c. 1989. 710 | * "expr~" and "fexpr~" conversion by Shahrokh Yadegari c. 1999,2000 711 | * 712 | * Copyright (c) IRCAM. 713 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL 714 | * WARRANTIES, see the file, "LICENSE-pd," in this distribution. 715 | */ 716 | 717 | void jit_fexpr_set_usage() { 718 | post("jit/fexpr~: set val ..."); 719 | post("jit/fexpr~: set {xy}[#] val ..."); 720 | } 721 | 722 | void jit_fexpr_clear_usage() { 723 | post("jit/fexpr~ usage: 'clear' or 'clear {xy}[#]'"); 724 | } 725 | 726 | // taken directly from x_vexpr_if.c and modified 727 | void jit_fexpr_tilde_set(t_jit_expr *x, t_symbol * /*s*/, int argc, t_atom *argv) { 728 | t_symbol *sx; 729 | int vecno, nargs; 730 | int vsize = x->cpp->dsp_buffer_size; 731 | 732 | if (!argc) 733 | return; 734 | sx = atom_getsymbolarg(0, argc, argv); 735 | switch(sx->s_name[0]) { 736 | case 'x': { 737 | if (!sx->s_name[1]) 738 | vecno = 0; 739 | else { 740 | vecno = atoi(sx->s_name + 1); 741 | if (vecno <= 0) { 742 | post("jit/fexpr~ set: bad set x vector number"); 743 | jit_fexpr_set_usage(); 744 | return; 745 | } 746 | vecno--; 747 | } 748 | auto it = x->cpp->saved_inputs.find(vecno); 749 | if (it == x->cpp->saved_inputs.end()) { 750 | post("jit/fexpr~ set: no signal at inlet %d", vecno + 1); 751 | return; 752 | } 753 | nargs = argc - 1; 754 | if (nargs <= 0) { 755 | post("jit/fexpr~ set: no argument to set"); 756 | return; 757 | } 758 | if (nargs > vsize) { 759 | post("jit/fexpr~ set: %d set values larger than vector size(%d)", nargs, vsize); 760 | post("jit/fexpr~ set: only the first %d values will be set", vsize); 761 | nargs = vsize; 762 | } 763 | for (int i = 0; i < nargs; i++) { 764 | it->second.first[vsize - i - 1] = atom_getfloatarg(i + 1, argc, argv); 765 | } 766 | } 767 | return; 768 | case 'y': { 769 | if (!sx->s_name[1]) 770 | vecno = 0; 771 | else { 772 | vecno = atoi(sx->s_name + 1); 773 | if (vecno <= 0) { 774 | post("jit/fexpr~ set: bad set y vector number"); 775 | jit_fexpr_set_usage(); 776 | return; 777 | } 778 | vecno--; 779 | } 780 | auto it = x->cpp->saved_outputs.find(vecno); 781 | if (it == x->cpp->saved_outputs.end()) { 782 | post("jit/fexpr~ set: outlet out of range"); 783 | return; 784 | } 785 | nargs = argc - 1; 786 | if (nargs <= 0) { 787 | post("jit/fexpr~ set: no argument to set"); 788 | return; 789 | } 790 | if (nargs > vsize) { 791 | post("jit/fexpr~ set: %d set values larger than vector size(%d)", nargs, vsize); 792 | post("jit/fexpr~ set: only the first %d values will be set", vsize); 793 | nargs = vsize; 794 | } 795 | for (int i = 0; i < nargs; i++) { 796 | it->second.first[vsize - i - 1] = atom_getfloatarg(i + 1, argc, argv); 797 | } 798 | } 799 | return; 800 | case 0: { 801 | int nouts = x->cpp->saved_outputs.size(); 802 | if (argc > nouts) { 803 | post("jit/fexpr~ set: only %d outlets available", nouts); 804 | post("jit/fexpr~ set: the extra set values are ignored"); 805 | } 806 | for (int i = 0; i < nouts && i < argc; i++) { 807 | auto it = x->cpp->saved_outputs.find(i); 808 | if (it == x->cpp->saved_outputs.end()) 809 | continue; 810 | it->second.first[vsize - 1] = atom_getfloatarg(i, argc, argv); 811 | } 812 | } 813 | return; 814 | default: 815 | jit_fexpr_set_usage(); 816 | return; 817 | } 818 | return; 819 | } 820 | 821 | // taken directly from x_vexpr_if.c and modified 822 | void jit_fexpr_tilde_clear(t_jit_expr *x, t_symbol * /*s */, int argc, t_atom *argv) { 823 | t_symbol *sx; 824 | int vecno; 825 | const int vsize = x->cpp->dsp_buffer_size * sizeof(t_sample); 826 | 827 | /* 828 | * if no argument clear all input and output buffers 829 | */ 830 | if (argc <= 0) { 831 | for (auto& it: x->cpp->saved_inputs) { 832 | memset(it.second.first, 0, it.second.second); 833 | } 834 | for (auto& it: x->cpp->saved_outputs) { 835 | memset(it.second.first, 0, it.second.second); 836 | } 837 | return; 838 | } 839 | if (argc > 1) { 840 | jit_fexpr_clear_usage(); 841 | return; 842 | } 843 | 844 | sx = atom_getsymbolarg(0, argc, argv); 845 | switch(sx->s_name[0]) { 846 | case 'x': 847 | if (!sx->s_name[1]) 848 | vecno = 0; 849 | else { 850 | vecno = atoi(sx->s_name + 1); 851 | if (vecno <= 0) { 852 | post("jit/fexpr~ clear: bad clear x vector number"); 853 | return; 854 | } 855 | vecno--; 856 | } 857 | { 858 | auto it = x->cpp->saved_inputs.find(vecno); 859 | if (it == x->cpp->saved_inputs.end()) { 860 | post("jit/fexpr~ clear: no signal at inlet %d", vecno + 1); 861 | return; 862 | } 863 | memset(it->second.first, 0, it->second.second); 864 | } 865 | return; 866 | case 'y': 867 | if (!sx->s_name[1]) 868 | vecno = 0; 869 | else { 870 | vecno = atoi(sx->s_name + 1); 871 | if (vecno <= 0) { 872 | post("jit/fexpr~ clear: bad clear y vector number"); 873 | return; 874 | } 875 | vecno--; 876 | } 877 | { 878 | auto it = x->cpp->saved_outputs.find(vecno); 879 | if (it == x->cpp->saved_outputs.end()) { 880 | post("jit/fexpr~ clear: no signal at outlet %d", vecno + 1); 881 | return; 882 | } 883 | memset(it->second.first, 0, it->second.second); 884 | } 885 | return; 886 | default: 887 | jit_fexpr_clear_usage(); 888 | return; 889 | } 890 | } 891 | -------------------------------------------------------------------------------- /src/jit_expr_version.h.in: -------------------------------------------------------------------------------- 1 | 2 | // the configured options and settings for jit_expr 3 | #define JIT_EXPR_VERSION_MAJOR @jit_expr_VERSION_MAJOR@ 4 | #define JIT_EXPR_VERSION_MINOR @jit_expr_VERSION_MINOR@ 5 | #define JIT_EXPR_VERSION_PATCH @jit_expr_VERSION_PATCH@ 6 | -------------------------------------------------------------------------------- /src/llvmcodegen/codegen.cc: -------------------------------------------------------------------------------- 1 | //Copyright (c) Alex Norman, 2018, see LICENSE-xnor 2 | //based on the Kaleidoscope example from the llvm tutorial https://llvm.org/docs/tutorial/ 3 | 4 | #include "codegen.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | namespace ast = xnor::ast; 35 | 36 | namespace { 37 | const std::string main_function_name = "jitexpr"; 38 | const std::map function_alias = { 39 | {"Sum", "jit_expr_table_sum"}, 40 | {"sum", "jit_expr_table_sum_all"}, 41 | {"size", "jit_expr_table_size"}, 42 | {"fact", "jit_expr_fact"}, 43 | {"max", "jit_expr_max"}, 44 | {"min", "jit_expr_min"}, 45 | {"random", "jit_expr_random"}, 46 | {"imodf", "jit_expr_imodf"}, 47 | {"modf", "jit_expr_modf"}, 48 | {"random", "jit_expr_random"}, 49 | {"isnan", "jit_expr_isnan"}, 50 | {"isinf", "jit_expr_isinf"}, 51 | {"finite", "jit_expr_finite"}, 52 | {"ln", "logf"}, 53 | {"abs", "fabsf"}, 54 | }; 55 | } 56 | 57 | namespace xnor { 58 | 59 | void LLVMCodeGenVisitor::init() { 60 | llvm::InitializeNativeTarget(); 61 | llvm::InitializeNativeTargetAsmPrinter(); 62 | llvm::InitializeNativeTargetAsmParser(); 63 | } 64 | 65 | LLVMCodeGenVisitor::LLVMCodeGenVisitor() : 66 | mContext(), 67 | mBuilder(mContext), 68 | mTargetMachine(llvm::EngineBuilder().selectTarget()), 69 | mDataLayout(mTargetMachine->createDataLayout()), 70 | mObjectLayer([]() { return std::make_shared(); }), 71 | mCompileLayer(mObjectLayer, llvm::orc::SimpleCompiler(*mTargetMachine)) 72 | { 73 | llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); //XXX do we want this? 74 | 75 | mModule = llvm::make_unique("jit/expr", mContext); 76 | mModule->setDataLayout(mDataLayout); 77 | 78 | mSymbolPtrType = llvm::PointerType::get(llvm::StructType::create(mContext, "t_symbol_ptr"), 0); //opaque 79 | mFloatType = llvm::Type::getFloatTy(mContext); 80 | mIntType = llvm::Type::getInt32Ty(mContext); 81 | 82 | mFunctionPassManager = llvm::make_unique(mModule.get()); 83 | #if 1 84 | // Do simple "peephole" optimizations and bit-twiddling optzns. 85 | mFunctionPassManager->add(llvm::createInstructionCombiningPass()); 86 | // Reassociate expressions. 87 | mFunctionPassManager->add(llvm::createReassociatePass()); 88 | // Eliminate Common SubExpressions. 89 | mFunctionPassManager->add(llvm::createGVNPass()); 90 | // Simplify the control flow graph (deleting unreachable blocks, etc). 91 | mFunctionPassManager->add(llvm::createCFGSimplificationPass()); 92 | #endif 93 | 94 | mFunctionPassManager->doInitialization(); 95 | 96 | std::vector argTypes; 97 | argTypes.push_back(llvm::PointerType::get(llvm::PointerType::get(mFloatType, 0), 0)); 98 | 99 | std::vector unionTypes; 100 | unionTypes.push_back(llvm::PointerType::get(mFloatType, 0)); 101 | mInputType = llvm::StructType::create(llvm::makeArrayRef(unionTypes), "union.input_arg_t"); 102 | auto sp = llvm::PointerType::get(mInputType, 0); 103 | argTypes.push_back(sp); 104 | 105 | argTypes.push_back(mIntType); 106 | 107 | llvm::FunctionType *ftype = llvm::FunctionType::get(llvm::Type::getVoidTy(mContext), llvm::makeArrayRef(argTypes), false); 108 | mMainFunction = llvm::Function::Create(ftype, llvm::GlobalValue::InternalLinkage, main_function_name, mModule.get()); 109 | mBlock = llvm::BasicBlock::Create(mContext, "entry", mMainFunction, 0); 110 | mBuilder.SetInsertPoint(mBlock); 111 | 112 | //setup arguments 113 | auto it = mMainFunction->args().begin(); 114 | it->setName("out"); 115 | mOutput = it; 116 | 117 | it++; 118 | it->setName("in"); 119 | 120 | mInput = it; 121 | llvm::Value * input = mBuilder.CreateAlloca(sp, (unsigned)0); 122 | /*llvm::Value * inarg =*/ mBuilder.CreateStore(mInput, input); 123 | mInput = input; 124 | 125 | it++; 126 | it->setName("veclen"); 127 | mFrameCount = it; 128 | } 129 | 130 | LLVMCodeGenVisitor::~LLVMCodeGenVisitor() { 131 | } 132 | 133 | void LLVMCodeGenVisitor::visit(ast::Variable* v){ 134 | //XXX is there a better index? 135 | auto index = llvm::ConstantInt::get(mIntType, v->input_index()); 136 | 137 | llvm::Value * cur = nullptr; 138 | 139 | if (v->type() != ast::Variable::VarType::OUTPUT) { 140 | cur = mBuilder.CreateLoad(mInput); 141 | cur = mBuilder.CreateInBoundsGEP(mInputType, cur, index); 142 | } 143 | 144 | switch(v->type()) { 145 | case ast::Variable::VarType::FLOAT: 146 | cur = mBuilder.CreateBitCast(cur, llvm::PointerType::get(mFloatType, 0)); 147 | mValue = mBuilder.CreateLoad(cur, "inputf" + std::to_string(v->input_index())); 148 | break; 149 | case ast::Variable::VarType::INT: 150 | { 151 | cur = mBuilder.CreateBitCast(cur, llvm::PointerType::get(mFloatType, 0)); 152 | cur = mBuilder.CreateLoad(cur); 153 | mValue = createFunctionCall("floorf", 154 | llvm::FunctionType::get(mFloatType, {mFloatType}, false), 155 | { cur }, "inputi" + std::to_string(v->input_index())); 156 | } 157 | break; 158 | case ast::Variable::VarType::VECTOR: 159 | { 160 | cur = mBuilder.CreateBitCast(cur, llvm::PointerType::get(llvm::PointerType::get(mFloatType, 0), 0)); 161 | cur = mBuilder.CreateLoad(cur); 162 | cur = mBuilder.CreateInBoundsGEP(mFloatType, cur, mFrameIndex); 163 | mValue = mBuilder.CreateLoad(cur, "inputv" + std::to_string(v->input_index())); 164 | } 165 | break; 166 | case ast::Variable::VarType::INPUT: 167 | { 168 | //returns a float pointer 169 | cur = mBuilder.CreateBitCast(cur, llvm::PointerType::get(llvm::PointerType::get(mFloatType, 0), 0)); 170 | mValue = mBuilder.CreateLoad(cur, "inputx" + std::to_string(v->input_index())); 171 | } 172 | break; 173 | case ast::Variable::VarType::OUTPUT: 174 | { 175 | //returns a float pointer 176 | cur = mBuilder.CreateLoad(mOutput); 177 | cur = mBuilder.CreateInBoundsGEP(llvm::PointerType::get(mFloatType, 0), cur, llvm::ConstantInt::get(mIntType, v->input_index())); 178 | mValue = mBuilder.CreateLoad(cur, "inputy" + std::to_string(v->input_index())); 179 | } 180 | break; 181 | case ast::Variable::VarType::SYMBOL: 182 | { 183 | //returns a symbols pointer 184 | mValue = mBuilder.CreateBitCast(cur, llvm::PointerType::get(mSymbolPtrType, 0)); 185 | mValue = mBuilder.CreateLoad(cur, "inputs" + std::to_string(v->input_index())); 186 | } 187 | break; 188 | default: 189 | throw std::runtime_error("type not supported yet"); 190 | } 191 | wrapIntIfNeeded(v); 192 | } 193 | 194 | void LLVMCodeGenVisitor::visit(ast::Value* v){ 195 | mValue = llvm::ConstantFP::get(mFloatType, static_cast(v->value())); 196 | wrapIntIfNeeded(v); 197 | } 198 | 199 | void LLVMCodeGenVisitor::visit(ast::Value* v){ 200 | mValue = llvm::ConstantFP::get(mFloatType, v->value()); 201 | } 202 | 203 | void LLVMCodeGenVisitor::visit(ast::Value* v){ 204 | auto sym = getSymbol(v->value()); 205 | mValue = createFunctionCall("jit_expr_value_get", 206 | llvm::FunctionType::get(mFloatType, {mSymbolPtrType}, false), 207 | {sym}, v->value().c_str()); 208 | wrapIntIfNeeded(v); 209 | } 210 | 211 | void LLVMCodeGenVisitor::visit(ast::Quoted* v){ 212 | if (v->value().size()) { 213 | mValue = getSymbol(v->value()); 214 | } else { 215 | v->variable()->accept(this); 216 | } 217 | } 218 | 219 | void LLVMCodeGenVisitor::visit(ast::UnaryOp* v){ 220 | v->node()->accept(this); 221 | auto right = mValue; 222 | 223 | switch (v->op()) { 224 | case ast::UnaryOp::Op::BIT_NOT: 225 | mValue = toFloat(mBuilder.CreateNot(toInt(right), "nottmp")); 226 | break; 227 | case ast::UnaryOp::Op::LOGICAL_NOT: 228 | //logical not is the same as x == 0 229 | mValue = wrapLogic(mBuilder.CreateFCmpOEQ(right, llvm::ConstantFP::get(mFloatType, 0.0f), "eqtmp")); 230 | break; 231 | case ast::UnaryOp::Op::NEGATE: { 232 | auto zero = llvm::ConstantFP::get(mFloatType, 0.0f); 233 | mValue = mBuilder.CreateFSub(zero, right, "negtmp"); 234 | } 235 | break; 236 | default: 237 | throw std::runtime_error("unimplemented"); 238 | } 239 | wrapIntIfNeeded(v); 240 | } 241 | 242 | void LLVMCodeGenVisitor::visit(ast::BinaryOp* v){ 243 | v->left()->accept(this); 244 | auto left = mValue; 245 | 246 | v->right()->accept(this); 247 | auto right = mValue; 248 | 249 | switch (v->op()) { 250 | case ast::BinaryOp::Op::ADD: 251 | mValue = mBuilder.CreateFAdd(left, right, "addtmp"); 252 | break; 253 | case ast::BinaryOp::Op::SUBTRACT: 254 | mValue = mBuilder.CreateFSub(left, right, "subtmp"); 255 | break; 256 | case ast::BinaryOp::Op::MULTIPLY: 257 | mValue = mBuilder.CreateFMul(left, right, "multmp"); 258 | break; 259 | case ast::BinaryOp::Op::DIVIDE: 260 | mValue = mBuilder.CreateFDiv(left, right, "divtmp"); 261 | break; 262 | case ast::BinaryOp::Op::MOD: 263 | //value = (float)((int)right != 0 ? (int)left % (int)right) : 0; 264 | { 265 | auto iright = toInt(right); 266 | mValue = createIfFunc([this, iright]() { 267 | return toFloat(iright); //truncated, if it != 0 then do mod, otherwise return 0 268 | }, 269 | [this, left, iright]() { 270 | return toFloat(mBuilder.CreateSRem(toInt(left), iright, "modtmp")); 271 | }, 272 | [this]() { 273 | return llvm::ConstantFP::get(mFloatType, 0.0f); 274 | } 275 | ); 276 | } 277 | break; 278 | case ast::BinaryOp::Op::SHIFT_LEFT: 279 | mValue = toFloat(mBuilder.CreateShl(toInt(left), toInt(right), "sltmp")); 280 | break; 281 | case ast::BinaryOp::Op::SHIFT_RIGHT: 282 | mValue = toFloat(mBuilder.CreateLShr(toInt(left), toInt(right), "srtmp")); 283 | break; 284 | case ast::BinaryOp::Op::COMP_EQUAL: 285 | mValue = wrapLogic(mBuilder.CreateFCmpOEQ(left, right, "eqtmp")); 286 | break; 287 | case ast::BinaryOp::Op::COMP_NOT_EQUAL: 288 | mValue = wrapLogic(mBuilder.CreateFCmpONE(left, right, "neqtmp")); 289 | break; 290 | case ast::BinaryOp::Op::COMP_GREATER: 291 | mValue = wrapLogic(mBuilder.CreateFCmpOGT(left, right, "gttmp")); 292 | break; 293 | case ast::BinaryOp::Op::COMP_LESS: 294 | mValue = wrapLogic(mBuilder.CreateFCmpOLT(left, right, "lttmp")); 295 | break; 296 | case ast::BinaryOp::Op::COMP_GREATER_OR_EQUAL: 297 | mValue = wrapLogic(mBuilder.CreateNot(mBuilder.CreateFCmpOLT(left, right, "lttmp"), "nottmp")); 298 | break; 299 | case ast::BinaryOp::Op::COMP_LESS_OR_EQUAL: 300 | mValue = wrapLogic(mBuilder.CreateNot(mBuilder.CreateFCmpOGT(left, right, "lttmp"), "nottmp")); 301 | break; 302 | case ast::BinaryOp::Op::LOGICAL_OR: 303 | //just use bitwise then not equal 0 304 | mValue = wrapLogic(mBuilder.CreateICmpNE(mBuilder.CreateOr(toInt(left), toInt(right), "ortmp"), 305 | llvm::ConstantInt::get(mIntType, 0), "neqtmp")); 306 | break; 307 | case ast::BinaryOp::Op::LOGICAL_AND: 308 | //just use bitwise then not equal 0 309 | mValue = wrapLogic(mBuilder.CreateICmpNE(mBuilder.CreateAnd(toInt(left), toInt(right), "andtmp"), 310 | llvm::ConstantInt::get(mIntType, 0), "neqtmp")); 311 | break; 312 | case ast::BinaryOp::Op::BIT_AND: 313 | mValue = wrapLogic(mBuilder.CreateAnd(toInt(left), toInt(right), "andtmp")); 314 | break; 315 | case ast::BinaryOp::Op::BIT_OR: 316 | mValue = wrapLogic(mBuilder.CreateOr(toInt(left), toInt(right), "ortmp")); 317 | break; 318 | case ast::BinaryOp::Op::BIT_XOR: 319 | mValue = wrapLogic(mBuilder.CreateXor(toInt(left), toInt(right), "xortmp")); 320 | break; 321 | default: 322 | throw std::runtime_error("not supported yet"); 323 | } 324 | wrapIntIfNeeded(v); 325 | } 326 | 327 | void LLVMCodeGenVisitor::visit(ast::FunctionCall* v){ 328 | auto n = v->name(); 329 | 330 | //if 331 | if (n == "if") { 332 | mValue = createIfFunc( 333 | [this, v]() { 334 | v->args().at(0)->accept(this); 335 | return mValue; 336 | }, 337 | [this, v]() { 338 | v->args().at(1)->accept(this); 339 | return mValue; 340 | }, 341 | [this, v]() { 342 | v->args().at(2)->accept(this); 343 | return mValue; 344 | }); 345 | wrapIntIfNeeded(v); 346 | return; 347 | } else if (n == "float") { //this doesn't do anything, all math is float 348 | v->args().at(0)->accept(this); 349 | return; 350 | } else if (n == "int") { 351 | v->args().at(0)->accept(this); 352 | wrapIntIfNeeded(v); 353 | return; 354 | } 355 | 356 | //table functions 357 | auto it = function_alias.find(n); 358 | if (it != function_alias.end()) { 359 | n = it->second; 360 | } else { 361 | n = n + "f"; //we're using the floating point version of these calls 362 | } 363 | 364 | llvm::Function * f = mModule->getFunction(n); 365 | if (!f) { 366 | std::vector args; 367 | for (auto a: v->args()) 368 | args.push_back(a->output_type() == ast::Node::OutputType::STRING ? mSymbolPtrType : mFloatType); 369 | 370 | llvm::FunctionType *ft = llvm::FunctionType::get(mFloatType, args, false); 371 | f = llvm::Function::Create(ft, llvm::Function::ExternalLinkage, n, mModule.get()); 372 | if (!f) 373 | throw std::runtime_error("cannot find function with name: " + v->name()); 374 | 375 | //XXX is it a leak if we don't store this somewhere?? 376 | } 377 | 378 | //visit the children, store them in the args 379 | std::vector args; 380 | for (auto a: v->args()) { 381 | a->accept(this); 382 | args.push_back(mValue); 383 | } 384 | 385 | mValue = mBuilder.CreateCall(f, args, "calltmp"); 386 | wrapIntIfNeeded(v); 387 | } 388 | 389 | void LLVMCodeGenVisitor::visit(ast::SampleAccess* v) { 390 | //get the index node 391 | v->index_node()->accept(this); 392 | auto index = mValue; 393 | 394 | //get the variable 395 | v->source()->accept(this); 396 | auto var = mValue; //this is a pointer to a float 397 | 398 | float clamp_top = (v->source()->type() == ast::Variable::VarType::OUTPUT) ? -1.0f : 0; 399 | auto top = llvm::ConstantFP::get(mFloatType, clamp_top); 400 | auto zero = llvm::ConstantFP::get(mFloatType, 0.0f); 401 | auto bottom = mBuilder.CreateFSub(zero, toFloat(mFrameCount), "bottom"); 402 | 403 | //clamp index between -frame_size and clamp_top 404 | //index < top ? index : top 405 | auto lt = mBuilder.CreateFCmpOLT(index, top, "lttmp"); 406 | index = mBuilder.CreateSelect(lt, index, top); 407 | 408 | //index < bottom ? bottom : index 409 | lt = mBuilder.CreateFCmpOLT(index, bottom, "lttmp"); 410 | index = mBuilder.CreateSelect(lt, bottom, index); 411 | 412 | //offset with the current sample index 413 | index = mBuilder.CreateFAdd(index, toFloat(mFrameIndex), "offset"); 414 | 415 | 416 | llvm::Value * arrayLength; 417 | //input buffers are actually 2x as long because you have to be able to previous values as well 418 | if (v->source()->type() == ast::Variable::VarType::INPUT) 419 | arrayLength = toFloat(mBuilder.CreateShl(mFrameCount, llvm::ConstantInt::get(mIntType, 1))); 420 | else 421 | arrayLength = toFloat(mFrameCount); 422 | 423 | //index < 0 : frame_count + index : index 424 | lt = mBuilder.CreateFCmpOLT(index, zero, "ltmp"); 425 | 426 | //actually just add zero or the array length 427 | auto offset = mBuilder.CreateSelect(lt, arrayLength, zero); 428 | index = mBuilder.CreateFAdd(index, offset); 429 | 430 | #if 0 431 | index = toInt(index); 432 | auto p = mBuilder.CreateInBoundsGEP(mFloatType, var, index); 433 | mValue = mBuilder.CreateLoad(p); 434 | #else 435 | mValue = createFunctionCall("jit_expr_array_read", 436 | llvm::FunctionType::get(mFloatType, {llvm::PointerType::get(mFloatType, 0), mFloatType, mIntType}, false), 437 | { var, index, toInt(arrayLength) }, "tmparrayinterp"); 438 | #endif 439 | wrapIntIfNeeded(v); 440 | } 441 | 442 | void LLVMCodeGenVisitor::visit(ast::ArrayAccess* v){ 443 | if (v->name().size()) { 444 | auto sym = getSymbol(v->name()); 445 | mValue = mBuilder.CreateBitCast(sym, mSymbolPtrType); 446 | } else { 447 | v->name_var()->accept(this); 448 | } 449 | 450 | auto name = mValue; 451 | 452 | v->index_node()->accept(this); 453 | auto index = mValue; 454 | 455 | mValue = createFunctionCall("jit_expr_table_value_ptr", 456 | llvm::FunctionType::get(llvm::PointerType::get(mFloatType, 0), {mSymbolPtrType, mFloatType}, false), 457 | { name, index }, "tmparrayaccess"); 458 | } 459 | 460 | void LLVMCodeGenVisitor::visit(ast::ValueAssignment* v){ 461 | auto sym = getSymbol(v->value_name()); 462 | v->value_node()->accept(this); 463 | 464 | auto value = mValue; 465 | mValue = createFunctionCall("jit_expr_value_assign", 466 | llvm::FunctionType::get(mFloatType, {mSymbolPtrType, mFloatType}, false), 467 | {sym, value}, "tmpvalueassign"); 468 | wrapIntIfNeeded(v); 469 | } 470 | 471 | void LLVMCodeGenVisitor::visit(ast::ArrayAssignment* v){ 472 | v->array()->accept(this); 473 | auto aptr = mValue; 474 | v->value_node()->accept(this); 475 | auto value = mValue; 476 | mBuilder.CreateStore(value, aptr); 477 | mValue = value; 478 | wrapIntIfNeeded(v); 479 | } 480 | 481 | void LLVMCodeGenVisitor::visit(ast::Deref* v) { 482 | v->value_node()->accept(this); //returns a pointer to a float 483 | 484 | //XXX gen code for this, its real simple 485 | mValue = createFunctionCall("jit_expr_deref", 486 | llvm::FunctionType::get(mFloatType, {llvm::PointerType::get(mFloatType, 0)}, false), 487 | {mValue}, "tmpderef"); 488 | wrapIntIfNeeded(v); 489 | } 490 | 491 | LLVMCodeGenVisitor::function_t LLVMCodeGenVisitor::function(std::vector statements, std::string& print_out) { 492 | llvm::Value * cur = nullptr; 493 | 494 | auto outargt = llvm::PointerType::get(llvm::PointerType::get(mFloatType, 0), 0); 495 | llvm::Value * output = mBuilder.CreateAlloca(outargt, (unsigned)0); 496 | mBuilder.CreateStore(mOutput, output); 497 | mOutput = output; 498 | 499 | //store 500 | llvm::Value * fcount = mBuilder.CreateAlloca(mIntType, (unsigned)0); 501 | mBuilder.CreateStore(mFrameCount, fcount); 502 | mFrameCount = mBuilder.CreateLoad(fcount, "framecnt"); 503 | 504 | //loop start 505 | llvm::Value * StartVal = llvm::ConstantInt::get(mIntType, 0); 506 | llvm::Function *TheFunction = mBuilder.GetInsertBlock()->getParent(); 507 | llvm::BasicBlock *PreheaderBB = mBuilder.GetInsertBlock(); 508 | llvm::BasicBlock *LoopBB = llvm::BasicBlock::Create(mContext, "loop", TheFunction); 509 | 510 | // Insert an explicit fall through from the current block to the LoopBB. 511 | mBuilder.CreateBr(LoopBB); 512 | // Start insertion in LoopBB. 513 | mBuilder.SetInsertPoint(LoopBB); 514 | 515 | // Start the PHI node with an entry for Start. 516 | llvm::PHINode *Variable = mBuilder.CreatePHI(mIntType, 2, "loopvar"); 517 | Variable->addIncoming(StartVal, PreheaderBB); 518 | 519 | mFrameIndex = Variable; 520 | //add statements 521 | for (unsigned int i = 0; i < statements.size(); i++) { 522 | auto index = llvm::ConstantInt::get(mIntType, i); 523 | cur = mBuilder.CreateLoad(mOutput); 524 | cur = mBuilder.CreateInBoundsGEP(llvm::PointerType::get(mFloatType, 0), cur, index); 525 | cur = mBuilder.CreateLoad(cur); 526 | cur = mBuilder.CreateInBoundsGEP(mFloatType, cur, mFrameIndex); 527 | 528 | statements.at(i)->accept(this); 529 | mBuilder.CreateStore(mValue, cur); 530 | } 531 | 532 | // Emit the step value. 533 | llvm::Value *NextVar = mBuilder.CreateAdd(Variable, llvm::ConstantInt::get(mIntType, 1), "nextvar"); 534 | llvm::Value *EndVal = mFrameCount; 535 | llvm::Value *EndCond = mBuilder.CreateICmpNE(EndVal, NextVar); 536 | 537 | // Create the "after loop" block and insert it. 538 | llvm::BasicBlock *LoopEndBB = mBuilder.GetInsertBlock(); 539 | llvm::BasicBlock *AfterBB = llvm::BasicBlock::Create(mContext, "afterloop", TheFunction); 540 | 541 | // Insert the conditional branch into the end of LoopEndBB. 542 | mBuilder.CreateCondBr(EndCond, LoopBB, AfterBB); 543 | 544 | // Any new code will be inserted in AfterBB. 545 | mBuilder.SetInsertPoint(AfterBB); 546 | 547 | // Add a new entry to the PHI node for the backedge. 548 | Variable->addIncoming(NextVar, LoopEndBB); 549 | 550 | mBuilder.CreateRet(nullptr); 551 | llvm::verifyFunction(*mMainFunction); 552 | mFunctionPassManager->run(*mMainFunction); 553 | 554 | { 555 | std::string s; 556 | llvm::raw_string_ostream ss(s); 557 | mModule->print(ss, nullptr); 558 | print_out = ss.str(); 559 | } 560 | 561 | auto Resolver = llvm::orc::createLambdaResolver( 562 | [&](const std::string &Name) { 563 | if (auto Sym = findMangledSymbol(Name)) 564 | return Sym; 565 | return llvm::JITSymbol(nullptr); 566 | }, 567 | [](const std::string &/*S*/) { return nullptr; }); 568 | auto H = llvm::cantFail(mCompileLayer.addModule(std::move(mModule), std::move(Resolver))); 569 | mModuleHandles.push_back(H); 570 | 571 | auto ExprSymbol = findSymbol(main_function_name); 572 | if (!ExprSymbol) 573 | throw std::runtime_error("couldn't find symbol " + main_function_name); 574 | 575 | function_t func = reinterpret_cast((uintptr_t)cantFail(ExprSymbol.getAddress())); 576 | return func; 577 | } 578 | 579 | llvm::JITSymbol LLVMCodeGenVisitor::findSymbol(const std::string Name) { 580 | return findMangledSymbol(mangle(Name)); 581 | } 582 | 583 | llvm::JITSymbol LLVMCodeGenVisitor::findMangledSymbol(const std::string &Name) { 584 | const bool ExportedSymbolsOnly = false; 585 | 586 | // Search modules in reverse order: from last added to first added. 587 | // This is the opposite of the usual search order for dlsym, but makes more 588 | // sense in a REPL where we want to bind to the newest available definition. 589 | 590 | for (auto H : llvm::make_range(mModuleHandles.rbegin(), mModuleHandles.rend())) 591 | if (auto Sym = mCompileLayer.findSymbolIn(H, Name, ExportedSymbolsOnly)) 592 | return Sym; 593 | 594 | // If we can't find the symbol in the JIT, try looking in the host process. 595 | if (auto SymAddr = llvm::RTDyldMemoryManager::getSymbolAddressInProcess(Name)) 596 | return llvm::JITSymbol(SymAddr, llvm::JITSymbolFlags::Exported); 597 | 598 | #ifdef LLVM_ON_WIN32 599 | // For Windows retry without "_" at beginning, as RTDyldMemoryManager uses 600 | // GetProcAddress and standard libraries like msvcrt.dll use names 601 | // with and without "_" (for example "_itoa" but "sin"). 602 | if (Name.length() > 2 && Name[0] == '_') 603 | if (auto SymAddr = 604 | llvm::RTDyldMemoryManager::getSymbolAddressInProcess(Name.substr(1))) 605 | return llvm::JITSymbol(SymAddr, llvm::JITSymbolFlags::Exported); 606 | #endif 607 | 608 | return nullptr; 609 | } 610 | 611 | std::string LLVMCodeGenVisitor::mangle(const std::string &Name) { 612 | std::string MangledName; 613 | { 614 | llvm::raw_string_ostream MangledNameStream(MangledName); 615 | llvm::Mangler::getNameWithPrefix(MangledNameStream, Name, mDataLayout); 616 | } 617 | return MangledName; 618 | } 619 | 620 | llvm::Value * LLVMCodeGenVisitor::wrapLogic(llvm::Value * v) { 621 | return mBuilder.CreateUIToFP(v, mFloatType, "cast"); 622 | } 623 | 624 | llvm::Value * LLVMCodeGenVisitor::toInt(llvm::Value * v) { 625 | return mBuilder.CreateFPToSI(v, mIntType, "cast"); 626 | } 627 | 628 | llvm::Value * LLVMCodeGenVisitor::toFloat(llvm::Value * v) { 629 | return mBuilder.CreateSIToFP(v, mFloatType, "cast"); 630 | } 631 | 632 | llvm::Value * LLVMCodeGenVisitor::getSymbol(const std::string& name) { 633 | t_symbol * sym = gensym(name.c_str()); //XXX hold onto symbol and free later? 634 | if (!sym) 635 | throw std::runtime_error("couldn't get symbol " + name); 636 | return llvm::ConstantInt::get(mDataLayout.getIntPtrType(mContext, 0), reinterpret_cast(sym)); 637 | } 638 | 639 | //condition is just a float, if it != 0.0 then the true getter value is returned, otherwise the false getter value is 640 | llvm::Value * LLVMCodeGenVisitor::createIfFunc(std::function condGetter, std::function trueGetter, std::function falseGetter) { 641 | //translated from kaleidoscope example, chapter 5 642 | llvm::Value * condValue = condGetter(); 643 | //v->args().at(0)->accept(this); 644 | 645 | //true if not equal to zero 646 | auto cond = mBuilder.CreateFCmpONE(condValue, llvm::ConstantFP::get(mFloatType, 0.0f), "cmptmp"); 647 | 648 | llvm::Function *f = mBuilder.GetInsertBlock()->getParent(); 649 | auto thenbb = llvm::BasicBlock::Create(mContext, "then", f); 650 | auto elsebb = llvm::BasicBlock::Create(mContext, "else"); 651 | auto mergebb = llvm::BasicBlock::Create(mContext, "ifcont"); 652 | 653 | mBuilder.CreateCondBr(cond, thenbb, elsebb); 654 | 655 | mBuilder.SetInsertPoint(thenbb); 656 | //v->args().at(1)->accept(this); 657 | auto thenv = trueGetter(); 658 | if (!thenv) 659 | throw std::runtime_error("couldn't create true expression"); 660 | 661 | mBuilder.CreateBr(mergebb); 662 | thenbb = mBuilder.GetInsertBlock(); //codegen of then can change current block so update thenbb for phi 663 | mMainFunction->getBasicBlockList().push_back(elsebb); 664 | 665 | mBuilder.SetInsertPoint(elsebb); 666 | //v->args().at(2)->accept(this); 667 | auto elsev = falseGetter(); 668 | if (!elsev) 669 | throw std::runtime_error("couldn't create else expression"); 670 | 671 | mBuilder.CreateBr(mergebb); 672 | elsebb = mBuilder.GetInsertBlock(); //codegen can change the current block so update elsebb for phi 673 | mMainFunction->getBasicBlockList().push_back(mergebb); 674 | 675 | //bring everything together 676 | mBuilder.SetInsertPoint(mergebb); 677 | auto *phi = mBuilder.CreatePHI(mFloatType, 2, "iftmp"); 678 | phi->addIncoming(thenv, thenbb); 679 | phi->addIncoming(elsev, elsebb); 680 | return phi; 681 | } 682 | 683 | void LLVMCodeGenVisitor::wrapIntIfNeeded(ast::Node * n) { 684 | if (n->output_type() == ast::Node::OutputType::INT) 685 | mValue = toFloat(toInt(mValue)); 686 | } 687 | 688 | //llvm::Value * LLVMCodeGenVisitor::linterpWithWrap(llvm::Value * fptr, llvm::Value * findex, llvm::Value * ilength) { 689 | //} 690 | llvm::Value * LLVMCodeGenVisitor::createFunctionCall( 691 | const std::string& func_name, 692 | llvm::FunctionType *func_type, 693 | std::vector args, std::string callname) { 694 | 695 | llvm::Function * f = mModule->getFunction(func_name); 696 | if (!f) { 697 | f = llvm::Function::Create(func_type, llvm::Function::ExternalLinkage, func_name, mModule.get()); 698 | if (!f) 699 | throw std::runtime_error("cannot find function with name " + func_name); 700 | //XXX is it a leak if we don't store this somewhere?? 701 | } 702 | return mBuilder.CreateCall(f, args, callname); 703 | } 704 | 705 | } 706 | -------------------------------------------------------------------------------- /src/llvmcodegen/codegen.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) Alex Norman, 2018, see LICENSE-xnor 2 | //based on the Kaleidoscope example from the llvm tutorial https://llvm.org/docs/tutorial/ 3 | 4 | #pragma once 5 | 6 | #include "ast.h" 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace llvm { 30 | class Module; 31 | class Function; 32 | class Value; 33 | class BasicBlock; 34 | class TargetMachine; 35 | } 36 | 37 | 38 | namespace xnor { 39 | class LLVMCodeGenVisitor : public xnor::ast::Visitor { 40 | public: 41 | static void init(); 42 | 43 | typedef union { 44 | t_float flt; 45 | t_symbol * sym; 46 | t_sample * vec; 47 | } input_arg_t; 48 | 49 | typedef void(*function_t)(float **, input_arg_t *, int nframes); 50 | 51 | using ObjLayerT = llvm::orc::RTDyldObjectLinkingLayer; 52 | using CompileLayerT = llvm::orc::IRCompileLayer; 53 | using ModuleHandleT = CompileLayerT::ModuleHandleT; 54 | 55 | LLVMCodeGenVisitor(); 56 | virtual ~LLVMCodeGenVisitor(); 57 | virtual void visit(xnor::ast::Variable* v); 58 | virtual void visit(xnor::ast::Value* v); 59 | virtual void visit(xnor::ast::Value* v); 60 | virtual void visit(xnor::ast::Value* v); 61 | virtual void visit(xnor::ast::Quoted* v); 62 | virtual void visit(xnor::ast::UnaryOp* v); 63 | virtual void visit(xnor::ast::BinaryOp* v); 64 | virtual void visit(xnor::ast::FunctionCall* v); 65 | virtual void visit(xnor::ast::SampleAccess* v); 66 | virtual void visit(xnor::ast::ArrayAccess* v); 67 | virtual void visit(xnor::ast::ValueAssignment* v); 68 | virtual void visit(xnor::ast::ArrayAssignment* v); 69 | virtual void visit(xnor::ast::Deref* v); 70 | 71 | function_t function(std::vector statements, std::string& print_out); 72 | private: 73 | llvm::LLVMContext mContext; 74 | llvm::IRBuilder<> mBuilder; 75 | 76 | std::unique_ptr mTargetMachine; 77 | std::unique_ptr mModule; 78 | std::unique_ptr mFunctionPassManager; 79 | 80 | llvm::Function * mMainFunction; 81 | llvm::Value * mValue; 82 | llvm::Value * mOutput; 83 | llvm::Value * mInput; 84 | llvm::Value * mFrameIndex; 85 | llvm::Value * mFrameCount; 86 | llvm::BasicBlock * mBlock; 87 | 88 | llvm::Type * mFloatType; 89 | llvm::Type * mIntType; 90 | llvm::Type * mInputType; 91 | llvm::Type * mSymbolPtrType; 92 | 93 | const llvm::DataLayout mDataLayout; 94 | ObjLayerT mObjectLayer; 95 | CompileLayerT mCompileLayer; 96 | 97 | std::vector mModuleHandles; 98 | 99 | llvm::JITSymbol findMangledSymbol(const std::string& name); 100 | llvm::JITSymbol findSymbol(const std::string name); 101 | std::string mangle(const std::string& name); 102 | llvm::Value * wrapLogic(llvm::Value * v); 103 | llvm::Value * toInt(llvm::Value * v); 104 | llvm::Value * toFloat(llvm::Value * v); 105 | llvm::Value * getSymbol(const std::string& name); 106 | llvm::Value * createIfFunc(std::function condGetter, std::function trueGetter, std::function falseGetter); 107 | void wrapIntIfNeeded(xnor::ast::Node * n); 108 | 109 | //returns float 110 | //llvm::Value * linterpWithWrap(llvm::Value * fptr, llvm::Value * findex, llvm::Value * ilength); 111 | 112 | llvm::Value * createFunctionCall( 113 | const std::string& func_name, 114 | llvm::FunctionType *ft, 115 | std::vector args, std::string callname = "tmpfcall"); 116 | }; 117 | } 118 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | //Copyright (c) Alex Norman, 2018, see LICENSE-xnor 2 | 3 | #include "parse/driver.hh" 4 | #include "print.h" 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | using std::cerr; 12 | using std::cout; 13 | using std::endl; 14 | 15 | #include "print.h" 16 | 17 | int main(int argc, char * argv[]) { 18 | if (argc != 2) 19 | throw std::runtime_error("must provide a file as an argument"); 20 | 21 | std::ifstream infile(argv[1]); 22 | 23 | parse::Driver driver; 24 | std::string line; 25 | 26 | while(std::getline(infile, line)) { 27 | try { 28 | cout << "parsing: " << line << endl; 29 | auto t = driver.parse_string(line); 30 | for (auto n : t) { 31 | xnor::AstPrintVisitor v; 32 | n->accept(&v); 33 | } 34 | 35 | /* 36 | std::array value {{ 0, 0, 0, 0 }}; 37 | std::array out {{ 38 | &value.front(), 39 | &value.front(), 40 | &value.front(), 41 | &value.front(), 42 | }}; 43 | 44 | //std::array invec {{ 0, 0, 0, 0 }}; 45 | xnor::LLVMCodeGenVisitor::input_arg_t * in = new xnor::LLVMCodeGenVisitor::input_arg_t[12]; 46 | in[0].flt = 53.2; 47 | 48 | xnor::LLVMCodeGenVisitor cv; 49 | std::string s; 50 | auto f = cv.function(t, s); 51 | f(&out.front(), in, 2); 52 | cout << "output " << value[0] << endl; 53 | cout << endl; 54 | */ 55 | } catch (std::runtime_error& e) { 56 | cerr << "fail: " << e.what() << endl; 57 | return -1; 58 | } 59 | } 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /src/parse/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 2 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 3 | include_directories(/usr/local/opt/flex/include/) 4 | 5 | file(GLOB H_FILES *.h) 6 | file(GLOB HH_FILES *.hh) 7 | file(GLOB SRC_FILES *.cc) 8 | file(GLOB HXX_FILES *.hxx) 9 | 10 | find_package(BISON) 11 | find_package(FLEX) 12 | 13 | bison_target(Parser parse.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cc) 14 | flex_target(Scanner scan.ll ${CMAKE_CURRENT_BINARY_DIR}/scanner.cc) 15 | 16 | add_flex_bison_dependency(Scanner Parser) 17 | 18 | add_library(parse 19 | STATIC 20 | ${FLEX_Scanner_OUTPUTS} 21 | ${BISON_Parser_OUTPUTS} 22 | ${H_FILES} 23 | ${HH_FILES} 24 | ${SRC_FILES} 25 | ${HXX_FILES} 26 | ) 27 | -------------------------------------------------------------------------------- /src/parse/ast.cc: -------------------------------------------------------------------------------- 1 | //Copyright (c) Alex Norman, 2018, see LICENSE-xnor 2 | 3 | #include "ast.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using std::cout; 10 | using std::endl; 11 | 12 | namespace a = xnor::ast; 13 | 14 | namespace { 15 | using ot = a::Node::OutputType; 16 | 17 | const std::regex var_regex("\\$([fisvxyFISVXY])(\\d*)"); 18 | const std::map var_type_map = { 19 | {"f", a::Variable::VarType::FLOAT}, 20 | {"F", a::Variable::VarType::FLOAT}, 21 | {"i", a::Variable::VarType::INT}, 22 | {"I", a::Variable::VarType::INT}, 23 | {"s", a::Variable::VarType::SYMBOL}, 24 | {"S", a::Variable::VarType::SYMBOL}, 25 | {"v", a::Variable::VarType::VECTOR}, 26 | {"V", a::Variable::VarType::VECTOR}, 27 | {"x", a::Variable::VarType::INPUT}, 28 | {"X", a::Variable::VarType::INPUT}, 29 | {"y", a::Variable::VarType::OUTPUT}, 30 | {"Y", a::Variable::VarType::OUTPUT}, 31 | }; 32 | 33 | const std::map > function_arg_map = { 34 | {"Sum", {ot::STRING, ot::FLOAT, ot::FLOAT}}, 35 | {"abs", {ot::FLOAT}}, 36 | {"acos", {ot::FLOAT}}, 37 | {"acosh", {ot::FLOAT}}, 38 | {"asin", {ot::FLOAT}}, 39 | {"asinh", {ot::FLOAT}}, 40 | {"atan2", {ot::FLOAT, ot::FLOAT}}, 41 | {"atanh", {ot::FLOAT}}, 42 | {"cbrt", {ot::FLOAT}}, 43 | {"ceil", {ot::FLOAT}}, 44 | {"copysign", {ot::FLOAT, ot::FLOAT}}, 45 | {"cos", {ot::FLOAT}}, 46 | {"erf", {ot::FLOAT}}, 47 | {"erfc", {ot::FLOAT}}, 48 | {"exp", {ot::FLOAT}}, 49 | {"expm1", {ot::FLOAT}}, 50 | {"fact", {ot::FLOAT}}, 51 | {"finite", {ot::FLOAT}}, 52 | {"float", {ot::FLOAT}}, 53 | {"floor", {ot::FLOAT}}, 54 | {"fmod", {ot::FLOAT, ot::FLOAT}}, 55 | {"if", {ot::FLOAT, ot::FLOAT, ot::FLOAT}}, 56 | {"imodf", {ot::FLOAT}}, 57 | {"int", {ot::FLOAT}}, 58 | {"isinf", {ot::FLOAT}}, 59 | {"isnan", {ot::FLOAT}}, 60 | {"ldexp", {ot::FLOAT, ot::FLOAT}}, 61 | {"ln", {ot::FLOAT}}, 62 | {"log", {ot::FLOAT}}, 63 | {"log10", {ot::FLOAT}}, 64 | {"log1p", {ot::FLOAT}}, 65 | {"max", {ot::FLOAT, ot::FLOAT}}, 66 | {"min", {ot::FLOAT, ot::FLOAT}}, 67 | {"modf", {ot::FLOAT}}, 68 | {"nearbyint", {ot::FLOAT}}, 69 | {"pow", {ot::FLOAT, ot::FLOAT}}, 70 | {"random", {ot::FLOAT, ot::FLOAT}}, 71 | {"remainder", {ot::FLOAT, ot::FLOAT}}, 72 | {"rint", {ot::FLOAT}}, 73 | {"round", {ot::FLOAT}}, 74 | {"sin", {ot::FLOAT}}, 75 | {"size", {ot::STRING}}, 76 | {"sqrt", {ot::FLOAT}}, 77 | {"sum", {ot::STRING}}, 78 | {"tan", {ot::FLOAT}}, 79 | {"trunc", {ot::FLOAT}}, 80 | }; 81 | } 82 | 83 | namespace xnor { 84 | namespace ast { 85 | Node::~Node() { 86 | } 87 | 88 | //default is numeric output 89 | Node::OutputType Node::output_type() const { return OutputType::FLOAT; } 90 | 91 | Variable::Variable(const std::string& v) { 92 | std::cmatch m; 93 | if (!std::regex_match(v.c_str(), m, var_regex)) 94 | throw std::runtime_error(v + " is not a valid variable declaration"); 95 | 96 | auto it = var_type_map.find(m[1]); 97 | if (it == var_type_map.end()) 98 | throw std::runtime_error(v + " has unknown type"); 99 | mType = it->second; 100 | 101 | std::string i(m[2]); 102 | if (i.size()) 103 | mInputIndex = std::stoi(i) - 1; 104 | } 105 | 106 | Variable::Variable(VarType t, unsigned int index) : mType(t), mInputIndex(index) { } 107 | 108 | Variable::~Variable() { 109 | } 110 | 111 | 112 | Node::OutputType Variable::output_type() const { 113 | return mType == VarType::INT ? OutputType::INT : OutputType::FLOAT; 114 | } 115 | 116 | 117 | unsigned int Variable::input_index() const { return mInputIndex; } 118 | Variable::VarType Variable::type() const { return mType; } 119 | 120 | Quoted::Quoted(const std::string& value) : mStringValue(value) { 121 | } 122 | 123 | Quoted::Quoted(VariablePtr var) : mQuotedVar(var) { 124 | if (var == nullptr) 125 | throw std::runtime_error("variable cannot be null"); 126 | if (var->type() != a::Variable::VarType::SYMBOL) 127 | throw std::runtime_error("quoted values can only be strings or symbol variables"); 128 | } 129 | 130 | Node::OutputType Quoted::output_type() const { return Node::OutputType::STRING; } 131 | 132 | UnaryOp::UnaryOp(Op op, NodePtr node) : mOp(op), mNode(node) { 133 | } 134 | 135 | Node::OutputType UnaryOp::output_type() const { return mNode->output_type(); } 136 | 137 | 138 | BinaryOp::BinaryOp(NodePtr left, Op op, NodePtr right) : mLeft(left), mOp(op), mRight(right) { 139 | } 140 | 141 | Node::OutputType BinaryOp::output_type() const { 142 | if (mRight->output_type() == OutputType::INT && mLeft->output_type() == OutputType::INT) 143 | return OutputType::INT; 144 | return OutputType::FLOAT; 145 | } 146 | 147 | FunctionCall::FunctionCall(const std::string& name, const std::vector& args) : mName(name), mArgs(args) { 148 | auto it = function_arg_map.find(name); 149 | if (it == function_arg_map.end()) 150 | throw std::runtime_error("function not found with name: " + name); 151 | const auto& arg_types = it->second; 152 | if (args.size() != arg_types.size()) { 153 | throw std::runtime_error("function " + name + " expects " + std::to_string(arg_types.size()) + " arguments, got: " + std::to_string(args.size())); 154 | } 155 | for (unsigned int i = 0; i < args.size(); i++) { 156 | if ((args.at(i)->output_type() == ot::STRING && arg_types.at(i) != ot::STRING) || (args.at(i)->output_type() != ot::STRING && arg_types.at(i) == ot::STRING)) { 157 | throw std::runtime_error("function " + name + " arg " + std::to_string(i) + " type mismatch" + (args.at(i)->output_type() == ot::STRING ? "IS STRING" : "NOT")); 158 | } 159 | } 160 | } 161 | 162 | Node::OutputType FunctionCall::output_type() const { 163 | if (mName == "int") 164 | return OutputType::INT; 165 | else if (mName == "float") 166 | return OutputType::FLOAT; 167 | for (auto a: mArgs) { 168 | if (a->output_type() == OutputType::FLOAT) 169 | return a->output_type(); 170 | } 171 | return OutputType::INT; 172 | } 173 | 174 | SampleAccess::SampleAccess(VariablePtr var, NodePtr accessor) : 175 | mSource(var), mAccessor(accessor) 176 | { 177 | if (var->type() != a::Variable::VarType::INPUT && var->type() != a::Variable::VarType::OUTPUT) 178 | throw std::runtime_error("only input and output variables can be accessed at the sample level"); 179 | } 180 | 181 | ArrayAccess::ArrayAccess(const std::string& name, NodePtr accessor) : 182 | mArrayName(name), mAccessor(accessor) 183 | { 184 | } 185 | 186 | ArrayAccess::ArrayAccess(VariablePtr varNode, NodePtr accessor) : 187 | mArrayVar(varNode), mAccessor(accessor) 188 | { 189 | } 190 | 191 | ValueAssignment::ValueAssignment(const std::string& name, NodePtr node) : mValueName(name), mValueNode(node) 192 | { 193 | } 194 | 195 | Node::OutputType ValueAssignment::output_type() const { 196 | return mValueNode->output_type(); 197 | } 198 | 199 | ArrayAssignment::ArrayAssignment(ArrayAccessPtr array, NodePtr node) : mArray(array), mValueNode(node) 200 | { 201 | } 202 | 203 | Node::OutputType ArrayAssignment::output_type() const { 204 | return mValueNode->output_type(); 205 | } 206 | 207 | Deref::Deref(ArrayAccessPtr array) : mValue(array) {} 208 | 209 | Node::OutputType Deref::output_type() const { 210 | return mValue->output_type(); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/parse/ast.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) Alex Norman, 2018, see LICENSE-xnor 2 | 3 | #ifndef XNOR_AST_H 4 | #define XNOR_AST_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace xnor { 12 | namespace ast { 13 | class Node; 14 | class Variable; 15 | template 16 | class Value; 17 | class Quoted; 18 | class UnaryOp; 19 | class BinaryOp; 20 | class FunctionCall; 21 | class SampleAccess; 22 | class ArrayAccess; 23 | class ValueAssignment; 24 | class ArrayAssignment; 25 | class Deref; 26 | typedef std::shared_ptr NodePtr; 27 | typedef std::shared_ptr VariablePtr; 28 | typedef std::shared_ptr SampleAccessPtr; 29 | typedef std::shared_ptr ArrayAccessPtr; 30 | typedef std::shared_ptr DerefPtr; 31 | typedef std::function PrintFunc; 32 | 33 | typedef std::vector VariableVector; 34 | 35 | class Visitor { 36 | public: 37 | virtual ~Visitor(){} 38 | virtual void visit(Variable* v) = 0; 39 | virtual void visit(Value* v) = 0; 40 | virtual void visit(Value* v) = 0; 41 | virtual void visit(Value* v) = 0; 42 | virtual void visit(Quoted* v) = 0; 43 | virtual void visit(UnaryOp* v) = 0; 44 | virtual void visit(BinaryOp* v) = 0; 45 | virtual void visit(FunctionCall* v) = 0; 46 | virtual void visit(SampleAccess* v) = 0; 47 | virtual void visit(ArrayAccess* v) = 0; 48 | virtual void visit(ValueAssignment* v) = 0; 49 | virtual void visit(ArrayAssignment* v) = 0; 50 | virtual void visit(Deref* v) = 0; 51 | }; 52 | 53 | class Node { 54 | public: 55 | virtual ~Node(); 56 | enum class OutputType { 57 | FLOAT, 58 | INT, 59 | STRING 60 | }; 61 | virtual OutputType output_type() const; 62 | virtual void accept(Visitor* v) = 0; 63 | }; 64 | 65 | //templatized to with CRTP to auto gen accept method 66 | template 67 | class VNode : public Node { 68 | public: 69 | virtual void accept(Visitor* v) override { 70 | v->visit(static_cast(this)); 71 | }; 72 | }; 73 | 74 | 75 | class Variable : public VNode { 76 | public: 77 | enum class VarType { 78 | FLOAT, 79 | INT, 80 | SYMBOL, 81 | VECTOR, 82 | INPUT, 83 | OUTPUT 84 | }; 85 | Variable(const std::string& n); 86 | Variable(VarType t, unsigned int index); 87 | virtual ~Variable(); 88 | virtual OutputType output_type() const override; 89 | 90 | unsigned int input_index() const; 91 | VarType type() const; 92 | private: 93 | unsigned int mInputIndex = 0; 94 | VarType mType = VarType::FLOAT; 95 | }; 96 | 97 | template 98 | class Value : public VNode> { 99 | public: 100 | Value(const T& v) : mValue(v) { } 101 | T value() const { return mValue; } 102 | void output_type(Node::OutputType v) { mOutputType = v; } 103 | virtual Node::OutputType output_type() const override { return mOutputType; } 104 | private: 105 | T mValue; 106 | Node::OutputType mOutputType = Node::OutputType::FLOAT; 107 | }; 108 | 109 | class Quoted : public VNode { 110 | public: 111 | Quoted(const std::string& value); 112 | Quoted(VariablePtr var); 113 | virtual OutputType output_type() const override; 114 | 115 | std::string value() const { return mStringValue; } 116 | VariablePtr variable() const { return mQuotedVar; } 117 | private: 118 | std::string mStringValue; 119 | VariablePtr mQuotedVar = nullptr; 120 | }; 121 | 122 | class UnaryOp : public VNode { 123 | public: 124 | enum class Op { 125 | BIT_NOT, 126 | LOGICAL_NOT, 127 | NEGATE 128 | }; 129 | UnaryOp(Op op, NodePtr node); 130 | virtual OutputType output_type() const override; 131 | Op op() const { return mOp; } 132 | NodePtr node() const { return mNode; } 133 | private: 134 | Op mOp; 135 | NodePtr mNode; 136 | }; 137 | 138 | class BinaryOp : public VNode { 139 | public: 140 | enum class Op { 141 | ADD, 142 | SUBTRACT, 143 | MULTIPLY, 144 | DIVIDE, 145 | MOD, 146 | SHIFT_LEFT, 147 | SHIFT_RIGHT, 148 | COMP_EQUAL, 149 | COMP_NOT_EQUAL, 150 | COMP_GREATER, 151 | COMP_LESS, 152 | COMP_GREATER_OR_EQUAL, 153 | COMP_LESS_OR_EQUAL, 154 | LOGICAL_OR, 155 | LOGICAL_AND, 156 | BIT_AND, 157 | BIT_OR, 158 | BIT_XOR 159 | }; 160 | BinaryOp(NodePtr left, Op op, NodePtr right); 161 | virtual OutputType output_type() const override; 162 | 163 | Op op() const { return mOp; } 164 | NodePtr left() const { return mLeft; } 165 | NodePtr right() const { return mRight; } 166 | private: 167 | NodePtr mLeft; 168 | Op mOp; 169 | NodePtr mRight; 170 | }; 171 | 172 | class FunctionCall : public VNode { 173 | public: 174 | FunctionCall(const std::string& name, const std::vector& args); 175 | virtual OutputType output_type() const override; 176 | std::string name() const { return mName; } 177 | const std::vector& args() const { return mArgs; } 178 | private: 179 | std::string mName; 180 | std::vector mArgs; 181 | }; 182 | 183 | class SampleAccess : public VNode { 184 | public: 185 | SampleAccess(VariablePtr varNode, NodePtr accessor); 186 | 187 | VariablePtr source() const { return mSource; } 188 | NodePtr index_node() const { return mAccessor; } 189 | private: 190 | VariablePtr mSource; 191 | NodePtr mAccessor; 192 | }; 193 | 194 | class ArrayAccess : public VNode { 195 | public: 196 | ArrayAccess(const std::string& name, NodePtr accessor); 197 | ArrayAccess(VariablePtr varNode, NodePtr accessor); 198 | 199 | std::string name() const { return mArrayName; } 200 | VariablePtr name_var() const { return mArrayVar; } 201 | NodePtr index_node() const { return mAccessor; } 202 | private: 203 | std::string mArrayName; 204 | VariablePtr mArrayVar = nullptr; 205 | NodePtr mAccessor; 206 | }; 207 | 208 | class ValueAssignment : public VNode { 209 | public: 210 | ValueAssignment(const std::string& name, NodePtr node); 211 | virtual OutputType output_type() const override; 212 | 213 | std::string value_name() const { return mValueName; } 214 | NodePtr value_node() const { return mValueNode; } 215 | private: 216 | std::string mValueName; 217 | NodePtr mValueNode; 218 | }; 219 | 220 | class ArrayAssignment : public VNode { 221 | public: 222 | ArrayAssignment(ArrayAccessPtr array, NodePtr node); 223 | virtual OutputType output_type() const override; 224 | 225 | ArrayAccessPtr array() const { return mArray; } 226 | NodePtr value_node() const { return mValueNode; } 227 | private: 228 | ArrayAccessPtr mArray; 229 | NodePtr mValueNode; 230 | }; 231 | 232 | class Deref : public VNode { 233 | public: 234 | Deref(ArrayAccessPtr v); 235 | NodePtr value_node() const { return mValue; } 236 | virtual OutputType output_type() const override; 237 | private: 238 | ArrayAccessPtr mValue; 239 | }; 240 | } 241 | } 242 | #endif 243 | -------------------------------------------------------------------------------- /src/parse/driver.cc: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017 Rémi Berson, modified for jit-expr 2018 by Alex Norman 2 | 3 | #include "driver.hh" 4 | #include "parser.hh" 5 | #include "scanner.hh" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace parse 11 | { 12 | Driver::Driver() 13 | : scanner_ (new Scanner()), 14 | parser_ (new Parser(*this)), 15 | location_ (new location()) 16 | { 17 | } 18 | 19 | 20 | Driver::~Driver() { 21 | delete parser_; 22 | delete scanner_; 23 | delete location_; 24 | } 25 | 26 | 27 | void Driver::reset() { 28 | mTrees.clear(); 29 | mInputs.clear(); 30 | 31 | delete location_; 32 | location_ = new location(); 33 | } 34 | 35 | TreeVector Driver::parse() { 36 | reset(); 37 | scanner_->switch_streams(&std::cin, &std::cerr); 38 | parser_->parse(); 39 | 40 | validate(); 41 | return trees(); 42 | } 43 | 44 | TreeVector Driver::parse_file(const std::string& path) { 45 | reset(); 46 | 47 | std::ifstream s(path.c_str(), std::ifstream::in); 48 | scanner_->switch_streams(&s, &std::cerr); 49 | 50 | parser_->parse(); 51 | 52 | s.close(); 53 | 54 | validate(); 55 | return trees(); 56 | } 57 | 58 | TreeVector Driver::parse_string(const std::string& value) { 59 | reset(); 60 | 61 | std::stringstream s; 62 | s << value; 63 | scanner_->switch_streams(&s, &std::cerr); 64 | parser_->parse(); 65 | 66 | validate(); 67 | return trees(); 68 | } 69 | 70 | parse::TreeVector Driver::trees() const { return mTrees; } 71 | xnor::ast::VariableVector Driver::inputs() const { 72 | xnor::ast::VariableVector i; 73 | //filter out outputs 74 | std::copy_if(mInputs.begin(), mInputs.end(), std::back_inserter(i), [](xnor::ast::VariablePtr v) { return v->type() != xnor::ast::Variable::VarType::OUTPUT; }); 75 | std::sort(i.begin(), i.end(), [](xnor::ast::VariablePtr a, xnor::ast::VariablePtr b) { return a->input_index() < b->input_index(); }); 76 | return i; 77 | } 78 | 79 | void Driver::add_tree(xnor::ast::NodePtr v) { mTrees.push_back(v); } 80 | xnor::ast::VariablePtr Driver::add_input(xnor::ast::VariablePtr v) { 81 | for (auto i: mInputs) { 82 | if (i->input_index() == v->input_index() && i->type() == v->type()) 83 | return i; 84 | } 85 | mInputs.push_back(v); 86 | return v; 87 | } 88 | void Driver::validate() { 89 | //make sure we have consecutive inputs 90 | std::map inputs; 91 | for (auto i: mInputs) { 92 | if (i->type() == xnor::ast::Variable::VarType::OUTPUT) { 93 | if (i->input_index() >= mTrees.size()) 94 | throw std::runtime_error("output var index is greater than number of outputs"); 95 | continue; 96 | } 97 | 98 | unsigned int idx = i->input_index(); 99 | auto it = inputs.find(idx); 100 | if (it != inputs.end()) { 101 | if (it->second != i->type()) 102 | throw std::runtime_error("mismatched input types at index: " + std::to_string(idx + 1)); 103 | continue; 104 | } 105 | inputs[idx] = i->type(); 106 | } 107 | 108 | for (unsigned int i = 0; i < inputs.size(); i++) { 109 | if (inputs.find(i) == inputs.end()) 110 | throw std::runtime_error("missing an input variable at index " + std::to_string(i + 1)); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/parse/driver.hh: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017 Rémi Berson, modified for jit-expr 2018 by Alex Norman 2 | 3 | #ifndef DRIVER_HH_ 4 | #define DRIVER_HH_ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "ast.h" 10 | 11 | namespace parse 12 | { 13 | /// Forward declarations of classes 14 | class Parser; 15 | class Scanner; 16 | class location; 17 | 18 | typedef std::vector TreeVector; 19 | class Driver 20 | { 21 | public: 22 | Driver(); 23 | ~Driver(); 24 | 25 | TreeVector parse(); 26 | TreeVector parse_file(const std::string& path); 27 | TreeVector parse_string(const std::string& value); 28 | 29 | TreeVector trees() const; 30 | xnor::ast::VariableVector inputs() const; 31 | 32 | void reset(); 33 | 34 | protected: 35 | void add_tree(xnor::ast::NodePtr root); 36 | xnor::ast::VariablePtr add_input(xnor::ast::VariablePtr var); 37 | void validate(); 38 | 39 | private: 40 | TreeVector mTrees; 41 | xnor::ast::VariableVector mInputs; 42 | 43 | Scanner* scanner_; 44 | Parser* parser_; 45 | location* location_; 46 | int error_; 47 | 48 | 49 | /// Allows Parser and Scanner to access private attributes 50 | /// of the Driver class 51 | friend class Parser; 52 | friend class Scanner; 53 | }; 54 | } 55 | 56 | #endif /* !DRIVER_HH_ */ 57 | 58 | -------------------------------------------------------------------------------- /src/parse/parse.yy: -------------------------------------------------------------------------------- 1 | %{ /* PARSER */ 2 | 3 | #include "parser.hh" 4 | #include "scanner.hh" 5 | 6 | #define yylex driver.scanner_->yylex 7 | %} 8 | 9 | %code requires 10 | { 11 | #include 12 | #include "driver.hh" 13 | #include "location.hh" 14 | #include "position.hh" 15 | #include "ast.h" 16 | #include 17 | #include 18 | } 19 | 20 | %code provides 21 | { 22 | namespace parse 23 | { 24 | // Forward declaration of the Driver class 25 | class Driver; 26 | 27 | inline void 28 | yyerror (const char* msg) 29 | { 30 | std::cerr << msg << std::endl; 31 | } 32 | } 33 | } 34 | 35 | 36 | 37 | %require "2.4" 38 | %language "C++" 39 | %locations 40 | %defines 41 | %debug 42 | 43 | %define api.namespace {parse} 44 | %define parser_class_name {Parser} 45 | 46 | %define api.value.type variant 47 | 48 | %parse-param { Driver &driver } 49 | %lex-param { Driver &driver } 50 | %error-verbose 51 | 52 | %token FLOAT 53 | %token INT 54 | %token COMMA SEMICOLON OPEN_PAREN CLOSE_PAREN OPEN_BRACKET CLOSE_BRACKET ASSIGN QUOTE 55 | 56 | %token NEG 57 | %token ADD 58 | %token MULTIPLY 59 | %token DIVIDE 60 | %token MOD 61 | %token COMP_EQUAL 62 | %token COMP_NOT_EQUAL 63 | %token COMP_GREATER 64 | %token COMP_LESS 65 | %token COMP_GREATER_OR_EQUAL 66 | %token COMP_LESS_OR_EQUAL 67 | %token LOGICAL_OR 68 | %token LOGICAL_AND 69 | %token SHIFT_RIGHT 70 | %token SHIFT_LEFT 71 | %token BIT_AND 72 | %token BIT_OR 73 | %token BIT_XOR 74 | 75 | %token UNOP_BIT_NOT 76 | %token UNOP_LOGICAL_NOT 77 | 78 | %token STRING 79 | %token VAR VAR_DOLLAR VAR_INDEXED VAR_SYMBOL 80 | 81 | %type var 82 | %type array_op 83 | %type sample_op 84 | %type constant binary_op unary_op statement function_call assign quoted call_arg 85 | %type > call_args 86 | 87 | /* Tokens */ 88 | %token TOK_EOF 0; 89 | 90 | 91 | /* XXX NOT SURE ABOUT PRECEDENCE */ 92 | /* lowest to highest */ 93 | %right ASSIGN 94 | %left COMP_EQUAL COMP_NOT_EQUAL COMP_GREATER COMP_LESS COMP_GREATER_OR_EQUAL COMP_LESS_OR_EQUAL 95 | %left LOGICAL_OR LOGICAL_AND 96 | %left ADD NEG 97 | %left MULTIPLY DIVIDE MOD 98 | %left BIT_AND BIT_OR BIT_XOR 99 | %left SHIFT_RIGHT SHIFT_LEFT 100 | %right UNOP_BIT_NOT UNOP_LOGICAL_NOT 101 | %right UMINUS 102 | 103 | 104 | /* Entry point of grammar */ 105 | %start statements 106 | 107 | %% 108 | 109 | statements: statement { driver.add_tree($1); } 110 | | statements SEMICOLON statement { driver.add_tree($3); } 111 | | statements SEMICOLON { } 112 | ; 113 | 114 | statement: 115 | var { $$ = $1; } 116 | | constant { $$ = $1; } 117 | | binary_op { $$ = $1; } 118 | | unary_op { $$ = $1; } 119 | | function_call { $$ = $1; } 120 | | array_op { $$ = std::make_shared($1); } 121 | | sample_op { $$ = $1; } 122 | | OPEN_PAREN statement CLOSE_PAREN { $$ = $2; } 123 | | assign { $$ = $1; } 124 | | statement NEG statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::SUBTRACT, $3); } 125 | | NEG statement %prec UMINUS { $$ = std::make_shared(xnor::ast::UnaryOp::Op::NEGATE, $2); } 126 | ; 127 | 128 | assign : 129 | STRING ASSIGN statement { $$ = std::make_shared($1, $3); } 130 | | array_op ASSIGN statement { $$ = std::make_shared($1, $3); } 131 | ; 132 | 133 | var : VAR { 134 | xnor::ast::VariablePtr var = std::make_shared($1); 135 | var = driver.add_input(var); 136 | $$ = var; 137 | } 138 | ; 139 | 140 | constant : INT { 141 | auto v = std::make_shared>($1); 142 | v->output_type(xnor::ast::Node::OutputType::INT); 143 | $$ = v; 144 | } 145 | | FLOAT { $$ = std::make_shared>($1); } 146 | | STRING { $$ = std::make_shared>($1); } 147 | | VAR_DOLLAR { $$ = std::make_shared>($1); } 148 | ; 149 | 150 | quoted : QUOTE STRING QUOTE { $$ = std::make_shared($2); } 151 | | QUOTE VAR_SYMBOL QUOTE { 152 | xnor::ast::VariablePtr var = std::make_shared($2); 153 | var = driver.add_input(var); 154 | $$ = std::make_shared(var); 155 | } 156 | ; 157 | 158 | binary_op : 159 | statement ADD statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::ADD, $3); } 160 | | statement MULTIPLY statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::MULTIPLY, $3); } 161 | | statement DIVIDE statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::DIVIDE, $3); } 162 | | statement MOD statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::MOD, $3); } 163 | | statement COMP_EQUAL statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::COMP_EQUAL, $3); } 164 | | statement COMP_NOT_EQUAL statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::COMP_NOT_EQUAL, $3); } 165 | | statement COMP_GREATER statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::COMP_GREATER, $3); } 166 | | statement COMP_LESS statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::COMP_LESS, $3); } 167 | | statement COMP_GREATER_OR_EQUAL statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::COMP_GREATER_OR_EQUAL, $3); } 168 | | statement COMP_LESS_OR_EQUAL statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::COMP_LESS_OR_EQUAL, $3); } 169 | | statement LOGICAL_OR statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::LOGICAL_OR, $3); } 170 | | statement LOGICAL_AND statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::LOGICAL_AND, $3); } 171 | | statement SHIFT_RIGHT statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::SHIFT_RIGHT, $3); } 172 | | statement SHIFT_LEFT statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::SHIFT_LEFT, $3); } 173 | | statement BIT_AND statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::BIT_AND, $3); } 174 | | statement BIT_OR statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::BIT_OR, $3); } 175 | | statement BIT_XOR statement { $$ = std::make_shared($1, xnor::ast::BinaryOp::Op::BIT_XOR, $3); } 176 | ; 177 | 178 | unary_op : UNOP_LOGICAL_NOT statement { $$ = std::make_shared(xnor::ast::UnaryOp::Op::LOGICAL_NOT, $2); } 179 | | UNOP_BIT_NOT statement { $$ = std::make_shared(xnor::ast::UnaryOp::Op::BIT_NOT, $2); } 180 | ; 181 | 182 | array_op : STRING OPEN_BRACKET statement CLOSE_BRACKET { $$ = std::make_shared($1, $3); } 183 | | VAR_SYMBOL OPEN_BRACKET statement CLOSE_BRACKET { 184 | xnor::ast::VariablePtr var = std::make_shared($1); 185 | var = driver.add_input(var); 186 | $$ = std::make_shared(var, $3); 187 | } 188 | ; 189 | 190 | sample_op : VAR_INDEXED OPEN_BRACKET statement CLOSE_BRACKET { 191 | xnor::ast::VariablePtr var = std::make_shared($1); 192 | var = driver.add_input(var); 193 | $$ = std::make_shared(var, $3); 194 | } 195 | | VAR_INDEXED { 196 | //$x# -> $x#[0] 197 | //$y# -> $y#[-1] 198 | xnor::ast::VariablePtr var = std::make_shared($1); 199 | var = driver.add_input(var); 200 | auto val = std::make_shared>(var->type() == xnor::ast::Variable::VarType::OUTPUT ? -1 : 0); 201 | $$ = std::make_shared(var, val); 202 | } 203 | 204 | function_call : STRING OPEN_PAREN call_args CLOSE_PAREN { $$ = std::make_shared($1, $3); } 205 | ; 206 | 207 | call_arg : statement { $$ = $1; } 208 | | quoted { $$ = $1; } 209 | ; 210 | 211 | call_args : { } 212 | | call_arg { $$.push_back($1); } 213 | | call_args COMMA call_arg { 214 | $1.push_back($3); 215 | $$ = $1; 216 | } 217 | ; 218 | 219 | %% 220 | 221 | #include 222 | 223 | namespace parse 224 | { 225 | void Parser::error(const location&, const std::string& m) { 226 | driver.error_ = (driver.error_ == 127 ? 127 : driver.error_ + 1); 227 | std::stringstream s; 228 | s << *driver.location_; 229 | throw std::runtime_error(s.str() + ": " + m); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/parse/scan.ll: -------------------------------------------------------------------------------- 1 | %{ /* -*- C++ -*- */ 2 | /* Copyright (c) 2017 Rémi Berson, modified for jit-expr 2018 by Alex Norman */ 3 | 4 | #include "parser.hh" 5 | #include "scanner.hh" 6 | #include "driver.hh" 7 | 8 | #include 9 | #include 10 | 11 | /* Defines some macros to update locations */ 12 | 13 | 14 | #define STEP() \ 15 | do { \ 16 | driver.location_->step (); \ 17 | } while (0) 18 | 19 | 20 | #define COL(Col) \ 21 | driver.location_->columns (Col) 22 | 23 | #define LINE(Line) \ 24 | do{ \ 25 | driver.location_->lines (Line); \ 26 | } while (0) 27 | 28 | #define YY_USER_ACTION \ 29 | COL(yyleng); 30 | 31 | 32 | typedef parse::Parser::token token; 33 | typedef parse::Parser::token_type token_type; 34 | 35 | #define yyterminate() return token::TOK_EOF 36 | 37 | %} 38 | 39 | %option debug 40 | %option c++ 41 | %option noyywrap 42 | %option never-interactive 43 | %option yylineno 44 | %option nounput 45 | %option batch 46 | %option prefix="parse" 47 | 48 | /* 49 | %option stack 50 | */ 51 | 52 | /* Abbreviations. */ 53 | 54 | blank [ \t]+ 55 | eol [\n\r]+ 56 | 57 | %% 58 | 59 | /* The rules. */ 60 | %{ 61 | STEP(); 62 | %} 63 | 64 | [ \t\n] ; 65 | eol ; 66 | 67 | \$[fivFIV][0-9]+ { yylval->build() = std::string(yytext); return token::VAR; } 68 | \$[sS][0-9]* { yylval->build() = std::string(yytext); return token::VAR_SYMBOL; } 69 | \$[xyXY][0-9]* { yylval->build() = std::string(yytext); return token::VAR_INDEXED; } 70 | \\\$[0-9]+ { yylval->build() = std::string(yytext); return token::VAR_DOLLAR; } 71 | 72 | -?[0-9]+\.[0-9]* { yylval->build() = std::stof(yytext); return token::FLOAT; } 73 | -?[0-9]+ { yylval->build() = std::stoi(yytext); return token::INT; } 74 | [a-zA-Z]([_a-zA-Z0-9]|\\\$[0-9]+)* { yylval->build() = std::string(yytext); return token::STRING; } 75 | 76 | "[" { return token::OPEN_BRACKET; } 77 | "]" { return token::CLOSE_BRACKET; } 78 | "(" { return token::OPEN_PAREN; } 79 | ")" { return token::CLOSE_PAREN; } 80 | "=" { return token::ASSIGN; } 81 | "\+" { return token::ADD; } 82 | "-" { return token::NEG; } 83 | "\*" { return token::MULTIPLY; } 84 | "\/" { return token::DIVIDE; } 85 | "%" { return token::MOD; } 86 | "==" { return token::COMP_EQUAL; } 87 | "!=" { return token::COMP_NOT_EQUAL; } 88 | ">" { return token::COMP_GREATER; } 89 | "<" { return token::COMP_LESS; } 90 | ">=" { return token::COMP_GREATER_OR_EQUAL; } 91 | "<=" { return token::COMP_LESS_OR_EQUAL; } 92 | "||" { return token::LOGICAL_OR; } 93 | "&&" { return token::LOGICAL_AND; } 94 | ">>" { return token::SHIFT_RIGHT; } 95 | "<<" { return token::SHIFT_LEFT; } 96 | 97 | "&" { return token::BIT_AND; } 98 | "|" { return token::BIT_OR; } 99 | "^" { return token::BIT_XOR; } 100 | 101 | "~" { return token::UNOP_BIT_NOT; } 102 | "!" { return token::UNOP_LOGICAL_NOT; } 103 | 104 | "\"" { return token::QUOTE; } 105 | "\\," { return token::COMMA; } 106 | "\\;" { return token::SEMICOLON; } 107 | 108 | . { 109 | std::stringstream s; 110 | s << *driver.location_ << " Unexpected token : " << *yytext; 111 | driver.error_ = (driver.error_ == 127 ? 127 : driver.error_ + 1); 112 | STEP (); 113 | throw std::runtime_error(s.str()); 114 | } 115 | 116 | %% 117 | 118 | 119 | /* 120 | 121 | CUSTOM C++ CODE 122 | 123 | */ 124 | 125 | namespace parse 126 | { 127 | 128 | Scanner::Scanner() 129 | : parseFlexLexer() 130 | { 131 | } 132 | 133 | Scanner::~Scanner() 134 | { 135 | } 136 | 137 | void Scanner::set_debug(bool b) 138 | { 139 | yy_flex_debug = b; 140 | } 141 | } 142 | 143 | #ifdef yylex 144 | # undef yylex 145 | #endif 146 | 147 | int parseFlexLexer::yylex() 148 | { 149 | std::cerr << "call parsepitFlexLexer::yylex()!" << std::endl; 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /src/parse/scanner.hh: -------------------------------------------------------------------------------- 1 | //Copyright (c) 2017 Rémi Berson, modified for jit-expr 2018 by Alex Norman 2 | 3 | #ifndef SCANPIT_HH_ 4 | # define SCANPIT_HH_ 5 | 6 | # include "parser.hh" 7 | 8 | 9 | # ifndef YY_DECL 10 | # define YY_DECL parse::Parser::token_type \ 11 | parse::Scanner::yylex(parse::Parser::semantic_type* yylval, \ 12 | parse::Parser::location_type*, \ 13 | parse::Driver& driver) 14 | # endif 15 | 16 | 17 | # ifndef __FLEX_LEXER_H 18 | # define yyFlexLexer parseFlexLexer 19 | # include 20 | # undef yyFlexLexer 21 | # endif 22 | 23 | 24 | namespace parse 25 | { 26 | class Scanner : public parseFlexLexer 27 | { 28 | public: 29 | Scanner(); 30 | 31 | virtual ~Scanner(); 32 | 33 | virtual Parser::token_type yylex( 34 | Parser::semantic_type* yylval, 35 | Parser::location_type* l, 36 | Driver& driver); 37 | 38 | void set_debug(bool b); 39 | }; 40 | } 41 | 42 | #endif // SCANPIT_HH_ 43 | -------------------------------------------------------------------------------- /src/print.cc: -------------------------------------------------------------------------------- 1 | //Copyright (c) Alex Norman, 2018, see LICENSE-xnor 2 | 3 | #include "print.h" 4 | #include "ast.h" 5 | #include 6 | 7 | using std::cout; 8 | using std::endl; 9 | 10 | namespace xnor { 11 | using namespace xnor::ast; 12 | 13 | void AstPrintVisitor::visit(xnor::ast::Variable* v){ 14 | std::string t; 15 | switch (v->type()) { 16 | case Variable::VarType::FLOAT: 17 | t = "f"; break; 18 | case Variable::VarType::INT: 19 | t = "i"; break; 20 | case Variable::VarType::SYMBOL: 21 | t = "s"; break; 22 | case Variable::VarType::VECTOR: 23 | t = "v"; break; 24 | case Variable::VarType::INPUT: 25 | t = "x"; break; 26 | case Variable::VarType::OUTPUT: 27 | t = "y"; break; 28 | } 29 | print("var $" + t + std::to_string(v->input_index())); 30 | } 31 | 32 | void AstPrintVisitor::visit(xnor::ast::Value* v){ 33 | print("const: " + std::to_string(v->value())); 34 | } 35 | 36 | void AstPrintVisitor::visit(xnor::ast::Value* v){ 37 | print("const: " + std::to_string(v->value())); 38 | } 39 | 40 | void AstPrintVisitor::visit(xnor::ast::Value* v){ 41 | print("value: " + v->value()); 42 | } 43 | 44 | void AstPrintVisitor::visit(xnor::ast::Quoted* v){ 45 | if (v->value().size()) { 46 | print("quoted: " + v->value()); 47 | return; 48 | } 49 | print("quoted with children:"); 50 | print_child(v->variable()); 51 | } 52 | 53 | void AstPrintVisitor::visit(xnor::ast::UnaryOp* v){ 54 | std::string op; 55 | switch (v->op()) { 56 | case xnor::ast::UnaryOp::Op::BIT_NOT: 57 | op = "~"; break; 58 | case xnor::ast::UnaryOp::Op::LOGICAL_NOT: 59 | op = "!"; break; 60 | case xnor::ast::UnaryOp::Op::NEGATE: 61 | op = "-"; break; 62 | } 63 | 64 | print("UnaryOp: " + op + " child:"); 65 | print_child(v->node()); 66 | } 67 | 68 | void AstPrintVisitor::visit(xnor::ast::BinaryOp* v){ 69 | std::string op; 70 | switch (v->op()) { 71 | case xnor::ast::BinaryOp::Op::ADD: 72 | op = "+"; break; 73 | case xnor::ast::BinaryOp::Op::SUBTRACT: 74 | op = "-"; break; 75 | case xnor::ast::BinaryOp::Op::MULTIPLY: 76 | op = "*"; break; 77 | case xnor::ast::BinaryOp::Op::DIVIDE: 78 | op = "/"; break; 79 | case xnor::ast::BinaryOp::Op::MOD: 80 | op = "%"; break; 81 | case xnor::ast::BinaryOp::Op::SHIFT_LEFT: 82 | op = "<<"; break; 83 | case xnor::ast::BinaryOp::Op::SHIFT_RIGHT: 84 | op = ">>"; break; 85 | case xnor::ast::BinaryOp::Op::COMP_EQUAL: 86 | op = "=="; break; 87 | case xnor::ast::BinaryOp::Op::COMP_NOT_EQUAL: 88 | op = "!="; break; 89 | case xnor::ast::BinaryOp::Op::COMP_GREATER: 90 | op = ">"; break; 91 | case xnor::ast::BinaryOp::Op::COMP_LESS: 92 | op = "<"; break; 93 | case xnor::ast::BinaryOp::Op::COMP_GREATER_OR_EQUAL: 94 | op = ">="; break; 95 | case xnor::ast::BinaryOp::Op::COMP_LESS_OR_EQUAL: 96 | op = "<="; break; 97 | case xnor::ast::BinaryOp::Op::LOGICAL_OR: 98 | op = "||"; break; 99 | case xnor::ast::BinaryOp::Op::LOGICAL_AND: 100 | op = "&&"; break; 101 | case xnor::ast::BinaryOp::Op::BIT_AND: 102 | op = "~"; break; 103 | case xnor::ast::BinaryOp::Op::BIT_OR: 104 | op = "|"; break; 105 | case xnor::ast::BinaryOp::Op::BIT_XOR: 106 | op = "^"; break; 107 | } 108 | print("BinaryOp: " + op); 109 | print_child(v->left()); 110 | print_child(v->right()); 111 | } 112 | 113 | void AstPrintVisitor::visit(xnor::ast::FunctionCall* v){ 114 | print("FunctionCall: " + v->name()); 115 | for (auto c: v->args()) 116 | print_child(c); 117 | } 118 | 119 | void AstPrintVisitor::visit(xnor::ast::SampleAccess* v) { 120 | print("SampleAccess: "); 121 | print_child(v->source()); 122 | print_child(v->index_node()); 123 | } 124 | 125 | void AstPrintVisitor::visit(xnor::ast::ArrayAccess* v){ 126 | if (v->name().size()) { 127 | print("ArrayAccess: " + v->name()); 128 | } else { 129 | print("ArrayAccess name: "); 130 | print_child(v->name_var()); 131 | } 132 | print("index: "); 133 | print_child(v->index_node()); 134 | } 135 | 136 | void AstPrintVisitor::visit(xnor::ast::ValueAssignment* v){ 137 | print("ValueAssignment " + v->value_name()); 138 | print_child(v->value_node()); 139 | } 140 | 141 | void AstPrintVisitor::visit(xnor::ast::ArrayAssignment* v){ 142 | print("ArrayAssignment :"); 143 | print_child(v->array()); 144 | print_child(v->value_node()); 145 | } 146 | 147 | void AstPrintVisitor::visit(xnor::ast::Deref* v) { 148 | print("Deref: "); 149 | print_child(v->value_node()); 150 | } 151 | 152 | void AstPrintVisitor::print(const std::string& v) { 153 | for (unsigned int i = 0; i < mDepth; i++) 154 | cout << " "; 155 | cout << v << endl; 156 | } 157 | 158 | void AstPrintVisitor::print_child(xnor::ast::NodePtr c) { 159 | mDepth++; 160 | c->accept(this); 161 | mDepth--; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/print.h: -------------------------------------------------------------------------------- 1 | //Copyright (c) Alex Norman, 2018, see LICENSE-xnor 2 | 3 | #pragma once 4 | 5 | #include "ast.h" 6 | 7 | namespace xnor { 8 | class AstPrintVisitor : public xnor::ast::Visitor { 9 | public: 10 | virtual void visit(xnor::ast::Variable* v); 11 | virtual void visit(xnor::ast::Value* v); 12 | virtual void visit(xnor::ast::Value* v); 13 | virtual void visit(xnor::ast::Value* v); 14 | virtual void visit(xnor::ast::Quoted* v); 15 | virtual void visit(xnor::ast::UnaryOp* v); 16 | virtual void visit(xnor::ast::BinaryOp* v); 17 | virtual void visit(xnor::ast::FunctionCall* v); 18 | virtual void visit(xnor::ast::SampleAccess* v); 19 | virtual void visit(xnor::ast::ArrayAccess* v); 20 | virtual void visit(xnor::ast::ValueAssignment* v); 21 | virtual void visit(xnor::ast::ArrayAssignment* v); 22 | virtual void visit(xnor::ast::Deref* v); 23 | 24 | private: 25 | unsigned int mDepth = 0; 26 | void print(const std::string& v); 27 | void print_child(xnor::ast::NodePtr c); 28 | }; 29 | } 30 | --------------------------------------------------------------------------------