├── .gitignore ├── LICENSE ├── MANIFEST.terms ├── README.md ├── build.sh ├── docs └── differences_from_erlang.md ├── include_md ├── luvviescript.hrl.md └── macros.hrl.md ├── js ├── README.txt ├── bert.js ├── convert.html ├── run.sh ├── terms │ └── terms.log └── type_conversion.js ├── luvviescript_js ├── erlang.js ├── erlangatom.js ├── erlangfloat.js ├── erlangint.js ├── erlanglist.js ├── erlangtuple.js ├── luvv_utils.js └── test.js ├── priv ├── demo │ ├── Elizabeth-Taylor.jpg │ ├── demo.css │ ├── demo.js │ ├── demo.js.map │ ├── demo.tar │ ├── index.html │ ├── runner.js │ └── test │ │ └── passing │ │ └── psrc │ │ └── demo.erl ├── first_time │ └── luvviescript.app ├── images │ ├── gordonguthrie.jpg │ └── luvviescript.png ├── json-prettyprinter │ ├── prettyjson.py │ └── prettyprinter.README └── rebar_plugins │ ├── compile_literate.erl │ ├── first_time.erl │ ├── make_luvv.erl │ ├── make_tests.erl │ └── markup.erl ├── rebar.config ├── src_md ├── bert_utils.erl.md ├── debug_json.erl.md ├── from_core.erl.md ├── lcompile.erl.md ├── luvv_utils.erl.md ├── luvviescript.app.src ├── luvviescript.erl.md ├── make_utils.erl.md ├── merge.erl.md ├── test_utils.erl.md ├── to_js_ast.erl.md └── tokens.erl.md └── test ├── not_passing ├── include │ └── included.hrl └── src │ ├── 1b_simpler.erl │ ├── 1c_simple.erl │ ├── 1d_records_and_fns.erl │ ├── 1e_multiple_arity_fns.erl │ ├── 1f_multiple_arity_fns_with_exports.erl │ ├── 2a_types_1.erl │ ├── 2b_operators_1.erl │ ├── 2c_assignment.erl │ ├── basic_brackets.erl │ ├── basic_casemod.erl │ ├── basic_clauses.erl │ ├── basic_clauses2.erl │ ├── basic_comparisons.erl │ ├── basic_functions.erl │ ├── basic_ifmod.erl │ ├── basic_including.erl │ ├── basic_line_endings.erl │ ├── basic_macros.erl │ ├── basic_records.erl │ └── basic_types2.erl ├── passing └── src │ ├── 1a_simplest.erl │ └── demo.erl ├── test_compile_hdr.part ├── test_run_hdr.part └── unsupported ├── clauses.erl ├── operators.erl └── types.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | deps 3 | *.o 4 | *.beam 5 | *.plt 6 | *.P 7 | ebin/ 8 | js/ 9 | *~ 10 | erl_crash.dump 11 | test/*_SUITE.erl 12 | priv/prezi/ 13 | logs/ 14 | src_md/.erl 15 | include_md/.hrl 16 | test/not_passing/debug/ 17 | *.ast 18 | *.ast2 19 | *.ast3 20 | *.tks 21 | *.tks2 22 | *.P 23 | *.P2 24 | include/ 25 | src/ 26 | priv/node_modules 27 | priv/rebar_plugins -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Vixo 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.terms: -------------------------------------------------------------------------------- 1 | %% this is a manifest file for the Stal Erlang Project Index 2 | % Stal is hosted at http://erlangotp.com 3 | {stal_conf_version, 1}. 4 | 5 | %% Project Details 6 | %% these are designed to help people find the software they need 7 | {name, "LuvvieScript"}. 8 | {project_version, "1"}. 9 | {license, "BSD 2-clause"}. 10 | {description, "Erlang-to-Javascript transpiler"}. 11 | {details, "An OTP-ish dialect of Erlang which can run on both the server-side and the browser"}. 12 | {tags, ["javascript", "transpiler", "compiler", "browser", "single-stack"]}. 13 | {status, "alpha"}. % alpha | beta | production 14 | 15 | %% Required contact details 16 | {url, "http://github.com/hypernumbers/LuvvieScript"}. 17 | 18 | %% Optional contact details 19 | % {irc, ""}. % optional 20 | {maintainers, [ 21 | [ 22 | {name, "Gordon Guthrie"}, 23 | {email, "gordon@hypernumbers.com"}, 24 | {twitter, "gordonguthrie"} 25 | ], 26 | [ 27 | {name, "Luvvie Script"}, 28 | {twitter, "luvviescript"} 29 | ] 30 | ]}. %optional 31 | {project_homepage, "http://luvv.ie"}. % optional 32 | 33 | %% How to include this code in your project 34 | %% are there other build system that need to be represented? 35 | %% what about Elixir ones? 36 | %% or LFe ones? 37 | %% or Joxa ones? 38 | % {rebar, {version, "1"}, {, {git, "git://github.com/hypernumbers/.git","master"}}}. 39 | % {'erlang.mk', ??}. % Erlang build script 40 | % {'mix.exs' ??}. % Elixir package manager 41 | % {'expm', ??}. % Elixir package manager 42 | {scm, "git clone https://github.com/hypernumbers/LuvvieScript.git"}. 43 | 44 | %% Some terms to control usage of this manifest by Stal 45 | {stal_include, true}. 46 | {stal_deprecated, false}. 47 | % {stal_superseded_by, "foo/bar"}. % optional 48 | 49 | %% Are there other manifest systems that might want to pull these terms in? -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **New** LuvvieScript is in the Google Summer Of Code 2014 - for details and applying see http://beamcommunity.github.io/ 2 | 3 | **New** our website is luvv.ie! 4 | 5 | **New** Interested in writing your own to-javascript-transpiler? Got some tricks'n'tips for ya in a new blog post! 6 | 7 |
8 |

Hey JS/Node Developers!

9 |

Did you know that Erlang was a big influence on the design of Node.js? 10 |

11 |

LuvvieScript is a compile-to-JavaScript dialect of Erlang that runs in the browser. It is a small but interesting early-stage project that will challenge preconceptions about how one should structure large JavaScript applcations. Exploring LuvvieScript will provide deep insight into different ways to think about Javascript apps. 12 |

13 |

It's an open source project, so feel free to install LuvvieScript join us and 14 | get started! 15 | Don't worry, you don't need to be an Erlang guru :-) You can jump right in and see how we are 16 | transpiling Erlang to JavaScript. 17 |

18 |
19 | 20 |
21 |

Erlang In The Browser

22 |

LuvvieScript is designed to deliver the simplicity of Erlang's message passing and pattern-matching paradigm to the developers of large scale web systems. LuvvieScript is a front-end language that works seamlessly with high-availability Erlang server clusters for fast and powerful web development. LuvvieScript is perfectly suited for DevOps and environments concerned with continuous integration/continuous deployment. 23 |

24 |
25 | 26 |
27 |

Fork Us On Github

28 |

Commit code to the project at GitHub and you will get one of these fab t-shirts: 29 |

30 | LuvvieScript T-Shirts 31 |

Keep in mind: these T-Shirts are not available in stores! 32 |

33 |
34 | 35 |

In Brief

36 | 42 |
43 |

If you have read this far you should follow @LuvvieScript or @gordonguthrie on Twitter.

44 |
45 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | rebar markup skip_deps=true && rebar compile_literate skip_deps=true && rebar compile skip_deps=true 2 | -------------------------------------------------------------------------------- /docs/differences_from_erlang.md: -------------------------------------------------------------------------------- 1 | Differences Between LuvvieScript And Erlang 2 | =========================================== 3 | 4 | LuvvieScript integers are expressed as doubles - whereas Erlang ones are bignums. 5 | 6 | There is no type difference between an int and a float in LuvvieScript so the following operations are different 7 | 8 | In erlang: 9 | 10 | ```erlang 11 | 1 == 1.0 -> true 12 | 1 =:= 1.0 -> false 13 | 1 /= 1.0 -> false 14 | 1=/= 1.0 -> true 15 | ``` 16 | In LuvvieScript: 17 | 18 | ```erlang 19 | 1 == 1.0 -> true 20 | 1 =:= 1.0 -> true 21 | 1 /= 1.0 -> false 22 | 1 =/= 1.0 -> false 23 | ``` 24 | -------------------------------------------------------------------------------- /include_md/luvviescript.hrl.md: -------------------------------------------------------------------------------- 1 | This file contains the record definitions for LuvvieScript 2 | 3 | ```erlang 4 | -record(module, { 5 | name = [] :: string() , 6 | file = [] :: string(), 7 | attributes = [] :: list(), 8 | export_all = false :: boolean(), 9 | exports = [] :: list(), 10 | includes = [] :: list(), 11 | records = [] :: list() 12 | }). 13 | 14 | -record(function, { 15 | name = [] :: string(), 16 | variables = [] :: list() 17 | }). 18 | 19 | -record(context, { 20 | module = #module{} :: #module{}, 21 | current_function = #function{} :: #function{} 22 | }). 23 | 24 | -record(js_context, { 25 | name = [] :: list(), 26 | exports = [] :: list() 27 | }). 28 | ``` 29 | -------------------------------------------------------------------------------- /include_md/macros.hrl.md: -------------------------------------------------------------------------------- 1 | ```erlang 2 | -define(NOSRCMAP, []). 3 | -define(PATTERNMATCHFAIL, to_js_ast:make_fail()). 4 | -define(EMPTYOBJECT, to_js_ast:make_object({obj, []}, [])). 5 | -define(WITHBREAK, true). 6 | -define(NOBREAK, false). 7 | -define(EMPTYJSONLIST, []). 8 | -define(NOTINITIALISED, []). 9 | ``` 10 | -------------------------------------------------------------------------------- /js/README.txt: -------------------------------------------------------------------------------- 1 | PURPOSE 2 | ------- 3 | 4 | To convert Erlang types to Javascript ones (and back) 5 | 6 | BUILDING TERMS 7 | -------------- 8 | 9 | The term file in js/terms/terms.log is built by running the erlang function bert_utils:all/0 10 | 11 | This term file (with a slight edit) is wheeched into the javascript. 12 | 13 | HOWTO USE 14 | --------- 15 | 16 | Make a symlink to the bert.js library by running the command 17 | ln -s ../deps/rusty.io/bert.js ./bert.js 18 | 19 | The open the page convert.html in a browser and it will run the module type_conversion.js 20 | -------------------------------------------------------------------------------- /js/bert.js: -------------------------------------------------------------------------------- 1 | ../deps/rustyio/bert.js -------------------------------------------------------------------------------- /js/convert.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Erlang Terms To Javascript Conversion

9 | (Having Problems? Not seeing anything? Have you symlinked bert.js in?) 10 |

11 | 
12 | 
13 | 


--------------------------------------------------------------------------------
/js/run.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | rhino -e type_conversion.js


--------------------------------------------------------------------------------
/js/terms/terms.log:
--------------------------------------------------------------------------------
1 | {"integer": "131,98,0,0,48,57"};
2 | {"float": "131,99,49,46,50,51,52,52,57,57,57,57,57,57,57,57,57,57,57,57,51,48,
3 |             55,50,101,43,48,48,0,0,0,0,0"};
4 | {"atom": "131,100,0,4,97,116,111,109"};
5 | {"null": "131,106"};
6 | {"string": "131,107,0,6,97,98,99,100,101,102"};
7 | {"tuple": "131,104,3,100,0,1,97,97,1,107,0,1,88"};
8 | {"complex_list": "131,108,0,0,0,3,100,0,1,97,97,1,107,0,1,88,106"};
9 | 


--------------------------------------------------------------------------------
/js/type_conversion.js:
--------------------------------------------------------------------------------
 1 | var type_conversion = {};
 2 | 
 3 | type_conversion.run = function() {
 4 | 
 5 |     var Cases = [{"integer": "131,98,0,0,48,57"},
 6 |                  {"float": "131,99,49,46,50,51,52,52,57,57,57,57,57,57,57,57,57,57,57,57,51,48,55,50,101,43,48,48,0,0,0,0,0"},
 7 |                  {"atom": "131,100,0,4,97,116,111,109"},
 8 |                  {"null": "131,106"},
 9 |                  {"string": "131,107,0,6,97,98,99,100,101,102"},
10 |                  {"tuple": "131,104,3,100,0,1,97,97,1,107,0,1,88"},
11 |                  {"complex_list": "131,108,0,0,0,3,100,0,1,97,97,1,107,0,1,88,106"}];
12 |     return Bert.decode("131,98,0,0,48,57");
13 | }
14 | 


--------------------------------------------------------------------------------
/luvviescript_js/erlang.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * This javascript library corresponds to the Erlang module in
 3 |  * pure Erlang
 4 |  *
 5 |  * LuvvieScript implements only a SUBSET of Erlang and this module
 6 |  * reflects that
 7 |  */
 8 | var erlang = (function() {
 9 | 
10 |     var internalfns = {}, exportedfns = {};
11 | 
12 |     // Internal Fns
13 |     internalfns.is = function(Obj, Type) {
14 |         if (("type" in Obj) && (Obj.type() === Type)) {
15 |             return true;
16 |         } else {
17 |             return false;
18 |         };
19 |     };
20 | 
21 |     exportedfns.is_int = function(Integer) {
22 |         return internalfns.is(Integer, "int");
23 |     };
24 | 
25 |     exportedfns.is_float = function(Float) {
26 |         return internalfns.is(Float, "float");
27 |     };
28 | 
29 |     exportedfns.is_atom = function(Atom) {
30 |         return internalfns.is(Atom, "atom");
31 |     };
32 | 
33 |     exportedfns.is_tuple = function(Tuple) {
34 |         return internalfns.is(Tuple, "tuple");
35 |     };
36 | 
37 |     exportedfns.is_list = function(List) {
38 |         return internalfns.is(List, "list");
39 |     };
40 | 
41 |     return exportedfns;
42 | })()


--------------------------------------------------------------------------------
/luvviescript_js/erlangatom.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * The implementation of erlang atoms as a Javascript Class
 3 |  */
 4 | 
 5 | // The compiiler will only ever emit a call to this with a single argument
 6 | function erlangatom () {
 7 |     this.value = arguments[0];
 8 | }
 9 | 
10 | erlangatom.prototype.type = function () {
11 |     return 'atom';
12 | }
13 | 
14 | erlangatom.prototype.external_format = function() {
15 |     var Json = {"atom": this.value}
16 |     return JSON.stringify(Json);
17 | }


--------------------------------------------------------------------------------
/luvviescript_js/erlangfloat.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * a naive implementation of floats for LuvvieScript
 3 |  * this does run time type checks
 4 |  *
 5 |  * might be switched to compile time casting later on
 6 |  */
 7 | 
 8 | function erlangfloat () {
 9 |     this.value = arguments[0];
10 | }
11 | 
12 | erlangfloat.prototype.type = function () {
13 |     return 'float';
14 | }
15 | 
16 | erlangfloat.prototype.external_format = function() {
17 |     var Json = {"float": this.value}
18 |     return JSON.stringify(Json);
19 | }


--------------------------------------------------------------------------------
/luvviescript_js/erlangint.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * a naive implementation of integers for LuvvieScript
 3 |  * this does run time type checks
 4 |  *
 5 |  * might be switched to compile time casting later on
 6 |  */
 7 | 
 8 | function erlangint () {
 9 |     this.list = arguments;
10 | }
11 | 
12 | erlangint.prototype.type = function () {
13 |     return 'int';
14 | }
15 | 
16 | erlangint.prototype.external_format = function() {
17 |     var Json = {"int": this.value}
18 |     return JSON.stringify(Json);
19 | }


--------------------------------------------------------------------------------
/luvviescript_js/erlanglist.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * a naive implementation of lists for LuvvieScript
 3 |  * this list module is 'copy on write' with all the penalties that incurs
 4 |  *
 5 |  * This module is to get a running system up so that it can be optimised laters
 6 |  */
 7 | 
 8 | function erlanglist () {
 9 |     this.value = arguments;
10 | }
11 | 
12 | erlanglist.prototype.type = function () {
13 |     return 'list';
14 | }
15 | 
16 | erlanglist.prototype.external_format = function() {
17 |     var Json = {"list": this.value}
18 |     return JSON.stringify(Json);
19 | }


--------------------------------------------------------------------------------
/luvviescript_js/erlangtuple.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * The implementation of erlang tuples as a Javascript Class
 3 |  * Thi is a very early and rough implentation to get an end-to-end
 4 |  * working system
 5 |  *
 6 |  * It is *probably* NOT how it will end up being
 7 |  */
 8 | 
 9 | function erlangtuple () {
10 |     this.value = arguments;
11 | }
12 | 
13 | erlangtuple.prototype.type = function () {
14 |     return 'tuple';
15 | }
16 | 
17 | erlangtuple.prototype.external_format = function() {
18 |     var Json = {"tuple": this.value}
19 |     return JSON.stringify(Json);
20 | }


--------------------------------------------------------------------------------
/luvviescript_js/luvv_utils.js:
--------------------------------------------------------------------------------
1 | luvv_utils = {};
2 | 
3 | luvv_utils.external_format = function (Obj) {
4 |     return Obj.external_format();
5 | }
6 | 


--------------------------------------------------------------------------------
/luvviescript_js/test.js:
--------------------------------------------------------------------------------
1 | load("erlang.js");
2 | load("erlangatom.js");
3 | load("erlanglist.jst")


--------------------------------------------------------------------------------
/priv/demo/Elizabeth-Taylor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypernumbers/LuvvieScript/d10da50e0719ff26690749570c75e1e614214381/priv/demo/Elizabeth-Taylor.jpg


--------------------------------------------------------------------------------
/priv/demo/demo.css:
--------------------------------------------------------------------------------
 1 | body {
 2 |     background-image : url("Elizabeth-Taylor.jpg");
 3 |     background-size  : 100%;
 4 |     font-family      : Verdana;
 5 | }
 6 | 
 7 | h2, h3 {
 8 |     color       : #FFF;
 9 | }
10 | 
11 | button {
12 |     padding          : 20px;
13 |     color            : #000;
14 |     background-color : #FFF
15 |     font-size        : 36px;
16 |     font-weight      : bold;
17 | }
18 | 
19 | #response {
20 |     padding-top : 20px;
21 |     font-size   : 10px;
22 |     color       : #FFF;
23 | }


