├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── doc └── overview.edoc ├── include └── effi.hrl ├── rebar.config ├── src ├── effi.app.src ├── effi.erl ├── effi_awk.erl ├── effi_bash.erl ├── effi_elixir.erl ├── effi_erlang.erl ├── effi_gnuplot.erl ├── effi_java.erl ├── effi_javascript.erl ├── effi_matlab.erl ├── effi_octave.erl ├── effi_perl.erl ├── effi_python.erl ├── effi_r.erl ├── effi_racket.erl └── effi_wal.erl └── test └── effi_wal_test.erl /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: build 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | jobs: 11 | ci: 12 | name: Run checks and tests over ${{matrix.otp}} and ${{matrix.os}} 13 | runs-on: ${{matrix.os}} 14 | strategy: 15 | matrix: 16 | otp: ['25.0', '26.0', '27.0'] 17 | os: [ubuntu-latest] 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: erlef/setup-beam@v1 21 | with: 22 | otp-version: ${{matrix.otp}} 23 | rebar3-version: '3.24.0' 24 | - run: rebar3 compile 25 | - run: rebar3 xref 26 | - run: rebar3 efmt -c 27 | - run: rebar3 eunit 28 | - run: rebar3 dialyzer 29 | - run: rebar3 edoc 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | deps 3 | *.o 4 | *.beam 5 | *.plt 6 | erl_crash.dump 7 | ebin 8 | rel/example_project 9 | .concrete/DEV_MODE 10 | .rebar 11 | *~ 12 | _build 13 | rebar.lock 14 | rebar3.crashdump 15 | doc/* 16 | !doc/overview.edoc 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - wget https://s3.amazonaws.com/rebar3/rebar3 && chmod +x rebar3 3 | language: erlang 4 | script: ./rebar3 eunit 5 | otp_release: 6 | - 23.0 7 | - 22.3 8 | - 22.2 9 | - 22.1 10 | - 22.0 11 | - 21.3 12 | - 21.2 13 | - 21.1 14 | - 21.0 15 | - 20.3 16 | # - 20.2 17 | - 20.1 18 | - 20.0 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Effi 2 | ###### Erlang foreign function interface 3 | 4 | [![hex.pm](https://img.shields.io/hexpm/v/effi.svg?style=flat-square)](https://hex.pm/packages/effi) 5 | 6 | The Erlang foreign function interface (Effi) is a way to run code in arbitrary foreign languages, e.g., Python or Octave from a native (Erlang) environment. 7 | 8 | Herein, the foreign code is executed in an environment that binds a defined set of input variables to values defined from the outside. After completion of the foreign code, a set of output variables is expected to be bound to values. 9 | 10 | The foreign code snippet along with input variable bindings is given to effi in the form of a key-value map in a format that can easily be decoded/encoded using the [jsone](https://github.com/sile/jsone) JSON serialization library. Accordingly, effi comes with a command-line application that reads and writes JSON files. 11 | 12 | ## Supported Languages 13 | 14 | The following programming languages are supported by effi: 15 | 16 | - [Awk](https://www.gnu.org/software/gawk/manual/gawk.html) 17 | - [Bash](https://www.gnu.org/software/bash/) 18 | - [Elixir](https://elixir-lang.org/) 19 | - [Erlang](http://www.erlang.org/) 20 | - [gnuplot](http://www.gnuplot.info/) 21 | - [Java](https://www.java.com) 22 | - [Javascript](https://www.w3schools.com/js/) 23 | - [Matlab](https://www.mathworks.com/products/matlab.html) 24 | - [Octave](https://www.gnu.org/software/octave/) 25 | - [Perl](https://www.perl.org/) 26 | - [Python](https://www.python.org/) 27 | - [R](https://www.r-project.org/) 28 | - [Racket](http://www.racket-lang.org/) 29 | - [Wal](https://wal-lang.org/) 30 | 31 | ## Integration and Build 32 | 33 | ### Adding Effi to a Project 34 | 35 | Although Effi can be imported also directly from GitHub, we recommend adding a dependency via [hex.pm](https://hex.pm). Here, we show how this can be done using the build tools [rebar3](https://www.rebar3.org) or mix. 36 | 37 | 38 | #### rebar3 39 | 40 | To integrate Effi into a rebar3-managed project change the `deps` entry in your application's `rebar.config` file to include the tuple `{effi, "0.1.8"}`. 41 | 42 | ```erlang 43 | {deps, [{effi, "0.1.8"}]}. 44 | ``` 45 | 46 | 47 | #### mix 48 | 49 | To integrate effi into a mix-managed project include the following 50 | 51 | ```elixir 52 | {:effi, "~> 0.1.8"} 53 | ``` 54 | 55 | ### Compiling 56 | 57 | Having rebar3 available on your system, compile as an Erlang project by entering 58 | 59 | rebar3 compile 60 | 61 | If you want to drive the project from the command line please compile it by entering 62 | 63 | rebar3 escriptize 64 | 65 | ### Building the Documentation 66 | 67 | rebar3 edoc 68 | 69 | ## Command Line Synopsis 70 | 71 | Compiling Effi using `escriptize` creates an Erlang script file `effi` whcih allows starting it via the command line. 72 | 73 | To display a help text enter 74 | 75 | ./effi --help 76 | 77 | This shows the command line synopsis, which looks like the following: 78 | 79 | ._,,,, ,,_,=_ 80 | W `_@__#__ The Erlang Foreign Function Interface (Effi) allows the 81 | @P+# F @F @ execution of functions defined in different programming 82 | _W y @ # qF languages (e.g., Bash, Python, or R) by specifying the 83 | ^^^^^ P qF ` function's arguments, body and output values. 84 | 85 | Copyright 2015 Jorgen Brandt 86 | 87 | Usage: effi [-v] [-h] [-d []] [-i ] [-o ] 88 | 89 | -v, --version Show effi version. 90 | -h, --help Show command line options. 91 | -d, --dir Working directory in which to look for input data and 92 | run the request. [default: .] 93 | -i, --input_file Input file holding the effi request (must be 94 | specified). 95 | -o, --output_file Output file into which to write the effi reply (must 96 | be specified). 97 | 98 | 99 | The input_file and output_file arguments must be specified. 100 | 101 | To start Effi from the command line consuming the request file `effi_request.json` and let it produce the reply file `effi_reply.json` enter 102 | 103 | ./effi -i effi_request.json -o effi_reply.json 104 | 105 | The format of the request and reply is described below. 106 | 107 | ## Erlang API 108 | 109 | Effi can be driven from an Erlang API by using the function `effi:handle_request/2`. Given an Erlang hash map using atoms as keys and binaries as values bound in the variable `EffiRequest` you can start effi by entering 110 | 111 | ```erlang 112 | EffiRequest = #{ ... }. 113 | Dir = "./". 114 | effi:handle_request( EffiRequest, Dir ). 115 | ``` 116 | 117 | Effi starts evaluating the request, expecting input data in and writing output data to the working directory given in the variable `Dir`. 118 | 119 | The `handle_request/2` function returns a reply-map summarizing the result of the computation. 120 | 121 | ## Effi JSON Exchange Format 122 | 123 | Effi is driven by task instantiations or applications. Running effi on an application results in a reply. Here, we describe the application format and the reply format. 124 | 125 | ### Example 126 | 127 | Below is an example for an Effi request (an application): 128 | 129 | ```json 130 | { "app_id": "1234", 131 | "lambda": { "lambda_name": "bowtie2-build", 132 | "arg_type_lst": [{ "arg_name": "fa", 133 | "arg_type": "File", 134 | "is_list": false }], 135 | "ret_type_lst": [{ "arg_name": "idx", 136 | "arg_type": "File", 137 | "is_list": false }], 138 | "lang": "Bash", 139 | "script": "bowtie2-build $fa bt2idx\nidx=idx.tar\ntar cf $idx --remove-files bt2idx.*\n" }, 140 | "arg_bind_lst": [{ "arg_name": "fa", 141 | "value": "chr22.fa" }] } 142 | ``` 143 | 144 | The following is an example for an Effi reply: 145 | 146 | ```json 147 | { "app_id": "1234", 148 | "result": { "status": "ok", 149 | "node": "cf_worker@x240", 150 | "stat": { "run": { "t_start": "1523007609917834743", 151 | "duration": "30391761645" } }, 152 | "ret_bind_lst": [{ "arg_name": "idx", 153 | "value": "idx.tar" }] } } 154 | ``` 155 | 156 | The start time `tstart` is given in nanoseconds from 1970-01-01 and also the the wall-clock running time `duration` is given in nanoseconds. So the example ran `bowtie2-build` for about 30.4 seconds. The `node` field identifies the Erlang node name of the worker instance that ran the task. 157 | 158 | ### Request Format 159 | 160 | The Effi request (application) format is what is consumed. An application `App` has the following form: 161 | 162 | App ::= { "app_id": S, 163 | "lambda": Lambda, 164 | "arg_bind_lst": [Bind, ...] } 165 | 166 | The application's lambda expression `Lambda` has the following form: 167 | 168 | Lambda ::= { "lambda_name": S, 169 | "arg_type_lst": [TArg, ...], 170 | "ret_type_lst": [TArg, ...], 171 | "lang": Lang, 172 | "script": S } 173 | 174 | A lambda expression's `arg_type_lst` pair lists specifications for the input parameters while the `ret_type_lst` pair lists specifications for the output parameters of a lambda. An input or output parameter specification `TArg` has the following form: 175 | 176 | TArg ::= { "arg_name": S, 177 | "arg_type": Type, 178 | "is_list": B } 179 | 180 | The `arg_type` pair provides the base type of the argument. The base type `Type` has the following form: 181 | 182 | Type ::= "Bool" 183 | | "Str" 184 | | "File" 185 | 186 | 187 | The `lang` pair provides the programming language in which the script is written. The language `Lang` has the following form: 188 | 189 | Lang ::= "Bash" 190 | | "Erlang" 191 | | "Java" 192 | | "Matlab" 193 | | "Octave" 194 | | "Perl" 195 | | "Python" 196 | | "R" 197 | | "Racket" 198 | 199 | A lambda expression contains a list of argument bindings `Bind` of the following form: 200 | 201 | Bind ::= { "arg_name": S, "value": S} 202 | | { "arg_name": S, "value": [S, ...] } 203 | 204 | B ::= true 205 | | false 206 | 207 | S ::= "..." 208 | 209 | ### Reply Format 210 | 211 | The Effi reply format is what is produced. 212 | 213 | Reply ::= { "app_id": S, 214 | "result": Result } 215 | 216 | Result ::= { "status": "ok", 217 | "node": S, 218 | "stat": { "run": { "t_start": S, "duration": S } }, 219 | "ret_bind_lst": [Bind, ...] } 220 | | { "status": "error", 221 | "node": S 222 | "stage": "run", 223 | "extended_script": S, 224 | "output": S } 225 | | { "status": "error", 226 | "node": S 227 | "stage": "stagein", 228 | "file_lst": [S, ...] } 229 | | { "status": "error", 230 | "node": S 231 | "stage": "stageout", 232 | "file_lst": [S, ...] } 233 | 234 | 235 | 236 | 237 | 238 | 239 | ## System Requirements 240 | 241 | - [Erlang](https://www.erlang.org) OTP 18.0 or higher 242 | - [Rebar3](https://www.rebar3.org) 3.0.0 or higher 243 | 244 | ## Resources 245 | 246 | - [joergen7/cf_worker](https://github.com/joergen7/cf_worker). A Cuneiform worker implementation. 247 | - [joergen7/cuneiform](https://github.com/joergen7/cuneiform). A functional language for large-scale data analysis. 248 | 249 | 250 | ## Authors 251 | 252 | - Jörgen Brandt ([@joergen7](https://github.com/joergen7/)) [joergen.brandt@onlinehome.de](mailto:joergen.brandt@onlinehome.de) 253 | - Carl Witt ([@carlwitt](https://github.com/carlwitt)) 254 | - [@bharendt](https://github.com/bharendt) 255 | 256 | ## License 257 | 258 | [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.html) 259 | -------------------------------------------------------------------------------- /doc/overview.edoc: -------------------------------------------------------------------------------- 1 | ** overview file for the application effi ** 2 | 3 | @author Jörgen Brandt 4 | @copyright 2015 5 | @title effi Documentation 6 | @doc 7 | 8 | == Introduction and Goals == 9 | 10 | The state of the art for describing computation is via programs or scripts. A script is a textual description of computation. As such, it is a reproducible artifact that can be executed anywhere by anyone. A script can inspect or alter its environment or write to the standard output. Thus, anything a script consumes or produces needs to be encoded either in the script's environment or in its output. The environment, i.e., command line arguments, the file system, environment variables, etc., is a very versatile way to exchange information between a script and its caller. However, this versatility leaves us with many ways to accomplish the same task and different programming languages, like Bash, Python, R, etc., lean towards different ways to interact with the environment. 11 | 12 | The effi tool allows a user to box computation. Herein, boxing computation means to describe a piece of computation in a textual format. This description can be sent to a remote computer to execute it. Just like a docker image describes the state of a file system in a reproducible way, an effi file describes a piece of computation in a reproducible way. Since it is a reproducible artifact the computation can be executed anywhere by anyone. 13 | 14 | === Requirements Overview === 15 | 16 | === Quality Goals === 17 | 18 | == Architecture Constraints == 19 | 20 | == System Scope and Context == 21 | 22 | == Building Block View == 23 | 24 | == Runtime View == 25 | 26 | == Deployment View == 27 | 28 | == Cross-Cutting Concepts == 29 | 30 | == Quality Requirements == 31 | 32 | == Risks and Technical Debts == 33 | 34 | 35 | @end -------------------------------------------------------------------------------- /include/effi.hrl: -------------------------------------------------------------------------------- 1 | -define(MSG, ""). 2 | -define(EOT, ""). 3 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | 3 | {deps, [{getopt, "1.0.3"}, 4 | {jsone, "1.9.0"}]}. 5 | 6 | {escript_incl_apps, [getopt, jsone]}. 7 | 8 | {profiles, 9 | [{test, [{cover_enabled, true}]}]}. 10 | 11 | {dialyzer, [{warnings, [unmatched_returns, 12 | error_handling]}, 13 | {plt_extra_apps, [getopt, jsone]}]}. 14 | 15 | {xref_checks, [undefined_function_calls, 16 | undefined_functions, 17 | locals_not_used, 18 | deprecated_function_calls, 19 | deprecated_functions]}. 20 | 21 | {project_plugins, [rebar3_efmt]}. 22 | -------------------------------------------------------------------------------- /src/effi.app.src: -------------------------------------------------------------------------------- 1 | {application, effi, 2 | [{description, "Erlang foreign function interface"}, 3 | {vsn, "0.1.8"}, 4 | {modules, [effi, 5 | effi_awk, 6 | effi_bash, 7 | effi_elixir, 8 | effi_erlang, 9 | effi_fs, 10 | effi_gnuplot, 11 | effi_java, 12 | effi_javascript, 13 | effi_matlab, 14 | effi_octave, 15 | effi_python, 16 | effi_r, 17 | effi_racket]}, 18 | {registered, []}, 19 | {applications, [kernel, 20 | stdlib]}, 21 | {env, []}, 22 | {maintainers, ["Jorgen Brandt"]}, 23 | {licenses, ["Apache 2.0"]}, 24 | {links, [{"GitHub", "https://github.com/joergen7/effi/"}]}]}. 25 | -------------------------------------------------------------------------------- /src/effi.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% @doc The standalone application entry point is {@link main/1}. 24 | %% The create_port callback defined here is an abstract way to execute child 25 | %% processes in foreign languages. 26 | %% There are two foreign language interfaces, both implementing this callback, 27 | %% {@link effi_script} (e.g., Perl, Python) and {@link effi_interact} (e.g., 28 | %% Bash, R). 29 | %% 30 | %% @end 31 | %% ------------------------------------------------------------------- 32 | 33 | -module(effi). 34 | 35 | %%==================================================================== 36 | %% Exports 37 | %%==================================================================== 38 | 39 | % handle_request function 40 | -export([handle_request/2]). 41 | 42 | % helper functions for implementing effi modules 43 | -export([get_type_info/2, create_port/2, listen_port/1]). 44 | 45 | % escript main functíon 46 | -export([main/1]). 47 | 48 | %%==================================================================== 49 | %% Includes 50 | %%==================================================================== 51 | 52 | -include("effi.hrl"). 53 | 54 | %%==================================================================== 55 | %% Definitions 56 | %%==================================================================== 57 | 58 | -define(BUF_SIZE, 1024). 59 | 60 | %%==================================================================== 61 | %% Callbacks 62 | %%==================================================================== 63 | 64 | 65 | -callback bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> 66 | binary(). 67 | 68 | -callback bind_singleton_string(ArgName :: binary(), Value :: binary()) -> 69 | binary(). 70 | 71 | -callback bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 72 | binary(). 73 | 74 | -callback bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 75 | binary(). 76 | 77 | -callback echo_singleton_boolean(ArgName :: binary()) -> 78 | binary(). 79 | 80 | -callback echo_singleton_string(ArgName :: binary()) -> 81 | binary(). 82 | 83 | -callback echo_boolean_list(ArgName :: binary()) -> 84 | binary(). 85 | 86 | -callback echo_string_list(ArgName :: binary()) -> 87 | binary(). 88 | 89 | -callback prefix() -> 90 | binary(). 91 | 92 | % Some programming languages return an exit code 0 even if an exception 93 | % terminated the program. As an extra safety measure, effi expects a redundant 94 | % end-of-transmission message and assumes a failure, if it is not received. 95 | -callback end_of_transmission() -> 96 | binary(). 97 | 98 | -callback suffix() -> 99 | binary(). 100 | 101 | -callback process_script(Script :: binary()) -> 102 | binary(). 103 | 104 | % Returns either ok or error followed by standard/error output binary. If the 105 | % run was successful also the return binding list is packaged in the return 106 | % value. 107 | -callback run_extended_script(ExtendedScript :: binary(), 108 | Dir :: string(), 109 | RunInfo :: _) -> 110 | {ok, binary(), [#{atom() => binary()}]} | 111 | {error, binary()}. 112 | 113 | -callback get_run_info(Request :: #{atom() => _}) -> _. 114 | 115 | %%==================================================================== 116 | %% Escript main function 117 | %%==================================================================== 118 | 119 | %% @doc Parses command line arguments and processes the request file. 120 | 121 | -spec main(ArgList :: [string()]) -> ok. 122 | 123 | main(CmdLine) -> 124 | 125 | ok = application:start(effi), 126 | 127 | try 128 | 129 | case getopt:parse(get_optspec_lst(), CmdLine) of 130 | 131 | {error, Reason} -> error(Reason); 132 | 133 | {ok, {OptLst, []}} -> 134 | 135 | % break if version needs to be displayed 136 | case lists:member(version, OptLst) of 137 | true -> throw(version); 138 | false -> ok 139 | end, 140 | 141 | % break if help needs to be displayed 142 | case lists:member(help, OptLst) of 143 | true -> throw(help); 144 | false -> ok 145 | end, 146 | 147 | % extract working directory 148 | {dir, Dir} = lists:keyfind(dir, 1, OptLst), 149 | 150 | % extract request input file 151 | InputFile = 152 | case lists:keyfind(input_file, 1, OptLst) of 153 | false -> throw(help); 154 | {input_file, I} -> I 155 | end, 156 | 157 | % extract reply output file 158 | OutputFile = 159 | case lists:keyfind(output_file, 1, OptLst) of 160 | false -> throw(help); 161 | {output_file, O} -> O 162 | end, 163 | 164 | % read script from file 165 | B = 166 | case file:read_file(InputFile) of 167 | {error, R1} -> error({R1, InputFile}); 168 | {ok, X} -> X 169 | end, 170 | 171 | % parse request 172 | Request = jsone:decode(B, [{keys, atom}]), 173 | 174 | % handle request 175 | Reply = handle_request(Request, Dir), 176 | 177 | % write reply output file 178 | case file:write_file(OutputFile, jsone:encode(Reply)) of 179 | {error, R2} -> error({R2, OutputFile}); 180 | ok -> ok 181 | end 182 | 183 | end 184 | 185 | catch 186 | throw:version -> print_version(); 187 | throw:help -> print_help() 188 | end. 189 | 190 | 191 | %%==================================================================== 192 | %% API functions 193 | %%==================================================================== 194 | 195 | 196 | %% @doc Parses a request input file, processes it, and writes the reply output 197 | %% file. 198 | -spec handle_request(Request, Dir) -> #{atom() => _} 199 | when Request :: #{atom() => _}, 200 | Dir :: string(). 201 | 202 | handle_request(Request, Dir) -> 203 | 204 | #{ 205 | app_id := AppId, 206 | lambda := Lambda, 207 | arg_bind_lst := ArgBindLst 208 | } = Request, 209 | 210 | #{ 211 | arg_type_lst := ArgTypeLst, 212 | ret_type_lst := RetTypeLst, 213 | script := Script, 214 | lang := Lang 215 | } = Lambda, 216 | 217 | % determine language module from lambda 218 | LangMod = get_lang_mod(Lang), 219 | 220 | % compute extended script 221 | ExtendedScript = get_extended_script(LangMod, 222 | ArgTypeLst, 223 | RetTypeLst, 224 | Script, 225 | ArgBindLst), 226 | 227 | % extract extra info 228 | RunInfo = LangMod:get_run_info(Request), 229 | 230 | % determine start time 231 | TStart = os:system_time(), 232 | 233 | % run extended script 234 | Result = 235 | case LangMod:run_extended_script(ExtendedScript, Dir, RunInfo) of 236 | 237 | {ok, _Output, RetBindLst} -> 238 | 239 | % determine duration 240 | Duration = os:system_time() - TStart, 241 | 242 | % construct statistics field 243 | RunStat = #{ 244 | t_start => integer_to_binary(TStart), 245 | duration => integer_to_binary(Duration) 246 | }, 247 | 248 | #{ 249 | status => <<"ok">>, 250 | node => atom_to_binary(node(), utf8), 251 | stat => #{run => RunStat}, 252 | ret_bind_lst => RetBindLst 253 | }; 254 | 255 | {error, Output} -> 256 | #{ 257 | status => <<"error">>, 258 | node => atom_to_binary(node(), utf8), 259 | stage => <<"run">>, 260 | extended_script => ExtendedScript, 261 | output => Output 262 | } 263 | 264 | end, 265 | 266 | % create reply data structure 267 | #{ 268 | app_id => AppId, 269 | result => Result 270 | }. 271 | 272 | 273 | -spec get_type_info(ArgName, TypeLst) -> #{atom() => _} 274 | when ArgName :: binary(), 275 | TypeLst :: [#{atom() => _}]. 276 | 277 | get_type_info(_ArgName, []) -> 278 | error(type_undefined); 279 | 280 | get_type_info(ArgName, [H = #{arg_name := N} | T]) 281 | when is_binary(ArgName), 282 | is_binary(N), 283 | is_list(T) -> 284 | 285 | case N of 286 | ArgName -> H; 287 | _ -> get_type_info(ArgName, T) 288 | end. 289 | 290 | 291 | -spec create_port(Call, Dir) -> port() 292 | when Call :: string(), 293 | Dir :: string(). 294 | 295 | create_port(Call, Dir) 296 | when is_list(Call), 297 | is_list(Dir) -> 298 | 299 | open_port({spawn, Call}, 300 | [exit_status, 301 | stderr_to_stdout, 302 | binary, 303 | {cd, Dir}, 304 | {line, ?BUF_SIZE}]). 305 | 306 | 307 | -spec listen_port(Port :: port()) -> 308 | {ok, binary(), [#{atom() => binary()}]} | 309 | {error, binary()}. 310 | 311 | listen_port(Port) -> 312 | listen_port(Port, <<>>, <<>>, [], false). 313 | 314 | 315 | %%==================================================================== 316 | %% Internal functions 317 | %%==================================================================== 318 | 319 | 320 | %% opt_spec_list/0 321 | %% @doc Returns the command line parameters that effi can parse, in a format that the getopt module understands. 322 | get_optspec_lst() -> 323 | [{version, $v, "version", undefined, "Show effi version."}, 324 | {help, $h, "help", undefined, "Show command line options."}, 325 | {dir, $d, "dir", {string, "."}, "Working directory in which to look for input data and run the request."}, 326 | {input_file, $i, "input_file", string, "Input file holding the effi request (must be specified)."}, 327 | {output_file, $o, "output_file", string, "Output file into which to write the effi reply (must be specified)."}]. 328 | 329 | 330 | get_banner() -> 331 | string:join([" ._,,,, ,,_,=_", 332 | " W `_@__#__ The Erlang Foreign Function Interface (Effi) allows the", 333 | " @P+# F @F @ execution of functions defined in different programming", 334 | " _W y @ # qF languages (e.g., Bash, Python, or R) by specifying the", 335 | " ^^^^^ P qF ` function's arguments, body and output values.", 336 | "", 337 | "Copyright 2015 Jorgen Brandt "], 338 | "\n"). 339 | 340 | 341 | print_help() -> 342 | io:format("~s~n~n", [get_banner()]), 343 | getopt:usage(get_optspec_lst(), "effi"), 344 | timer:sleep(10), 345 | io:format("~nThe input_file and output_file arguments must be specified.~n~n"). 346 | 347 | 348 | print_version() -> 349 | {ok, Version} = application:get_key(effi, vsn), 350 | io:format("effi ~s~n", [Version]). 351 | 352 | 353 | -spec get_lang_mod(B :: nonempty_binary()) -> atom(). 354 | 355 | get_lang_mod(<<"Awk">>) -> effi_awk; 356 | get_lang_mod(<<"Bash">>) -> effi_bash; 357 | get_lang_mod(<<"Elixir">>) -> effi_elixir; 358 | get_lang_mod(<<"Erlang">>) -> effi_erlang; 359 | get_lang_mod(<<"Gnuplot">>) -> effi_gnuplot; 360 | get_lang_mod(<<"Java">>) -> effi_java; 361 | get_lang_mod(<<"Javascript">>) -> effi_javascript; 362 | get_lang_mod(<<"Matlab">>) -> effi_matlab; 363 | get_lang_mod(<<"Python">>) -> effi_python; 364 | get_lang_mod(<<"Octave">>) -> effi_octave; 365 | get_lang_mod(<<"Perl">>) -> effi_perl; 366 | get_lang_mod(<<"R">>) -> effi_r; 367 | get_lang_mod(<<"Racket">>) -> effi_racket; 368 | get_lang_mod(<<"Wal">>) -> effi_wal; 369 | get_lang_mod(B) when is_binary(B) -> error({lang_not_recognized, B}). 370 | 371 | 372 | -spec listen_port(Port, LineAcc, Output, RetBindLst, Success) -> 373 | {ok, binary(), [#{atom() => binary()}]} | 374 | {error, binary()} 375 | when Port :: port(), 376 | LineAcc :: binary(), 377 | Output :: binary(), 378 | RetBindLst :: [#{atom() => binary()}], 379 | Success :: boolean(). 380 | 381 | listen_port(Port, LineAcc, Output, RetBindLst, Success) 382 | when is_port(Port), 383 | is_binary(LineAcc), 384 | is_binary(Output), 385 | is_list(RetBindLst), 386 | is_boolean(Success) -> 387 | 388 | receive 389 | 390 | % no line feed, buffer line and continue 391 | {Port, {data, {noeol, PartLine}}} -> 392 | LineAcc1 = <>, 393 | listen_port(Port, LineAcc1, Output, RetBindLst, Success); 394 | 395 | % line feed encountered 396 | {Port, {data, {eol, PartLine}}} -> 397 | 398 | % reconstruct line 399 | Line = <>, 400 | 401 | case Line of 402 | 403 | % end of transmission 404 | <> -> 405 | listen_port(Port, LineAcc, Output, RetBindLst, true); 406 | 407 | % line is a special message 408 | <> -> 409 | 410 | try 411 | 412 | % parse line 413 | RetBind = jsone:decode(AssocStr, [{keys, atom}]), 414 | 415 | % continue 416 | listen_port(Port, <<>>, Output, [RetBind | RetBindLst], Success) 417 | 418 | catch 419 | error:Reason -> 420 | S = io_lib:format("Cuneiform internal error: could not decode output: ~p~n", [Reason]), 421 | B = list_to_binary(S), 422 | {error, <>} 423 | end; 424 | 425 | % line is an ordinary output 426 | _ -> 427 | 428 | % continue 429 | listen_port(Port, 430 | <<>>, 431 | <>, 432 | RetBindLst, 433 | Success) 434 | 435 | end; 436 | 437 | % process succeeded but no end of transmission was received 438 | {Port, {exit_status, 0}} -> 439 | case Success of 440 | true -> {ok, Output, RetBindLst}; 441 | false -> {error, Output} 442 | end; 443 | 444 | % process failed 445 | {Port, {exit_status, _}} -> 446 | {error, Output} 447 | 448 | % if nothing matches, ignore 449 | 450 | end. 451 | 452 | 453 | -spec get_extended_script(Mod, ArgTypeLst, RetTypeLst, Script, ArgBindLst) -> 454 | binary() 455 | when Mod :: atom(), 456 | ArgTypeLst :: [#{atom() => _}], 457 | RetTypeLst :: [#{atom() => _}], 458 | Script :: binary(), 459 | ArgBindLst :: [#{atom() => _}]. 460 | 461 | get_extended_script(Mod, ArgTypeLst, RetTypeLst, Script, ArgBindLst) 462 | when is_atom(Mod), 463 | is_list(ArgTypeLst), 464 | is_list(RetTypeLst), 465 | is_binary(Script), 466 | is_list(ArgBindLst) -> 467 | 468 | Bind = 469 | fun(#{arg_name := ArgName, value := Value}, B) -> 470 | 471 | TypeInfo = get_type_info(ArgName, ArgTypeLst), 472 | #{ 473 | arg_type := ArgType, 474 | is_list := IsList 475 | } = TypeInfo, 476 | 477 | X = 478 | case IsList of 479 | 480 | false -> 481 | case ArgType of 482 | 483 | <<"Bool">> -> 484 | Mod:bind_singleton_boolean(ArgName, Value); 485 | 486 | T when T =:= <<"Str">> orelse T =:= <<"File">> -> 487 | Mod:bind_singleton_string(ArgName, Value) 488 | 489 | end; 490 | 491 | true -> 492 | case ArgType of 493 | 494 | <<"Bool">> -> 495 | Mod:bind_boolean_list(ArgName, Value); 496 | 497 | T when T =:= <<"Str">> orelse T =:= <<"File">> -> 498 | Mod:bind_string_list(ArgName, Value) 499 | 500 | end 501 | 502 | end, 503 | 504 | <> 505 | end, 506 | 507 | Echo = 508 | fun(TypeInfo, B) -> 509 | 510 | #{ 511 | arg_name := ArgName, 512 | arg_type := ArgType, 513 | is_list := IsList 514 | } = TypeInfo, 515 | 516 | X = 517 | case IsList of 518 | 519 | false -> 520 | case ArgType of 521 | 522 | <<"Bool">> -> 523 | Mod:echo_singleton_boolean(ArgName); 524 | 525 | T when T =:= <<"Str">> orelse T =:= <<"File">> -> 526 | Mod:echo_singleton_string(ArgName) 527 | 528 | end; 529 | 530 | true -> 531 | case ArgType of 532 | 533 | <<"Bool">> -> 534 | Mod:echo_boolean_list(ArgName); 535 | 536 | T when T =:= <<"Str">> orelse T =:= <<"File">> -> 537 | Mod:echo_string_list(ArgName) 538 | 539 | end 540 | 541 | end, 542 | 543 | <> 544 | 545 | end, 546 | 547 | Binding = lists:foldl(Bind, <<>>, ArgBindLst), 548 | Echoing = lists:foldl(Echo, <<>>, RetTypeLst), 549 | EndOfTransmission = Mod:end_of_transmission(), 550 | Prefix = Mod:prefix(), 551 | Suffix = Mod:suffix(), 552 | Script1 = Mod:process_script(Script), 553 | 554 | <>. 560 | -------------------------------------------------------------------------------- /src/effi_awk.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_awk). 28 | -behaviour(effi). 29 | 30 | % effi callbacks 31 | -export([bind_singleton_boolean/2, 32 | bind_singleton_string/2, 33 | bind_boolean_list/2, 34 | bind_string_list/2, 35 | echo_singleton_boolean/1, 36 | echo_singleton_string/1, 37 | echo_boolean_list/1, 38 | echo_string_list/1, 39 | prefix/0, 40 | end_of_transmission/0, 41 | suffix/0, 42 | process_script/1, 43 | run_extended_script/3, 44 | get_run_info/1]). 45 | 46 | -include("effi.hrl"). 47 | 48 | -record(run_info, {src_file}). 49 | 50 | %%==================================================================== 51 | %% Effi callback functions 52 | %%==================================================================== 53 | 54 | 55 | -spec run_extended_script(ExtendedScript, Dir, RunInfo) -> Result 56 | when ExtendedScript :: binary(), 57 | Dir :: string(), 58 | RunInfo :: #run_info{}, 59 | Result :: {ok, binary(), [#{atom() => binary()}]} | 60 | {error, binary()}. 61 | 62 | run_extended_script(ExtendedScript, Dir, RunInfo) 63 | when is_binary(ExtendedScript), 64 | is_list(Dir) -> 65 | 66 | #run_info{src_file = SrcFile} = RunInfo, 67 | 68 | ScriptFile = string:join([Dir, "__script.awk"], "/"), 69 | Call = io_lib:format("awk -f __script.awk ~s | awk '/^/ { print } /^$/ { print } !($0~~/^/||$0~~/^$/) { print > \"__result\" }'", [SrcFile]), 70 | 71 | ok = file:write_file(ScriptFile, ExtendedScript), 72 | Port = effi:create_port(Call, Dir), 73 | 74 | effi:listen_port(Port). 75 | 76 | 77 | -spec get_run_info(Request :: #{atom() => _}) -> #run_info{}. 78 | 79 | get_run_info(Request) -> 80 | #{arg_bind_lst := ArgBindLst} = Request, 81 | [#{value := Value} | _] = ArgBindLst, 82 | #run_info{src_file = Value}. 83 | 84 | 85 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> 86 | binary(). 87 | 88 | bind_singleton_boolean(ArgName, Value) 89 | when is_binary(ArgName), 90 | is_binary(Value) -> 91 | error(nyi). 92 | 93 | 94 | -spec bind_singleton_string(ArgName, Value) -> binary() 95 | when ArgName :: binary(), 96 | Value :: binary(). 97 | 98 | bind_singleton_string(ArgName, Value) 99 | when is_binary(ArgName), 100 | is_binary(Value) -> 101 | <<"BEGIN { ", ArgName/binary, " = \"", Value/binary, "\" }\n">>. 102 | 103 | 104 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 105 | binary(). 106 | 107 | bind_boolean_list(_ArgName, _Value) -> 108 | error(nyi). 109 | 110 | 111 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 112 | binary(). 113 | 114 | bind_string_list(ArgName, Value) 115 | when is_binary(ArgName), 116 | is_list(Value) -> 117 | error(nyi). 118 | 119 | 120 | -spec echo_singleton_boolean(ArgName :: binary()) -> 121 | binary(). 122 | 123 | echo_singleton_boolean(_ArgName) -> 124 | error(nyi). 125 | 126 | 127 | -spec echo_singleton_string(ArgName :: binary()) -> binary(). 128 | 129 | echo_singleton_string(ArgName) 130 | when is_binary(ArgName) -> 131 | <<"END { print \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 132 | "\\\",\\\"value\\\":\\\"\" ", ArgName/binary, " \"\\\"}\" }\n">>. 133 | 134 | 135 | -spec echo_boolean_list(ArgName :: binary()) -> 136 | binary(). 137 | 138 | echo_boolean_list(_ArgName) -> 139 | error(nyi). 140 | 141 | 142 | -spec echo_string_list(ArgName :: binary()) -> 143 | binary(). 144 | 145 | echo_string_list(_ArgName) -> 146 | error(nyi). 147 | 148 | 149 | -spec prefix() -> 150 | binary(). 151 | 152 | prefix() -> 153 | <<"BEGIN { result = \"__result\"; system( \"touch \" result ) }\n">>. 154 | 155 | 156 | -spec end_of_transmission() -> 157 | binary(). 158 | 159 | end_of_transmission() -> 160 | <<"END { print \"", ?EOT, "\" }\n">>. 161 | 162 | 163 | -spec suffix() -> 164 | binary(). 165 | 166 | suffix() -> 167 | <<>>. 168 | 169 | 170 | -spec process_script(Script :: binary()) -> 171 | binary(). 172 | 173 | process_script(Script) -> 174 | Script. 175 | -------------------------------------------------------------------------------- /src/effi_bash.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_bash). 28 | -behaviour(effi). 29 | 30 | % effi callbacks 31 | -export([bind_singleton_boolean/2, 32 | bind_singleton_string/2, 33 | bind_boolean_list/2, 34 | bind_string_list/2, 35 | echo_singleton_boolean/1, 36 | echo_singleton_string/1, 37 | echo_boolean_list/1, 38 | echo_string_list/1, 39 | prefix/0, 40 | end_of_transmission/0, 41 | suffix/0, 42 | process_script/1, 43 | run_extended_script/3, 44 | get_run_info/1]). 45 | 46 | -include("effi.hrl"). 47 | 48 | %%==================================================================== 49 | %% Effi callback functions 50 | %%==================================================================== 51 | 52 | 53 | -spec run_extended_script(ExtendedScript :: binary(), Dir :: string(), RunInfo :: _) -> 54 | {ok, binary(), [#{atom() => binary()}]} | 55 | {error, binary()}. 56 | 57 | run_extended_script(ExtendedScript, Dir, _) 58 | when is_binary(ExtendedScript), 59 | is_list(Dir) -> 60 | 61 | ScriptFile = string:join([Dir, "__script.sh"], "/"), 62 | Call = "bash __script.sh", 63 | 64 | ok = file:write_file(ScriptFile, ExtendedScript), 65 | 66 | Port = effi:create_port(Call, Dir), 67 | 68 | effi:listen_port(Port). 69 | 70 | 71 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> 72 | binary(). 73 | 74 | bind_singleton_boolean(ArgName, Value) 75 | when is_binary(ArgName), 76 | is_binary(Value) -> 77 | <>. 78 | 79 | 80 | -spec bind_singleton_string(ArgName, Value) -> binary() 81 | when ArgName :: binary(), 82 | Value :: binary(). 83 | 84 | bind_singleton_string(ArgName, Value) 85 | when is_binary(ArgName), 86 | is_binary(Value) -> 87 | 88 | <>. 89 | 90 | 91 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 92 | binary(). 93 | 94 | bind_boolean_list(ArgName, Value) -> 95 | bind_string_list(ArgName, Value). 96 | 97 | 98 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 99 | binary(). 100 | 101 | bind_string_list(ArgName, Value) 102 | when is_binary(ArgName), 103 | is_list(Value) -> 104 | SLst = [ "'" ++ binary_to_list(V) ++ "'" || V <- Value ], 105 | S = string:join(SLst, " "), 106 | B = list_to_binary(S), 107 | <>. 108 | 109 | 110 | -spec echo_singleton_boolean(ArgName :: binary()) -> 111 | binary(). 112 | 113 | echo_singleton_boolean(ArgName) -> 114 | <<"if [ $", ArgName/binary, " == 'true' ]\n", 115 | "then\n", 116 | " echo '", ?MSG, "{\"arg_name\":\"", ArgName/binary, "\",\"value\":\"true\"}'\n", 117 | "else\n", 118 | " echo '", ?MSG, "{\"arg_name\":\"", ArgName/binary, "\",\"value\":\"false\"}'\n", 119 | "fi\n\n">>. 120 | 121 | 122 | -spec echo_singleton_string(ArgName :: binary()) -> binary(). 123 | 124 | echo_singleton_string(ArgName) 125 | when is_binary(ArgName) -> 126 | 127 | <<"echo \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 128 | "\\\",\\\"value\\\":\\\"$", ArgName/binary, "\\\"}\"\n">>. 129 | 130 | 131 | -spec echo_boolean_list(ArgName :: binary()) -> 132 | binary(). 133 | 134 | echo_boolean_list(ArgName) -> 135 | B = echo_string_list(ArgName), 136 | <<"for x in ${", ArgName/binary, "[@]}\n", 137 | "do\n", 138 | " if [ $x != 'true' ]\n", 139 | " then\n", 140 | " if [ $x != 'false' ]\n", 141 | " then\n", 142 | " echo non-Boolean value in ", ArgName/binary, "\n", 143 | " exit -1\n", 144 | " fi\n", 145 | " fi\n", 146 | "done\n", 147 | B/binary>>. 148 | 149 | 150 | -spec echo_string_list(ArgName :: binary()) -> 151 | binary(). 152 | 153 | echo_string_list(ArgName) -> 154 | <<"if (( ${#", 155 | ArgName/binary, 156 | "[@]} ))\n", 157 | "then\n", 158 | " TMP=`printf \",\\\"%s\\\"\" ${", 159 | ArgName/binary, 160 | "[@]}`\n", 161 | " TMP=${TMP:1}\n", 162 | " echo \"", 163 | ?MSG, 164 | "{\\\"arg_name\\\":\\\"", 165 | ArgName/binary, 166 | "\\\",\\\"value\\\":[$TMP]}\"\n" 167 | "else\n", 168 | " echo \"", 169 | ?MSG, 170 | "{\\\"arg_name\\\":\\\"", 171 | ArgName/binary, 172 | "\\\",\\\"value\\\":[]}\"\n" 173 | "fi\n\n">>. 174 | 175 | 176 | -spec prefix() -> 177 | binary(). 178 | 179 | prefix() -> 180 | <<"set -eu -o pipefail\n">>. 181 | 182 | 183 | -spec end_of_transmission() -> 184 | binary(). 185 | 186 | end_of_transmission() -> 187 | <<"echo '", ?EOT, "'\n">>. 188 | 189 | 190 | -spec suffix() -> 191 | binary(). 192 | 193 | suffix() -> 194 | <<>>. 195 | 196 | 197 | -spec process_script(Script :: binary()) -> 198 | binary(). 199 | 200 | process_script(Script) -> 201 | Script. 202 | 203 | 204 | -spec get_run_info(Request :: #{atom() => _}) -> []. 205 | 206 | get_run_info(_Request) -> 207 | []. 208 | -------------------------------------------------------------------------------- /src/effi_elixir.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_elixir). 28 | -behaviour(effi). 29 | 30 | -export([bind_singleton_boolean/2, 31 | bind_singleton_string/2, 32 | bind_boolean_list/2, 33 | bind_string_list/2, 34 | prefix/0, 35 | suffix/0, 36 | end_of_transmission/0, 37 | process_script/1, 38 | run_extended_script/3, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | get_run_info/1]). 44 | 45 | -include("effi.hrl"). 46 | 47 | 48 | -spec run_extended_script(ExtendedScript, Dir, RunInfo) -> 49 | {ok, binary(), [#{atom() => binary()}]} | 50 | {error, binary()} 51 | when ExtendedScript :: binary(), 52 | Dir :: string(), 53 | RunInfo :: _. 54 | 55 | run_extended_script(ExtendedScript, Dir, _) 56 | when is_binary(ExtendedScript), 57 | is_list(Dir) -> 58 | 59 | ScriptFile = string:join([Dir, "__script.exs"], "/"), 60 | Call = "elixir __script.exs", 61 | 62 | ok = file:write_file(ScriptFile, ExtendedScript), 63 | 64 | Port = effi:create_port(Call, Dir), 65 | 66 | effi:listen_port(Port). 67 | 68 | 69 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> 70 | binary(). 71 | 72 | bind_singleton_boolean(ArgName, Value) 73 | when is_binary(ArgName), 74 | is_binary(Value) -> 75 | <>. 76 | 77 | 78 | -spec bind_singleton_string(ArgName :: binary(), Value :: binary()) -> 79 | binary(). 80 | 81 | bind_singleton_string(ArgName, Value) -> 82 | <>. 83 | 84 | 85 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 86 | binary(). 87 | 88 | bind_boolean_list(ArgName, Value) -> 89 | S = string:join([ binary_to_list(V) || V <- Value ], ", "), 90 | B = list_to_binary(S), 91 | <>. 92 | 93 | 94 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 95 | binary(). 96 | 97 | bind_string_list(ArgName, Value) -> 98 | S = string:join([ "\"" ++ binary_to_list(V) ++ "\"" || V <- Value ], ", "), 99 | B = list_to_binary(S), 100 | <>. 101 | 102 | 103 | -spec echo_singleton_boolean(ArgName :: binary()) -> 104 | binary(). 105 | 106 | echo_singleton_boolean(ArgName) -> 107 | <<":io.format( \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 108 | "\\\",\\\"value\\\":\\\"~p\\\"}~n\", [", ArgName/binary, "] )\n">>. 109 | 110 | 111 | -spec echo_singleton_string(ArgName :: binary()) -> 112 | binary(). 113 | 114 | echo_singleton_string(ArgName) -> 115 | <<":io.format( \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 116 | "\\\",\\\"value\\\":\\\"~s\\\"}~n\", [", ArgName/binary, "] )\n">>. 117 | 118 | 119 | -spec echo_boolean_list(ArgName :: binary()) -> 120 | binary(). 121 | 122 | echo_boolean_list(ArgName) -> 123 | <<":io.format( \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 124 | "\\\",\\\"value\\\":[~s]}~n\", [Enum.join( ( for v <- ", 125 | ArgName/binary, ", do: if v, do: \"\\\"true\\\"\", else: \"\\\"false\\\"\" ), \", \" )] )\n">>. 126 | 127 | 128 | -spec echo_string_list(ArgName :: binary()) -> 129 | binary(). 130 | 131 | echo_string_list(ArgName) -> 132 | <<":io.format( \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 133 | "\\\",\\\"value\\\":[~s]}~n\", [Enum.join( ( for v <- ", 134 | ArgName/binary, ", do: \"\\\"#{v}\\\"\" ), \", \" )] )\n">>. 135 | 136 | 137 | -spec prefix() -> 138 | binary(). 139 | 140 | prefix() -> 141 | <<>>. 142 | 143 | 144 | -spec end_of_transmission() -> 145 | binary(). 146 | 147 | end_of_transmission() -> 148 | <<"IO.puts( \"", ?EOT, "\\n\" )\n">>. 149 | 150 | 151 | -spec suffix() -> 152 | binary(). 153 | 154 | suffix() -> 155 | <<>>. 156 | 157 | 158 | -spec process_script(Script :: binary()) -> 159 | binary(). 160 | 161 | process_script(Script) -> 162 | Script. 163 | 164 | 165 | -spec get_run_info(Request :: #{atom() => _}) -> []. 166 | 167 | get_run_info(_Request) -> 168 | []. 169 | -------------------------------------------------------------------------------- /src/effi_erlang.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_erlang). 28 | -behaviour(effi). 29 | 30 | %%==================================================================== 31 | %% Exports 32 | %%==================================================================== 33 | 34 | % effi callbacks 35 | -export([bind_singleton_boolean/2, 36 | bind_singleton_string/2, 37 | bind_boolean_list/2, 38 | bind_string_list/2, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | prefix/0, 44 | end_of_transmission/0, 45 | suffix/0, 46 | process_script/1, 47 | run_extended_script/3, 48 | get_run_info/1]). 49 | 50 | %%==================================================================== 51 | %% Includes 52 | %%==================================================================== 53 | 54 | -include("effi.hrl"). 55 | 56 | %%==================================================================== 57 | %% Effi callback function implementations 58 | %%==================================================================== 59 | 60 | 61 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> 62 | binary(). 63 | 64 | bind_singleton_boolean(ArgName, Value) -> 65 | <>. 66 | 67 | 68 | -spec bind_singleton_string(ArgName :: binary(), Value :: binary()) -> 69 | binary(). 70 | 71 | bind_singleton_string(ArgName, Value) -> 72 | <>. 73 | 74 | 75 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 76 | binary(). 77 | 78 | bind_boolean_list(ArgName, ValueLst) -> 79 | S = string:join([ binary_to_list(Value) || Value <- ValueLst ], ", "), 80 | B = list_to_binary(S), 81 | <>. 82 | 83 | 84 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 85 | binary(). 86 | 87 | bind_string_list(ArgName, ValueLst) -> 88 | S = string:join([ "\"" ++ binary_to_list(Value) ++ "\"" || Value <- ValueLst ], ", "), 89 | B = list_to_binary(S), 90 | <>. 91 | 92 | 93 | -spec echo_singleton_boolean(ArgName :: binary()) -> 94 | binary(). 95 | 96 | echo_singleton_boolean(ArgName) -> 97 | <<"io:format( \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 98 | "\\\",\\\"value\\\":\\\"~p\\\"}~n\", [", ArgName/binary, "] ),\n">>. 99 | 100 | 101 | -spec echo_singleton_string(ArgName :: binary()) -> 102 | binary(). 103 | 104 | echo_singleton_string(ArgName) -> 105 | <<"io:format( \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 106 | "\\\",\\\"value\\\":\\\"~s\\\"}~n\", [", ArgName/binary, "] ),\n">>. 107 | 108 | 109 | -spec echo_boolean_list(ArgName :: binary()) -> 110 | binary(). 111 | 112 | echo_boolean_list(ArgName) -> 113 | <<"io:format( \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 114 | "\\\",\\\"value\\\":[~s]}~n\", [string:join( [if V -> \"\\\"true\\\"\"; true -> \"\\\"false\\\"\" end || V <- ", 115 | ArgName/binary, "], \", \" )] ),\n">>. 116 | 117 | 118 | -spec echo_string_list(ArgName :: binary()) -> 119 | binary(). 120 | 121 | echo_string_list(ArgName) -> 122 | <<"io:format( \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 123 | "\\\",\\\"value\\\":[~s]}~n\", [string:join( [\"\\\"\"++V++\"\\\"\" || V <- ", 124 | ArgName/binary, "], \", \" )] ),\n">>. 125 | 126 | 127 | -spec prefix() -> 128 | binary(). 129 | 130 | prefix() -> 131 | <<"-module( __script ).\n-export( [main/1] ).\n\nmain( _ ) ->\n\n">>. 132 | 133 | 134 | -spec end_of_transmission() -> 135 | binary(). 136 | 137 | end_of_transmission() -> 138 | <<"io:format( \"", ?EOT, "\\n\" ),\n">>. 139 | 140 | 141 | -spec suffix() -> 142 | binary(). 143 | 144 | suffix() -> 145 | <<"ok.\n">>. 146 | 147 | 148 | -spec process_script(Script :: binary()) -> 149 | binary(). 150 | 151 | process_script(Script) -> 152 | Script1 = string:trim(Script, trailing), 153 | Skip = byte_size(Script1) - 1, 154 | case Script1 of 155 | <> -> <>; 156 | X -> <> 157 | end. 158 | 159 | 160 | -spec run_extended_script(ExtendedScript :: binary(), Dir :: string(), RunInfo :: _) -> 161 | {ok, binary(), [#{atom() => binary()}]} | 162 | {error, binary()}. 163 | 164 | run_extended_script(ExtendedScript, Dir, _) -> 165 | 166 | ScriptFile = string:join([Dir, "__script.erl"], "/"), 167 | Call = "escript __script.erl", 168 | 169 | ok = file:write_file(ScriptFile, ExtendedScript), 170 | 171 | Port = effi:create_port(Call, Dir), 172 | 173 | effi:listen_port(Port). 174 | 175 | 176 | -spec get_run_info(Request :: #{atom() => _}) -> []. 177 | 178 | get_run_info(_Request) -> 179 | []. 180 | -------------------------------------------------------------------------------- /src/effi_gnuplot.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_gnuplot). 28 | -behaviour(effi). 29 | 30 | % effi callbacks 31 | -export([bind_singleton_boolean/2, 32 | bind_singleton_string/2, 33 | bind_boolean_list/2, 34 | bind_string_list/2, 35 | echo_singleton_boolean/1, 36 | echo_singleton_string/1, 37 | echo_boolean_list/1, 38 | echo_string_list/1, 39 | prefix/0, 40 | end_of_transmission/0, 41 | suffix/0, 42 | process_script/1, 43 | run_extended_script/3, 44 | get_run_info/1]). 45 | 46 | -include("effi.hrl"). 47 | 48 | %%==================================================================== 49 | %% Effi callback functions 50 | %%==================================================================== 51 | 52 | 53 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> 54 | binary(). 55 | 56 | bind_singleton_boolean(ArgName, Value) -> 57 | V = case Value of 58 | <<"true">> -> <<"1">>; 59 | <<"false">> -> <<"0">> 60 | end, 61 | <>. 62 | 63 | 64 | -spec bind_singleton_string(ArgName :: binary(), Value :: binary()) -> 65 | binary(). 66 | 67 | bind_singleton_string(ArgName, Value) -> 68 | <>. 69 | 70 | 71 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 72 | binary(). 73 | 74 | bind_boolean_list(ArgName, Value) -> 75 | F = fun(<<"true">>) -> "1"; 76 | (<<"false">>) -> "0" 77 | end, 78 | L = list_to_binary(integer_to_list(length(Value))), 79 | SLst = [ F(V) || V <- Value ], 80 | B = list_to_binary(string:join(SLst, ",")), 81 | <<"array ", ArgName/binary, "[", L/binary, "]=[", B/binary, "]\n">>. 82 | 83 | 84 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 85 | binary(). 86 | 87 | bind_string_list(ArgName, Value) -> 88 | L = list_to_binary(integer_to_list(length(Value))), 89 | SLst = [ "\"" ++ binary_to_list(V) ++ "\"" || V <- Value ], 90 | B = list_to_binary(string:join(SLst, ",")), 91 | <<"array ", ArgName/binary, "[", L/binary, "]=[", B/binary, "]\n">>. 92 | 93 | 94 | -spec echo_singleton_boolean(ArgName :: binary()) -> 95 | binary(). 96 | 97 | echo_singleton_boolean(ArgName) -> 98 | <<"if (", ArgName/binary, ") {print \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 99 | "\\\",\\\"value\\\":\\\"true\\\"}\"} else {print \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 100 | "\\\",\\\"value\\\":\\\"false\\\"}\"}">>. 101 | 102 | 103 | -spec echo_singleton_string(ArgName :: binary()) -> 104 | binary(). 105 | 106 | echo_singleton_string(ArgName) -> 107 | <<"print \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, 108 | "\\\",\\\"value\\\":\\\"\".", ArgName/binary, ".\"\\\"}\"\n">>. 109 | 110 | 111 | -spec echo_boolean_list(ArgName :: binary()) -> 112 | binary(). 113 | 114 | echo_boolean_list(_ArgName) -> 115 | error(nyi). 116 | 117 | 118 | -spec echo_string_list(ArgName :: binary()) -> 119 | binary(). 120 | 121 | echo_string_list(_ArgName) -> 122 | error(nyi). 123 | 124 | 125 | -spec prefix() -> 126 | binary(). 127 | 128 | prefix() -> 129 | <<>>. 130 | 131 | 132 | -spec end_of_transmission() -> 133 | binary(). 134 | 135 | end_of_transmission() -> 136 | <<"print \"", ?EOT, "\"\n">>. 137 | 138 | 139 | -spec suffix() -> 140 | binary(). 141 | 142 | suffix() -> 143 | <<>>. 144 | 145 | 146 | -spec process_script(Script :: binary()) -> 147 | binary(). 148 | 149 | process_script(Script) -> 150 | Script. 151 | 152 | 153 | -spec get_run_info(Request :: #{atom() => _}) -> []. 154 | 155 | get_run_info(_Request) -> 156 | []. 157 | 158 | 159 | -spec run_extended_script(ExtendedScript :: binary(), 160 | Dir :: string(), 161 | RunInfo :: []) -> 162 | {ok, binary(), [#{atom() => binary()}]} | 163 | {error, binary()}. 164 | 165 | run_extended_script(ExtendedScript, Dir, _RunInfo) -> 166 | 167 | ScriptFile = string:join([Dir, "__script"], "/"), 168 | Call = "gnuplot __script", 169 | 170 | ok = file:write_file(ScriptFile, ExtendedScript), 171 | 172 | Port = effi:create_port(Call, Dir), 173 | 174 | effi:listen_port(Port). 175 | -------------------------------------------------------------------------------- /src/effi_java.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_java). 28 | -behaviour(effi). 29 | 30 | %%==================================================================== 31 | %% Exports 32 | %%==================================================================== 33 | 34 | % effi callbacks 35 | -export([bind_singleton_boolean/2, 36 | bind_singleton_string/2, 37 | bind_boolean_list/2, 38 | bind_string_list/2, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | prefix/0, 44 | end_of_transmission/0, 45 | suffix/0, 46 | process_script/1, 47 | run_extended_script/3, 48 | get_run_info/1]). 49 | 50 | %%==================================================================== 51 | %% Includes 52 | %%==================================================================== 53 | 54 | -include("effi.hrl"). 55 | 56 | %%==================================================================== 57 | %% Effi callback function implementations 58 | %%==================================================================== 59 | 60 | 61 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> 62 | binary(). 63 | 64 | bind_singleton_boolean(ArgName, Value) -> 65 | <<"final boolean ", ArgName/binary, " = ", Value/binary, ";\n">>. 66 | 67 | 68 | -spec bind_singleton_string(ArgName :: binary(), Value :: binary()) -> 69 | binary(). 70 | 71 | bind_singleton_string(ArgName, Value) -> 72 | <<"final String ", ArgName/binary, " = \"", Value/binary, "\";\n">>. 73 | 74 | 75 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 76 | binary(). 77 | 78 | bind_boolean_list(ArgName, ValueLst) -> 79 | 80 | S = string:join([ binary_to_list(Value) || Value <- ValueLst ], ", "), 81 | B = list_to_binary(S), 82 | 83 | <<"final boolean[] ", ArgName/binary, " = { ", B/binary, " };\n">>. 84 | 85 | 86 | -spec bind_string_list(ArgName :: binary(), ValueLst :: [binary()]) -> 87 | binary(). 88 | 89 | bind_string_list(ArgName, ValueLst) 90 | when is_binary(ArgName), 91 | is_list(ValueLst) -> 92 | 93 | F = 94 | fun(X) when is_binary(X) -> 95 | "\"" ++ binary_to_list(X) ++ "\"" 96 | end, 97 | 98 | S = string:join([ F(Value) || Value <- ValueLst ], ", "), 99 | B = list_to_binary(S), 100 | 101 | <<"final String[] ", ArgName/binary, " = { ", B/binary, " };\n">>. 102 | 103 | 104 | -spec echo_singleton_boolean(ArgName :: binary()) -> 105 | binary(). 106 | 107 | echo_singleton_boolean(ArgName) -> 108 | <<"System.out.println( new StringBuffer().append( \"", ?MSG, "{ \\\"arg_name\\\": \\\"", ArgName/binary, "\\\", \\\"value\\\": \\\"\" ).append( ", ArgName/binary, " ).append( \"\\\" }\") );\n">>. 109 | 110 | 111 | -spec echo_singleton_string(ArgName :: binary()) -> 112 | binary(). 113 | 114 | echo_singleton_string(ArgName) -> 115 | <<"System.out.println( new StringBuffer().append( \"", ?MSG, "{ \\\"arg_name\\\": \\\"", ArgName/binary, "\\\", \\\"value\\\": \\\"\" ).append( ", ArgName/binary, " ).append( \"\\\" }\" ) );\n">>. 116 | 117 | 118 | -spec echo_boolean_list(ArgName :: binary()) -> 119 | binary(). 120 | 121 | echo_boolean_list(ArgName) -> 122 | <<"StringBuffer __s = new StringBuffer();\n", 123 | "__s.append( \"", 124 | ?MSG, 125 | "{ \\\"arg_name\\\": \\\"", 126 | ArgName/binary, 127 | "\\\", \\\"value\\\": [\" );\n", 128 | "boolean __comma = false;\n" 129 | "for( boolean x : ", 130 | ArgName/binary, 131 | " ) {\n", 132 | " if( __comma )\n", 133 | " __s.append( ',' );\n" 134 | " __comma = true;" 135 | " __s.append( '\"' ).append( x ).append( '\"' );\n", 136 | "}\n", 137 | "System.out.println( __s.append( \"] }\" ) );\n\n">>. 138 | 139 | 140 | -spec echo_string_list(ArgName :: binary()) -> 141 | binary(). 142 | 143 | echo_string_list(ArgName) -> 144 | <<"StringBuffer __s = new StringBuffer();\n", 145 | "__s.append( \"", 146 | ?MSG, 147 | "{ \\\"arg_name\\\": \\\"", 148 | ArgName/binary, 149 | "\\\", \\\"value\\\": [\" );\n", 150 | "boolean __comma = false;\n" 151 | "for( String x : ", 152 | ArgName/binary, 153 | " ) {\n", 154 | " if( __comma )\n", 155 | " __s.append( ',' );\n" 156 | " __comma = true;" 157 | " __s.append( '\"' ).append( x ).append( '\"' );\n", 158 | "}\n", 159 | "System.out.println( __s.append( \"] }\" ) );\n\n">>. 160 | 161 | 162 | -spec prefix() -> 163 | binary(). 164 | 165 | prefix() -> 166 | <<"import java.io.BufferedReader;\n", 167 | "import java.io.BufferedWriter;\n", 168 | "import java.io.FileReader;\n", 169 | "import java.io.FileWriter;\n", 170 | "import java.io.File;\n", 171 | "import java.io.IOException;\n", 172 | "public class Main {\n", 173 | "public static void main( String[] __args ) throws IOException {\n">>. 174 | 175 | 176 | -spec end_of_transmission() -> 177 | binary(). 178 | 179 | end_of_transmission() -> 180 | <<"System.out.println( \"", ?EOT, "\" );\n">>. 181 | 182 | 183 | -spec suffix() -> 184 | binary(). 185 | 186 | suffix() -> 187 | <<"}\n}\n">>. 188 | 189 | 190 | -spec process_script(Script :: binary()) -> 191 | binary(). 192 | 193 | process_script(Script) -> 194 | Script. 195 | 196 | 197 | -spec run_extended_script(ExtendedScript :: binary(), Dir :: string(), RunInfo :: _) -> 198 | {ok, binary(), [#{atom() => binary()}]} | 199 | {error, binary()}. 200 | 201 | run_extended_script(ExtendedScript, Dir, _) -> 202 | 203 | JavaFile = string:join([Dir, "Main.java"], "/"), 204 | ok = file:write_file(JavaFile, ExtendedScript), 205 | 206 | ScriptFile = string:join([Dir, "__script.sh"], "/"), 207 | ok = file:write_file(ScriptFile, "set -eu -o pipefail\njavac Main.java\njava Main\n"), 208 | 209 | Call = "bash __script.sh", 210 | 211 | Port = effi:create_port(Call, Dir), 212 | 213 | effi:listen_port(Port). 214 | 215 | 216 | -spec get_run_info(Request :: #{atom() => _}) -> []. 217 | 218 | get_run_info(_Request) -> 219 | []. 220 | -------------------------------------------------------------------------------- /src/effi_javascript.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_javascript). 28 | -behaviour(effi). 29 | 30 | -export([bind_singleton_boolean/2, 31 | bind_singleton_string/2, 32 | bind_boolean_list/2, 33 | bind_string_list/2, 34 | prefix/0, 35 | suffix/0, 36 | end_of_transmission/0, 37 | process_script/1, 38 | run_extended_script/3, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | get_run_info/1]). 44 | 45 | -include("effi.hrl"). 46 | 47 | 48 | -spec run_extended_script(ExtendedScript, Dir, RunInfo) -> 49 | {ok, binary(), [#{atom() => binary()}]} | 50 | {error, binary()} 51 | when ExtendedScript :: binary(), 52 | Dir :: string(), 53 | RunInfo :: _. 54 | 55 | run_extended_script(ExtendedScript, Dir, _) 56 | when is_binary(ExtendedScript), 57 | is_list(Dir) -> 58 | 59 | ScriptFile = string:join([Dir, "__script.js"], "/"), 60 | Call = "node __script.js", 61 | 62 | ok = file:write_file(ScriptFile, ExtendedScript), 63 | 64 | Port = effi:create_port(Call, Dir), 65 | 66 | effi:listen_port(Port). 67 | 68 | 69 | -spec bind_singleton_boolean(ArgName, Value) -> 70 | binary() 71 | when ArgName :: binary(), 72 | Value :: binary(). 73 | 74 | bind_singleton_boolean(ArgName, <<"true">>) 75 | when is_binary(ArgName) -> 76 | <<"var ", ArgName/binary, " = true;\n">>; 77 | 78 | bind_singleton_boolean(ArgName, <<"false">>) 79 | when is_binary(ArgName) -> 80 | <<"var ", ArgName/binary, " = false;\n">>. 81 | 82 | 83 | -spec bind_singleton_string(ArgName, Value) -> 84 | binary() 85 | when ArgName :: binary(), 86 | Value :: binary(). 87 | 88 | bind_singleton_string(ArgName, Value) 89 | when is_binary(ArgName), 90 | is_binary(Value) -> 91 | <<"var ", ArgName/binary, " = '", Value/binary, "';\n">>. 92 | 93 | 94 | -spec bind_boolean_list(ArgName, Value) -> 95 | binary() 96 | when ArgName :: binary(), 97 | Value :: [binary()]. 98 | 99 | bind_boolean_list(ArgName, Value) 100 | when is_binary(ArgName), 101 | is_list(Value) -> 102 | SLst = [ binary_to_list(V) || V <- Value ], 103 | B = list_to_binary(string:join(SLst, ", ")), 104 | <<"var ", ArgName/binary, " = [ ", B/binary, " ];\n">>. 105 | 106 | 107 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 108 | binary(). 109 | 110 | bind_string_list(ArgName, Value) 111 | when is_binary(ArgName), 112 | is_list(Value) -> 113 | SLst = [ "'" ++ binary_to_list(V) ++ "'" || V <- Value ], 114 | B = list_to_binary(string:join(SLst, ", ")), 115 | <<"var ", ArgName/binary, " = [ ", B/binary, " ];\n">>. 116 | 117 | 118 | -spec prefix() -> 119 | binary(). 120 | 121 | prefix() -> <<>>. 122 | 123 | 124 | -spec end_of_transmission() -> 125 | binary(). 126 | 127 | end_of_transmission() -> 128 | <<"console.log( '", ?EOT, "' );\n">>. 129 | 130 | 131 | -spec suffix() -> 132 | binary(). 133 | 134 | suffix() -> <<>>. 135 | 136 | 137 | -spec process_script(Script :: binary()) -> 138 | binary(). 139 | 140 | process_script(Script) 141 | when is_binary(Script) -> 142 | Script. 143 | 144 | 145 | -spec echo_singleton_boolean(ArgName :: binary()) -> 146 | binary(). 147 | 148 | echo_singleton_boolean(ArgName) 149 | when is_binary(ArgName) -> 150 | <<"console.log( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 151 | "\",\"value\":\"'+String( ", ArgName/binary, " )+'\"}' );\n">>. 152 | 153 | 154 | -spec echo_singleton_string(ArgName :: binary()) -> 155 | binary(). 156 | 157 | echo_singleton_string(ArgName) 158 | when is_binary(ArgName) -> 159 | <<"console.log( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 160 | "\",\"value\":\"'+", ArgName/binary, "+'\"}' );\n">>. 161 | 162 | 163 | -spec echo_boolean_list(ArgName :: binary()) -> 164 | binary(). 165 | 166 | echo_boolean_list(ArgName) 167 | when is_binary(ArgName) -> 168 | <<"console.log( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 169 | "\",\"value\":['+String( ", ArgName/binary, 170 | ".map( x => '\"'+String( x )+'\"' ) )+']}' );\n">>. 171 | 172 | 173 | -spec echo_string_list(ArgName :: binary()) -> 174 | binary(). 175 | 176 | echo_string_list(ArgName) 177 | when is_binary(ArgName) -> 178 | <<"console.log( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 179 | "\",\"value\":['+String( ", ArgName/binary, 180 | ".map( x => '\"'+x+'\"' ) )+']}' );\n">>. 181 | 182 | 183 | -spec get_run_info(Request :: #{atom() => _}) -> []. 184 | 185 | get_run_info(_Request) -> 186 | []. 187 | -------------------------------------------------------------------------------- /src/effi_matlab.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% @doc The standalone application entry point is {@link main/1}. 24 | %% The create_port callback defined here is an abstract way to execute child 25 | %% processes in foreign languages. 26 | %% There are two foreign language interfaces, both implementing this callback, 27 | %% {@link effi_script} (e.g., Perl, Python) and {@link effi_interact} (e.g., 28 | %% Bash, R). 29 | %% 30 | %% @end 31 | %% ------------------------------------------------------------------- 32 | 33 | -module(effi_matlab). 34 | 35 | -behaviour(effi). 36 | 37 | %%==================================================================== 38 | %% Exports 39 | %%==================================================================== 40 | 41 | % effi callbacks 42 | -export([bind_singleton_boolean/2, 43 | bind_singleton_string/2, 44 | bind_boolean_list/2, 45 | bind_string_list/2, 46 | echo_singleton_boolean/1, 47 | echo_singleton_string/1, 48 | echo_boolean_list/1, 49 | echo_string_list/1, 50 | prefix/0, 51 | end_of_transmission/0, 52 | suffix/0, 53 | process_script/1, 54 | run_extended_script/3, 55 | get_run_info/1]). 56 | 57 | %%==================================================================== 58 | %% Includes 59 | %%==================================================================== 60 | 61 | -include("effi.hrl"). 62 | 63 | %%==================================================================== 64 | %% Effi callback function implementations 65 | %%==================================================================== 66 | 67 | 68 | bind_singleton_boolean(ArgName, Value) -> 69 | effi_octave:bind_singleton_boolean(ArgName, Value). 70 | 71 | 72 | bind_singleton_string(Argname, Value) -> 73 | effi_octave:bind_singleton_string(Argname, Value). 74 | 75 | 76 | bind_boolean_list(ArgName, Value) -> 77 | effi_octave:bind_boolean_list(ArgName, Value). 78 | 79 | 80 | bind_string_list(ArgName, Value) -> 81 | effi_octave:bind_string_list(ArgName, Value). 82 | 83 | 84 | echo_singleton_boolean(ArgName) -> 85 | effi_octave:echo_singleton_boolean(ArgName). 86 | 87 | 88 | echo_singleton_string(ArgName) -> 89 | effi_octave:echo_singleton_string(ArgName). 90 | 91 | 92 | echo_boolean_list(ArgName) -> 93 | effi_octave:echo_boolean_list(ArgName). 94 | 95 | 96 | echo_string_list(ArgName) -> 97 | effi_octave:echo_string_list(ArgName). 98 | 99 | 100 | prefix() -> 101 | effi_octave:prefix(). 102 | 103 | 104 | end_of_transmission() -> 105 | effi_octave:end_of_transmission(). 106 | 107 | 108 | suffix() -> 109 | Suffix = effi_octave:suffix(), 110 | <>. 111 | 112 | 113 | process_script(Script) -> 114 | effi_octave:process_script(Script). 115 | 116 | 117 | -spec run_extended_script(ExtendedScript, Dir, RunInfo) -> Result 118 | when ExtendedScript :: binary(), 119 | Dir :: string(), 120 | RunInfo :: _, 121 | Result :: {ok, binary(), [#{atom() => binary()}]} | 122 | {error, binary()}. 123 | 124 | run_extended_script(ExtendedScript, Dir, _) 125 | when is_binary(ExtendedScript), 126 | is_list(Dir) -> 127 | 128 | ScriptFile = string:join([Dir, "script.m"], "/"), 129 | Call = "matlab -nodisplay -nojvm -r script", 130 | 131 | ok = file:write_file(ScriptFile, ExtendedScript), 132 | 133 | Port = effi:create_port(Call, Dir), 134 | 135 | effi:listen_port(Port). 136 | 137 | 138 | -spec get_run_info(Request :: #{atom() => _}) -> []. 139 | 140 | get_run_info(_Request) -> 141 | []. 142 | -------------------------------------------------------------------------------- /src/effi_octave.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% @doc The standalone application entry point is {@link main/1}. 24 | %% The create_port callback defined here is an abstract way to execute child 25 | %% processes in foreign languages. 26 | %% There are two foreign language interfaces, both implementing this callback, 27 | %% {@link effi_script} (e.g., Perl, Python) and {@link effi_interact} (e.g., 28 | %% Bash, R). 29 | %% 30 | %% @end 31 | %% ------------------------------------------------------------------- 32 | 33 | -module(effi_octave). 34 | 35 | -behaviour(effi). 36 | 37 | %%==================================================================== 38 | %% Exports 39 | %%==================================================================== 40 | 41 | % effi callbacks 42 | -export([bind_singleton_boolean/2, 43 | bind_singleton_string/2, 44 | bind_boolean_list/2, 45 | bind_string_list/2, 46 | echo_singleton_boolean/1, 47 | echo_singleton_string/1, 48 | echo_boolean_list/1, 49 | echo_string_list/1, 50 | prefix/0, 51 | suffix/0, 52 | end_of_transmission/0, 53 | process_script/1, 54 | run_extended_script/3, 55 | get_run_info/1]). 56 | 57 | %%==================================================================== 58 | %% Includes 59 | %%==================================================================== 60 | 61 | -include("effi.hrl"). 62 | 63 | %%==================================================================== 64 | %% Effi callback function implementations 65 | %%==================================================================== 66 | 67 | 68 | -spec run_extended_script(ExtendedScript, Dir, RunInfo) -> 69 | {ok, binary(), [#{atom() => binary()}]} | 70 | {error, binary()} 71 | when ExtendedScript :: binary(), 72 | Dir :: string(), 73 | RunInfo :: _. 74 | 75 | run_extended_script(ExtendedScript, Dir, _) 76 | when is_binary(ExtendedScript), 77 | is_list(Dir) -> 78 | 79 | ScriptFile = string:join([Dir, "__script.m"], "/"), 80 | Call = "octave __script.m", 81 | 82 | ok = file:write_file(ScriptFile, ExtendedScript), 83 | 84 | Port = effi:create_port(Call, Dir), 85 | 86 | effi:listen_port(Port). 87 | 88 | 89 | -spec bind_singleton_string(ArgName, Value) -> binary() 90 | when ArgName :: binary(), 91 | Value :: binary(). 92 | 93 | bind_singleton_string(ArgName, Value) 94 | when is_binary(ArgName), 95 | is_binary(Value) -> 96 | 97 | <>. 98 | 99 | 100 | -spec bind_singleton_boolean(ArgName, Value) -> binary() 101 | when ArgName :: binary(), 102 | Value :: binary(). 103 | 104 | bind_singleton_boolean(ArgName, <<"true">>) -> 105 | <>; 106 | 107 | bind_singleton_boolean(ArgName, <<"false">>) -> 108 | <>. 109 | 110 | 111 | -spec bind_boolean_list(ArgName, Value) -> binary() 112 | when ArgName :: binary(), 113 | Value :: [binary()]. 114 | 115 | bind_boolean_list(ArgName, Value) 116 | when is_binary(ArgName), 117 | is_list(Value) -> 118 | 119 | StrLst = lists:map(fun binary_to_list/1, Value), 120 | B = list_to_binary("{" ++ string:join(StrLst, ", ") ++ "}"), 121 | <>. 122 | 123 | 124 | -spec bind_string_list(ArgName, Value) -> binary() 125 | when ArgName :: binary(), 126 | Value :: [binary()]. 127 | 128 | bind_string_list(ArgName, Value) 129 | when is_binary(ArgName), 130 | is_list(Value) -> 131 | 132 | StrLst = [ "'" ++ binary_to_list(V) ++ "'" || V <- Value ], 133 | B = list_to_binary("{" ++ string:join(StrLst, ", ") ++ "}"), 134 | <>. 135 | 136 | 137 | -spec echo_singleton_string(ArgName :: binary()) -> binary(). 138 | 139 | echo_singleton_string(ArgName) 140 | when is_binary(ArgName) -> 141 | 142 | <<"if ~ischar( ", ArgName/binary, " )\n", 143 | " error( '", ArgName/binary, " not a string' )\n", 144 | "end\n", 145 | "display( ['", ?MSG, "{\"arg_name\":\"", ArgName/binary, 146 | "\",\"value\":\"', ", ArgName/binary, ", '\"}\\n'] )\n\n">>. 147 | 148 | 149 | echo_singleton_boolean(ArgName) 150 | when is_binary(ArgName) -> 151 | 152 | <<"if ~islogical( ", ArgName/binary, " )\n", 153 | " error( '", ArgName/binary, " not a logical' )\n", 154 | "end\n", 155 | "if ", ArgName/binary, "\n", 156 | " display( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 157 | "\",\"value\":\"true\"}\\n' )\n", 158 | "else\n", 159 | " display( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 160 | "\",\"value\":\"false\"}\\n' )\n", 161 | "end\n\n">>. 162 | 163 | 164 | -spec echo_string_list(ArgName :: binary()) -> binary(). 165 | 166 | echo_string_list(ArgName) 167 | when is_binary(ArgName) -> 168 | 169 | <<"if ~iscell( ", ArgName/binary, " )\n", 170 | " error( '", ArgName/binary, " not a cell' )\n", 171 | "end\n", 172 | "for i = 1:prod( size( ", ArgName/binary, " ) )\n", 173 | " if ~ischar( ", ArgName/binary, "{ i } )\n", 174 | " error( '", ArgName/binary, " contains non-string elements' )\n", 175 | " end\n", 176 | "end\n", 177 | "fprintf( 1, '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 178 | "\",\"value\":[' )\n", 179 | "for i = 1:prod( size( ", ArgName/binary, " ) )\n", 180 | " if i ~= 1\n", 181 | " fprintf( 1, ',' )\n", 182 | " end\n", 183 | " fprintf( 1, '\"%s\"', ", ArgName/binary, "{ i } )\n", 184 | "end\n", 185 | "fprintf( 1, ']}\\n' )\n\n">>. 186 | 187 | 188 | -spec echo_boolean_list(ArgName :: binary()) -> binary(). 189 | 190 | echo_boolean_list(ArgName) -> 191 | 192 | <<"if ~iscell( ", ArgName/binary, " )\n", 193 | " error( '", ArgName/binary, " not a cell' )\n", 194 | "end\n", 195 | "for i = 1:prod( size( ", ArgName/binary, " ) )\n", 196 | " if ~islogical( ", ArgName/binary, "{ i } )\n", 197 | " error( '", ArgName/binary, " contains non-logical elements' )\n", 198 | " end\n", 199 | "end\n", 200 | "fprintf( 1, '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 201 | "\",\"value\":[' )\n", 202 | "for i = 1:prod( size( ", ArgName/binary, " ) )\n", 203 | " if i ~= 1\n", 204 | " fprintf( 1, ',' )\n", 205 | " end\n", 206 | " if ", ArgName/binary, "\n", 207 | " fprintf( 1, '\"true\"' )\n", 208 | " else\n", 209 | " fprintf( 1, '\"false\"' )\n", 210 | " end\n", 211 | "end\n", 212 | "fprintf( 1, ']}\\n' )\n\n">>. 213 | 214 | 215 | end_of_transmission() -> 216 | <<"display( '", ?EOT, "' )\n">>. 217 | 218 | 219 | prefix() -> 220 | <<"try\n">>. 221 | 222 | 223 | suffix() -> 224 | <<"catch e\n display( e );\n exit( -1 );\nend\n">>. 225 | 226 | 227 | process_script(Script) -> 228 | Script. 229 | 230 | 231 | -spec get_run_info(Request :: #{atom() => _}) -> []. 232 | 233 | get_run_info(_Request) -> 234 | []. 235 | -------------------------------------------------------------------------------- /src/effi_perl.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_perl). 28 | -behaviour(effi). 29 | 30 | %%==================================================================== 31 | %% Exports 32 | %%==================================================================== 33 | 34 | % effi callbacks 35 | -export([bind_singleton_boolean/2, 36 | bind_singleton_string/2, 37 | bind_boolean_list/2, 38 | bind_string_list/2, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | prefix/0, 44 | end_of_transmission/0, 45 | suffix/0, 46 | process_script/1, 47 | run_extended_script/3, 48 | get_run_info/1]). 49 | 50 | %%==================================================================== 51 | %% Includes 52 | %%==================================================================== 53 | 54 | -include("effi.hrl"). 55 | 56 | %%==================================================================== 57 | %% Effi callback function implementations 58 | %%==================================================================== 59 | 60 | 61 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> binary(). 62 | 63 | bind_singleton_boolean(ArgName, <<"true">>) -> 64 | <<"$", ArgName/binary, " = 1;\n">>; 65 | 66 | bind_singleton_boolean(ArgName, <<"false">>) -> 67 | <<"$", ArgName/binary, " = 0;\n">>. 68 | 69 | 70 | -spec bind_singleton_string(ArgName :: binary(), Value :: binary()) -> binary(). 71 | 72 | bind_singleton_string(ArgName, Value) -> 73 | <<"$", ArgName/binary, " = \"", Value/binary, "\";\n">>. 74 | 75 | 76 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 77 | binary(). 78 | 79 | bind_boolean_list(ArgName, ValueLst) -> 80 | 81 | F = 82 | fun(<<"true">>) -> "1"; 83 | (<<"false">>) -> "0" 84 | end, 85 | 86 | S = string:join([ F(Value) || Value <- ValueLst ], ", "), 87 | B = list_to_binary(S), 88 | 89 | <<"@", ArgName/binary, " = (", B/binary, ");\n">>. 90 | 91 | 92 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 93 | binary(). 94 | 95 | bind_string_list(ArgName, ValueLst) -> 96 | 97 | Quote = 98 | fun(X) -> 99 | "\"" ++ binary_to_list(X) ++ "\"" 100 | end, 101 | 102 | S = string:join([ Quote(Value) || Value <- ValueLst ], ", "), 103 | B = list_to_binary(S), 104 | 105 | <<"@", ArgName/binary, " = (", B/binary, ");\n">>. 106 | 107 | 108 | -spec echo_singleton_boolean(ArgName :: binary()) -> binary(). 109 | 110 | echo_singleton_boolean(ArgName) -> 111 | <<"if( $", 112 | ArgName/binary, 113 | " ) {\n", 114 | " print \"", 115 | ?MSG, 116 | "{\\\"arg_name\\\":\\\"", 117 | ArgName/binary, 118 | "\\\",\\\"value\\\":\\\"true\\\"}\\n\";\n", 119 | "} else {\n" 120 | " print \"", 121 | ?MSG, 122 | "{\\\"arg_name\\\":\\\"", 123 | ArgName/binary, 124 | "\\\",\\\"value\\\":\\\"false\\\"}\\n\";\n", 125 | "}\n">>. 126 | 127 | 128 | -spec echo_singleton_string(ArgName :: binary()) -> binary(). 129 | 130 | echo_singleton_string(ArgName) -> 131 | <<"print \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":\\\"$", ArgName/binary, "\\\"}\\n\";\n">>. 132 | 133 | 134 | -spec echo_boolean_list(ArgName :: binary()) -> binary(). 135 | 136 | echo_boolean_list(ArgName) -> 137 | <<"$TMP = join( \", \", map { $_ ? \"\\\"true\\\"\" : \"\\\"false\\\"\" } @", ArgName/binary, " );\n", 138 | "print \"", ?MSG, "{ \\\"arg_name\\\": \\\"", ArgName/binary, "\\\", \\\"value\\\": [$TMP] }\\n\";\n">>. 139 | 140 | 141 | -spec echo_string_list(ArgName :: binary()) -> binary(). 142 | 143 | echo_string_list(ArgName) -> 144 | <<"$TMP = join( \", \", map { \"\\\"$_\\\"\" } @", ArgName/binary, " );\n", 145 | "print \"", ?MSG, "{ \\\"arg_name\\\": \\\"", ArgName/binary, "\\\", \\\"value\\\": [$TMP] }\\n\";\n">>. 146 | 147 | 148 | -spec prefix() -> <<>>. 149 | 150 | prefix() -> 151 | <<>>. 152 | 153 | 154 | -spec end_of_transmission() -> binary(). 155 | 156 | end_of_transmission() -> 157 | <<"print \"", ?EOT, "\\n\";\n">>. 158 | 159 | 160 | -spec suffix() -> <<>>. 161 | 162 | suffix() -> 163 | <<>>. 164 | 165 | 166 | -spec process_script(Script :: binary()) -> 167 | binary(). 168 | 169 | process_script(Script) -> 170 | Script. 171 | 172 | 173 | -spec run_extended_script(ExtendedScript :: binary(), Dir :: string(), RunInfo :: _) -> 174 | {ok, binary(), [#{atom() => binary()}]} | 175 | {error, binary()}. 176 | 177 | run_extended_script(ExtendedScript, Dir, _) -> 178 | 179 | ScriptFile = string:join([Dir, "__script.pl"], "/"), 180 | Call = "perl __script.pl", 181 | 182 | ok = file:write_file(ScriptFile, ExtendedScript), 183 | 184 | Port = effi:create_port(Call, Dir), 185 | 186 | effi:listen_port(Port). 187 | 188 | 189 | -spec get_run_info(Request :: #{atom() => _}) -> []. 190 | 191 | get_run_info(_Request) -> 192 | []. 193 | -------------------------------------------------------------------------------- /src/effi_python.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_python). 28 | -behaviour(effi). 29 | 30 | %%==================================================================== 31 | %% Exports 32 | %%==================================================================== 33 | 34 | % effi callbacks 35 | -export([bind_singleton_boolean/2, 36 | bind_singleton_string/2, 37 | bind_boolean_list/2, 38 | bind_string_list/2, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | prefix/0, 44 | end_of_transmission/0, 45 | suffix/0, 46 | process_script/1, 47 | run_extended_script/3, 48 | get_run_info/1]). 49 | 50 | %%==================================================================== 51 | %% Includes 52 | %%==================================================================== 53 | 54 | -include("effi.hrl"). 55 | 56 | %%==================================================================== 57 | %% Effi callback function implementations 58 | %%==================================================================== 59 | 60 | 61 | -spec run_extended_script(ExtendedScript, Dir, RunInfo) -> 62 | {ok, binary(), [#{atom() => binary()}]} | 63 | {error, binary()} 64 | when ExtendedScript :: binary(), 65 | Dir :: string(), 66 | RunInfo :: _. 67 | 68 | run_extended_script(ExtendedScript, Dir, _) 69 | when is_binary(ExtendedScript), 70 | is_list(Dir) -> 71 | 72 | ScriptFile = string:join([Dir, "__script.py"], "/"), 73 | Call = "python __script.py", 74 | 75 | ok = file:write_file(ScriptFile, ExtendedScript), 76 | 77 | Port = effi:create_port(Call, Dir), 78 | 79 | effi:listen_port(Port). 80 | 81 | 82 | bind_singleton_boolean(ArgName, <<"true">>) -> 83 | <>; 84 | 85 | bind_singleton_boolean(ArgName, <<"false">>) -> 86 | <>. 87 | 88 | 89 | -spec bind_singleton_string(ArgName, Value) -> binary() 90 | when ArgName :: binary(), 91 | Value :: binary(). 92 | 93 | bind_singleton_string(ArgName, Value) 94 | when is_binary(ArgName), 95 | is_binary(Value) -> 96 | 97 | <>. 98 | 99 | 100 | bind_boolean_list(ArgName, Value) -> 101 | 102 | F = 103 | fun(<<"true">>) -> "True"; 104 | (<<"false">>) -> "False" 105 | end, 106 | 107 | S = string:join([ F(V) || V <- Value ], ","), 108 | B = list_to_binary(S), 109 | <>. 110 | 111 | 112 | bind_string_list(ArgName, Value) -> 113 | S = string:join([ "'" ++ binary_to_list(V) ++ "'" || V <- Value ], ","), 114 | B = list_to_binary(S), 115 | <>. 116 | 117 | 118 | echo_singleton_boolean(ArgName) 119 | when is_binary(ArgName) -> 120 | 121 | <<"if ", ArgName/binary, ":\n print( '", ?MSG, "{\"arg_name\":\"", 122 | ArgName/binary, "\",\"value\":\"true\"}\\n' )\nelse:\n print( '", ?MSG, 123 | "{\"arg_name\":\"", ArgName/binary, "\",\"value\":\"false\"}\\n' )\n">>. 124 | 125 | 126 | -spec echo_singleton_string(ArgName :: binary()) -> binary(). 127 | 128 | echo_singleton_string(ArgName) 129 | when is_binary(ArgName) -> 130 | 131 | <<"print( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 132 | "\",\"value\":\"'+str( ", ArgName/binary, " )+'\"}\\n' )\n">>. 133 | 134 | 135 | echo_boolean_list(ArgName) -> 136 | <<"print( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 137 | "\",\"value\":['+','.join( map( lambda x: '\"true\"' if x else '\"false\"', ", 138 | ArgName/binary, " ) )+']}\\n')\n">>. 139 | 140 | 141 | -spec echo_string_list(ArgName :: binary()) -> binary(). 142 | 143 | echo_string_list(ArgName) 144 | when is_binary(ArgName) -> 145 | <<"print( '", ?MSG, "{\"arg_name\":\"", ArgName/binary, 146 | "\",\"value\":['+','.join( map( lambda x: '\"%s\"'%(x), ", ArgName/binary, 147 | " ) )+']}\\n')\n">>. 148 | 149 | 150 | prefix() -> 151 | <<>>. 152 | 153 | 154 | end_of_transmission() -> 155 | <<"print( '", ?EOT, "' )\n">>. 156 | 157 | 158 | suffix() -> 159 | <<>>. 160 | 161 | 162 | process_script(Script) -> 163 | B1 = binary:replace(Script, <<$\r>>, <<"">>, [global]), 164 | B2 = binary:replace(B1, <<$\n>>, <<"\n ">>, [global]), 165 | <<"if True:\n ", B2/binary>>. 166 | 167 | 168 | -spec get_run_info(Request :: #{atom() => _}) -> []. 169 | 170 | get_run_info(_Request) -> 171 | []. 172 | -------------------------------------------------------------------------------- /src/effi_r.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_r). 28 | -behaviour(effi). 29 | 30 | %%==================================================================== 31 | %% Exports 32 | %%==================================================================== 33 | 34 | % effi callbacks 35 | -export([bind_singleton_boolean/2, 36 | bind_singleton_string/2, 37 | bind_boolean_list/2, 38 | bind_string_list/2, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | prefix/0, 44 | end_of_transmission/0, 45 | suffix/0, 46 | process_script/1, 47 | run_extended_script/3, 48 | get_run_info/1]). 49 | 50 | %%==================================================================== 51 | %% Includes 52 | %%==================================================================== 53 | 54 | -include("effi.hrl"). 55 | 56 | %%==================================================================== 57 | %% Effi callback function implementations 58 | %%==================================================================== 59 | 60 | 61 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> binary(). 62 | 63 | bind_singleton_boolean(ArgName, <<"true">>) -> 64 | <>; 65 | 66 | bind_singleton_boolean(ArgName, <<"false">>) -> 67 | <>. 68 | 69 | 70 | -spec bind_singleton_string(ArgName :: binary(), Value :: binary()) -> binary(). 71 | 72 | bind_singleton_string(ArgName, Value) -> 73 | <>. 74 | 75 | 76 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 77 | binary(). 78 | 79 | bind_boolean_list(ArgName, ValueLst) -> 80 | 81 | F = 82 | fun(<<"true">>) -> "TRUE"; 83 | (<<"false">>) -> "FALSE" 84 | end, 85 | 86 | S = string:join([ F(Value) || Value <- ValueLst ], ", "), 87 | B = list_to_binary(S), 88 | 89 | <>. 90 | 91 | 92 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 93 | binary(). 94 | 95 | bind_string_list(ArgName, ValueLst) -> 96 | 97 | Quote = 98 | fun(X) -> 99 | "\"" ++ binary_to_list(X) ++ "\"" 100 | end, 101 | 102 | S = string:join([ Quote(Value) || Value <- ValueLst ], ", "), 103 | B = list_to_binary(S), 104 | 105 | <>. 106 | 107 | 108 | -spec echo_singleton_boolean(ArgName :: binary()) -> binary(). 109 | 110 | echo_singleton_boolean(ArgName) -> 111 | <<"if( ", ArgName/binary, " ) {\n", 112 | " cat( \"", ?MSG, "{\\\"arg_name\\\": \\\"", ArgName/binary, "\\\", \\\"value\\\": \\\"true\\\"}\\n\", sep=\"\" )\n", 113 | "} else {\n", 114 | " cat( \"", ?MSG, "{\\\"arg_name\\\": \\\"", ArgName/binary, "\\\", \\\"value\\\": \\\"false\\\"}\\n\", sep=\"\" )\n", 115 | "}\n">>. 116 | 117 | 118 | -spec echo_singleton_string(ArgName :: binary()) -> binary(). 119 | 120 | echo_singleton_string(ArgName) -> 121 | <<"cat( \"", ?MSG, "{\\\"arg_name\\\": \\\"", ArgName/binary, "\\\", \\\"value\\\": \\\"\", ", ArgName/binary, ", \"\\\"}\\n\", sep=\"\" )\n">>. 122 | 123 | 124 | -spec echo_boolean_list(ArgName :: binary()) -> binary(). 125 | 126 | echo_boolean_list(ArgName) -> 127 | <<"cat( \"", ?MSG, "{\\\"arg_name\\\": \\\"", ArgName/binary, 128 | "\\\", \\\"value\\\": [\", Reduce( function( x, y ) paste( x, y, sep=\",\" ), Map( function( x ) if( x ) \"\\\"true\\\"\" else \"\\\"false\\\"\", ", 129 | ArgName/binary, " ) ), \"] }\\n\", sep=\"\" )\n">>. 130 | 131 | 132 | -spec echo_string_list(ArgName :: binary()) -> binary(). 133 | 134 | echo_string_list(ArgName) -> 135 | <<"cat( \"", ?MSG, "{\\\"arg_name\\\": \\\"", ArgName/binary, 136 | "\\\", \\\"value\\\": [\", Reduce( function( x, y ) paste( x, y, sep=\",\" ), Map( function( x ) paste( \"\\\"\", x, \"\\\"\", sep=\"\" ), ", 137 | ArgName/binary, " ) ), \"] }\\n\", sep=\"\" )\n">>. 138 | 139 | 140 | -spec prefix() -> <<>>. 141 | 142 | prefix() -> 143 | <<>>. 144 | 145 | 146 | -spec end_of_transmission() -> binary(). 147 | 148 | end_of_transmission() -> 149 | <<"cat( \"", ?EOT, "\\n\" )\n">>. 150 | 151 | 152 | -spec suffix() -> <<>>. 153 | 154 | suffix() -> 155 | <<>>. 156 | 157 | 158 | -spec process_script(Script :: binary()) -> 159 | binary(). 160 | 161 | process_script(Script) -> 162 | Script. 163 | 164 | 165 | -spec run_extended_script(ExtendedScript :: binary(), Dir :: string(), RunInfo :: _) -> 166 | {ok, binary(), [#{atom() => binary()}]} | 167 | {error, binary()}. 168 | 169 | run_extended_script(ExtendedScript, Dir, _) -> 170 | 171 | ScriptFile = string:join([Dir, "__script.R"], "/"), 172 | Call = "Rscript --vanilla __script.R", 173 | 174 | ok = file:write_file(ScriptFile, ExtendedScript), 175 | 176 | Port = effi:create_port(Call, Dir), 177 | 178 | effi:listen_port(Port). 179 | 180 | 181 | -spec get_run_info(Request :: #{atom() => _}) -> []. 182 | 183 | get_run_info(_Request) -> 184 | []. 185 | -------------------------------------------------------------------------------- /src/effi_racket.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_racket). 28 | -behaviour(effi). 29 | 30 | %%==================================================================== 31 | %% Exports 32 | %%==================================================================== 33 | 34 | % effi callbacks 35 | -export([bind_singleton_boolean/2, 36 | bind_singleton_string/2, 37 | bind_boolean_list/2, 38 | bind_string_list/2, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | prefix/0, 44 | end_of_transmission/0, 45 | suffix/0, 46 | process_script/1, 47 | run_extended_script/3, 48 | get_run_info/1]). 49 | 50 | %%==================================================================== 51 | %% Includes 52 | %%==================================================================== 53 | 54 | -include("effi.hrl"). 55 | 56 | %%==================================================================== 57 | %% Effi callback function implementations 58 | %%==================================================================== 59 | 60 | 61 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> binary(). 62 | 63 | bind_singleton_boolean(ArgName, <<"true">>) -> 64 | <<"(define ", ArgName/binary, " #t)\n">>; 65 | 66 | bind_singleton_boolean(ArgName, <<"false">>) -> 67 | <<"(define ", ArgName/binary, " #f)\n">>. 68 | 69 | 70 | -spec bind_singleton_string(ArgName :: binary(), Value :: binary()) -> binary(). 71 | 72 | bind_singleton_string(ArgName, Value) -> 73 | <<"(define ", ArgName/binary, " \"", Value/binary, "\")\n">>. 74 | 75 | 76 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 77 | binary(). 78 | 79 | bind_boolean_list(ArgName, ValueLst) -> 80 | 81 | F = 82 | fun(<<"true">>, Acc) -> <>; 83 | (<<"false">>, Acc) -> <> 84 | end, 85 | 86 | B = lists:foldl(F, <<>>, ValueLst), 87 | 88 | <<"(define ", ArgName/binary, " (list", B/binary, "))\n">>. 89 | 90 | 91 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 92 | binary(). 93 | 94 | bind_string_list(ArgName, ValueLst) -> 95 | 96 | F = 97 | fun(X, Acc) -> 98 | <> 99 | end, 100 | 101 | B = lists:foldl(F, <<>>, ValueLst), 102 | 103 | <<"(define ", ArgName/binary, " (list", B/binary, "))\n">>. 104 | 105 | 106 | -spec echo_singleton_boolean(ArgName :: binary()) -> binary(). 107 | 108 | echo_singleton_boolean(ArgName) -> 109 | <<"(if ", ArgName/binary, "\n", 110 | " (printf \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":\\\"true\\\"}\\n\")\n", 111 | " (printf \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":\\\"false\\\"}\\n\"))\n">>. 112 | 113 | 114 | -spec echo_singleton_string(ArgName :: binary()) -> binary(). 115 | 116 | echo_singleton_string(ArgName) -> 117 | <<"(printf \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":~s}\\n\" ", ArgName/binary, ")\n">>. 118 | 119 | 120 | -spec echo_boolean_list(ArgName :: binary()) -> binary(). 121 | 122 | echo_boolean_list(ArgName) -> 123 | <<"(let* ([boolean-to-string (lambda (x) (if x \"\\\"true\\\"\" \"\\\"false\\\"\"))]\n", 124 | " [l (map boolean-to-string ", ArgName/binary, ")]\n", 125 | " [s (string-join l \",\")])\n", 126 | " (printf \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":[~a]}\\n\" s))\n">>. 127 | 128 | 129 | -spec echo_string_list(ArgName :: binary()) -> binary(). 130 | 131 | echo_string_list(ArgName) -> 132 | <<"(let* ([quote-string (lambda (x) (string-append \"\\\"\" x \"\\\"\"))]\n", 133 | " [l (map quote-string ", ArgName/binary, ")]\n", 134 | " [s (string-join l \",\")])\n", 135 | " (printf \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":[~a]}\\n\" s))\n">>. 136 | 137 | 138 | -spec prefix() -> binary(). 139 | 140 | prefix() -> 141 | <<"#lang racket/base\n(require (only-in racket/string string-join))\n">>. 142 | 143 | 144 | -spec end_of_transmission() -> binary(). 145 | 146 | end_of_transmission() -> 147 | <<"(printf \"", ?EOT, "\\n\")\n">>. 148 | 149 | 150 | -spec suffix() -> <<>>. 151 | 152 | suffix() -> 153 | <<>>. 154 | 155 | 156 | -spec process_script(Script :: binary()) -> 157 | binary(). 158 | 159 | process_script(Script) -> 160 | Script. 161 | 162 | 163 | -spec run_extended_script(ExtendedScript :: binary(), Dir :: string(), RunInfo :: _) -> 164 | {ok, binary(), [#{atom() => binary()}]} | 165 | {error, binary()}. 166 | 167 | run_extended_script(ExtendedScript, Dir, _) -> 168 | 169 | ScriptFile = string:join([Dir, "__script.rkt"], "/"), 170 | Call = "racket __script.rkt", 171 | 172 | ok = file:write_file(ScriptFile, ExtendedScript), 173 | 174 | Port = effi:create_port(Call, Dir), 175 | 176 | effi:listen_port(Port). 177 | 178 | 179 | -spec get_run_info(Request :: #{atom() => _}) -> []. 180 | 181 | get_run_info(_Request) -> 182 | []. 183 | -------------------------------------------------------------------------------- /src/effi_wal.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_wal). 28 | -behaviour(effi). 29 | 30 | %%==================================================================== 31 | %% Exports 32 | %%==================================================================== 33 | 34 | % effi callbacks 35 | -export([bind_singleton_boolean/2, 36 | bind_singleton_string/2, 37 | bind_boolean_list/2, 38 | bind_string_list/2, 39 | echo_singleton_boolean/1, 40 | echo_singleton_string/1, 41 | echo_boolean_list/1, 42 | echo_string_list/1, 43 | prefix/0, 44 | end_of_transmission/0, 45 | suffix/0, 46 | process_script/1, 47 | run_extended_script/3, 48 | get_run_info/1]). 49 | 50 | -include("effi.hrl"). 51 | 52 | 53 | -spec bind_singleton_boolean(ArgName :: binary(), Value :: binary()) -> binary(). 54 | 55 | bind_singleton_boolean(ArgName, <<"true">>) when is_binary(ArgName) -> 56 | <<"(set (", ArgName/binary, " #t))\n">>; 57 | 58 | bind_singleton_boolean(ArgName, <<"false">>) when is_binary(ArgName) -> 59 | <<"(set (", ArgName/binary, " #f))\n">>. 60 | 61 | 62 | -spec bind_singleton_string(ArgName :: binary(), Value :: binary()) -> binary(). 63 | 64 | bind_singleton_string(ArgName, Value) when is_binary(ArgName), is_binary(Value) -> 65 | <<"(set (", ArgName/binary, " \"", Value/binary, "\"))\n">>. 66 | 67 | 68 | -spec bind_boolean_list(ArgName :: binary(), Value :: [binary()]) -> 69 | binary(). 70 | 71 | bind_boolean_list(ArgName, Value) when is_binary(ArgName), is_list(Value) -> 72 | 73 | F = 74 | fun(<<"true">>, Acc) -> <>; 75 | (<<"false">>, Acc) -> <> 76 | end, 77 | 78 | B = lists:foldl(F, <<>>, Value), 79 | 80 | <<"(set (", ArgName/binary, " (list", B/binary, ")))\n">>. 81 | 82 | 83 | -spec bind_string_list(ArgName :: binary(), Value :: [binary()]) -> 84 | binary(). 85 | 86 | bind_string_list(ArgName, Value) -> 87 | F = fun(S, Acc) -> <> end, 88 | B = lists:foldl(F, <<>>, Value), 89 | <<"(set (", ArgName/binary, " (list", B/binary, ")))\n">>. 90 | 91 | 92 | -spec echo_singleton_boolean(ArgName :: binary()) -> binary(). 93 | 94 | echo_singleton_boolean(ArgName) when is_binary(ArgName) -> 95 | <<"(printf \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":\\\"%s\\\"}\\n\" (if ", ArgName/binary, " \"true\" \"false\"))\n">>. 96 | 97 | 98 | -spec echo_singleton_string(ArgName :: binary()) -> binary(). 99 | 100 | echo_singleton_string(ArgName) -> 101 | <<"(printf \"", ?MSG, "{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":\\\"%s\\\"}\\n\" ", ArgName/binary, ")\n">>. 102 | 103 | 104 | -spec echo_boolean_list(ArgName :: binary()) -> binary(). 105 | 106 | echo_boolean_list(ArgName) -> 107 | <<"(let ((f (lambda (x) (if x \"true\" \"false\"))))\n (printf \"{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":[\")\n (if (length ", ArgName/binary, ")\n (let ((hd (first ", ArgName/binary, ")) (tl (rest ", ArgName/binary, "))) (printf \"\\\"%s\\\"\" (f hd)) (map (lambda (x) (printf \",\\\"%s\\\"\" (f x))) tl)))\n (printf \"]}\\n\"))\n">>. 108 | 109 | 110 | -spec echo_string_list(ArgName :: binary()) -> binary(). 111 | 112 | echo_string_list(ArgName) -> 113 | <<"(printf \"{\\\"arg_name\\\":\\\"", ArgName/binary, "\\\",\\\"value\\\":[\")\n(if (length ", ArgName/binary, ")\n (let ((hd (first ", ArgName/binary, ")) (tl (rest ", ArgName/binary, "))) (printf \"\\\"%s\\\"\" hd) (map (lambda (x) (printf \",\\\"%s\\\"\" x)) tl)))\n(printf \"]}\\n\")\n">>. 114 | 115 | 116 | -spec prefix() -> <<>>. 117 | 118 | prefix() -> 119 | <<>>. 120 | 121 | 122 | -spec end_of_transmission() -> binary(). 123 | 124 | end_of_transmission() -> 125 | <<"(print \"", ?EOT, "\")\n">>. 126 | 127 | 128 | -spec suffix() -> <<>>. 129 | 130 | suffix() -> 131 | <<>>. 132 | 133 | 134 | -spec process_script(Script :: binary()) -> 135 | binary(). 136 | 137 | process_script(Script) when is_binary(Script) -> 138 | Script. 139 | 140 | 141 | -spec run_extended_script(ExtendedScript :: binary(), 142 | Dir :: string(), 143 | RunInfo :: _) -> 144 | {ok, binary(), [#{atom() => binary()}]} | 145 | {error, binary()}. 146 | 147 | run_extended_script(ExtendedScript, Dir, _RunInfo) -> 148 | 149 | ScriptFile = string:join([Dir, "__script.wal"], "/"), 150 | Call = "wal __script.wal", 151 | 152 | ok = file:write_file(ScriptFile, ExtendedScript), 153 | Port = effi:create_port(Call, Dir), 154 | 155 | effi:listen_port(Port). 156 | 157 | 158 | -spec get_run_info(Request :: #{atom() => _}) -> _. 159 | 160 | get_run_info(_Request) -> 161 | []. 162 | -------------------------------------------------------------------------------- /test/effi_wal_test.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | %% 3 | %% Effi: Erlang foreign function interface 4 | %% 5 | %% Copyright 2015 Jörgen Brandt 6 | %% 7 | %% Licensed under the Apache License, Version 2.0 (the "License"); 8 | %% you may not use this file except in compliance with the License. 9 | %% You may obtain a copy of the License at 10 | %% 11 | %% http://www.apache.org/licenses/LICENSE-2.0 12 | %% 13 | %% Unless required by applicable law or agreed to in writing, software 14 | %% distributed under the License is distributed on an "AS IS" BASIS, 15 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | %% See the License for the specific language governing permissions and 17 | %% limitations under the License. 18 | %% 19 | %% ------------------------------------------------------------------- 20 | %% @author Jörgen Brandt 21 | %% @copyright 2015 22 | %% 23 | %% 24 | %% @end 25 | %% ------------------------------------------------------------------- 26 | 27 | -module(effi_wal_test). 28 | 29 | -include_lib("eunit/include/eunit.hrl"). 30 | 31 | -import(effi_wal, 32 | [bind_singleton_boolean/2, 33 | bind_singleton_string/2, 34 | bind_boolean_list/2, 35 | bind_string_list/2, 36 | echo_singleton_boolean/1, 37 | echo_singleton_string/1, 38 | echo_boolean_list/1, 39 | echo_string_list/1, 40 | prefix/0, 41 | end_of_transmission/0, 42 | suffix/0, 43 | process_script/1, 44 | get_run_info/1]). 45 | 46 | 47 | effi_wal_test_() -> 48 | {foreach, 49 | fun() -> ok end, 50 | fun(_) -> ok end, 51 | [{"bind_singleton_boolean true", fun test_bind_singleton_boolean_true/0}, 52 | {"bind_singleton_boolean false", fun test_bind_singleton_boolean_false/0}, 53 | {"bind_singleton_string on empty string", fun test_bind_singleton_string_empty/0}, 54 | {"bind_singleton_string on non-empty string", fun test_bind_singleton_string_nonempty/0}, 55 | {"bind_boolean_list on empty list", fun test_bind_boolean_list_empty/0}, 56 | {"bind_boolean_list on non-empty list", fun test_bind_boolean_list_nonempty/0}, 57 | {"bind_string_list on empty list", fun test_bind_string_list_empty/0}, 58 | {"bind_string_list on non-empty list", fun test_bind_string_list_nonempty/0}, 59 | {"echo_singleton_boolean", fun test_echo_singleton_boolean/0}, 60 | {"echo_singleton_string", fun test_echo_singleton_string/0}, 61 | {"echo_boolean_list", fun test_echo_boolean_list/0}, 62 | {"echo_string_list", fun test_echo_string_list/0}, 63 | {"prefix", fun test_prefix/0}, 64 | {"end_of_transmission", fun test_end_of_transmission/0}, 65 | {"suffix", fun test_suffix/0}, 66 | {"process_script empty", fun test_process_script_empty/0}, 67 | {"process_script non-empty", fun test_process_script_nonempty/0}, 68 | {"get_run_info", fun test_get_run_info/0}]}. 69 | 70 | 71 | test_bind_singleton_boolean_true() -> 72 | ?assertEqual(<<"(set (x #t))\n">>, bind_singleton_boolean(<<"x">>, <<"true">>)). 73 | 74 | 75 | test_bind_singleton_boolean_false() -> 76 | ?assertEqual(<<"(set (x #f))\n">>, bind_singleton_boolean(<<"x">>, <<"false">>)). 77 | 78 | 79 | test_bind_singleton_string_empty() -> 80 | ?assertEqual(<<"(set (x \"\"))\n">>, bind_singleton_string(<<"x">>, <<"">>)). 81 | 82 | 83 | test_bind_singleton_string_nonempty() -> 84 | ?assertEqual(<<"(set (x \"blub\"))\n">>, bind_singleton_string(<<"x">>, <<"blub">>)). 85 | 86 | 87 | test_bind_boolean_list_empty() -> 88 | ?assertEqual(<<"(set (l (list)))\n">>, bind_boolean_list(<<"l">>, [])). 89 | 90 | 91 | test_bind_boolean_list_nonempty() -> 92 | ?assertEqual(<<"(set (l (list #t #f #t)))\n">>, bind_boolean_list(<<"l">>, [<<"true">>, <<"false">>, <<"true">>])). 93 | 94 | 95 | test_bind_string_list_empty() -> 96 | ?assertEqual(<<"(set (l (list)))\n">>, bind_string_list(<<"l">>, [])). 97 | 98 | 99 | test_bind_string_list_nonempty() -> 100 | ?assertEqual(<<"(set (l (list \"bla\" \"blub\")))\n">>, bind_string_list(<<"l">>, [<<"bla">>, <<"blub">>])). 101 | 102 | 103 | test_echo_singleton_boolean() -> 104 | ?assertEqual(<<"(printf \"{\\\"arg_name\\\":\\\"a\\\",\\\"value\\\":\\\"%s\\\"}\\n\" (if a \"true\" \"false\"))\n">>, echo_singleton_boolean(<<"a">>)). 105 | 106 | 107 | test_echo_singleton_string() -> 108 | ?assertEqual(<<"(printf \"{\\\"arg_name\\\":\\\"a\\\",\\\"value\\\":\\\"%s\\\"}\\n\" a)\n">>, echo_singleton_string(<<"a">>)). 109 | 110 | 111 | test_echo_boolean_list() -> 112 | ?assertEqual(<<"(let ((f (lambda (x) (if x \"true\" \"false\"))))\n (printf \"{\\\"arg_name\\\":\\\"l\\\",\\\"value\\\":[\")\n (if (length l)\n (let ((hd (first l)) (tl (rest l))) (printf \"\\\"%s\\\"\" (f hd)) (map (lambda (x) (printf \",\\\"%s\\\"\" (f x))) tl)))\n (printf \"]}\\n\"))\n">>, echo_boolean_list(<<"l">>)). 113 | 114 | 115 | test_echo_string_list() -> 116 | ?assertEqual(<<"(printf \"{\\\"arg_name\\\":\\\"l\\\",\\\"value\\\":[\")\n(if (length l)\n (let ((hd (first l)) (tl (rest l))) (printf \"\\\"%s\\\"\" hd) (map (lambda (x) (printf \",\\\"%s\\\"\" x)) tl)))\n(printf \"]}\\n\")\n">>, echo_string_list(<<"l">>)). 117 | 118 | 119 | test_prefix() -> 120 | ?assertEqual(<<>>, prefix()). 121 | 122 | 123 | test_end_of_transmission() -> 124 | ?assertEqual(<<"(print \"\")\n">>, end_of_transmission()). 125 | 126 | 127 | test_suffix() -> 128 | ?assertEqual(<<>>, suffix()). 129 | 130 | 131 | test_process_script_empty() -> 132 | ?assertEqual(<<>>, process_script(<<>>)). 133 | 134 | 135 | test_process_script_nonempty() -> 136 | ?assertEqual(<<"blub">>, process_script(<<"blub">>)). 137 | 138 | 139 | test_get_run_info() -> 140 | ?assertEqual([], get_run_info(#{})). 141 | --------------------------------------------------------------------------------