├── .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 |
31 |
Keep in mind: these T-Shirts are not available in stores!
32 |
33 |
34 |
35 | In Brief
36 |
42 |
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 | Action!
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 |
--------------------------------------------------------------------------------