--------------------------------------------------------------------------------
/priv/demo/demo.js:
--------------------------------------------------------------------------------
 1 | var exports = {};
 2 | exports.test = function () {
 3 |     _args = arguments.length;
 4 |     switch (_args) {
 5 |     case 0:
 6 |         return test();
 7 |         break;
 8 |     default:
 9 |     }
10 | };
11 | first = function () {
12 |     _args = arguments.length;
13 |     switch (_args) {
14 |     case 0:
15 |         return 1;
16 |         break;
17 |     default:
18 |         return 'throw error';
19 |     }
20 | };
21 | second = function () {
22 |     _args = arguments.length;
23 |     switch (_args) {
24 |     case 0:
25 |         return 2;
26 |         break;
27 |     default:
28 |         return 'throw error';
29 |     }
30 | };
31 | test = function () {
32 |     _args = arguments.length;
33 |     switch (_args) {
34 |     case 0:
35 |         var A;
36 |         var B;
37 |         var C;
38 |         var cor3;
39 |         A = first();
40 |         B = second();
41 |         C = third();
42 |         cor3 = B / C;
43 |         return A + cor3;
44 |         break;
45 |     default:
46 |         return 'throw error';
47 |     }
48 | };
49 | third = function () {
50 |     _args = arguments.length;
51 |     switch (_args) {
52 |     case 0:
53 |         return 3;
54 |         break;
55 |     default:
56 |         return 'throw error';
57 |     }
58 | };
59 | //@ sourceMappingURL=demo.js.map


--------------------------------------------------------------------------------
/priv/demo/demo.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"test/passing/src/../psrc/demo.erl","sources":["test/passing/src/../psrc/demo.erl"],"names":[],"mappings":";;;;;;;;;;;;;;eAaK,C;;;;;;;;;;eAGA,C;;;;;;;;;;;;;;QATA,C;QACA,C;QACA,C;;;;;;;;;;;;eAUA,C"}


--------------------------------------------------------------------------------
/priv/demo/demo.tar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypernumbers/LuvvieScript/d10da50e0719ff26690749570c75e1e614214381/priv/demo/demo.tar


--------------------------------------------------------------------------------
/priv/demo/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   LuvvieScript Demo - Erlang In The Browser
 4 |   
 5 |   
 6 |   
 7 | 
 8 | 
 9 | 

LuvvieScript Demo

10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 61 | 62 | -------------------------------------------------------------------------------- /priv/demo/runner.js: -------------------------------------------------------------------------------- 1 | btn_click = function() { 2 | var response = test(); 3 | console.log(response); 4 | var dv = document.getElementById('response'); 5 | console.log(dv); 6 | dv.innerHTML = response; 7 | }; -------------------------------------------------------------------------------- /priv/demo/test/passing/psrc/demo.erl: -------------------------------------------------------------------------------- 1 | -file("test/passing/src/demo.erl", 1). 2 | 3 | -module(demo). 4 | 5 | -export([test/0]). 6 | 7 | test() -> 8 | A = first(), 9 | B = second(), 10 | C = third(), 11 | A + B / C. 12 | 13 | first() -> 14 | 1. 15 | 16 | second() -> 17 | 2. 18 | 19 | third() -> 20 | 3. 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /priv/first_time/luvviescript.app: -------------------------------------------------------------------------------- 1 | {application,luvviescript, 2 | [{description,"luvviescript"}, 3 | {vsn,"1"}, 4 | {registered,[]}, 5 | {applications,[kernel,stdlib]}, 6 | {env,[]}, 7 | {modules,[]}]}. 8 | -------------------------------------------------------------------------------- /priv/images/gordonguthrie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypernumbers/LuvvieScript/d10da50e0719ff26690749570c75e1e614214381/priv/images/gordonguthrie.jpg -------------------------------------------------------------------------------- /priv/images/luvviescript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hypernumbers/LuvvieScript/d10da50e0719ff26690749570c75e1e614214381/priv/images/luvviescript.png -------------------------------------------------------------------------------- /priv/json-prettyprinter/prettyjson.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Convert JSON data to human-readable form. 4 | 5 | (Reads from stdin and writes to stdout) 6 | """ 7 | 8 | import sys 9 | import json 10 | 11 | 12 | print json.dumps(json.loads(sys.stdin.read()), indent=4) 13 | sys.exit(0) -------------------------------------------------------------------------------- /priv/json-prettyprinter/prettyprinter.README: -------------------------------------------------------------------------------- 1 | To use this pretty printer simply cat a json file through it and pipe the output to a file 2 | 3 | cat somefile.json | ./path/to/prettyjson.py > somefile.pretty.json -------------------------------------------------------------------------------- /priv/rebar_plugins/compile_literate.erl: -------------------------------------------------------------------------------- 1 | %%% @author Gordon Guthrie 2 | %%% @copyright (C) 2013, Gordon Guthrie 3 | %%% @doc This is a rebar plugin for compiling literate erlang 4 | %%% 5 | %%% @end 6 | %%% Created : 2 Sep 2013 by gordon@vixo.com 7 | -module(compile_literate). 8 | 9 | -export([ 10 | compile_literate/2 11 | ]). 12 | 13 | compile_literate(Config, AppFile) -> 14 | App = filename:basename(AppFile), 15 | %% once you have run this rebar plugin once the AppFile changes :( 16 | case App of 17 | "luvviescript.app" -> ok = compile_l(Config); 18 | "luvviescript.app.src" -> ok = compile_l(Config); 19 | _ -> ok 20 | end. 21 | 22 | compile_l(Config) -> 23 | ErlOpts = rebar_config:get(Config, erl_opts, []), 24 | SrcDirs = get_src_dirs(ErlOpts), 25 | ok = clear_down(SrcDirs), 26 | CompilerOptions = get_compiler_options(ErlOpts), 27 | ErlFiles = [filelib:wildcard(X ++ "/../src_md/*.erl.md") || X <- SrcDirs], 28 | [ok = compile_file(X, CompilerOptions, erl) || X <- lists:merge(ErlFiles)], 29 | HrlFiles = [filelib:wildcard(X ++ "/../include_md/*.hrl.md") || X <- SrcDirs], 30 | [ok = compile_file(X, CompilerOptions, hrl) || X <- lists:merge(HrlFiles)], 31 | ok. 32 | 33 | compile_file(File, CompilerOptions, Type) -> 34 | CWD = rebar_utils:get_cwd(), 35 | {ok, Lines} = read_lines(CWD ++ "/" ++ File), 36 | Source = make_erlang_source(Lines), 37 | ok = write_source_and_compile(Source, File, CompilerOptions, Type). 38 | 39 | make_erlang_source(Lines) -> 40 | make_erl2(Lines, comment, []). 41 | 42 | make_erl2([], _Type, Acc) -> 43 | lists:flatten(lists:reverse(Acc)); 44 | make_erl2(["```erlang" ++ _Rest | T], comment, Acc) -> 45 | make_erl3(T, erlang, ["%%%```erlang\n" | Acc]); 46 | make_erl2([H | T], comment, Acc) -> 47 | make_erl2(T, comment, ["%%%" ++ H | Acc]). 48 | 49 | make_erl3([], _Type, Acc) -> 50 | lists:flatten(lists:reverse(Acc)); 51 | make_erl3(["\n" | T], erlang, Acc) -> 52 | make_erl3(T, erlang, ["\n" | Acc]); 53 | make_erl3([" " ++ Rest | T], erlang, Acc) -> 54 | make_erl3(T, erlang, [Rest | Acc]); 55 | make_erl3(["```" ++ _Rest | T], erlang, Acc) -> 56 | make_erl2(T, comment, ["%%%```\n" | Acc]); 57 | %% Oops, not indented? lets comment out then 58 | make_erl3(List, erlang, Acc) -> 59 | make_erl2(List, comment, Acc). 60 | 61 | read_lines(File) -> 62 | case file:open(File, read) of 63 | {error, Err} -> {error, Err}; 64 | {ok, Id} -> read_l2(Id, []) 65 | end. 66 | 67 | read_l2(Id, Acc) -> 68 | case file:read_line(Id) of 69 | {ok, Data} -> read_l2(Id, [Data | Acc]); 70 | {error, Err} -> {error, Err}; 71 | eof -> {ok, lists:reverse(Acc)} 72 | end. 73 | 74 | get_src_dirs(ErlOpts) -> 75 | case proplists:get_value(src_dirs, ErlOpts) of 76 | undefined -> ["src"]; 77 | SrcDirs -> SrcDirs 78 | end. 79 | 80 | get_compiler_options(ErlOpts) -> 81 | proplists:delete(src_dirs, ErlOpts). 82 | 83 | write_source_and_compile(Source, File, CompilerOptions, Type) -> 84 | File2 = filename:basename(filename:rootname(File)), 85 | Dir = filename:dirname(File), 86 | NewCompOpts = adjust_output_dirs(CompilerOptions, Dir), 87 | Dir2 = case Type of 88 | erl -> Dir ++ "/../src/"; 89 | hrl -> Dir ++ "/../include/" 90 | end, 91 | ok = filelib:ensure_dir(Dir2), 92 | ok = file:write_file(Dir2 ++ File2, Source), 93 | case Type of 94 | erl -> os:cmd("cp " ++ Dir ++ "/*.app.src " ++ Dir2); 95 | hrl -> ok 96 | end, 97 | ok. 98 | 99 | adjust_output_dirs(CompilerOptions, Dir) -> 100 | 101 | case proplists:is_defined(outdirs, CompilerOptions) of 102 | false -> 103 | OutputDir = "ebin/", 104 | filelib:ensure_dir(OutputDir), 105 | [{outdir, OutputDir} | CompilerOptions]; 106 | true -> 107 | CompilerOptions 108 | end. 109 | 110 | clear_down(SrcDirs) -> 111 | %% need to ensure that src/ and include/ directories exist 112 | %% because the first time you run this they don't 113 | [ok = filelib:ensure_dir(X) || X <- SrcDirs], 114 | [ok = filelib:ensure_dir(X ++ "/../include/") || X <- SrcDirs], 115 | WildCards = lists:merge([[ 116 | X ++ "/../include/*", 117 | X ++ "/../src/*" 118 | ] || X <- SrcDirs]), 119 | Files = lists:merge([filelib:wildcard(X) || X <- WildCards]), 120 | [ok = file:delete(X) || X <- Files], 121 | ok. 122 | -------------------------------------------------------------------------------- /priv/rebar_plugins/first_time.erl: -------------------------------------------------------------------------------- 1 | %%% @author Gordon Guthrie 2 | %%% @copyright (C) 2013, Gordon Guthrie 3 | %%% @doc Sets up luvviescript the first time 4 | %%% 5 | %%% @end 6 | %%% Created : 15 Aug 2013 by gordonguthrie@backawinner.gg 7 | 8 | -module(first_time). 9 | 10 | -export([ 11 | first_time/2 12 | ]). 13 | 14 | first_time(_Config, AppFile) -> 15 | App = filename:basename(AppFile), 16 | case App of 17 | "undefined" -> ok = first_t(); 18 | _ -> ok 19 | end. 20 | 21 | first_t() -> 22 | io:format("Creating the luvviescript.app file that rebar needs~n"), 23 | ok = filelib:ensure_dir("ebin/"), 24 | {ok, _} = file:copy("priv/first_time/luvviescript.app", "ebin/luvviescript.app"), 25 | ok. 26 | -------------------------------------------------------------------------------- /priv/rebar_plugins/make_luvv.erl: -------------------------------------------------------------------------------- 1 | %%% @author Gordon Guthrie 2 | %%% @copyright (C) 2013, Gordon Guthrie 3 | %%% @doc This script compiles Erlang to Javascript 4 | %%% 5 | %%% @end 6 | %%% Created : 15 Aug 2013 by gordonguthrie@backawinner.gg 7 | 8 | -module(make_luvv). 9 | 10 | -export([ 11 | make_luvv/2 12 | ]). 13 | 14 | -define(JSDIR, "js/"). 15 | -define(TESTDIRS, ["test/passing"]). 16 | 17 | make_luvv(_Config, AppFile) -> 18 | App = filename:basename(AppFile), 19 | case App of 20 | "luvviescript.app.src" -> 21 | true = code:add_patha("./ebin"), 22 | [ok = make_utils:compile(X, debug) || X <- ?TESTDIRS], 23 | ok; 24 | _ -> 25 | ok 26 | end. 27 | 28 | -------------------------------------------------------------------------------- /priv/rebar_plugins/make_tests.erl: -------------------------------------------------------------------------------- 1 | %%% @author Gordon Guthrie 2 | %%% @copyright (C) 2013, Gordon Guthrie 3 | %%% @doc This script makes common test suites 4 | %%% 5 | %%% @end 6 | %%% Created : 15 Aug 2013 by gordonguthrie@backawinner.gg 7 | 8 | -module(make_tests). 9 | 10 | -export([ 11 | make_tests/2 12 | ]). 13 | 14 | -define(JSDIR, "js/"). 15 | -define(TESTDIRS, ["test/passing"]). 16 | 17 | make_tests(_Config, AppFile) -> 18 | App = filename:basename(AppFile), 19 | case App of 20 | "luvviescript.app.src" -> 21 | code:add_patha("./ebin"), 22 | [ok = make_utils:make_tests(X) || X <- ?TESTDIRS], 23 | ok; 24 | _ -> 25 | ok 26 | end. 27 | -------------------------------------------------------------------------------- /priv/rebar_plugins/markup.erl: -------------------------------------------------------------------------------- 1 | %%% @author Gordon Guthrie 2 | %%% @copyright (C) 2013, Gordon Guthrie 3 | %%% @doc This is a rebar plugin for turning Erlang source 4 | %%% into literate Erlang 5 | %%% 6 | %%% @end 7 | %%% Created : 2 Sep 2013 by gordon@vixo.com 8 | -module(markup). 9 | 10 | -export([ 11 | markup/2 12 | ]). 13 | 14 | markup(Config, AppFile) -> 15 | App = filename:basename(AppFile), 16 | case App of 17 | "luvviescript.app.src" -> markup2(Config); 18 | "luvviescript.app" -> markup2(Config); 19 | _ -> ok 20 | end. 21 | 22 | markup2(Config) -> 23 | ErlOpts = rebar_config:get(Config, erl_opts, []), 24 | SrcDirs = get_src_dirs(ErlOpts), 25 | ErlFiles = get_files(SrcDirs, erl), 26 | HrlFiles = get_files(SrcDirs, hrl), 27 | [ok = markup_to_literate(X, erl) || X <- ErlFiles], 28 | [ok = markup_to_literate(X, hrl) || X <- HrlFiles], 29 | ok. 30 | 31 | get_files(SrcDirs, Type) -> 32 | WildCards = case Type of 33 | erl -> "/../src_md/.erl/*.erl"; 34 | hrl -> "/../include_md/.hrl/*.hrl" 35 | end, 36 | Files = lists:merge([filelib:wildcard(X ++ WildCards) || X <- SrcDirs]), 37 | FilterFun = fun(X) -> 38 | not filelib:is_dir(X) 39 | end, 40 | lists:filter(FilterFun, Files). 41 | 42 | markup_to_literate(File, Type) -> 43 | CWD = rebar_utils:get_cwd(), 44 | {ok, Lines} = read_lines(CWD ++ "/" ++ File), 45 | Source = make_markdown_source(Lines), 46 | ok = write_source(Source, File, Type). 47 | 48 | make_markdown_source(Lines) -> 49 | make_markdown(Lines, []). 50 | 51 | make_markdown([], Acc) -> 52 | lists:flatten(lists:reverse(Acc)); 53 | make_markdown(["%%%```erlang" ++ _Rest | T], Acc) -> 54 | make_erlang(T, ["```erlang\n" | Acc]); 55 | make_markdown(["%%```erlang" ++ _Rest | T], Acc) -> 56 | make_erlang(T, ["```erlang\n" | Acc]); 57 | make_markdown(["%```erlang" ++ _Rest | T], Acc) -> 58 | make_erlang(T, ["```erlang\n" | Acc]); 59 | %% order matters! 60 | make_markdown(["%%%" ++ Rest | T], Acc) -> 61 | make_markdown(T, [Rest | Acc]); 62 | make_markdown(["%%" ++ Rest | T], Acc) -> 63 | make_markdown(T, [Rest | Acc]); 64 | make_markdown(["%" ++ Rest | T], Acc) -> 65 | make_markdown(T, [Rest | Acc]); 66 | make_markdown(["\n" | T], Acc) -> 67 | make_markdown(T, ["\n" | Acc]); 68 | make_markdown([H | T], Acc) -> 69 | make_erlang([H | T], ["```erlang\n" | Acc]). 70 | 71 | make_erlang([], Acc) -> 72 | lists:flatten(lists:reverse(["```\n" | Acc])); 73 | make_erlang(["\n" | T], Acc) -> 74 | make_erlang(T, ["\n" | Acc]); 75 | %% order matters! 76 | make_erlang(["%%%```" ++ Rest | T], Acc) -> 77 | make_markdown(T, ["```\n" | Acc]); 78 | make_erlang(["%%```" ++ Rest | T], Acc) -> 79 | make_markdown(T, ["```\n" | Acc]); 80 | make_erlang(["%```" ++ Rest | T], Acc) -> 81 | make_markdown(T, ["```\n" | Acc]); 82 | make_erlang(["%%%" ++ Rest | T], Acc) -> 83 | make_markdown(T, [Rest, "```\n" | Acc]); 84 | make_erlang(["%%" ++ Rest | T], Acc) -> 85 | make_markdown(T, [Rest, "```\n" | Acc]); 86 | make_erlang(["%" ++ Rest | T], Acc) -> 87 | make_markdown(T, [Rest, "```\n" | Acc]); 88 | make_erlang([H | T], Acc) -> 89 | make_erlang(T, [" " ++ H | Acc]). 90 | 91 | read_lines(File) -> 92 | case file:open(File, read) of 93 | {error, Err} -> {error, Err}; 94 | {ok, Id} -> read_l2(Id, []) 95 | end. 96 | 97 | read_l2(Id, Acc) -> 98 | case file:read_line(Id) of 99 | {ok, Data} -> read_l2(Id, [Data | Acc]); 100 | {error, Err} -> {error, Err}; 101 | eof -> {ok, lists:reverse(Acc)} 102 | end. 103 | 104 | get_src_dirs(ErlOpts) -> 105 | case proplists:get_value(src_dirs, ErlOpts) of 106 | undefined -> ["src"]; 107 | SrcDirs -> SrcDirs 108 | end. 109 | 110 | write_source(Source, File, Type) -> 111 | File2 = filename:basename(File) ++ ".md", 112 | Dir = case Type of 113 | erl -> filename:dirname(File) ++ "/../../src_md/"; 114 | hrl -> filename:dirname(File) ++ "/../../include_md/" 115 | end, 116 | ok = filelib:ensure_dir(Dir), 117 | ok = file:write_file(Dir ++ File2, Source). 118 | 119 | 120 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- 2 | %% ex: ts=4 sw=4 ft=erlang et 3 | %% This is a sample rebar.conf file that shows examples of some of rebar's 4 | %% options. 5 | 6 | %% == Erlang Compiler == 7 | 8 | %% Erlang files to compile before the rest. Rebar automatically compiles 9 | %% parse_transforms and custom behaviours before anything other than the files 10 | %% in this list. 11 | 12 | %% Erlang compiler options 13 | {erl_opts, [no_debug_info, 14 | {i, "include"}, 15 | {src_dirs, ["src"]} 16 | ]}. 17 | 18 | %% == EUnit == 19 | 20 | %% Options for eunit:test() 21 | {eunit_opts, []}. 22 | 23 | %% Additional compile options for eunit. erl_opts is also used 24 | {eunit_compile_opts, []}. 25 | 26 | %% Same as erl_first_files, but used only when running 'eunit' 27 | {eunit_first_files, []}. 28 | 29 | %% == Cover == 30 | 31 | %% Whether to enable coverage reporting. Default is `false' 32 | {cover_enabled, false}. 33 | 34 | %% Whether to print coverage report to console. Default is `false' 35 | {cover_print_enabled, false}. 36 | 37 | %% Whether to export coverage report to file. Default is `false' 38 | {cover_export_enabled, false}. 39 | 40 | %% == Common Test == 41 | 42 | %% Override the default "test" directory in which SUITEs are located 43 | {ct_dir, "test"}. 44 | 45 | %% Override the default "logs" directory in which SUITEs are logged 46 | {ct_log_dir, "logs/"}. 47 | 48 | %% Option to pass extra parameters when launching Common Test 49 | {ct_extra_params, "-boot start_sasl"}. 50 | 51 | %% Option to use short names (i.e., -sname test) when starting ct 52 | {ct_use_short_names, true}. 53 | 54 | 55 | %% == OTP Applications == 56 | 57 | %% Enable validation of the OTP app module list. Default is 'true' 58 | {validate_app_modules, true}. 59 | 60 | %% == Dependencies == 61 | 62 | %% Where to put any downloaded dependencies. Default is "deps" 63 | {deps_dir, "deps"}. 64 | 65 | %% What dependencies we have, dependencies can be of 3 forms, an application 66 | %% name as an atom, eg. mochiweb, a name and a version (from the .app file), or 67 | %% an application name, a version and the SCM details on how to fetch it (SCM 68 | %% type, location and revision). 69 | %% Rebar currently supports git, hg, bzr, svn, and rsync. 70 | 71 | {deps, [ 72 | {mochiweb, ".*", {git, "git://github.com/mochi/mochiweb.git", "master"}}, 73 | {rfc4627_jsonrpc, ".*", {git, "git://github.com/tonyg/erlang-rfc4627", "master"}} 74 | ]}. 75 | 76 | %% {post_hooks, [{clean, "touch file1.out"}, 77 | %% {"freebsd", compile, "c_src/freebsd_tweaks.sh"}, 78 | %% {eunit, "touch file2.out"}, 79 | %% {compile, "touch postcompile.out"}]}. 80 | 81 | {plugins, [ 82 | make_luvv, 83 | make_tests, 84 | compile_literate, 85 | markup, 86 | first_time 87 | ]}. 88 | 89 | {plugin_dir, "priv/rebar_plugins"}. 90 | -------------------------------------------------------------------------------- /src_md/bert_utils.erl.md: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------- 2 | @author Gordon Guthrie 3 | @copyright (C) 2014, Gordon Guthrie 4 | @doc Some utilities for using Bert to explore data types 5 | 6 | @end 7 | Created : 26 Feb 2014 by gordon@vixo.com 8 | ------------------------------------------------------------------- 9 | ```erlang 10 | -module(bert_utils). 11 | 12 | 13 | -export([ 14 | all/0 15 | ]). 16 | 17 | all() -> 18 | Cases = [ 19 | {"integer", 12345}, 20 | {"float", 1.2345}, 21 | {"atom", atom}, 22 | {"null", []}, 23 | {"string", "abcdef"}, 24 | {"tuple", {a, 1, "X"}}, 25 | {"complex_list", [a, 1, "X"]} 26 | ], 27 | [write(Ident, Term) || {Ident, Term} <- Cases], 28 | ok. 29 | 30 | write(Identifier, Term) -> 31 | Binary = term_to_binary(Term), 32 | Json = io_lib:format("{~p: ~p};", [Identifier, Binary]), 33 | Json2 = re:replace(Json, "<<", "\"", [{return, list}]), 34 | Json3 = re:replace(Json2, ">>", "\"", [{return, list}]), 35 | Dir = "../js/terms/", 36 | File = "terms.log", 37 | case file:open(Dir ++ File, [write, append]) of 38 | {ok, IODevice} -> 39 | io:fwrite(IODevice, "~s~n", [Json3]), 40 | file:close(IODevice); 41 | {error, Err} -> 42 | io:format("Error ~p opening file~n", [Err]), 43 | exit(banjo) 44 | end. 45 | ``` 46 | -------------------------------------------------------------------------------- /src_md/debug_json.erl.md: -------------------------------------------------------------------------------- 1 | @author Gordon Guthrie 2 | @copyright (C) 2014, Gordon Guthrie 3 | @doc sometimes you need to dig into the json you 4 | have created - this module can help debug it 5 | mebbies, YMMV 6 | 7 | @end 8 | Created : 10th January 2014 by gordon@vixo.com 9 | ```erlang 10 | -module(debug_json). 11 | 12 | -export([ 13 | debug/1 14 | ]). 15 | 16 | debug(Json) -> 17 | dbg(Json, []), 18 | ok. 19 | 20 | dbg([], Acc) -> 21 | Acc; 22 | dbg({obj, List}, Acc) -> 23 | dbg(List, Acc); 24 | dbg(List, Acc) when is_list(List) -> 25 | %% io:format("List is ~p~n", [List]), 26 | {Keys, _Vals} = lists:unzip(List), 27 | %% io:format("Keys are ~p~n-in ~p~n", [Keys, lists:reverse(Acc)]), 28 | io:format("Keys are ~p~n", [Keys]), 29 | NewA = case lists:keyfind("type", 1, List) of 30 | {"type", Type} -> 31 | [Type | Acc]; 32 | _ -> 33 | io:format("No type!~n"), 34 | Acc 35 | end, 36 | TraverseFn = fun({"body", X}, FnAcc) -> 37 | dbg(X, ["body" | FnAcc]); 38 | ({"expression", false}, FnAcc) -> 39 | FnAcc; 40 | ({"expression", X}, FnAcc) -> 41 | dbg(X, ["expression" | FnAcc]); 42 | %% ({"callee", X}, FnAcc) -> 43 | %% dbg(X, ["callee" | FnAcc]); 44 | %% ({"name", X}, FnAcc) -> 45 | %% dbg(X, ["name" | FnAcc]); 46 | ({"left", X}, FnAcc) -> 47 | dbg(X, ["left" | FnAcc]); 48 | ({"right", X}, FnAcc) -> 49 | dbg(X, ["right" | FnAcc]); 50 | ({"cases", X}, FnAcc) -> 51 | dbg(X, ["cases" | FnAcc]); 52 | ({"consequent", X}, FnAcc) -> 53 | dbg(X, ["consequent" | FnAcc]); 54 | ({"discriminant", X}, FnAcc) -> 55 | dbg(X, ["discriminant" | FnAcc]); 56 | ({"loc", _X}, FnAcc) -> 57 | FnAcc; 58 | ({"type", _X}, FnAcc) -> 59 | FnAcc; 60 | ({obj, X}, FnAcc) -> 61 | dbg(X, FnAcc); 62 | (X, FnAcc) -> 63 | io:format("Ignoring ~p~n", [X]), 64 | FnAcc 65 | end, 66 | lists:foldl(TraverseFn, NewA, List). 67 | ``` 68 | -------------------------------------------------------------------------------- /src_md/from_core.erl.md: -------------------------------------------------------------------------------- 1 | @author Gordon Guthrie 2 | @copyright (C) 2014, Gordon Guthrie 3 | @doc This module prepares the (slightly amended) 4 | core Erlang AST for conversion tothe javascript AST 5 | 6 | @end 7 | Created : 10th January 2014 by gordon@vixo.com 8 | ```erlang 9 | -module(from_core). 10 | 11 | -export([ 12 | conv/1 13 | ]). 14 | 15 | -include_lib("compiler/src/core_parse.hrl"). 16 | -include("luvviescript.hrl"). 17 | -include("macros.hrl"). 18 | 19 | conv(#c_module{} = Module) -> 20 | #c_module{anno = Annotations, 21 | name = Name, 22 | exports = Exports, 23 | attrs = Attrs, 24 | defs = Defs} = Module, 25 | %% io:format("Module is called ~p~n-Annotations is ~p~n-Exports is ~p~n" ++ 26 | %% "-Attrs is ~p~n-Defs is ~p~n", 27 | %% [Name, Annotations, Exports, Attrs, Defs]), 28 | Context = #js_context{name = Name, 29 | exports = Exports}, 30 | {Decl, Body} = lists:unzip([conv(X, Context) || X <- Defs]), 31 | NDecls = length(Decl) + 1, 32 | Decl2 = lists:zip([exports | Decl], lists:duplicate(NDecls, ?EMPTYOBJECT)), 33 | Decls = to_js_ast:make_declarations(Decl2, ?NOSRCMAP), 34 | Exp = conv_exports(Exports), 35 | Comment = "This Module is compiled LuvvieScript\n" 36 | ++ "See http://luvv.ie\n" 37 | ++ "\n" 38 | ++ "Our philosophy is 'make it work then fix it up'\n" 39 | ++ "(We are still in phase one)" 40 | ++ "\n" 41 | ++ "Follow @gordonguthrie or @luvviescript on Twitter for updates\n", 42 | Comm = to_js_ast:make_comment(Comment, block, ?NOSRCMAP), 43 | to_js_ast:make_programme([Comm], [Decls] ++ Exp ++ Body, ?NOSRCMAP). 44 | 45 | conv({#c_var{name = {FnName, _}} = CVar, FnList}, Context) -> 46 | FnBody = conv_fn(FnList, ?NOSRCMAP), 47 | Body = to_js_ast:make_fn_body(?EMPTYJSONLIST, ?EMPTYJSONLIST, FnBody, ?NOSRCMAP), 48 | Loc = get_loc(CVar), 49 | Left = to_js_ast:make_identifier(atom_to_list(FnName), Loc), 50 | Fn = to_js_ast:make_fn(Left, Body, Loc), 51 | {FnName, Fn}. 52 | 53 | conv_exports(Exports) -> 54 | Exps2 = group_exps_of_diff_arity(Exports), 55 | [conv_exp(X) || X <- Exps2]. 56 | 57 | conv_exp({Fn, Arities}) -> 58 | FnName = to_js_ast:make_identifier(atom_to_list(Fn), ?NOSRCMAP), 59 | Call = to_js_ast:make_call_expr(FnName, [], ?NOSRCMAP), 60 | Call2 = to_js_ast:make_return(Call, ?NOSRCMAP), 61 | Cases = [{X, [Call2], ?WITHBREAK} || X <- Arities] ++ 62 | [{null, to_js_ast:make_return(?PATTERNMATCHFAIL, ?NOSRCMAP), ?NOBREAK}], 63 | Switch = to_js_ast:make_switch(<<"_args">>, Cases, ?NOSRCMAP), 64 | Left = to_js_ast:make_identifier("_args", ?NOSRCMAP), 65 | Right = to_js_ast:make_method("arguments", "length", ?NOSRCMAP), 66 | ArgsDef = to_js_ast:make_operator("=", Left, Right, ?NOSRCMAP), 67 | Body = to_js_ast:make_block_statement([ 68 | ArgsDef, 69 | Switch 70 | ], ?NOSRCMAP), 71 | FnBody = to_js_ast:make_fn_body(?EMPTYJSONLIST, ?EMPTYJSONLIST, Body, ?NOSRCMAP), 72 | FnVar = to_js_ast:make_method("exports", Fn, ?NOSRCMAP), 73 | _Return = to_js_ast:make_fn(FnVar, FnBody, ?NOSRCMAP). 74 | 75 | group_exps_of_diff_arity(Exports) -> 76 | GroupFn = fun(#c_var{name = {Fn, Arity}}, List) -> 77 | NewT = case lists:keyfind(Fn, 1, List) of 78 | false -> {Fn, [Arity]}; 79 | {Fn, As} -> {Fn, [Arity | As]} 80 | end, 81 | lists:keystore(Fn, 1, List, NewT) 82 | end, 83 | lists:foldl(GroupFn, [], Exports). 84 | 85 | conv_fn([], Acc) -> 86 | %% add the default case 87 | Cases = lists:reverse([{null, [to_js_ast:make_return(?PATTERNMATCHFAIL, 88 | ?NOSRCMAP)], 89 | ?NOBREAK} | Acc]), 90 | Switch = to_js_ast:make_switch(<<"_args">>, Cases, ?NOSRCMAP), 91 | Left = to_js_ast:make_identifier("_args", ?NOSRCMAP), 92 | Right = to_js_ast:make_method("arguments", "length", ?NOSRCMAP), 93 | ArgsDef = to_js_ast:make_operator("=", Left, Right, ?NOSRCMAP), 94 | _Body = to_js_ast:make_block_statement([ 95 | ArgsDef, 96 | Switch 97 | ], ?NOSRCMAP); 98 | conv_fn([#c_fun{vars = Vs} = CFn | T], Acc) -> 99 | NewAcc = {length(Vs), conv_fn2(CFn), ?WITHBREAK}, 100 | conv_fn(T, [NewAcc | Acc]). 101 | 102 | conv_fn2(#c_fun{} = CFn) -> 103 | conv_body(CFn#c_fun.body). 104 | 105 | conv_body(#c_literal{val = Val} = Body) -> 106 | Loc = get_loc(Body), 107 | [to_js_ast:make_return(to_js_ast:make_literal(Val, Loc), ?NOSRCMAP)]; 108 | conv_body(#c_let{} = CLet) -> 109 | conv_let(CLet, [], []); 110 | conv_body(#c_apply{} = CApply) -> 111 | [to_js_ast:make_return(conv_args(CApply), ?NOSRCMAP)]; 112 | conv_body(#c_call{module = Mod, name = Fn, args = Args}) -> 113 | ModFun = atom_to_list(Mod) ++ "." ++ atom_to_list(Fn), 114 | to_js_ast:make_call_expr(ModFun, Args, ?NOSRCMAP); 115 | conv_body(Body) -> 116 | io:format("Need to convert body ~p~n", [Body]), 117 | [{obj, [ 118 | {"type", <<"not implemented conv_body (2)">>} 119 | ] 120 | }]. 121 | 122 | conv_let([], Acc1, [H | Acc2]) -> 123 | Return = to_js_ast:make_return(H, ?NOSRCMAP), 124 | lists:reverse(Acc1) ++ lists:reverse([Return | Acc2]); 125 | conv_let(#c_let{vars = [Var], arg = Arg, body = B} = Body, Acc1, Acc2) -> 126 | Loc = get_loc(Body), 127 | Nm = atom_to_list(Var#c_var.name), 128 | Decl = to_js_ast:make_declarations([{Nm, null}], ?NOSRCMAP), 129 | Left = to_js_ast:make_identifier(Nm, get_loc(Var)), 130 | Right = conv_args(Arg), 131 | Expr = to_js_ast:make_operator("=", Left, Right, Loc), 132 | case B of 133 | #c_let{} -> conv_let(B, [Decl | Acc1], [Expr | Acc2]); 134 | #c_call{} -> NewExpr = conv_args(B), 135 | conv_let([], [Decl | Acc1], [NewExpr, Expr | Acc2]) 136 | end. 137 | 138 | conv_args(#c_apply{} = CApply) -> 139 | to_js_ast:make_apply(CApply, ?NOSRCMAP); 140 | conv_args(#c_call{module = Mod, name = Fn, args = Args} = CCall) -> 141 | Loc = get_loc(CCall), 142 | case Mod#c_literal.val of 143 | erlang -> to_js_ast:make_erlang_call(Fn#c_literal.val, Args, Loc); 144 | Module -> {obj, [ 145 | {"type", list_to_binary("not implemented conv_args " 146 | ++ atom_to_list(Module))} 147 | ] 148 | } 149 | end. 150 | 151 | get_loc(Rec) -> 152 | Attrs = element(2, Rec), 153 | case lists:keyfind("loc", 1, Attrs) of 154 | false -> []; 155 | Loc -> [Loc] 156 | end. 157 | ``` 158 | -------------------------------------------------------------------------------- /src_md/lcompile.erl.md: -------------------------------------------------------------------------------- 1 | @author Gordon Guthrie 2 | @copyright (C) 2014, Gordon Guthrie 3 | @doc This module compiles the base Erlang to core 4 | AST, dotP2, etc, etc 5 | 6 | @end 7 | Created : 10th January 2014 by gordon@vixo.com 8 | ```erlang 9 | -module(lcompile). 10 | 11 | -export([ 12 | to_ast/1, 13 | make_dot_P2/1 14 | ]). 15 | 16 | to_ast(File) -> 17 | IncludeDir = filename:dirname(File) ++ "../include", 18 | PDir = filename:dirname(File) ++ "/../psrc", 19 | File2 = PDir ++ "/" ++ filename:rootname(filename:basename(File)), 20 | compile:file(File2, [{i, IncludeDir}, binary, to_core]). 21 | 22 | make_dot_P2(File) -> 23 | IncludeDir = filename:dirname(File) ++ "/../include", 24 | DebugDir = filename:dirname(File) ++ "/../debug", 25 | PDir = filename:dirname(File) ++ "/../psrc", 26 | case compile:file(File, [ 27 | 'P', 28 | {i, IncludeDir}, 29 | {outdir, DebugDir} 30 | ]) of 31 | {ok, []} -> File2 = filename:rootname(filename:basename(File)) ++ ".P", 32 | {ok, P2} = case file:open(DebugDir ++ "/" ++ File2, read) of 33 | {error, Err} -> exit(Err); 34 | {ok, ID} -> FileNameFlag = false, 35 | scan(ID, FileNameFlag, []) 36 | end, 37 | File3 = filename:rootname(filename:basename(File)) ++ ".erl", 38 | ok = write_file(P2, PDir ++ "/" ++ File3), 39 | {ok, P2}; 40 | error -> io:format("Cannae compile ~p~n", [File]), 41 | {error, cant_compile} 42 | end. 43 | 44 | write_file(List, File) -> 45 | _Ret = filelib:ensure_dir(File), 46 | case file:open(File, [append]) of 47 | {ok, Id} -> write_f2(List, Id), 48 | file:close(Id), 49 | ok; 50 | _ -> error 51 | end, 52 | ok. 53 | 54 | write_f2([], _Id) -> ok; 55 | write_f2([H | T], Id) -> io:fwrite(Id, "~s", [H]), 56 | write_f2(T, Id). 57 | 58 | scan(ID, Flag, Acc) -> 59 | case {file:read_line(ID), Flag} of 60 | {{ok, "-file(" ++ _Rest = F}, false} -> scan(ID, true, [F | Acc]); 61 | {{ok, "-file(" ++ _Rest}, true} -> scan(ID, Flag, Acc); 62 | {{ok, Line}, _} -> scan(ID, Flag, [Line | Acc]); 63 | {eof, _} -> Rev = lists:reverse(Acc), 64 | {ok, Rev} 65 | end. 66 | ``` 67 | -------------------------------------------------------------------------------- /src_md/luvv_utils.erl.md: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------- 2 | @author Gordon Guthrie 3 | @copyright (C) 2014, Gordon Guthrie 4 | @doc This is a utility file that is available client and 5 | server side where it performs slightly different roles 6 | 7 | @end 8 | Created : 2 Mar 2014 by gordon@vixo.com 9 | ------------------------------------------------------------------- 10 | ```erlang 11 | -module(luvv_utils). 12 | 13 | -export([ 14 | external_format/1 15 | ]). 16 | 17 | external_format(X) when is_integer(X) -> io_lib:format("{\"int\": ~p}", [X]); 18 | external_format(X) when is_float(X) -> io_lib:format("{\"float\": ~p}", [X]); 19 | external_format(X) when is_atom(X) -> io_lib:format("{\"atom\": ~p}", [X]); 20 | external_format(X) when is_tuple(X) -> io_lib:format("{\"tuple\": ~p}", [X]); 21 | external_format(X) when is_list(X) -> io_lib:format("{\"list\": ~p}", [X]). 22 | ``` 23 | -------------------------------------------------------------------------------- /src_md/luvviescript.app.src: -------------------------------------------------------------------------------- 1 | {application, luvviescript, 2 | [ 3 | {description, "luvviescript"}, 4 | {vsn, "1"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {mod, { luvviescript_app, []}}, 11 | {env, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /src_md/luvviescript.erl.md: -------------------------------------------------------------------------------- 1 | @author Gordon Guthrie 2 | @copyright (C) 2013, Gordon Guthrie 3 | @doc This is the luvviescript compiler 4 | 5 | @end 6 | Created : 17 Aug 2013 by gordon@vixo.com 7 | ```erlang 8 | -module(luvviescript). 9 | 10 | -export([ 11 | compile/1, 12 | compile/2 13 | ]). 14 | 15 | -include("luvviescript.hrl"). 16 | ``` 17 | include the core erlang syntax records that we are going to act on 18 | this file is in /usr/local/lib/erlang/lib/compiler-N.N.N/src 19 | or the equivalent. That dir needs to be set in the rebar compiler 20 | options for this to compile 21 | ```erlang 22 | -include_lib("compiler/src/core_parse.hrl"). 23 | 24 | compile(File) -> 25 | compile(File, production). 26 | 27 | compile(File, Environment) -> 28 | io:format("Compiling ~p~n", [File]), 29 | %% for debugging we write out the source code of the Erlang core 30 | %% file whose AST we will be transforming 31 | ok = maybe_core(Environment, File), 32 | %% we are going to compile the .P2 version of the Erlang file 33 | %% not the plain one, so we create that version first 34 | {ok, DotP2} = lcompile:make_dot_P2(File), 35 | {ok, _, Syntax} = lcompile:to_ast(File), 36 | ok = maybe_write(Environment, File, Syntax, ".ast", term), 37 | #c_module{defs = Body} = Syntax, 38 | %% Erlang regards somefun/1 and somefun/N as two different 39 | %% fns, Javascript thinks they are they same. 40 | %% so we need to group multiple arity erlang fns. 41 | %% easiest way to do that is to sort the fns by name/arity 42 | SortFn = fun({Var1, _}, {Var2, _}) -> 43 | Var1#c_var.name < Var2#c_var.name 44 | end, 45 | Body2 = lists:sort(SortFn, Body), 46 | Syntax2 = Syntax#c_module{defs = Body2}, 47 | ok = maybe_write(Environment, File, Syntax2, ".ast2", term), 48 | %% we are going to use a .P file as the souce file for the purposes of 49 | %% having a source map. .P files have predictable and normalised layout 50 | %% which makes it easier to use them to collect column information 51 | %% for the source map file 52 | {ok, Tokens, _} = erl_scan:string(lists:flatten(DotP2), 1, 53 | [ 54 | return_white_spaces, 55 | return_comments, 56 | text 57 | ]), 58 | ok = maybe_write(Environment, File, Tokens, ".tks", term), 59 | %% now we collect all the tokens with the line/col information 60 | {ok, Tokens2} = tokens:collect(Tokens), 61 | ok = maybe_write(Environment, File, Tokens2, ".tks2", term), 62 | %% now transform the Erlang core AST file by merging in the column 63 | %% information from the collected tokens so that we can write the 64 | %% Javascript AST with the right info to get a Source Map 65 | Body3 = merge:add_line_info(Syntax2#c_module.defs, Tokens2), 66 | Syntax3 = fix_exports(Syntax2#c_module{defs = Body3}), 67 | ok = maybe_write(Environment, File, Syntax3, ".ast3", term), 68 | %% finally we can start coverting Erlang (core) to Javascript 69 | Js_Ast = from_core:conv(Syntax3), 70 | ok = maybe_write(Environment, File, Js_Ast, ".js_ast", term), 71 | %% ok = debug_json:debug(Js_Ast), 72 | Js_Ast2 = io_lib:format("~s", [rfc4627:encode(Js_Ast)]), 73 | ok = maybe_write(Environment, File, Js_Ast2, ".js_ast2", string), 74 | %% life is easier for everyone if the json we output is actually 75 | %% readable so make it so 76 | ok = pretty_print_json(File), 77 | %% now turn our json AST for Javascript into actual Javascript 78 | ok = make_javascript(File), 79 | ok. 80 | 81 | make_javascript(File) -> 82 | DirIn = filename:dirname(File) ++ "/../debug/", 83 | DirOut = filename:dirname(File) ++ "/../js/", 84 | SrcDir = filename:dirname(File) ++ "/../psrc/", 85 | FileRoot = filename:rootname(filename:basename(File)), 86 | FileIn = DirIn ++ FileRoot ++ ".json", 87 | FileOut = DirOut ++ FileRoot ++ ".js", 88 | SourceMap = DirOut ++ FileRoot ++ ".js.map", 89 | SourceFile = SrcDir ++ FileRoot ++ ".erl", 90 | CodeGen = "escodegen-cl2", 91 | Cmd = CodeGen 92 | ++ " --js_ast " ++ FileIn 93 | ++ " --js_output " ++ FileOut 94 | ++ " --source_file " ++ SourceFile 95 | ++ " --source_map " ++ SourceMap, 96 | case os:cmd(Cmd) of 97 | [] -> ok; % fine and doody 98 | Msg -> io:format("Invalid JSON AST for ~p~n" ++ Msg, [FileOut]) 99 | end, 100 | ok. 101 | 102 | pretty_print_json(File) -> 103 | Dir = filename:dirname(File) ++ "/../debug/", 104 | FileRoot = Dir ++ filename:rootname(filename:basename(File)), 105 | FileIn = FileRoot ++ ".js_ast2", 106 | FileOut = FileRoot ++ ".json", 107 | PP = "priv/json-prettyprinter/prettyjson.py", 108 | [] = os:cmd("cat " ++ FileIn ++ " | " ++ PP ++ " > " ++ FileOut), 109 | ok. 110 | 111 | maybe_core(production, _) -> 112 | ok; 113 | maybe_core(debug, File) -> 114 | IncDir = filename:dirname(File) ++ "../include", 115 | ODir = filename:dirname(File) ++ "/../debug/", 116 | {ok, _} = compile:file(File, [{i, IncDir}, {outdir, ODir}, to_core]), 117 | ok. 118 | 119 | maybe_write(production, _, _, _, _) -> 120 | ok; 121 | maybe_write(debug, File, Contents, FileType, Format) -> 122 | OutputDir = filename:dirname(File) ++ "/../debug/", 123 | write(OutputDir, File, Contents, FileType, Format). 124 | 125 | write(Dir, File, Contents, FileType, Format) -> 126 | OutputFile = filename:rootname(filename:basename(File)) ++ FileType, 127 | _Return = filelib:ensure_dir(File), 128 | ok = make_utils:write_file(Contents, Dir ++ OutputFile, Format). 129 | 130 | fix_exports(#c_module{exports = Exps} = CMod) -> 131 | FilterFn = fun(#c_var{name = {module_info, _}}) -> false; 132 | (_) -> true 133 | end, 134 | NewExps = lists:filter(FilterFn, Exps), 135 | CMod#c_module{exports = NewExps}. 136 | 137 | ``` 138 | -------------------------------------------------------------------------------- /src_md/make_utils.erl.md: -------------------------------------------------------------------------------- 1 | @author vagrant 2 | @copyright (C) 2013, vagrant 3 | @doc utils for rebar to call from plugins 4 | 5 | @end 6 | Created : 5 Sep 2013 by vagrant 7 | 8 | ```erlang 9 | -module(make_utils). 10 | 11 | -export([ 12 | compile/1, 13 | compile/2, 14 | make_tests/1, 15 | write_file/3 16 | ]). 17 | 18 | ``` 19 | for debugging stuff 20 | ```erlang 21 | -export([ 22 | plain_log/2 23 | ]). 24 | 25 | make_tests(Dir) -> 26 | SubDirs = ["/ebin", "/debug"], 27 | [do_housekeeping(Dir ++ X) || X <- SubDirs], 28 | code:add_patha(Dir ++ "/ebin/"), 29 | Dir2 = Dir ++ "/src/", 30 | Files = filelib:wildcard(Dir2 ++ "*.erl"), 31 | [ok = compile_erlang(File) || File <- Files], 32 | Modules = [list_to_atom(filename:rootname(filename:basename(X))) 33 | || X <- Files], 34 | ok = load_beam_files(Modules), 35 | ok = make_compile_tests(Modules, Dir), 36 | Tests = [get_tests(X) || X <- Modules], 37 | Results = [get_results(X) || X <- Tests], 38 | TestName = filename:basename(Dir), 39 | Name = TestName ++ "_run_SUITE", 40 | {TestFns, TestCode} = make_tests(Results, TestName, [], []), 41 | All = "all() ->\n[\n" ++ string:join(TestFns, ",\n") ++ "\n].\n", 42 | Suite = lists:flatten([ 43 | "%%% this file is GENERATED - DO NOT EDIT\n", 44 | "-module(" ++ Name ++ ").\n", 45 | "\n", 46 | "-include(\"test_run_hdr.part\").\n", 47 | "\n", 48 | All, 49 | "\n", 50 | TestCode 51 | ]), 52 | ok = file:write_file("test/" ++ Name ++ ".erl", Suite). 53 | 54 | make_tests([], _TestName, Acc1, Acc2) -> 55 | {lists:reverse(Acc1), lists:flatten(lists:reverse(Acc2))}; 56 | make_tests([{Mod, Tests} | T], TestName, Acc1, Acc2) -> 57 | NewAcc1 = ["'" ++ atom_to_list(Mod) ++ "_" ++ atom_to_list(X) ++ "_test'" 58 | || {X, _} <- Tests], 59 | NewAcc2 = [make_t2(TestName, Mod, Test) || Test <- Tests], 60 | make_tests(T, TestName, lists:merge(NewAcc1, Acc1), [NewAcc2 | Acc2]). 61 | 62 | make_t2(TestName, Mod, {Fun, Result}) -> 63 | "?TESTFN(" 64 | ++ "'" ++ atom_to_list(Mod) ++ "_" ++ atom_to_list(Fun) ++ "_test', " 65 | ++ TestName ++ ", " 66 | ++ "'" ++ atom_to_list(Mod) ++ "', " 67 | ++ "'" ++ atom_to_list(Fun) ++ "', " 68 | ++ to_s(Result) 69 | ++ ").\n". 70 | 71 | make_compile_tests(Modules, Dir) -> 72 | Dir2 = filename:basename(Dir), 73 | Name = Dir2 ++ "_compile_SUITE", 74 | Tests = ["?TESTFN(" 75 | ++ "'" ++ atom_to_list(X) ++ "_test', " 76 | ++ Dir2 ++ ", " 77 | ++ "'" ++ atom_to_list(X) ++ "'" 78 | ++ ").\n" 79 | || X <- Modules], 80 | All = string:join(["'" ++ atom_to_list(X) ++ "_test'" 81 | || X <- Modules], ",\n"), 82 | All2 = "all() ->\n[\n" ++ All ++ "\n].\n", 83 | Suite = lists:flatten([ 84 | "%%% this file is GENERATED - DO NOT EDIT\n", 85 | "-module(" ++ Name ++ ").\n", 86 | "\n", 87 | "-include(\"test_compile_hdr.part\").\n", 88 | "\n", 89 | All2, 90 | "\n" 91 | | Tests 92 | ]), 93 | ok = file:write_file("test/" ++ Name ++ ".erl", Suite). 94 | 95 | get_results({Mod, Fns}) -> 96 | {Mod, [{X, Mod:X()} || X <- Fns]}. 97 | 98 | get_tests(Module) -> 99 | Funs = Module:module_info(exports), 100 | {Module, [Fn || {Fn, 0} <- Funs, 101 | Fn /= module_info]}. 102 | 103 | load_beam_files([]) -> 104 | ok; 105 | load_beam_files([H | T]) -> 106 | {module, H} = code:load_file(H), 107 | load_beam_files(T). 108 | 109 | compile_erlang(File) -> 110 | io:format("Compiling ~p~n", [File]), 111 | IncludeDir = filename:dirname(File) ++ "/../include", 112 | OutDir = filename:dirname(File) ++ "/../ebin", 113 | {ok, _} = compile:file(File, [ 114 | {i, IncludeDir}, 115 | {outdir, OutDir} 116 | ]), 117 | ok. 118 | 119 | compile(Dir) -> 120 | compile(Dir, production). 121 | 122 | compile(Dir, Environment) -> 123 | SubDirs = ["/js", "/psrc", "/debug"], 124 | [do_housekeeping(Dir ++ X) || X <- SubDirs], 125 | code:add_patha("ebin/"), 126 | Dir2 = Dir ++ "/src/", 127 | Files = filelib:wildcard(Dir2 ++ "*.erl"), 128 | [ok = luvviescript:compile(File, Environment) || File <- Files], 129 | ok. 130 | 131 | clear_old_files(Dir) -> 132 | case file:list_dir(Dir) of 133 | {error, _} -> ok; % directory doesn't exist, that's ok 134 | {ok, Files} -> [ok = file:delete(Dir ++ "/" ++ X) || X <- Files], 135 | ok 136 | end. 137 | 138 | has_dir(Dir) -> 139 | case file:list_dir(Dir) of 140 | {error, _} -> false; 141 | {ok, _} -> true 142 | end. 143 | 144 | do_housekeeping(Dir) -> 145 | case has_dir(Dir) of 146 | true -> ok = clear_old_files(Dir); 147 | false -> filelib:ensure_dir(Dir ++ "/nonce.file") 148 | end. 149 | 150 | write_file(Term, File, term) -> 151 | write_f2(Term, File, "~p~n"); 152 | write_file(Term, File, string) -> 153 | write_f2(Term, File, "~s~n"). 154 | 155 | write_f2(Term, File, Format) -> 156 | _Return = filelib:ensure_dir(File), 157 | case file:open(File, [write]) of 158 | {ok, Id} -> 159 | io:fwrite(Id, Format, [Term]), 160 | file:close(Id); 161 | _ -> 162 | error 163 | end. 164 | 165 | plain_log(String, File) -> 166 | _Return = filelib:ensure_dir(File), 167 | 168 | case file:open(File, [append]) of 169 | {ok, Id} -> 170 | io:fwrite(Id, "~s~n", [String]), 171 | file:close(Id); 172 | _ -> 173 | error 174 | end. 175 | 176 | to_s(Int) when is_integer(Int) -> integer_to_list(Int); 177 | to_s(Flt) when is_float(Flt) -> 178 | %% definetaly a better way to test this (3.0 = "3") 179 | case erlang:trunc(Flt) == Flt andalso Flt < 99999 of 180 | true -> integer_to_list(erlang:trunc(Flt)); 181 | false -> string:to_upper(mochinum:digits(Flt)) 182 | end; 183 | to_s(Str) when is_list(Str) -> "\"" ++ Str ++ "\"" ; 184 | to_s(A) when is_atom(A) -> atom_to_list(A). 185 | ``` 186 | -------------------------------------------------------------------------------- /src_md/merge.erl.md: -------------------------------------------------------------------------------- 1 | @author Gordon Guthrie 2 | @copyright (C) 2013, Gordon Guthrie 3 | @doc This module merges the line/column information 4 | into the core Erlang AST 5 | 6 | @end 7 | Created : 10th January 2014 by gordon@vixo.com 8 | ```erlang 9 | -module(merge). 10 | 11 | -export([ 12 | add_line_info/2 13 | ]). 14 | 15 | ``` 16 | include the core erlang syntax records that we are going to act on 17 | this file is in /usr/local/lib/erlang/lib/compiler-N.N.N/src 18 | or the equivalent. That dir needs to be set in the rebar compiler 19 | options for this to compile 20 | ```erlang 21 | -include_lib("compiler/src/core_parse.hrl"). 22 | 23 | add_line_info(Syntax, Tokens) -> 24 | merge(Syntax, Tokens, []). 25 | 26 | merge([], _Tokens, Acc) -> 27 | lists:reverse(Acc); 28 | ``` 29 | drop the module_info fns 30 | ```erlang 31 | merge([{#c_var{name = {module_info, _}}, _} | T], Tokens, Acc) -> 32 | merge(T, Tokens, Acc); 33 | merge([{#c_var{name = {Name, _}} = First, #c_fun{} = Fn} | T], Tokens, Acc) -> 34 | {NewFn, NewToks} = merge_fn(Name, Fn, Tokens, []), 35 | merge(T, NewToks, [{First, NewFn} | Acc]); 36 | merge([H | T], Tokens, Acc) -> 37 | io:format("In merge Skipping ~p~n", [H]), 38 | merge(T, Tokens, Acc). 39 | 40 | merge_fn(Name, #c_fun{vars = Vars, body = Body} = Fn, Tokens, Acc) -> 41 | Line = get_line_var(Fn), 42 | {NewVars, NewToks, _Context} = get_new_vars(Vars, Tokens), 43 | {{Line, Offset}, NewToks2} = get_details(NewToks, Line, Name), 44 | {NewBody, NewToks3} = merge_body(Body, NewToks2, []), 45 | NewFn = set_col(Offset, Fn#c_fun{vars = NewVars, body = NewBody}), 46 | {[NewFn | Acc], NewToks3}. 47 | 48 | merge_body([], Tokens, _Context) -> 49 | {[], Tokens}; 50 | merge_body(#c_literal{anno = []} = CLit, Tokens, _Context) -> 51 | {CLit, Tokens}; 52 | merge_body(#c_literal{val = Val} = CLit, Tokens, _Context) -> 53 | Line = get_line_var(CLit), 54 | {{Line, Offset}, NewToks} = get_details(Tokens, Line, Val), 55 | NewLit = set_col(Offset, CLit), 56 | {NewLit, NewToks}; 57 | merge_body(#c_let{vars = Vars, body = Body} = CLet, Tokens, _Context) -> 58 | {NewVars, NewToks, NewContext} = get_new_vars(Vars, Tokens), 59 | {NewBody, NewToks2} = merge_body(Body, NewToks, NewContext), 60 | CLet2 = CLet#c_let{vars = NewVars, body = NewBody}, 61 | {CLet2, NewToks2}; 62 | merge_body(#c_apply{op = Op, args = Args} = CApp, Tokens, Context) 63 | when is_record(Op, c_var) -> 64 | {NewOp, NewToks} = merge_var(Op, Tokens, Context), 65 | {NewArgs, NewToks2} = merge_args(Args, NewToks, Context), 66 | {CApp#c_apply{op = NewOp, args = NewArgs}, NewToks2}; 67 | merge_body(#c_call{name = Name, args = Args} = CCall, Tokens, Context) -> 68 | io:format("skipping ~p~n", [CCall]), 69 | io:format("Name is ~p~nArgs is ~p~n", [Name, Args]), 70 | dump_tokens("merge_body", CCall, Tokens), 71 | {CCall, Tokens}; 72 | merge_body(Body, Tokens, _Context) -> 73 | io:format("in merge_body Skipping ~p~n", [Body]), 74 | {Body, Tokens}. 75 | 76 | merge_var(#c_var{name = {Name, _}} = Var, Tokens, _Context) -> 77 | case get_line_var(Var) of 78 | none -> {Var, Tokens}; 79 | Line -> {Line, Tks} = lists:keyfind(Line, 1, Tokens), 80 | Match = lists:keyfind(Name, 1, Tks), 81 | {_, {_, Offset}, _} = Match, 82 | NewVar = set_col(Offset, Var), 83 | NewTks = lists:keydelete(Name, 1, Tks), 84 | NewTokens = lists:keystore(Line, 1, Tokens, {Line, NewTks}), 85 | {NewVar, NewTokens} 86 | end. 87 | 88 | merge_args(Args, Tokens, Context) -> 89 | merge_a2(Args, Tokens, Context, []). 90 | 91 | merge_a2([], Tokens, _Context, Acc) -> 92 | {lists:reverse(Acc), Tokens}; 93 | merge_a2([H | T], Tokens, Context, Acc) -> 94 | {NewAcc, NewToks} = merge_var(H, Tokens, Context), 95 | merge_a2(T, NewToks, Context, [NewAcc | Acc]). 96 | 97 | get_new_vars([], Tokens) -> 98 | {[], Tokens, []}; 99 | get_new_vars([H | _T] = List, Tokens) -> 100 | case get_line_var(H) of 101 | none -> {List, Tokens, []}; 102 | Line -> {Line, Tks} = lists:keyfind(Line, 1, Tokens), 103 | Matches = get_matches(Tks, []), 104 | NewToks = lists:keydelete(Line, 1, Tokens), 105 | {NewVars, Context} = get_new_vars2(List, Matches, [], []), 106 | {NewVars, NewToks, Context} 107 | end. 108 | 109 | get_new_vars2([], _Matches, Context, Acc) -> 110 | {lists:reverse(Acc), lists:reverse(Context)}; 111 | get_new_vars2([H1 | T1], [H2 | T2], Context, Acc) -> 112 | Offset = get_first_offset(H2), 113 | NewH1 = set_col(Offset, H1), 114 | NewContext = [{H1#c_var.name, H2} | Context], 115 | get_new_vars2(T1, T2, NewContext, [NewH1 | Acc]). 116 | 117 | get_first_offset({_, Offset}) -> 118 | Offset; 119 | get_first_offset([{_, Offset} | _T]) -> 120 | Offset. 121 | 122 | get_details(Tokens, Ln, Name) -> 123 | case lists:keyfind(Ln, 1, Tokens) of 124 | false -> 125 | {{Ln, none}, Tokens}; 126 | {Ln, Tks} -> 127 | case lists:keyfind(Name, 1, Tks) of 128 | false -> 129 | {{Ln, none}, Tokens}; 130 | {Name, Details, _} -> 131 | NewTks = lists:keydelete(Name, 1, Tks), 132 | NewTokens = lists:keystore(Ln, 1, Tokens, {Ln, NewTks}), 133 | {Details, NewTokens} 134 | end 135 | end. 136 | 137 | ``` 138 | Core Erlang uses 'made up' variable names in its function definitions 139 | We need to match these variable names to the ones used in the source 140 | code so that we can build a source map 141 | here we are stepping through the tokens of a function defintion 142 | to build the context 143 | ```erlang 144 | get_matches([], Acc) -> 145 | lists:reverse(Acc); 146 | get_matches([{'{', _, _} | T], Acc) -> 147 | {NewTail, NewAcc} = grab_tuple(T, Acc), 148 | get_matches(NewTail, NewAcc); 149 | get_matches([{'[', _, _} | T], Acc) -> 150 | {NewTail, NewAcc} = grab_list(T, Acc), 151 | get_matches(NewTail, NewAcc); 152 | get_matches([{Var, {_, Offset}, var} | T], Acc) -> 153 | get_matches(T, [{Var, Offset} | Acc]); 154 | get_matches([_H | T], Acc) -> 155 | get_matches(T, Acc). 156 | 157 | grab_tuple(List, Acc) -> 158 | grab(List, '}', Acc). 159 | 160 | grab_list(List, Acc) -> 161 | grab(List, ']', Acc). 162 | 163 | grab([{CloseToken, _, _} | Tail], CloseToken, Acc) -> 164 | {Tail, [lists:reverse(Acc)]}; 165 | grab([{Var, {_, Offset}, var} | T], CloseToken, Acc) -> 166 | grab(T, CloseToken, [{Var, Offset} | Acc]); 167 | grab([_H | T], CloseToken, Acc) -> 168 | grab(T, CloseToken, Acc). 169 | 170 | get_line_var(Rec) when is_tuple(Rec) -> 171 | Attrs = element(2, Rec), 172 | case Attrs of 173 | [] -> none; 174 | A -> FilterFn = fun(X) when is_integer(X) -> true; 175 | (_X) -> false 176 | end, 177 | [N] = lists:filter(FilterFn, A), 178 | N 179 | end. 180 | 181 | set_col(none, Rec) when is_tuple(Rec) -> Rec; 182 | set_col({_N, none}, Rec) when is_tuple(Rec) -> Rec; 183 | set_col({Start, End}, Rec) when is_tuple(Rec) -> 184 | Line = get_line_var(Rec), 185 | Attrs = element(2, Rec), 186 | Loc = {"loc", {obj, [ 187 | {"start", {obj, [ 188 | {"line", Line}, 189 | {"column", Start} 190 | ] 191 | } 192 | }, 193 | {"end", {obj, [ 194 | {"line", Line}, 195 | {"column", End} 196 | ] 197 | } 198 | } 199 | ] 200 | } 201 | }, 202 | NewAttrs = [Loc | Attrs], 203 | setelement(2, Rec, NewAttrs). 204 | 205 | ``` 206 | when you are trying to write merge functionality you spend a lot of time 207 | squinting at some Core Erlang code and going "which bits am I to put 208 | location data to. ``dump_tokens/3`` is a little debug fn to drop into 209 | the code that that point 210 | ```erlang 211 | dump_tokens(String, Rec, Tokens) -> 212 | case get_line_var(Rec) of 213 | none -> io:format(String ++ ": no line variable~n"); 214 | Line -> {Line, Tks} = lists:keyfind(Line, 1, Tokens), 215 | io:format(String ++ ": Tokens on line ~p~n~p~n", [Line, Tks]) 216 | end. 217 | 218 | 219 | 220 | ``` 221 | -------------------------------------------------------------------------------- /src_md/test_utils.erl.md: -------------------------------------------------------------------------------- 1 | 2 | This is a helper module. 3 | you can write some Javascript, parse it to the javascript AST 4 | on http://esprima.org/demo 5 | and copy it into here to generate the body of the unit tests 6 | in to_jast.erl 7 | 8 | ```erlang 9 | -module(test_utils). 10 | 11 | -export([ 12 | generate_switch/0, 13 | generate_args/0, 14 | generate_fn/0, 15 | generate_return/0, 16 | generate_declarations/0, 17 | generate_fncall/0, 18 | generate_array/0 19 | ]). 20 | 21 | generate_switch() -> 22 | J = "{ 23 | \"type\": \"SwitchStatement\", 24 | \"discriminant\": { 25 | \"type\": \"Identifier\", 26 | \"name\": \"args\" 27 | }, 28 | \"cases\": [ 29 | { 30 | \"type\": \"SwitchCase\", 31 | \"test\": { 32 | \"type\": \"Literal\", 33 | \"value\": 0, 34 | \"raw\": \"0\" 35 | }, 36 | \"consequent\": [ 37 | { 38 | \"type\": \"ExpressionStatement\", 39 | \"expression\": { 40 | \"type\": \"Literal\", 41 | \"value\": \"erk\", 42 | \"raw\": \"\\\"erk\\\"\" 43 | } 44 | }, 45 | { 46 | \"type\": \"BreakStatement\", 47 | \"label\": null 48 | } 49 | ] 50 | }, 51 | { 52 | \"type\": \"SwitchCase\", 53 | \"test\": { 54 | \"type\": \"Literal\", 55 | \"value\": 1, 56 | \"raw\": \"1\" 57 | }, 58 | \"consequent\": [ 59 | { 60 | \"type\": \"ExpressionStatement\", 61 | \"expression\": { 62 | \"type\": \"Literal\", 63 | \"value\": \"jerk\", 64 | \"raw\": \"\\\"jerk\\\"\" 65 | } 66 | }, 67 | { 68 | \"type\": \"BreakStatement\", 69 | \"label\": null 70 | } 71 | ] 72 | }, 73 | { 74 | \"type\": \"SwitchCase\", 75 | \"test\": null, 76 | \"consequent\": [ 77 | { 78 | \"type\": \"ExpressionStatement\", 79 | \"expression\": { 80 | \"type\": \"Literal\", 81 | \"value\": \"shirk\", 82 | \"raw\": \"\\\"shirk\\\"\" 83 | } 84 | } 85 | ] 86 | } 87 | ] 88 | }", 89 | {ok, Json, []} = rfc4627:decode(J), 90 | JStr = io_lib:format("~p", [Json]), 91 | make_utils:plain_log(JStr, "/tmp/test_utils.txt"). 92 | 93 | generate_args() -> 94 | J = "{ 95 | \"type\": \"ExpressionStatement\", 96 | \"expression\": { 97 | \"type\": \"AssignmentExpression\", 98 | \"operator\": \"=\", 99 | \"left\": { 100 | \"type\": \"Identifier\", 101 | \"name\": \"_args\" 102 | }, 103 | \"right\": { 104 | \"type\": \"CallExpression\", 105 | \"callee\": { 106 | \"type\": \"MemberExpression\", 107 | \"computed\": false, 108 | \"object\": { 109 | \"type\": \"Identifier\", 110 | \"name\": \"arguments\" 111 | }, 112 | \"property\": { 113 | \"type\": \"Identifier\", 114 | \"name\": \"length\" 115 | } 116 | }, 117 | \"arguments\": [] 118 | } 119 | } 120 | }", 121 | {ok, Json, []} = rfc4627:decode(J), 122 | JStr = io_lib:format("~p", [Json]), 123 | make_utils:plain_log(JStr, "/tmp/test_utils.txt"). 124 | 125 | generate_fn() -> 126 | J = "{ 127 | \"type\": \"ExpressionStatement\", 128 | \"expression\": { 129 | \"type\": \"AssignmentExpression\", 130 | \"operator\": \"=\", 131 | \"left\": { 132 | \"type\": \"Identifier\", 133 | \"name\": \"simplefn\" 134 | }, 135 | \"right\": { 136 | \"type\": \"FunctionExpression\", 137 | \"id\": null, 138 | \"params\": [], 139 | \"defaults\": [], 140 | \"body\": { 141 | \"type\": \"BlockStatement\", 142 | \"body\": [ 143 | { 144 | \"type\": \"ReturnStatement\", 145 | \"argument\": { 146 | \"type\": \"Literal\", 147 | \"value\": \"banjolette\", 148 | \"raw\": \"\\\"banjolette\\\"\" 149 | } 150 | } 151 | ] 152 | }, 153 | \"rest\": null, 154 | \"generator\": false, 155 | \"expression\": false 156 | } 157 | } 158 | }", 159 | {ok, Json, []} = rfc4627:decode(J), 160 | JStr = io_lib:format("~p", [Json]), 161 | make_utils:plain_log(JStr, "/tmp/test_utils.txt"). 162 | 163 | ``` 164 | var fn = function () { 165 | var a; 166 | var b; 167 | a = 1; 168 | b = 2; 169 | return a/b; 170 | } 171 | ```erlang 172 | generate_return() -> 173 | J = "[ 174 | { 175 | \"type\": \"VariableDeclaration\", 176 | \"declarations\": [ 177 | { 178 | \"type\": \"VariableDeclarator\", 179 | \"id\": { 180 | \"type\": \"Identifier\", 181 | \"name\": \"fn\" 182 | }, 183 | \"init\": { 184 | \"type\": \"FunctionExpression\", 185 | \"id\": null, 186 | \"params\": [], 187 | \"defaults\": [], 188 | \"body\": { 189 | \"type\": \"BlockStatement\", 190 | \"body\": [ 191 | { 192 | \"type\": \"VariableDeclaration\", 193 | \"declarations\": [ 194 | { 195 | \"type\": \"VariableDeclarator\", 196 | \"id\": { 197 | \"type\": \"Identifier\", 198 | \"name\": \"a\" 199 | }, 200 | \"init\": null 201 | } 202 | ], 203 | \"kind\": \"var\" 204 | }, 205 | { 206 | \"type\": \"VariableDeclaration\", 207 | \"declarations\": [ 208 | { 209 | \"type\": \"VariableDeclarator\", 210 | \"id\": { 211 | \"type\": \"Identifier\", 212 | \"name\": \"b\" 213 | }, 214 | \"init\": null 215 | } 216 | ], 217 | \"kind\": \"var\" 218 | }, 219 | { 220 | \"type\": \"ExpressionStatement\", 221 | \"expression\": { 222 | \"type\": \"AssignmentExpression\", 223 | \"operator\": \"=\", 224 | \"left\": { 225 | \"type\": \"Identifier\", 226 | \"name\": \"a\" 227 | }, 228 | \"right\": { 229 | \"type\": \"Literal\", 230 | \"value\": 1, 231 | \"raw\": \"1\" 232 | } 233 | } 234 | }, 235 | { 236 | \"type\": \"ExpressionStatement\", 237 | \"expression\": { 238 | \"type\": \"AssignmentExpression\", 239 | \"operator\": \"=\", 240 | \"left\": { 241 | \"type\": \"Identifier\", 242 | \"name\": \"b\" 243 | }, 244 | \"right\": { 245 | \"type\": \"Literal\", 246 | \"value\": 2, 247 | \"raw\": \"2\" 248 | } 249 | } 250 | }, 251 | { 252 | \"type\": \"ReturnStatement\", 253 | \"argument\": { 254 | \"type\": \"BinaryExpression\", 255 | \"operator\": \"/\", 256 | \"left\": { 257 | \"type\": \"Identifier\", 258 | \"name\": \"a\" 259 | }, 260 | \"right\": { 261 | \"type\": \"Identifier\", 262 | \"name\": \"b\" 263 | } 264 | } 265 | } 266 | ] 267 | }, 268 | \"rest\": null, 269 | \"generator\": false, 270 | \"expression\": false 271 | } 272 | } 273 | ], 274 | \"kind\": \"var\" 275 | } 276 | ]", 277 | {ok, Json, []} = rfc4627:decode(J), 278 | JStr = io_lib:format("~p", [Json]), 279 | make_utils:plain_log(JStr, "/tmp/test_utils.txt"). 280 | 281 | generate_declarations() -> 282 | J = "[{ 283 | \"type\": \"VariableDeclaration\", 284 | \"declarations\": [ 285 | { 286 | \"type\": \"VariableDeclarator\", 287 | \"id\": { 288 | \"type\": \"Identifier\", 289 | \"name\": \"a\" 290 | }, 291 | \"init\": null 292 | } 293 | ], 294 | \"kind\": \"var\" 295 | }, 296 | { 297 | \"type\": \"VariableDeclaration\", 298 | \"declarations\": [ 299 | { 300 | \"type\": \"VariableDeclarator\", 301 | \"id\": { 302 | \"type\": \"Identifier\", 303 | \"name\": \"b\" 304 | }, 305 | \"init\": null 306 | } 307 | ], 308 | \"kind\": \"var\" 309 | }]", 310 | {ok, Json, []} = rfc4627:decode(J), 311 | JStr = io_lib:format("~p", [Json]), 312 | make_utils:plain_log(JStr, "/tmp/test_utils.txt"). 313 | 314 | generate_fncall() -> 315 | %% somefn = function() { 316 | %% return anotherfn(); 317 | %% } 318 | J = "[ 319 | { 320 | \"type\": \"ExpressionStatement\", 321 | \"expression\": { 322 | \"type\": \"AssignmentExpression\", 323 | \"operator\": \"=\", 324 | \"left\": { 325 | \"type\": \"Identifier\", 326 | \"name\": \"somefn\" 327 | }, 328 | \"right\": { 329 | \"type\": \"FunctionExpression\", 330 | \"id\": null, 331 | \"params\": [], 332 | \"defaults\": [], 333 | \"body\": { 334 | \"type\": \"BlockStatement\", 335 | \"body\": [ 336 | { 337 | \"type\": \"ReturnStatement\", 338 | \"argument\": { 339 | \"type\": \"CallExpression\", 340 | \"callee\": { 341 | \"type\": \"Identifier\", 342 | \"name\": \"anotherfn\" 343 | }, 344 | \"arguments\": [] 345 | } 346 | } 347 | ] 348 | }, 349 | \"rest\": null, 350 | \"generator\": false, 351 | \"expression\": false 352 | } 353 | } 354 | } 355 | ]", 356 | 357 | {ok, Json, []} = rfc4627:decode(J), 358 | JStr = io_lib:format("~p", [Json]), 359 | make_utils:plain_log(JStr, "/tmp/test_utils.txt"). 360 | 361 | generate_array() -> 362 | J = "{ 363 | \"type\": \"ArrayExpression\", 364 | \"elements\": [ 365 | { 366 | \"type\": \"Literal\", 367 | \"value\": 1, 368 | \"raw\": \"1\" 369 | }, 370 | { 371 | \"type\": \"Literal\", 372 | \"value\": 2, 373 | \"raw\": \"2\" 374 | }, 375 | { 376 | \"type\": \"Literal\", 377 | \"value\": 3, 378 | \"raw\": \"3\" 379 | }, 380 | { 381 | \"type\": \"Literal\", 382 | \"value\": 4, 383 | \"raw\": \"4\" 384 | } 385 | ] 386 | }", 387 | {ok, Json, []} = rfc4627:decode(J), 388 | JStr = io_lib:format("~p", [Json]), 389 | make_utils:plain_log(JStr, "/tmp/test_utils.txt"). 390 | 391 | ``` 392 | -------------------------------------------------------------------------------- /src_md/to_js_ast.erl.md: -------------------------------------------------------------------------------- 1 | @author Gordon Guthrie 2 | @copyright (C) 2013, Gordon Guthrie 3 | @doc This module generates the javascript AST 4 | To see examples of the javascript AST go to 5 | http://esprima.org/demo/parse.html 6 | 7 | @end 8 | Created : 17 Aug 2013 by gordon@vixo.com 9 | ```erlang 10 | -module(to_js_ast). 11 | 12 | -export([ 13 | make_erlang_call/3, 14 | make_comment/3, 15 | make_apply/2, 16 | make_return/2, 17 | make_fail/0, 18 | make_declarations/2, 19 | make_array/2, 20 | make_object/2, 21 | make_property/3, 22 | make_block_statement/2, 23 | make_fn/3, 24 | make_fn_body/4, 25 | make_switch/3, 26 | make_cases/2, 27 | make_programme/3, 28 | make_identifier/2, 29 | make_literal/2, 30 | make_method/3, 31 | make_call_expr/3, 32 | make_operator/4, 33 | make_expression/2 34 | ]). 35 | 36 | -include_lib("compiler/src/core_parse.hrl"). 37 | -include("luvviescript.hrl"). 38 | -include("macros.hrl"). 39 | 40 | ``` 41 | Use this definition where you know the source map is humped 42 | but will need to be fixed up later 43 | %```erlang 44 | ```erlang 45 | make_erlang_call('*', [A, B], Loc) -> 46 | {A1, B1} = rectify(A, B), 47 | make_operator("*", A1, B1, Loc); 48 | make_erlang_call('/', [A, B], Loc) -> 49 | {A1, B1} = rectify(A, B), 50 | make_operator("/", A1, B1, Loc); 51 | make_erlang_call('+', [A, B], Loc) -> 52 | {A1, B1} = rectify(A, B), 53 | make_operator("+", A1, B1, Loc); 54 | make_erlang_call('-', [A, B], Loc) -> 55 | {A1, B1} = rectify(A, B), 56 | make_operator("-", A1, B1, Loc). 57 | 58 | rectify(#c_var{name = A}, #c_var{name = B}) -> 59 | {make_identifier(atom_to_list(A), ?NOSRCMAP), 60 | make_identifier(atom_to_list(B), ?NOSRCMAP)}. 61 | 62 | make_comment(Comment, Type, Loc) when Type == line orelse Type == block -> 63 | Type2 = case Type of 64 | block -> "Block"; 65 | line -> "Line" 66 | end, 67 | {obj, lists:flatten([ 68 | {"type", enc_v(Type2)}, 69 | {"value", enc_v(Comment)}, 70 | Loc 71 | ]) 72 | }. 73 | 74 | make_apply(#c_apply{op = Op, args = Args}, Loc) -> 75 | {Name, _} = Op#c_var.name, 76 | make_call_expr(make_identifier(atom_to_list(Name), Loc), Args, ?NOSRCMAP). 77 | 78 | make_return(Return, Loc) -> 79 | {obj, lists:flatten([ 80 | {"type", <<"ReturnStatement">>}, 81 | {"argument", Return}, 82 | Loc 83 | ]) 84 | }. 85 | 86 | make_fail() -> 87 | make_literal("throw error", ?NOSRCMAP). 88 | 89 | make_declarations(List, Loc) when is_list(List) -> 90 | Decs = make_decs(List, []), 91 | {obj, lists:flatten([ 92 | {"type", <<"VariableDeclaration">>}, 93 | {"declarations", Decs}, 94 | {"kind", <<"var">>}, 95 | Loc 96 | ]) 97 | }. 98 | 99 | make_decs([], Acc) -> 100 | lists:reverse(Acc); 101 | make_decs([{Name, []} | T], Acc) -> 102 | make_decs([{Name, null} | T], Acc); 103 | make_decs([{Name, Init} | T], Acc) -> 104 | NewAcc = {obj, [ 105 | {"type", <<"VariableDeclarator">>}, 106 | {"id", {obj, [ 107 | {"type", <<"Identifier">>}, 108 | {"name", enc_v(Name)} 109 | ] 110 | } 111 | }, 112 | {"init", Init} 113 | ] 114 | }, 115 | make_decs(T, [NewAcc | Acc]). 116 | 117 | make_object(List, Loc) when is_list(List) -> 118 | Properties = [make_property(K, V, ?NOSRCMAP) || {K , V} <- List], 119 | {obj, lists:flatten([ 120 | {"type", <<"ObjectExpression">>}, 121 | {"properties", Properties}, 122 | Loc 123 | ]) 124 | }. 125 | 126 | make_property(Key, Val, Loc) -> 127 | {obj, lists:flatten([ 128 | {"type", <<"Property">>}, 129 | {"key", Key}, 130 | {"value", Val}, 131 | {"kind", <<"init">>}, 132 | Loc 133 | ]) 134 | }. 135 | 136 | make_array(List, Loc) -> 137 | make_a2(List, Loc, []). 138 | 139 | make_a2([], Loc, Acc) -> 140 | {obj, lists:flatten([ 141 | {"type", <<"ArrayExpression">>}, 142 | {"elements", lists:reverse(Acc)}, 143 | Loc 144 | ]) 145 | }; 146 | make_a2([H | T], Loc, Acc) -> 147 | NewAcc = make_literal(H, ?NOSRCMAP), 148 | make_a2(T, Loc, [NewAcc | Acc]). 149 | 150 | make_block_statement(Block, Loc) when is_list(Block) -> 151 | {obj, 152 | lists:flatten([ 153 | {"type", <<"BlockStatement">>}, 154 | {"body", Block}, 155 | Loc 156 | ]) 157 | }. 158 | 159 | make_fn(Left, Body, Loc) -> 160 | _Expr = make_operator("=", Left, Body, Loc). 161 | 162 | make_fn_body(Params, Defaults, Body, Loc) -> 163 | {obj, lists:flatten([ 164 | {"type", <<"FunctionExpression">>}, 165 | {"id", null}, 166 | {"params", Params}, 167 | {"defaults", Defaults}, 168 | {"body", Body}, 169 | {"rest", null}, 170 | {"generator", false}, 171 | {"expression", false}, 172 | Loc 173 | ]) 174 | }. 175 | 176 | make_switch(Variable, Cases, Loc) -> 177 | {obj, lists:flatten([ 178 | {"type", <<"SwitchStatement">>}, 179 | {"discriminant", {obj, [ 180 | {"type", <<"Identifier">>}, 181 | {"name", Variable} 182 | ] 183 | } 184 | 185 | }, 186 | {"cases", make_cases(Cases, [])}, 187 | Loc 188 | ]) 189 | }. 190 | 191 | make_cases([], Acc) -> 192 | lists:reverse(Acc); 193 | make_cases([{Val, Body, HasBreak} | T], Acc) -> 194 | Body2 = case HasBreak of 195 | true -> Body ++ [ 196 | {obj, [ 197 | {"type", <<"BreakStatement">>}, 198 | {"label", null} 199 | ] 200 | } 201 | ]; 202 | false -> Body 203 | end, 204 | NewAcc = {obj, [ 205 | {"type", <<"SwitchCase">>}, 206 | {"test", make_literal(Val, [])}, 207 | {"consequent", Body2} 208 | ] 209 | }, 210 | make_cases(T, [NewAcc | Acc]). 211 | 212 | make_programme(Comments, Body, Loc) when is_list(Body) andalso 213 | is_list(Comments) -> 214 | Com2 = case Comments of 215 | "" -> []; 216 | _ -> {"comments", Comments} 217 | end, 218 | {obj, lists:flatten([ 219 | {"type", <<"Program">>}, 220 | Com2, 221 | {"body", Body}, 222 | Loc 223 | ]) 224 | }. 225 | 226 | make_identifier(Val, Loc) -> 227 | {obj, lists:flatten([ 228 | {"type", <<"Identifier">>}, 229 | {"name", enc_v(Val)}, 230 | Loc 231 | ]) 232 | }. 233 | 234 | make_literal(null, _Loc) -> 235 | null; 236 | make_literal(Val, Loc) when is_integer(Val) -> 237 | make_l2(Val, Loc); 238 | make_literal(Val, Loc) when is_float(Val) -> 239 | make_l2(Val, Loc); 240 | make_literal(Val, Loc) when is_atom(Val) -> 241 | Atom2 = make_identifier("atom", Loc), 242 | Val2 = atom_to_list(Val), 243 | Literal2 = make_l2(Val2, Loc), 244 | make_object([{Atom2, Literal2}], Loc); 245 | make_literal(Val, Loc) when is_list(Val) -> 246 | make_array(Val, Loc). 247 | 248 | make_l2(Val, Loc) -> 249 | {obj, lists:flatten([ 250 | {"type", <<"Literal">>}, 251 | {"value", enc_v(Val)}, 252 | {"raw", raw_enc_v(Val)}, 253 | Loc 254 | ]) 255 | }. 256 | 257 | make_method(Obj, Fn, Loc) -> 258 | {obj, lists:flatten([ 259 | {"type", <<"MemberExpression">>}, 260 | {"computed", false}, 261 | {"object", {obj, [ 262 | {"type", <<"Identifier">>}, 263 | {"name", enc_v(Obj)} 264 | ] 265 | } 266 | }, 267 | {"property", {obj, 268 | [ 269 | {"type", <<"Identifier">>}, 270 | {"name", enc_v(Fn)} 271 | ] 272 | } 273 | }, 274 | Loc 275 | ]) 276 | }. 277 | 278 | make_call_expr(Callee, Args, Loc) -> 279 | {obj, 280 | lists:flatten([ 281 | {"type", <<"CallExpression">>}, 282 | {"callee", Callee}, 283 | {"arguments", enc_v(Args)}, 284 | Loc 285 | ]) 286 | }. 287 | 288 | make_operator("=", Left, Right, Loc) -> 289 | Op = make_op2("=", <<"AssignmentExpression">>, Left, Right, Loc), 290 | make_expression(Op, ?NOSRCMAP); 291 | make_operator("*", Left, Right, Loc) -> 292 | make_op2("*", <<"BinaryExpression">>, Left, Right, Loc); 293 | make_operator("/", Left, Right, Loc) -> 294 | make_op2("/", <<"BinaryExpression">>, Left, Right, Loc); 295 | make_operator("+", Left, Right, Loc) -> 296 | make_op2("+", <<"BinaryExpression">>, Left, Right, Loc); 297 | make_operator("-", Left, Right, Loc) -> 298 | make_op2("-", <<"BinaryExpression">>, Left, Right, Loc). 299 | 300 | make_op2(Operator, OpDesc, Left, Right, Loc) -> 301 | {obj, 302 | lists:flatten([ 303 | {"type", OpDesc}, 304 | {"operator", enc_v(Operator)}, 305 | {"left", Left}, 306 | {"right", Right}, 307 | Loc 308 | ]) 309 | }. 310 | 311 | make_expression(Expr, Loc) -> 312 | {obj, lists:flatten([ 313 | {"type", <<"ExpressionStatement">>}, 314 | {"expression", Expr}, 315 | Loc 316 | ]) 317 | }. 318 | 319 | raw_enc_v(Str) when is_list(Str) -> enc_v("\"" ++ Str ++ "\""); 320 | raw_enc_v(Atom) when is_atom(Atom) -> Atom; %% Todo fix up 321 | raw_enc_v(Int) when is_integer(Int) -> list_to_binary(integer_to_list(Int)); 322 | raw_enc_v(Flt) when is_float(Flt) -> 323 | %% definetaly a better way to test this (3.0 = "3") 324 | Str = case erlang:trunc(Flt) == Flt andalso Flt < 99999 of 325 | true -> integer_to_list(erlang:trunc(Flt)); 326 | false -> string:to_upper(mochinum:digits(Flt)) 327 | end, 328 | list_to_binary(Str). 329 | 330 | enc_v([]) -> []; 331 | enc_v(Str) when is_list(Str) -> list_to_binary(Str); 332 | enc_v(Atom) when is_atom(Atom) -> Atom; 333 | enc_v(Int) when is_integer(Int) -> Int; 334 | enc_v(Flt) when is_float(Flt) -> Flt; 335 | enc_v(Tuple) when is_tuple(Tuple) -> Tuple. 336 | 337 | ``` 338 | 339 | Unit Tests 340 | 341 | ```erlang 342 | 343 | ``` 344 | -ifdef(TEST). 345 | ```erlang 346 | -include_lib("eunit/include/eunit.hrl"). 347 | 348 | log_output(Strap, Got, Expected) -> 349 | code:add_patha("../deps/rfc4627_jsonrpc/ebin"), 350 | filelib:ensure_dir("/tmp/js_ast/got.log"), 351 | %% do the Gots 352 | GotMsg = io_lib:format(Strap ++ "~n~p~n", [Got]), 353 | GotJson = rfc4627:encode(Got), 354 | GotJsonMsg = io_lib:format(Strap ++ " Json~n~s~n", [GotJson]), 355 | make_utils:plain_log(GotMsg, "/tmp/js_ast/got.log"), 356 | make_utils:plain_log(GotJsonMsg, "/tmp/js_ast/got.log"), 357 | %% then do the Exps 358 | ExpMsg = io_lib:format(Strap ++ "~n~p~n", [Expected]), 359 | ExpJson = rfc4627:encode(Expected), 360 | ExpJsonMsg = io_lib:format(Strap ++ " Json~n~s~n", [ExpJson]), 361 | make_utils:plain_log(ExpMsg, "/tmp/js_ast/exp.log"), 362 | make_utils:plain_log(ExpJsonMsg, "/tmp/js_ast/exp.log"), 363 | ok. 364 | 365 | log(Prefix, Term) -> 366 | filelib:ensure_dir("/tmp/js_ast/debug.log"), 367 | Msg = io_lib:format(Prefix ++ "~n~p", [Term]), 368 | make_utils:plain_log(Msg, "/tmp/js_ast/debug.log"). 369 | 370 | switch_test_() -> 371 | Exp = {obj,[{"type",<<"SwitchStatement">>}, 372 | {"discriminant",{obj,[{"type",<<"Identifier">>},{"name",<<"args">>}]}}, 373 | {"cases", 374 | [{obj,[{"type",<<"SwitchCase">>}, 375 | {"test", 376 | {obj,[{"type",<<"Literal">>},{"value",0},{"raw",<<"0">>}]}}, 377 | {"consequent", 378 | [{obj,[{"type",<<"ExpressionStatement">>}, 379 | {"expression", 380 | {obj,[{"type",<<"ArrayExpression">>}, 381 | {"elements", 382 | [{obj,[{"type",<<"Literal">>}, 383 | {"value",106}, 384 | {"raw",<<"106">>}]}, 385 | {obj,[{"type",<<"Literal">>}, 386 | {"value",101}, 387 | {"raw",<<"101">>}]}, 388 | {obj,[{"type",<<"Literal">>}, 389 | {"value",114}, 390 | {"raw",<<"114">>}]}, 391 | {obj,[{"type",<<"Literal">>}, 392 | {"value",107}, 393 | {"raw",<<"107">>}]} 394 | ]} 395 | ]}} 396 | ]}, 397 | {obj,[{"type",<<"BreakStatement">>},{"label",null}]}]}]}, 398 | {obj,[{"type",<<"SwitchCase">>}, 399 | {"test", 400 | {obj,[{"type",<<"Literal">>},{"value",1},{"raw",<<"1">>}]}}, 401 | {"consequent", 402 | [{obj,[{"type",<<"ExpressionStatement">>}, 403 | {"expression", 404 | {obj,[{"type",<<"ArrayExpression">>}, 405 | {"elements", 406 | [{obj,[{"type",<<"Literal">>}, 407 | {"value",101}, 408 | {"raw",<<"101">>}]}, 409 | {obj,[{"type",<<"Literal">>}, 410 | {"value",114}, 411 | {"raw",<<"114">>}]}, 412 | {obj,[{"type",<<"Literal">>}, 413 | {"value",107}, 414 | {"raw",<<"107">>}]}]}]}}]}, 415 | {obj,[{"type",<<"BreakStatement">>},{"label",null}]} 416 | ]}]}, 417 | {obj,[{"type",<<"SwitchCase">>}, 418 | {"test",null}, 419 | {"consequent", 420 | [{obj,[{"type",<<"ExpressionStatement">>}, 421 | {"expression", 422 | {obj,[{"type",<<"ArrayExpression">>}, 423 | {"elements", 424 | [{obj,[{"type",<<"Literal">>}, 425 | {"value",114}, 426 | {"raw",<<"114">>}]}, 427 | {obj,[{"type",<<"Literal">>}, 428 | {"value",107}, 429 | {"raw",<<"107">>}]} 430 | ]} 431 | ]}} 432 | ]} 433 | ]} 434 | ]} 435 | ]} 436 | ] 437 | }, 438 | J = make_expression(make_literal("jerk", ?NOSRCMAP), ?NOSRCMAP), 439 | E = make_expression(make_literal("erk", ?NOSRCMAP), ?NOSRCMAP), 440 | R = make_expression(make_literal("rk", ?NOSRCMAP), ?NOSRCMAP), 441 | Got = make_switch(<<"args">>, [{0, [J], ?WITHBREAK}, 442 | {1, [E], ?WITHBREAK}, 443 | {null, [R], ?NOBREAK}], ?NOSRCMAP), 444 | %% log_output("Switch", Got, Exp), 445 | ?_assertEqual(Got, Exp). 446 | 447 | args_test_() -> 448 | Exp = {obj,[ 449 | {"type",<<"ExpressionStatement">>}, 450 | {"expression", 451 | {obj,[ 452 | {"type",<<"AssignmentExpression">>}, 453 | {"operator",<<"=">>}, 454 | {"left", 455 | {obj,[ 456 | {"type",<<"Identifier">>}, 457 | {"name",<<"_args">>} 458 | ] 459 | } 460 | }, 461 | {"right", 462 | {obj,[ 463 | {"type",<<"CallExpression">>}, 464 | {"callee", 465 | {obj,[ 466 | {"type",<<"MemberExpression">>}, 467 | {"computed",false}, 468 | {"object", 469 | {obj,[ 470 | {"type",<<"Identifier">>}, 471 | {"name",<<"arguments">>} 472 | ] 473 | } 474 | }, 475 | {"property", 476 | {obj,[ 477 | {"type",<<"Identifier">>}, 478 | {"name",<<"length">>} 479 | ] 480 | } 481 | } 482 | ] 483 | } 484 | }, 485 | {"arguments", []} 486 | ] 487 | } 488 | } 489 | ] 490 | } 491 | } 492 | ] 493 | }, 494 | Left = make_identifier("_args", ?NOSRCMAP), 495 | Method = make_method("arguments", "length", ?NOSRCMAP), 496 | Right = make_call_expr(Method, [], ?NOSRCMAP), 497 | Got = make_operator("=", Left, Right, ?NOSRCMAP), 498 | %% log_output("Args", Got, Exp), 499 | ?_assertEqual(Got, Exp). 500 | 501 | fns_test_() -> 502 | Exp = {obj, 503 | [ 504 | {"type",<<"ExpressionStatement">>}, 505 | {"expression", 506 | {obj, 507 | [ 508 | {"type",<<"AssignmentExpression">>}, 509 | {"operator",<<"=">>}, 510 | {"left", 511 | {obj, 512 | [ 513 | {"type",<<"Identifier">>}, 514 | {"name",<<"simplefn">>} 515 | ]}}, 516 | {"right", 517 | {obj, 518 | [ 519 | {"type",<<"FunctionExpression">>}, 520 | {"id",null}, 521 | {"params",[]}, 522 | {"defaults",[]}, 523 | {"body", 524 | {obj, 525 | [ 526 | {"type",<<"BlockStatement">>}, 527 | {"body", 528 | [ 529 | {obj, 530 | [ 531 | {"type",<<"ReturnStatement">>}, 532 | {"argument", 533 | {obj, 534 | [{"type",<<"Literal">>}, 535 | {"value",111}, 536 | {"raw",<<"111">>} 537 | ]}} 538 | ]} 539 | ]} 540 | ]}}, 541 | {"rest",null}, 542 | {"generator",false}, 543 | {"expression",false} 544 | ]}} 545 | ]}} 546 | ]}, 547 | FnName = make_identifier("simplefn", ?NOSRCMAP), 548 | Params = ?EMPTYJSONLIST, 549 | Defaults = ?EMPTYJSONLIST, 550 | Literal = make_literal(111, ?NOSRCMAP), 551 | Return = make_return(Literal, ?NOSRCMAP), 552 | Body = make_block_statement([Return], ?NOSRCMAP), 553 | FnBody = make_fn_body(Params, Defaults, Body, ?NOSRCMAP), 554 | Got = make_fn(FnName, FnBody, ?NOSRCMAP), 555 | %% log_output("Fns", Got, Exp), 556 | ?_assertEqual(Got, Exp). 557 | 558 | return_test_() -> 559 | %% var fn = function () { 560 | %% var a; 561 | %% var b; 562 | %% a = 1; 563 | %% b = 2; 564 | %% return a/b; 565 | %% } 566 | Exp = {obj, 567 | [{"type",<<"ExpressionStatement">>}, 568 | {"expression", 569 | {obj, 570 | [{"type",<<"AssignmentExpression">>}, 571 | {"operator",<<"=">>}, 572 | {"left",{obj,[{"type",<<"Identifier">>},{"name",<<"fn">>}]}}, 573 | {"right", 574 | {obj, 575 | [{"type",<<"FunctionExpression">>}, 576 | {"id",null}, 577 | {"params",[]}, 578 | {"defaults",[]}, 579 | {"body", 580 | {obj, 581 | [{"type",<<"BlockStatement">>}, 582 | {"body", 583 | [{obj, 584 | [{"type",<<"VariableDeclaration">>}, 585 | {"declarations", 586 | [{obj, 587 | [{"type",<<"VariableDeclarator">>}, 588 | {"id",{obj,[{"type",<<"Identifier">>},{"name",<<"a">>}]}}, 589 | {"init",null}]}]}, 590 | {"kind",<<"var">>}]}, 591 | {obj, 592 | [{"type",<<"VariableDeclaration">>}, 593 | {"declarations", 594 | [{obj, 595 | [{"type",<<"VariableDeclarator">>}, 596 | {"id",{obj,[{"type",<<"Identifier">>},{"name",<<"b">>}]}}, 597 | {"init",null}]}]}, 598 | {"kind",<<"var">>}]}, 599 | {obj, 600 | [{"type",<<"ExpressionStatement">>}, 601 | {"expression", 602 | {obj, 603 | [{"type",<<"AssignmentExpression">>}, 604 | {"operator",<<"=">>}, 605 | {"left",{obj,[{"type",<<"Identifier">>},{"name",<<"a">>}]}}, 606 | {"right", 607 | {obj, 608 | [{"type",<<"Literal">>}, 609 | {"value",1}, 610 | {"raw",<<"1">>}]}}]}}]}, 611 | {obj, 612 | [{"type",<<"ExpressionStatement">>}, 613 | {"expression", 614 | {obj, 615 | [{"type",<<"AssignmentExpression">>}, 616 | {"operator",<<"=">>}, 617 | {"left",{obj,[{"type",<<"Identifier">>},{"name",<<"b">>}]}}, 618 | {"right", 619 | {obj, 620 | [{"type",<<"Literal">>}, 621 | {"value",2}, 622 | {"raw",<<"2">>}]}}]}}]}, 623 | {obj, 624 | [{"type",<<"ReturnStatement">>}, 625 | {"argument", 626 | {obj, 627 | [{"type",<<"BinaryExpression">>}, 628 | {"operator",<<"/">>}, 629 | {"left",{obj,[{"type",<<"Identifier">>},{"name",<<"a">>}]}}, 630 | {"right", 631 | {obj, 632 | [{"type",<<"Identifier">>},{"name",<<"b">>}]}}]}}]}]}]}}, 633 | {"rest",null}, 634 | {"generator",false}, 635 | {"expression",false}]}}]}}]}, 636 | 637 | FnName = make_identifier("fn", ?NOSRCMAP), 638 | Params = ?EMPTYJSONLIST, 639 | Defaults = ?EMPTYJSONLIST, 640 | Decls = lists:flatten([ 641 | make_declarations([{"a", ?NOTINITIALISED}], ?NOSRCMAP), 642 | make_declarations([{"b", ?NOTINITIALISED}], ?NOSRCMAP) 643 | ]), 644 | A1 = make_identifier("a", ?NOSRCMAP), 645 | B1 = make_identifier("b", ?NOSRCMAP), 646 | Ass1 = make_operator("=", A1, make_literal(1, ?NOSRCMAP), ?NOSRCMAP), 647 | Ass2 = make_operator("=", B1, make_literal(2, ?NOSRCMAP), ?NOSRCMAP), 648 | Expr = make_operator("/", A1, B1, ?NOSRCMAP), 649 | Return = make_return(Expr, ?NOSRCMAP), 650 | Body = make_block_statement(lists:flatten([Decls, Ass1, Ass2, Return]), 651 | ?NOSRCMAP), 652 | FnBody = make_fn_body(Params, Defaults, Body, ?NOSRCMAP), 653 | Got = make_fn(FnName, FnBody, ?NOSRCMAP), 654 | %% log_output("Fns", Got, Exp), 655 | ?_assertEqual(Got, Exp). 656 | 657 | declarations_test_() -> 658 | Exp = [{obj,[{"type",<<"VariableDeclaration">>}, 659 | {"declarations", 660 | [{obj,[{"type",<<"VariableDeclarator">>}, 661 | {"id",{obj,[{"type",<<"Identifier">>},{"name",<<"a">>}]}}, 662 | {"init",null}]}]}, 663 | {"kind",<<"var">>}]}, 664 | {obj,[{"type",<<"VariableDeclaration">>}, 665 | {"declarations", 666 | [{obj,[{"type",<<"VariableDeclarator">>}, 667 | {"id",{obj,[{"type",<<"Identifier">>},{"name",<<"b">>}]}}, 668 | {"init",null}]}]}, 669 | {"kind",<<"var">>}]}], 670 | Got = [ 671 | make_declarations([ 672 | {"a", ?NOTINITIALISED} 673 | ], ?NOSRCMAP), 674 | make_declarations([ 675 | {"b", ?NOTINITIALISED} 676 | ], ?NOSRCMAP) 677 | ], 678 | %% log_output("Declarations", Got, Exp), 679 | ?_assertEqual(Got, Exp). 680 | 681 | fncall_test_() -> 682 | %% somefn = function() { 683 | %% return anotherfn(); 684 | %% } 685 | Exp = {obj, 686 | [{"type",<<"ExpressionStatement">>}, 687 | {"expression", 688 | {obj, 689 | [{"type",<<"AssignmentExpression">>}, 690 | {"operator",<<"=">>}, 691 | {"left",{obj,[{"type",<<"Identifier">>},{"name",<<"somefn">>}]}}, 692 | {"right", 693 | {obj, 694 | [{"type",<<"FunctionExpression">>}, 695 | {"id",null}, 696 | {"params",[]}, 697 | {"defaults",[]}, 698 | {"body", 699 | {obj, 700 | [{"type",<<"BlockStatement">>}, 701 | {"body", 702 | [{obj, 703 | [{"type",<<"ReturnStatement">>}, 704 | {"argument", 705 | {obj, 706 | [{"type",<<"CallExpression">>}, 707 | {"callee", 708 | {obj, 709 | [{"type",<<"Identifier">>},{"name",<<"anotherfn">>}]}}, 710 | {"arguments",[]}]}}]}]}]}}, 711 | {"rest",null}, 712 | {"generator",false}, 713 | {"expression",false}]}}]}}]}, 714 | Left = make_identifier("somefn", ?NOSRCMAP), 715 | Right = make_call_expr(make_identifier("anotherfn", ?NOSRCMAP), [], ?NOSRCMAP), 716 | Return = make_return(Right, ?NOSRCMAP), 717 | Block = make_block_statement([Return], ?NOSRCMAP), 718 | Body = make_fn_body([], [], Block, ?NOSRCMAP), 719 | Got = make_fn(Left, Body, ?NOSRCMAP), 720 | %% log_output("Fn Call", Got, Exp), 721 | ?_assertEqual(Got, Exp). 722 | 723 | array_test_() -> 724 | 725 | Exp = {obj,[{"type",<<"ArrayExpression">>}, 726 | {"elements", 727 | [{obj,[{"type",<<"Literal">>},{"value",1},{"raw",<<"1">>}]}, 728 | {obj,[{"type",<<"Literal">>},{"value",2},{"raw",<<"2">>}]}, 729 | {obj,[{"type",<<"Literal">>},{"value",3},{"raw",<<"3">>}]}, 730 | {obj,[{"type",<<"Literal">>},{"value",4},{"raw",<<"4">>}]}]}]}, 731 | Got = make_array([1, 2, 3, 4], ?NOSRCMAP), 732 | %% log_output("Array", Got, Exp), 733 | ?_assertEqual(Got, Exp). 734 | 735 | object_test_() -> 736 | Exp = {obj,[{"type",<<"ObjectExpression">>}, 737 | {"properties", 738 | [{obj,[{"type",<<"Property">>}, 739 | {"key",{obj,[{"type",<<"Identifier">>},{"name",<<"atom">>}]}}, 740 | {"value", 741 | {obj,[{"type",<<"Literal">>}, 742 | {"value",<<"berk">>}, 743 | {"raw",<<"\"berk\"">>}]}}, 744 | {"kind",<<"init">>}]}]}]}, 745 | Got = make_literal(berk, ?NOSRCMAP), 746 | %% log_output("Atom", Got, Exp), 747 | ?_assertEqual(Got, Exp). 748 | 749 | ``` 750 | Mod.Fn = function () { 751 | return "erk"; 752 | } 753 | ```erlang 754 | mod_fn_test_() -> 755 | Exp = [ 756 | {obj, 757 | [{"type",<<"ExpressionStatement">>}, 758 | {"expression", 759 | {obj, 760 | [{"type",<<"AssignmentExpression">>}, 761 | {"operator",<<"=">>}, 762 | {"left", 763 | {obj, 764 | [{"type",<<"MemberExpression">>}, 765 | {"computed",false}, 766 | {"object",{obj,[{"type",<<"Identifier">>},{"name",<<"Mod">>}]}}, 767 | {"property", 768 | {obj,[{"type",<<"Identifier">>},{"name",<<"Fn">>}]}}]}}, 769 | {"right", 770 | {obj, 771 | [{"type",<<"FunctionExpression">>}, 772 | {"id",null}, 773 | {"params",[]}, 774 | {"defaults",[]}, 775 | {"body", 776 | {obj, 777 | [{"type",<<"BlockStatement">>}, 778 | {"body", 779 | [{obj, 780 | [{"type",<<"ReturnStatement">>}, 781 | {"argument", 782 | {obj, 783 | [{"type",<<"Literal">>}, 784 | {"value",<<"erk">>}, 785 | {"raw",<<"\"erk\"">>}]}}]}]}]}}, 786 | {"rest",null}, 787 | {"generator",false}, 788 | {"expression",false}]}}]}}]} 789 | ], 790 | FnName = make_identifier("Fn", ?NOSRCMAP), 791 | Params = ?EMPTYJSONLIST, 792 | Defaults = ?EMPTYJSONLIST, 793 | Literal = make_literal("erk", ?NOSRCMAP), 794 | Return = make_return(Literal, ?NOSRCMAP), 795 | Body = make_block_statement([Return], ?NOSRCMAP), 796 | FnBody = make_fn_body(Params, Defaults, Body, ?NOSRCMAP), 797 | Fn = make_fn(FnName, FnBody, ?NOSRCMAP), 798 | Got = make_method("Mod", Fn, ?NOSRCMAP), 799 | log_output("Mod:Fn", Got, Exp), 800 | ?_assertEqual(Got, Exp). 801 | 802 | ``` 803 | -endif. 804 | -------------------------------------------------------------------------------- /src_md/tokens.erl.md: -------------------------------------------------------------------------------- 1 | @author Gordon Guthrie 2 | @copyright (C) 2014, Gordon Guthrie 3 | @doc This module process tokens to 4 | build the line/col info needed for 5 | sourcemaps 6 | @end 7 | Created : 10th January 2014 by gordon@vixo.com 8 | ```erlang 9 | -module(tokens). 10 | 11 | -export([ 12 | collect/1 13 | ]). 14 | 15 | collect(List) -> {ok, col2(List, 1, 1, [], [])}. 16 | 17 | col2([], Line, Indent, A1, A2) -> 18 | {Entry, _NewIndent} = make_entry(lists:reverse(A1), Indent, []), 19 | lists:reverse([{Line, Entry} | A2]); 20 | col2([H | T], Line, Indent, A1, A2) -> 21 | Details = element(2, H), 22 | Ln = get_line(Details), 23 | case Line of 24 | Ln -> col2(T, Ln, Indent, [H | A1], A2); 25 | _ -> {Entry, NewIndent} = make_entry(lists:reverse(A1), Indent, []), 26 | col2([H | T], Ln, NewIndent, [], [{Line, Entry} | A2]) 27 | end. 28 | 29 | make_entry([], Indent, Acc) -> 30 | {lists:reverse(Acc), Indent}; 31 | make_entry([{Operator, Details} | T], Indent, Acc) -> 32 | {Loc, NewIndent} = make_location(Details, Indent), 33 | NewAcc = {Operator, Loc, operator}, 34 | make_entry(T, NewIndent, [NewAcc | Acc]); 35 | make_entry([{white_space, Details, WS} | T], Indent, Acc) -> 36 | %% if whitespace starts with a new line it is a terminal whitespace 37 | %% so reset indent counter to it (ie don't add it to Indent) 38 | %% otherwise business as usual... 39 | [{line, Line}, {text, Txt}] = Details, 40 | [H | _] = Txt, 41 | NewIndent = case H of 42 | $\n -> length(Txt); 43 | _ -> length(Txt) + Indent 44 | end, 45 | NewAcc = {WS, {Line, Indent}, white_space}, 46 | make_entry(T, NewIndent, [NewAcc | Acc]); 47 | ``` 48 | comments are either whole line (don't care about the length) 49 | or at the end of a line followed by whitespace (don't care about the length). 50 | ```erlang 51 | make_entry([{comment, _, _} | T], Indent, Acc) -> 52 | make_entry(T, Indent, Acc); 53 | make_entry([{Type, Details, Thing} | T], Indent, Acc) -> 54 | {Loc, NewIndent} = make_location(Details, Indent), 55 | NewAcc = {Thing, Loc, Type}, 56 | make_entry(T, NewIndent, [NewAcc | Acc]). 57 | 58 | get_line([{line, Ln}, _]) -> Ln. 59 | 60 | make_location([{line, Ln}, {text, Txt}], Indent) -> 61 | End = Indent + length(Txt), 62 | {{Ln, {Indent, End}}, End + 1}. 63 | 64 | ``` 65 | -------------------------------------------------------------------------------- /test/not_passing/include/included.hrl: -------------------------------------------------------------------------------- 1 | -record(brannigan, {hip, 2 | hop, 3 | 'hippety-hop', 4 | 'dont stop'}). 5 | 6 | 7 | dingo() -> 8 | bogbrush. 9 | -------------------------------------------------------------------------------- /test/not_passing/src/1b_simpler.erl: -------------------------------------------------------------------------------- 1 | -module('1b_simpler'). 2 | 3 | -export([ 4 | simple_fn/0 5 | ]). 6 | 7 | simple_fn() -> 8 | not_exported_fn(). 9 | 10 | not_exported_fn() -> 11 | luvv_utils:external_format(django). 12 | -------------------------------------------------------------------------------- /test/not_passing/src/1c_simple.erl: -------------------------------------------------------------------------------- 1 | -module('1b_simple'). 2 | 3 | -export([ 4 | singlefn/1, 5 | doublefn/2, 6 | complexfn/1, 7 | complexfn2/1 8 | ]). 9 | 10 | singlefn(A) -> 11 | A. 12 | 13 | doublefn(A, B) -> 14 | {A, B}. 15 | 16 | 17 | complexfn({_A, _B, _C}) -> 18 | erk. 19 | 20 | complexfn2([_A, _B | _C]) -> 21 | berk. 22 | -------------------------------------------------------------------------------- /test/not_passing/src/1d_records_and_fns.erl: -------------------------------------------------------------------------------- 1 | -module('1c_records'). 2 | 3 | -record(bish, { 4 | bash = [], 5 | bosh = erk 6 | }). 7 | 8 | -export([ 9 | recordfn/1 10 | ]). 11 | 12 | recordfn(#bish{bash = Bash}) -> 13 | Bash. 14 | -------------------------------------------------------------------------------- /test/not_passing/src/1e_multiple_arity_fns.erl: -------------------------------------------------------------------------------- 1 | -module('1e_multiple_arity_fns'). 2 | 3 | -export([ 4 | arity/0, 5 | arity/1, 6 | inserted/0 7 | ]). 8 | 9 | arity() -> 10 | erk. 11 | 12 | %% inserted just mucks up the order inside the AST 13 | inserted() -> 14 | dynamite. 15 | 16 | arity(B) -> 17 | B. 18 | -------------------------------------------------------------------------------- /test/not_passing/src/1f_multiple_arity_fns_with_exports.erl: -------------------------------------------------------------------------------- 1 | -module('1f_multiple_arity_fns_with_exports'). 2 | 3 | -export([ 4 | arity/0 5 | ]). 6 | 7 | arity() -> 8 | arity(erk). 9 | 10 | 11 | arity(B) -> 12 | B. 13 | -------------------------------------------------------------------------------- /test/not_passing/src/2a_types_1.erl: -------------------------------------------------------------------------------- 1 | -module('2a_types_1'). 2 | 3 | -compile(export_all). 4 | 5 | int_fn() -> 6 | A = 1, 7 | A. 8 | 9 | float_fn() -> 10 | B = 2.3, 11 | B. 12 | 13 | boolean_fn() -> 14 | C = true, 15 | C. 16 | 17 | atom1_fn() -> 18 | D = blue, 19 | D. 20 | 21 | atom2_fn() -> 22 | E = 'Blue 4 U', 23 | E. 24 | 25 | string_fn() -> 26 | F = "string theory", 27 | F. 28 | -------------------------------------------------------------------------------- /test/not_passing/src/2b_operators_1.erl: -------------------------------------------------------------------------------- 1 | -module('2b_operators_1'). 2 | 3 | -compile(export_all). 4 | 5 | plus() -> 6 | 1 + 2. 7 | 8 | unary_plus() -> 9 | + 4. 10 | 11 | minus() -> 12 | 1 - 2. 13 | 14 | unary_minus() -> 15 | - 3. 16 | 17 | times() -> 18 | 3 * 5. 19 | 20 | floating_div() -> 21 | 3/4. 22 | 23 | int_div() -> 24 | 7 div 3. 25 | 26 | remainder() -> 27 | 7 rem 3. 28 | -------------------------------------------------------------------------------- /test/not_passing/src/2c_assignment.erl: -------------------------------------------------------------------------------- 1 | -module('2c_assignment'). 2 | 3 | -export([ 4 | assignment_fn/0 5 | ]). 6 | 7 | assignment_fn() -> 8 | A = 1, 9 | B = 2, 10 | C = 0, 11 | {A, B} = {1 + C, 2 + C}. 12 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_brackets.erl: -------------------------------------------------------------------------------- 1 | -module(basic_brackets). 2 | 3 | -compile(export_all). 4 | %% yargle 5 | brackets() -> 6 | B = 1, 7 | C = 33, 8 | ((1 + B) * C / 88). % donught 9 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_casemod.erl: -------------------------------------------------------------------------------- 1 | -module(basic_casemod). 2 | 3 | -compile(export_all). 4 | 5 | casefn() -> 6 | N = 5, 7 | case N of 8 | 5 -> 9 | ok; 10 | _ -> 11 | erk 12 | end. 13 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_clauses.erl: -------------------------------------------------------------------------------- 1 | -module(basic_clauses). 2 | 3 | -compile(export_all). 4 | 5 | -record(myrec, {bish, 6 | bosh}). 7 | 8 | 9 | atom() -> 10 | clause(berk). 11 | 12 | boolean() -> 13 | clause(true). 14 | 15 | float() -> 16 | clause(1.2). 17 | 18 | function1() -> 19 | Fun = fun() -> 20 | ok 21 | end, 22 | clause(Fun). 23 | 24 | function2() -> 25 | Fun = fun(_Bish, _Bash, _Bosh) -> 26 | ok 27 | end, 28 | clause(Fun, 3). 29 | 30 | function3() -> 31 | Fun = fun(bish, _Bash, _Bosh) -> 32 | ok; 33 | (erk, _Berk, _Jerk) -> 34 | ok; 35 | (Bingo, Bango, Bongo) when is_list(Bingo) andalso 36 | is_tuple(Bango) orelse 37 | is_atom(Bongo) -> 38 | ok 39 | end, 40 | clause(Fun, 3). 41 | 42 | 43 | integer() -> 44 | clause(1). 45 | 46 | number1() -> 47 | clause(1.3). 48 | 49 | number2() -> 50 | clause(1). 51 | 52 | pid() -> 53 | clause(self()). 54 | 55 | record() -> 56 | clause(#myrec{}). 57 | 58 | reference() -> 59 | clause(make_ref()). 60 | 61 | tuple() -> 62 | clause({1, 2, 3}). 63 | 64 | list() -> 65 | clause([bish, bash, bosh]). 66 | 67 | clause(X) when is_atom(X) -> 68 | ok; 69 | clause(X) when is_boolean(X) -> 70 | ok; 71 | clause(X) when is_float(X) -> 72 | ok; 73 | clause(X) when is_function(X) -> 74 | ok; 75 | clause(X) when is_integer(X) -> 76 | ok; 77 | clause(X) when is_number(X) -> 78 | ok; 79 | clause(X) when is_pid(X) -> 80 | ok; 81 | clause(X) when is_record(X, myrec) -> 82 | ok; 83 | clause(X) when is_reference(X) -> 84 | ok; 85 | clause(X) when is_tuple(X) -> 86 | ok; 87 | clause(X) when is_list(X) -> 88 | ok. 89 | 90 | clause(X, Y) when is_function(X, Y) -> 91 | ok. 92 | 93 | multiguard(X) when is_list(X) orelse 94 | is_tuple(X) -> 95 | ok; 96 | multiguard(X) when is_list(X) andalso 97 | is_tuple(X) -> 98 | ok; 99 | multiguard(X) when is_list(X), is_tuple(X) -> 100 | ok; 101 | multiguard(X) when is_list(X); is_tuple(X) -> 102 | ok. 103 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_clauses2.erl: -------------------------------------------------------------------------------- 1 | -module(basic_clauses2). 2 | 3 | -compile(export_all). 4 | 5 | clauses_1() -> 6 | case_clause(1). 7 | 8 | clauses_2()-> 9 | case_clause(2). 10 | 11 | clauses_3() -> 12 | if_clause(44). 13 | 14 | clauses_4() -> 15 | if_clause(3). 16 | 17 | clauses_5() -> 18 | if_clause(2). 19 | 20 | case_clause(B) -> 21 | case B of 22 | 1 -> pandy; 23 | 2 -> andy 24 | end. 25 | 26 | if_clause(B) -> 27 | if 28 | B > 3 -> erk; 29 | B == 3 -> smerk; 30 | B =< 3 -> berk 31 | end. 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_comparisons.erl: -------------------------------------------------------------------------------- 1 | -module(basic_comparisons). 2 | 3 | -compile(export_all). 4 | 5 | equals() -> 6 | 4 == 5, 7 | 4.0 == 4.0, 8 | a == a, 9 | true == false, 10 | {bish, bosh} == {andy, pandy}, 11 | [a, b] == [a, b]. 12 | 13 | not_equals() -> 14 | 4 /= 5, 15 | 4.0 /= 4.0, 16 | a /= a, 17 | true /= false, 18 | {bish, bosh} /= {andy, pandy}, 19 | [a, b] /= [a, b]. 20 | 21 | less_than() -> 22 | 4 =< 5, 23 | 4.0 =< 4.0, 24 | a =< a, 25 | true =< false, 26 | {bish, bosh} =< {andy, pandy}, 27 | [a, b] =< [a, b]. 28 | 29 | less() -> 30 | 4 < 5, 31 | 4.0 < 4.0, 32 | a < a, 33 | true < false, 34 | {bish, bosh} < {andy, pandy}, 35 | [a, b] < [a, b]. 36 | 37 | greater_than() -> 38 | 4 >= 5, 39 | 4.0 >= 4.0, 40 | a >= a, 41 | true >= false, 42 | {bish, bosh} >= {andy, pandy}, 43 | [a, b] >= [a, b]. 44 | 45 | greater() -> 46 | 4 > 5, 47 | 4.0 > 4.0, 48 | a > a, 49 | true > false, 50 | {bish, bosh} > {andy, pandy}, 51 | [a, b] > [a, b]. 52 | 53 | exactly_eq() -> 54 | 4 =:= 5, 55 | 4.0 =:= 4.0, 56 | a =:= a, 57 | true =:= false, 58 | {bish, bosh} =:= {andy, pandy}, 59 | [a, b] =:= [a, b]. 60 | 61 | exactly_neq() -> 62 | 4 =/= 5, 63 | 4.0 =/= 4.0, 64 | a =/= a, 65 | true =/= false, 66 | {bish, bosh} =/= {andy, pandy}, 67 | [a, b] =/= [a, b]. 68 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_functions.erl: -------------------------------------------------------------------------------- 1 | -module(basic_functions). 2 | 3 | %%% this module is designed to test the rejigging of functions that have one clause 4 | 5 | -export([ 6 | zero_arity/0 7 | ]). 8 | 9 | zero_arity() -> 10 | A = arity(a), 11 | B = arity("B", 2), 12 | C = arity(3, 5, "C33"), 13 | {A, B, C}. 14 | 15 | arity(_One, _Two, _Three) -> 16 | berk. 17 | 18 | arity(erko) -> 19 | limbo; 20 | arity("one") -> 21 | rhubarbo; 22 | arity(One) when is_list(One) -> 23 | interpolate(One); 24 | arity(_One) -> 25 | erko. 26 | 27 | interpolate(_Yando) -> 28 | smando. 29 | 30 | arity(one, _Two) -> 31 | shambo; 32 | arity("One", _Two) -> 33 | bambo. 34 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_ifmod.erl: -------------------------------------------------------------------------------- 1 | -module(basic_ifmod). 2 | 3 | -compile(export_all). 4 | 5 | if_fn() -> 6 | N = 5, 7 | if 8 | N > 5 -> 9 | ok; 10 | N =< 5 -> 11 | dont 12 | end. 13 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_including.erl: -------------------------------------------------------------------------------- 1 | -module(basic_including). 2 | 3 | -compile(export_all). 4 | 5 | -include("included.hrl"). 6 | 7 | %% This module tests, inter alia, how comments are handled in the `.P` file 8 | %% generation 9 | 10 | including() -> 11 | 12 | 13 | #brannigan{}. 14 | 15 | location() -> dingo(). 16 | 17 | complex_clauses() -> 18 | A=1, 19 | B = case A of 20 | 1 -> 77; % stick the return up here 21 | 3 -> 22 | 33 % stick it down here 23 | end, C = 7, 24 | Z1 = [X + 2 25 | || 26 | X <- [A, B, C]], 27 | Z2 = [X + 2 || X <- [A, B, C]], 28 | {Z1, Z2}. 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_line_endings.erl: -------------------------------------------------------------------------------- 1 | -module(basic_line_endings). 2 | 3 | -compile(export_all). 4 | 5 | single() -> A = 1, B = 3, A + B. 6 | 7 | complex_expressions() -> 8 | A = 1, 9 | B = 2, 10 | C = (88 * 4) + B/A, 11 | C/2. 12 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_macros.erl: -------------------------------------------------------------------------------- 1 | -module(basic_macros). 2 | 3 | -compile(export_all). 4 | 5 | -define(ARGYBARGY, 1). 6 | 7 | macro_fn() -> 8 | ?ARGYBARGY. 9 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_records.erl: -------------------------------------------------------------------------------- 1 | -module(basic_records). 2 | 3 | -compile(export_all). 4 | 5 | -record(bingo, {bish, 6 | bash = garle, 7 | bosh = running_dog()}). 8 | 9 | luvvie_record_1() -> 10 | A = #bingo{}. 11 | 12 | luvvie_record_2() -> 13 | A = #bingo{}, 14 | A#bingo.bosh. 15 | 16 | luvvie_record_3() -> 17 | #bingo.bash. 18 | 19 | running_dog() -> 20 | "yowza". 21 | -------------------------------------------------------------------------------- /test/not_passing/src/basic_types2.erl: -------------------------------------------------------------------------------- 1 | -module(basic_types2). 2 | 3 | -compile(export_all). 4 | 5 | list() -> 6 | A = [a, b], 7 | A. 8 | 9 | hd() -> 10 | A = [a, b], 11 | hd(A). 12 | 13 | tuple() -> 14 | B = {1, 2}, 15 | B. 16 | 17 | setelement() -> 18 | B = {1, a, "bb"}, 19 | setelement(2, B, yando). 20 | 21 | ref() -> 22 | D = make_ref(), 23 | D. 24 | 25 | chars() -> 26 | [$n, $o, $w, $,, $\t, $i, $s, $\t, $t, $h, $e, $\t, $w, $i, $n, $t, $e, $r]. 27 | 28 | radishes() -> 29 | [2#10, 16#104, 32#4545]. 30 | 31 | exponents() -> 32 | [2.3e+9, 2.3e-9]. 33 | -------------------------------------------------------------------------------- /test/passing/src/1a_simplest.erl: -------------------------------------------------------------------------------- 1 | -module('1a_simplest'). 2 | 3 | -random(guthrie_attributes). 4 | 5 | -export([ 6 | simplestfn/0 7 | ]). 8 | 9 | simplestfn() -> "banjolette". 10 | -------------------------------------------------------------------------------- /test/passing/src/demo.erl: -------------------------------------------------------------------------------- 1 | -module(demo). 2 | 3 | -export([ 4 | test/0 5 | ]). 6 | 7 | test() -> 8 | A = first(), 9 | B = second(), 10 | C = third(), 11 | A + B / C. 12 | 13 | first() -> 14 | 1. 15 | 16 | second() -> 17 | 2. 18 | 19 | third() -> 20 | 3. 21 | 22 | -------------------------------------------------------------------------------- /test/test_compile_hdr.part: -------------------------------------------------------------------------------- 1 | %% This is the included header file for the tests that check that the generated 2 | %% javascript is well formed 3 | -compile(export_all). 4 | -include("ct.hrl"). 5 | 6 | -define(TESTFN(Fn, Name, Module), 7 | Fn(_Config) -> 8 | File = atom_to_list(Name) ++ "/js/" 9 | ++ atom_to_list(Module) ++ ".js", 10 | Cmd = "rhino ../../test/" ++ File, 11 | {ok, Dir} = file:get_cwd(), 12 | io:format("Cmd is ~p~n", [Cmd]), 13 | io:format("Dir is ~p~n", [Dir]), 14 | case os:cmd(Cmd) of 15 | [] -> ok; 16 | Other -> io:format("Compiling ~p resulted in ~p~n", 17 | [File, Other]), 18 | exit(compile_failed) 19 | end). 20 | 21 | init_per_suite(Config) -> 22 | Config. 23 | 24 | init_per_testcase(_TestCase, Config) -> 25 | Config. 26 | 27 | end_per_suite(_Config) -> 28 | ok. 29 | 30 | end_per_testcase(_TestCase, _Config) -> 31 | ok. 32 | -------------------------------------------------------------------------------- /test/test_run_hdr.part: -------------------------------------------------------------------------------- 1 | %% this is the included header file for the tests that check Erlang/LuvviScript 2 | %% compatibility - the same fn run as Erlang or LuvvieScript returns the same value 3 | -compile(export_all). 4 | -include("ct.hrl"). 5 | 6 | -define(TESTFN(Test, Name, Module, Fn, Result), 7 | Test(_Config) -> 8 | File = atom_to_list(Name) ++ "/js/" 9 | ++ atom_to_list(Module) ++ ".js", 10 | io:format("File is ~p~n", [File]), 11 | {ok, JS} = file:read_file("../../test/" ++ File), 12 | JS2 = binary_to_list(JS) ++ "print(" ++ atom_to_list(Fn) ++ "());", 13 | io:format("JS2 is ~p~n", [JS2]), 14 | Cmd = "rhino -e \"" ++ JS2 ++ "\"", 15 | io:format("Cmd is ~p~n", [Cmd]), 16 | Return = string:strip(os:cmd(Cmd), both, $\n), 17 | Result2 = to_s(Result), 18 | case Return of 19 | Result2 -> ok; 20 | Got -> io:format("Expected : ~p~nGot : ~p~n", 21 | [Result2, Got]), 22 | exit(fail) 23 | end). 24 | 25 | to_s(Int) when is_integer(Int) -> integer_to_list(Int); 26 | to_s(Flt) when is_float(Flt) -> 27 | %% definetaly a better way to test this (3.0 = "3") 28 | case erlang:trunc(Flt) == Flt andalso Flt < 99999 of 29 | true -> integer_to_list(erlang:trunc(Flt)); 30 | false -> string:to_upper(mochinum:digits(Flt)) 31 | end; 32 | to_s(Str) when is_list(Str) -> Str; 33 | to_s(A) when is_atom(A) -> atom_to_list(A). 34 | -------------------------------------------------------------------------------- /test/unsupported/clauses.erl: -------------------------------------------------------------------------------- 1 | -module(clauses). 2 | 3 | -compile(export_all). 4 | 5 | clause(X) when is_binary(X) -> 6 | ok; 7 | clause(X) when is_bitstring(X) -> 8 | ok; 9 | clause(X) when is_port(X) -> 10 | ok. 11 | -------------------------------------------------------------------------------- /test/unsupported/operators.erl: -------------------------------------------------------------------------------- 1 | -module(operators). 2 | 3 | -compile(export_all). 4 | 5 | fn_bnot() -> 6 | 2#10 bnot 2#01. 7 | 8 | fn_band() -> 9 | 2#10 band 2#01. 10 | 11 | fn_bor() -> 12 | 2#10 bor 2#01. 13 | 14 | fn_bxor() -> 15 | 2#10 bxor 2#01. 16 | 17 | fn_bsl() -> 18 | 2#10 bsl 2#01. 19 | 20 | fn_bsr() -> 21 | 2#10 bsr 2#01. 22 | -------------------------------------------------------------------------------- /test/unsupported/types.erl: -------------------------------------------------------------------------------- 1 | -module(types). 2 | 3 | -compile(export_all). 4 | 5 | types() -> 6 | A = <<"blah">>. 7 | --------------------------------------------------------------------------------