├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── .npmignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.json ├── LANGUAGE_FEATURES.md ├── LICENSE ├── README.md ├── derw-package.json ├── docs ├── constants.md └── tests.md ├── examples ├── SideEffects.derw ├── SideEffects.elm ├── SideEffects.js ├── SideEffects.ts ├── advent_of_code │ ├── day_1 │ │ ├── input.txt │ │ ├── part_1.derw │ │ ├── part_1.elm │ │ ├── part_1.js │ │ ├── part_1.ts │ │ ├── part_2.derw │ │ ├── part_2.elm │ │ ├── part_2.js │ │ └── part_2.ts │ ├── day_2 │ │ ├── input.txt │ │ ├── part_1.derw │ │ ├── part_1.elm │ │ ├── part_1.js │ │ ├── part_1.ts │ │ ├── part_2.derw │ │ ├── part_2.elm │ │ ├── part_2.js │ │ └── part_2.ts │ └── day_3 │ │ ├── input.txt │ │ ├── part_1.derw │ │ ├── part_1.elm │ │ ├── part_1.js │ │ └── part_1.ts ├── complex_union.derw ├── complex_union.elm ├── complex_union.js ├── complex_union.ts ├── derw_imports │ ├── main.derw │ └── other.derw ├── errors │ ├── mismatching_types.derw │ └── name_collisions.derw ├── nested_union.derw ├── nested_union.elm ├── nested_union.js ├── nested_union.ts ├── simple_union.derw ├── simple_union.elm ├── simple_union.js └── simple_union.ts ├── package-lock.json ├── package.json ├── src ├── Blocks.derw ├── Blocks.ts ├── Collisions.derw ├── Collisions.ts ├── Generator.derw ├── Generator.ts ├── Tokens.derw ├── Tokens.ts ├── Tokens_types_kernel.ts ├── Utils.derw ├── Utils.ts ├── __snapshots__ │ └── src │ │ └── tests │ │ └── simple_union_test │ │ └── snapshotGeneratedDerw.ts ├── bench │ └── examples_bench.ts ├── builtins.ts ├── cli.ts ├── cli │ ├── bundle.ts │ ├── compile.ts │ ├── format.ts │ ├── info.ts │ ├── init.ts │ ├── install.ts │ ├── repl.ts │ ├── template.ts │ ├── testing.ts │ ├── utils.ts │ └── version.ts ├── compile.ts ├── debugging │ ├── ast.ts │ └── tokens.ts ├── derw.ts ├── errors │ ├── distance.ts │ └── names.ts ├── generators │ ├── Common.derw │ ├── Common.ts │ ├── CommonToEcma.derw │ ├── CommonToEcma.ts │ ├── Derw.derw │ ├── Derw.ts │ ├── Elm.derw │ ├── Elm.ts │ ├── English.derw │ ├── English.ts │ ├── Js.derw │ ├── Js.ts │ ├── Ts.derw │ └── Ts.ts ├── package.ts ├── parser.ts ├── stdlib │ ├── Bitwise.derw │ ├── Bitwise.ts │ ├── Bitwise_kernel.ts │ ├── List.derw │ ├── List.ts │ ├── List_kernel.ts │ ├── Maybe.derw │ └── Maybe.ts ├── tests │ ├── accessor_test.ts │ ├── array_test.ts │ ├── array_with_left_pipe_test.ts │ ├── array_with_right_pipe_test.ts │ ├── boolean_logic_test.ts │ ├── case_nested_test.ts │ ├── case_with_fn_call_test.ts │ ├── case_with_let_and_nested_types_test.ts │ ├── case_with_let_test.ts │ ├── case_with_list_destructure_and_multiple_union_test.ts │ ├── case_with_list_destructure_and_union_single_unpacking_test.ts │ ├── case_with_list_destructure_and_union_test.ts │ ├── case_with_list_destructure_test.ts │ ├── case_with_nested_let_and_pipe_test.ts │ ├── case_with_nested_let_test.ts │ ├── case_with_string_list_destructure_test.ts │ ├── case_with_string_test.ts │ ├── cli_test.ts │ ├── collisions_test.ts │ ├── comment_in_let_test.ts │ ├── comment_test.ts │ ├── complex_union_test.ts │ ├── const_case_test.ts │ ├── const_if_test.ts │ ├── const_nested_if_test.ts │ ├── const_with_let_and_case_test.ts │ ├── const_with_let_and_if_test.ts │ ├── const_with_let_test.ts │ ├── derw_function_call_test.ts │ ├── double_list_prepend_test.ts │ ├── dynamic_list_ranges_test.ts │ ├── empty_array_test.ts │ ├── empty_function_call_test.ts │ ├── empty_string_test.ts │ ├── empty_type_alias_test.ts │ ├── errors │ │ ├── incomplete_case_statement_test.ts │ │ ├── let_and_do_errors_test.ts │ │ ├── names_in_scope_test.ts │ │ ├── names_out_of_scope_test.ts │ │ ├── names_test.ts │ │ └── types │ │ │ └── int_and_string_test.ts │ ├── examples_test.ts │ ├── export_test.ts │ ├── export_tests_test.ts │ ├── format │ │ └── auto_name_for_unamed_function_args_test.ts │ ├── format_string_function_test.ts │ ├── format_string_values_test.ts │ ├── if_with_else_if_nested_test.ts │ ├── if_with_else_if_test.ts │ ├── if_with_else_ifs_and_lets_test.ts │ ├── if_with_else_ifs_test.ts │ ├── if_with_fn_call_test.ts │ ├── if_with_let_parent_types_test.ts │ ├── if_with_let_test.ts │ ├── import_overlapping_types_const_let_block_test.ts │ ├── import_overlapping_types_const_test.ts │ ├── import_overlapping_types_fn_and_do_test.ts │ ├── import_overlapping_types_fn_arg_test.ts │ ├── import_overlapping_types_fn_test.ts │ ├── import_overlapping_types_type_alias_test.ts │ ├── import_test.ts │ ├── inline_if_with_fn_call_test.ts │ ├── lambda_filter_test.ts │ ├── lambda_in_brackets_test.ts │ ├── lambda_in_brackets_with_if_test.ts │ ├── lambda_in_object_literal_test.ts │ ├── lambda_in_union_test.ts │ ├── lambda_test.ts │ ├── left_pipe_chained_lambda_test.ts │ ├── left_pipe_chained_test.ts │ ├── left_pipe_constructor_test.ts │ ├── left_pipe_in_parens_test.ts │ ├── left_pipe_test.ts │ ├── let_test.ts │ ├── let_with_type_test.ts │ ├── list_prepend_multiple_test.ts │ ├── list_prepend_test.ts │ ├── list_prepend_with_fn_call_test.ts │ ├── list_prepend_with_module_reference_test.ts │ ├── list_prepend_with_object_literal_outside_test.ts │ ├── list_prepend_with_object_literal_test.ts │ ├── list_prepend_with_union_test.ts │ ├── list_ranges_test.ts │ ├── list_with_records_test.ts │ ├── mathematical_operators_test.ts │ ├── mixed_left_pipes_test.ts │ ├── mixed_pipes_test.ts │ ├── module_reference_test.ts │ ├── multi_line_comments_test.ts │ ├── multiline_comment_test.ts │ ├── multiline_string_test.ts │ ├── multiline_string_within_if_test.ts │ ├── multiple_union_test.ts │ ├── negative_number_literal_test.ts │ ├── nested_array_test.ts │ ├── nested_array_with_fn_test.ts │ ├── nested_double_simple_function_with_type_argument_fn_test.ts │ ├── nested_fn_call_test.ts │ ├── nested_object_literal_test.ts │ ├── nested_simple_function_with_type_argument_fn_test.ts │ ├── nested_simple_function_with_type_argument_test.ts │ ├── nested_string_test.ts │ ├── object_literal_empty_test.ts │ ├── object_literal_test.ts │ ├── object_literal_with_base_test.ts │ ├── object_literal_with_fn_call_test.ts │ ├── object_literal_with_or_test.ts │ ├── operators_with_fn_calls_test.ts │ ├── package_test.ts │ ├── piped_lambda_test.ts │ ├── promise_any_main_test.ts │ ├── result_value_test.ts │ ├── result_with_nested_arg_test.ts │ ├── right_pipe_test.ts │ ├── simple_function_test.ts │ ├── simple_function_with_complex_args_test.ts │ ├── simple_function_with_do_case_test.ts │ ├── simple_function_with_do_fetch_test.ts │ ├── simple_function_with_do_if_test.ts │ ├── simple_function_with_do_test.ts │ ├── simple_function_with_empty_fn_call_in_fn_call_test.ts │ ├── simple_function_with_empty_fn_call_test.ts │ ├── simple_function_with_fn_call_in_object_literal_test.ts │ ├── simple_function_with_function_arg_test.ts │ ├── simple_function_with_function_fixed_type_arg_test.ts │ ├── simple_function_with_function_list_arg_test.ts │ ├── simple_function_with_imported_type_test.ts │ ├── simple_function_with_let_and_do_test.ts │ ├── simple_function_with_multiple_type_arguments_test.ts │ ├── simple_function_with_nested_fn_call_in_object_literal_test.ts │ ├── simple_function_with_nested_type_argument_test.ts │ ├── simple_function_with_then_test.ts │ ├── simple_function_with_type_argument_test.ts │ ├── simple_union_test.ts │ ├── single_line_comments_test.ts │ ├── stdlib_test.ts │ ├── string_with_three_dots_test.ts │ ├── tokens_test.ts │ ├── type_alias_test.ts │ ├── type_alias_with_fns_test.ts │ ├── type_alias_with_maybe_test.ts │ ├── type_alias_with_type_property_test.ts │ ├── type_checking_const_test.ts │ ├── type_checking_function_test.ts │ ├── type_checking_scope_const_test.ts │ ├── type_checking_scope_function_test.ts │ ├── type_checking_test.ts │ ├── type_checking_types_test.ts │ ├── type_tokens_test.ts │ ├── typeclass_test.ts │ ├── union_type_with_type_property_test.ts │ ├── union_untagged_test.ts │ ├── union_untagged_with_case_test.ts │ ├── union_untagged_with_const_test.ts │ ├── union_with_case_test.ts │ ├── union_with_fn_argument_test.ts │ ├── union_with_fn_call_test.ts │ ├── union_with_multiple_type_arguments_test.ts │ ├── union_with_recursive_type_argument_test.ts │ ├── union_with_type_argument_test.ts │ └── values_test.ts ├── type_checking.ts └── types.ts ├── test_on_chromebook.sh ├── translation_progress.sh └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.derw linguist-language=Elm 2 | *.derw gitlab-language=elm 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | # bump branch 3 | github: [eeue56] 4 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [main] 10 | pull_request: 11 | branches: [main] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - name: Check out main 27 | uses: actions/checkout@v2 28 | with: 29 | path: derw 30 | 31 | - name: Checkout stdlib 32 | uses: actions/checkout@v2 33 | with: 34 | repository: derw-lang/stdlib 35 | path: derw-lang/stdlib 36 | 37 | - name: Setup Node.js environment 38 | uses: actions/setup-node@v2.5.0 39 | 40 | - name: Install ts-node 41 | working-directory: derw 42 | run: npm install ts-node 43 | 44 | - name: Install npm packages 45 | working-directory: derw 46 | run: npm install 47 | 48 | - name: Build stdlib 49 | working-directory: derw 50 | run: npm run stdlib 51 | 52 | - name: Run test 53 | working-directory: derw 54 | run: npm run test 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | build 107 | examples/derw_imports/*.ts 108 | examples/derw_imports/*.elm 109 | 110 | examples/elm.json 111 | examples/elm-stuff 112 | test/ 113 | stdlib/*.ts 114 | derw-packages 115 | 116 | # derw 117 | 118 | derw-packages/ 119 | 120 | # derw generated files -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/tests 2 | build/tests 3 | examples 4 | test/ 5 | bench/ 6 | derw-packages/ 7 | src/debugging 8 | build/debugging 9 | src/bench 10 | build/bench 11 | .github/workflows/ 12 | build/__snapshots__ 13 | src/__snapshots__ -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v17.0.1 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | .cache 3 | dist -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "bracketSpacing": true 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Noah 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # derw 2 | 3 | Welcome to Derw! Derw is a type-safe language in the ML family designed to replace or augment your TypeScript and JavaScript code - both on the client and the server. If you've never encountered an ML language before, some of the core principles - which Derw follows - is a clean and concise syntax, paired with a powerful type system. Here's some real world Derw code: 4 | 5 | ```elm 6 | generateTypeAlias: TypeAlias -> string 7 | generateTypeAlias syntax = 8 | let 9 | properties: string 10 | properties = 11 | List.map generateProperty syntax.properties 12 | |> (\y -> y.join ",\n ") 13 | 14 | typeDef: string 15 | typeDef = 16 | generateType syntax.type 17 | in 18 | if syntax.properties.length == 0 then 19 | `type alias ${typeDef} = {\n}` 20 | else 21 | `type alias ${typeDef} = {\n ${properties}\n}` 22 | ``` 23 | 24 | ## Why might you use Derw? 25 | 26 | Derw is a language for those searching for a better syntax for writing type-heavy code. It is a general purpose language, for both the server and the client, built on top of the JavaScript platform. It has interop with Javascript and TypeScript built in - so that you can use existing code and libraries with minimal effort. Derw targets multiple languages - TypeScript, JavaScript, Elm, English and Derw itself. Derw's output generation is documented in the [Gitbook](https://docs.derw-lang.com/), so it's easy to create code to interface between Derw and TypeScript. 27 | 28 | If you want to write a website, both backend and frontend, Derw is a perfect choice for you. 29 | 30 | ## Batteries built-in 31 | 32 | - A testing framework (all of Derw's compiler tests use this library!) 33 | - Performant web framework with server side rendering and hydration 34 | - Bundling built into the CLI 35 | - Write better code by leveraging a type system that guides your code. 36 | - Integrate with your existing code bases through interop with JavaScript and TypeScript. 37 | 38 | ## Getting Started 39 | 40 | Head over to the [Gitbook](https://docs.derw-lang.com/). 41 | 42 | ## Staying up to date 43 | 44 | Homepage: https://www.derw-lang.com/ 45 | 46 | Blog: http://derw.substack.com/ 47 | 48 | Follow Derw on Twitter: https://twitter.com/derwlang 49 | 50 | # Name 51 | 52 | derw (/ˈdeːruː/, Welsh “oak”) is one of the native trees in Wales, famous for long life, tall stature, and hard, good quality wood. An English speaker might pronounce it as “deh-ru”. 53 | -------------------------------------------------------------------------------- /derw-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "derw", 3 | "exposing": [], 4 | "dependencies": { 5 | } 6 | } -------------------------------------------------------------------------------- /docs/constants.md: -------------------------------------------------------------------------------- 1 | # Constants 2 | 3 | Constants are the foundation of code: they are immutable values which remain with the same name and value. Constants can be assigned values from literals or a function call. 4 | 5 | ## Literals 6 | 7 | ```derw 8 | helloMessage: string 9 | helloMessage = 10 | "Hello reader!" 11 | 12 | myAge: number 13 | myAge = 14 | 28 15 | 16 | twoAges: number 17 | twoAges = 18 | 28 + 29 19 | 20 | type Person = { 21 | name: string, 22 | age: number 23 | } 24 | 25 | me: Person 26 | me = 27 | { name: "Noah", age: myAge } 28 | 29 | guests: List string 30 | guests = 31 | [ "Dave", "James", "Harry" ] 32 | 33 | scores: List number 34 | scores = 35 | [ 99, 100, 57 ] 36 | ``` 37 | 38 | ## Functions 39 | 40 | ```derw 41 | scoreAverage: number 42 | scoreAverage = 43 | [ 99, 100, 57 ] 44 | |> List.foldl (\x xs -> x + xs) 45 | |> (\x -> x / 3) 46 | ``` -------------------------------------------------------------------------------- /docs/tests.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | Tests in Derw follow three simple steps: 4 | 5 | - Import `Test` from stdlib 6 | - Write a function `testAnExample: boolean? -> void` 7 | - Write the function body using `Test.equals` or `Test.notEquals` 8 | - The file should be named `SomeName_test.derw` 9 | 10 | You can then run `derw test` from inside your project to run all tests. There no need to manually expose tests as Derw will do it for you. 11 | 12 | You can run specific tests via `derw test --file SomeName_test.derw --function testAnExample`. 13 | 14 | To see examples of tests, check out the [stdlib examples](https://github.com/derw-lang/stdlib/tree/main/src). 15 | 16 | Under the hood, [Bach](https://github.com/eeue56/bach) is used for running tests. -------------------------------------------------------------------------------- /examples/SideEffects.derw: -------------------------------------------------------------------------------- 1 | sayHi: string -> void 2 | sayHi name = 3 | do 4 | globalThis.console.log "Hello" name 5 | return 6 | undefined 7 | -------------------------------------------------------------------------------- /examples/SideEffects.elm: -------------------------------------------------------------------------------- 1 | module Examples.SideEffects exposing (..) 2 | 3 | sayHi: String -> String 4 | sayHi name = 5 | undefined 6 | -------------------------------------------------------------------------------- /examples/SideEffects.js: -------------------------------------------------------------------------------- 1 | async function sayHi(name) { 2 | await globalThis.console.log("Hello", name); 3 | return undefined; 4 | } 5 | -------------------------------------------------------------------------------- /examples/SideEffects.ts: -------------------------------------------------------------------------------- 1 | async function sayHi(name: string): Promise { 2 | await globalThis.console.log("Hello", name); 3 | return undefined; 4 | } 5 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_1/part_1.derw: -------------------------------------------------------------------------------- 1 | import fs 2 | import globalThis exposing ( Buffer ) 3 | 4 | type alias Iteration = { 5 | value: number, 6 | count: number 7 | } 8 | 9 | isIncrease: number -> number -> boolean 10 | isIncrease x y = 11 | x < y 12 | 13 | countHelper: Iteration -> number -> Iteration 14 | countHelper count x = 15 | if isIncrease count.value x then 16 | { 17 | value: x, 18 | count: count.count + 1 19 | } 20 | else 21 | { 22 | value: x, 23 | count: count.count 24 | } 25 | 26 | getCount: Iteration -> number 27 | getCount iteration = 28 | iteration.count 29 | 30 | countIncreases: List number -> number 31 | countIncreases xs = 32 | xs.reduce countHelper { 33 | value: 9999, 34 | count: 0 35 | } 36 | |> getCount 37 | 38 | adventInput: List number 39 | adventInput = 40 | fs.readFileSync "input.txt" 41 | |> toString 42 | |> split 43 | |> toNumbers 44 | 45 | toInt: string -> number 46 | toInt str = 47 | globalThis.parseInt str 10 48 | 49 | toNumbers: List string -> List number 50 | toNumbers list = 51 | list.map toInt 52 | 53 | split: string -> List string 54 | split file = 55 | file.split "\n" 56 | 57 | toString: Buffer -> string 58 | toString buffer = 59 | buffer.toString() 60 | 61 | exampleMain: void 62 | exampleMain = 63 | countIncreases [ 64 | 199, 65 | 200, 66 | 208, 67 | 210, 68 | 200, 69 | 207, 70 | 240, 71 | 269, 72 | 260, 73 | 263 74 | ] 75 | |> globalThis.console.log 76 | 77 | main: void 78 | main = 79 | countIncreases adventInput 80 | |> globalThis.console.log 81 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_1/part_1.elm: -------------------------------------------------------------------------------- 1 | module Examples.advent_of_code.day_1.part_1 exposing (..) 2 | 3 | import fs 4 | 5 | import globalThis exposing ( Buffer ) 6 | 7 | type alias Iteration = { 8 | value: Float, 9 | count: Float 10 | } 11 | 12 | isIncrease: Float -> Float -> Bool 13 | isIncrease x y = 14 | x < y 15 | 16 | countHelper: Iteration -> Float -> Iteration 17 | countHelper count x = 18 | if isIncrease count.value x then 19 | { 20 | value = x, 21 | count = count.count + 1 22 | } 23 | else 24 | { 25 | value = x, 26 | count = count.count 27 | } 28 | 29 | getCount: Iteration -> Float 30 | getCount iteration = 31 | iteration.count 32 | 33 | countIncreases: List Float -> Float 34 | countIncreases xs = 35 | xs.reduce countHelper { 36 | value = 9999, 37 | count = 0 38 | } 39 | |> getCount 40 | 41 | adventInput: List Float 42 | adventInput = 43 | fs.readFileSync "input.txt" 44 | |> toString 45 | |> split 46 | |> toNumbers 47 | 48 | toInt: String -> Float 49 | toInt str = 50 | globalThis.parseInt str 10 51 | 52 | toNumbers: List String -> List Float 53 | toNumbers list = 54 | list.map toInt 55 | 56 | split: String -> List String 57 | split file = 58 | file.split "\n" 59 | 60 | toString: Buffer -> String 61 | toString buffer = 62 | buffer.toString() 63 | 64 | exampleMain: String 65 | exampleMain = 66 | countIncreases [ 67 | 199, 68 | 200, 69 | 208, 70 | 210, 71 | 200, 72 | 207, 73 | 240, 74 | 269, 75 | 260, 76 | 263 77 | ] 78 | |> globalThis.console.log 79 | 80 | main: String 81 | main = 82 | countIncreases adventInput 83 | |> globalThis.console.log 84 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_1/part_1.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | function Iteration(args) { 4 | return { 5 | ...args, 6 | }; 7 | } 8 | 9 | function isIncrease(x, y) { 10 | return x < y; 11 | } 12 | 13 | function countHelper(count, x) { 14 | if (isIncrease(count.value, x)) { 15 | return { 16 | value: x, 17 | count: count.count + 1 18 | }; 19 | } else { 20 | return { 21 | value: x, 22 | count: count.count 23 | }; 24 | } 25 | } 26 | 27 | function getCount(iteration) { 28 | return iteration.count; 29 | } 30 | 31 | function countIncreases(xs) { 32 | return getCount(xs.reduce(countHelper, { 33 | value: 9999, 34 | count: 0 35 | })); 36 | } 37 | 38 | const adventInput = toNumbers(split(toString(fs.readFileSync("input.txt")))); 39 | 40 | function toInt(str) { 41 | return globalThis.parseInt(str, 10); 42 | } 43 | 44 | function toNumbers(list) { 45 | return list.map(toInt); 46 | } 47 | 48 | function split(file) { 49 | return file.split("\n"); 50 | } 51 | 52 | function toString(buffer) { 53 | return buffer.toString(); 54 | } 55 | 56 | const exampleMain = globalThis.console.log(countIncreases([ 199, 200, 208, 210, 200, 207, 240, 269, 260, 263 ])); 57 | 58 | const main = globalThis.console.log(countIncreases(adventInput)); 59 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_1/part_1.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | type Iteration = { 4 | value: number; 5 | count: number; 6 | } 7 | 8 | function Iteration(args: { value: number, count: number }): Iteration { 9 | return { 10 | ...args, 11 | }; 12 | } 13 | 14 | function isIncrease(x: number, y: number): boolean { 15 | return x < y; 16 | } 17 | 18 | function countHelper(count: Iteration, x: number): Iteration { 19 | if (isIncrease(count.value, x)) { 20 | return { 21 | value: x, 22 | count: count.count + 1 23 | }; 24 | } else { 25 | return { 26 | value: x, 27 | count: count.count 28 | }; 29 | } 30 | } 31 | 32 | function getCount(iteration: Iteration): number { 33 | return iteration.count; 34 | } 35 | 36 | function countIncreases(xs: number[]): number { 37 | return getCount(xs.reduce(countHelper, { 38 | value: 9999, 39 | count: 0 40 | })); 41 | } 42 | 43 | const adventInput: number[] = toNumbers(split(toString(fs.readFileSync("input.txt")))); 44 | 45 | function toInt(str: string): number { 46 | return globalThis.parseInt(str, 10); 47 | } 48 | 49 | function toNumbers(list: string[]): number[] { 50 | return list.map(toInt); 51 | } 52 | 53 | function split(file: string): string[] { 54 | return file.split("\n"); 55 | } 56 | 57 | function toString(buffer: Buffer): string { 58 | return buffer.toString(); 59 | } 60 | 61 | const exampleMain: void = globalThis.console.log(countIncreases([ 199, 200, 208, 210, 200, 207, 240, 269, 260, 263 ])); 62 | 63 | const main: void = globalThis.console.log(countIncreases(adventInput)); 64 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_1/part_2.derw: -------------------------------------------------------------------------------- 1 | import fs 2 | import globalThis exposing ( Buffer ) 3 | 4 | type alias Iteration = { 5 | a: number, 6 | b: number, 7 | c: number, 8 | value: number, 9 | count: number 10 | } 11 | 12 | isIncrease: number -> number -> boolean 13 | isIncrease x y = 14 | x < y 15 | 16 | sumIteration: Iteration -> number 17 | sumIteration iteration = 18 | iteration.a + iteration.b + iteration.c 19 | 20 | countHelper: Iteration -> number -> Iteration 21 | countHelper count x = 22 | let 23 | sumCurrent: number 24 | sumCurrent = 25 | sumIteration count 26 | 27 | sumNext: number 28 | sumNext = 29 | count.b + count.c + x 30 | in 31 | if count.a == 9999 then 32 | { 33 | a: x, 34 | b: count.b, 35 | c: count.c, 36 | value: x, 37 | count: count.count 38 | } 39 | else 40 | if count.b == 9999 then 41 | { 42 | a: count.a, 43 | b: x, 44 | c: count.c, 45 | value: x, 46 | count: count.count 47 | } 48 | else 49 | if count.c == 9999 then 50 | { 51 | a: count.a, 52 | b: x, 53 | c: x, 54 | value: x, 55 | count: count.count 56 | } 57 | else 58 | if isIncrease sumCurrent sumNext then 59 | { 60 | a: count.b, 61 | b: count.c, 62 | c: x, 63 | value: x, 64 | count: count.count + 1 65 | } 66 | else 67 | { 68 | a: count.b, 69 | b: count.c, 70 | c: x, 71 | value: x, 72 | count: count.count 73 | } 74 | 75 | getCount: Iteration -> number 76 | getCount iteration = 77 | iteration.count 78 | 79 | countIncreases: List number -> number 80 | countIncreases xs = 81 | xs.reduce countHelper { 82 | a: 9999, 83 | b: 9999, 84 | c: 9999, 85 | value: 9999, 86 | count: 0 87 | } 88 | |> getCount 89 | 90 | adventInput: List number 91 | adventInput = 92 | fs.readFileSync "input.txt" 93 | |> toString 94 | |> split 95 | |> toNumbers 96 | 97 | toInt: string -> number 98 | toInt str = 99 | globalThis.parseInt str 10 100 | 101 | toNumbers: List string -> List number 102 | toNumbers list = 103 | list.map toInt 104 | 105 | split: string -> List string 106 | split file = 107 | file.split "\n" 108 | 109 | toString: Buffer -> string 110 | toString buffer = 111 | buffer.toString() 112 | 113 | exampleMain: void 114 | exampleMain = 115 | countIncreases [ 116 | 199, 117 | 200, 118 | 208, 119 | 210, 120 | 200, 121 | 207, 122 | 240, 123 | 269, 124 | 260, 125 | 263 126 | ] 127 | |> console.log 128 | 129 | main: void 130 | main = 131 | countIncreases adventInput 132 | |> globalThis.console.log 133 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_1/part_2.elm: -------------------------------------------------------------------------------- 1 | module Examples.advent_of_code.day_1.part_2 exposing (..) 2 | 3 | import fs 4 | 5 | import globalThis exposing ( Buffer ) 6 | 7 | type alias Iteration = { 8 | a: Float, 9 | b: Float, 10 | c: Float, 11 | value: Float, 12 | count: Float 13 | } 14 | 15 | isIncrease: Float -> Float -> Bool 16 | isIncrease x y = 17 | x < y 18 | 19 | sumIteration: Iteration -> Float 20 | sumIteration iteration = 21 | iteration.a + iteration.b + iteration.c 22 | 23 | countHelper: Iteration -> Float -> Iteration 24 | countHelper count x = 25 | let 26 | sumCurrent: Float 27 | sumCurrent = 28 | sumIteration count 29 | 30 | sumNext: Float 31 | sumNext = 32 | count.b + count.c + x 33 | in 34 | if count.a == 9999 then 35 | { 36 | a = x, 37 | b = count.b, 38 | c = count.c, 39 | value = x, 40 | count = count.count 41 | } 42 | else 43 | if count.b == 9999 then 44 | { 45 | a = count.a, 46 | b = x, 47 | c = count.c, 48 | value = x, 49 | count = count.count 50 | } 51 | else 52 | if count.c == 9999 then 53 | { 54 | a = count.a, 55 | b = x, 56 | c = x, 57 | value = x, 58 | count = count.count 59 | } 60 | else 61 | if isIncrease sumCurrent sumNext then 62 | { 63 | a = count.b, 64 | b = count.c, 65 | c = x, 66 | value = x, 67 | count = count.count + 1 68 | } 69 | else 70 | { 71 | a = count.b, 72 | b = count.c, 73 | c = x, 74 | value = x, 75 | count = count.count 76 | } 77 | 78 | getCount: Iteration -> Float 79 | getCount iteration = 80 | iteration.count 81 | 82 | countIncreases: List Float -> Float 83 | countIncreases xs = 84 | xs.reduce countHelper { 85 | a = 9999, 86 | b = 9999, 87 | c = 9999, 88 | value = 9999, 89 | count = 0 90 | } 91 | |> getCount 92 | 93 | adventInput: List Float 94 | adventInput = 95 | fs.readFileSync "input.txt" 96 | |> toString 97 | |> split 98 | |> toNumbers 99 | 100 | toInt: String -> Float 101 | toInt str = 102 | globalThis.parseInt str 10 103 | 104 | toNumbers: List String -> List Float 105 | toNumbers list = 106 | list.map toInt 107 | 108 | split: String -> List String 109 | split file = 110 | file.split "\n" 111 | 112 | toString: Buffer -> String 113 | toString buffer = 114 | buffer.toString() 115 | 116 | exampleMain: String 117 | exampleMain = 118 | countIncreases [ 119 | 199, 120 | 200, 121 | 208, 122 | 210, 123 | 200, 124 | 207, 125 | 240, 126 | 269, 127 | 260, 128 | 263 129 | ] 130 | |> Debug.log "" 131 | 132 | main: String 133 | main = 134 | countIncreases adventInput 135 | |> globalThis.console.log 136 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_1/part_2.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | function Iteration(args) { 4 | return { 5 | ...args, 6 | }; 7 | } 8 | 9 | function isIncrease(x, y) { 10 | return x < y; 11 | } 12 | 13 | function sumIteration(iteration) { 14 | return iteration.a + iteration.b + iteration.c; 15 | } 16 | 17 | function countHelper(count, x) { 18 | const sumCurrent = sumIteration(count); 19 | const sumNext = count.b + count.c + x; 20 | if (count.a === 9999) { 21 | return { 22 | a: x, 23 | b: count.b, 24 | c: count.c, 25 | value: x, 26 | count: count.count 27 | }; 28 | } else { 29 | if (count.b === 9999) { 30 | return { 31 | a: count.a, 32 | b: x, 33 | c: count.c, 34 | value: x, 35 | count: count.count 36 | }; 37 | } else { 38 | if (count.c === 9999) { 39 | return { 40 | a: count.a, 41 | b: x, 42 | c: x, 43 | value: x, 44 | count: count.count 45 | }; 46 | } else { 47 | if (isIncrease(sumCurrent, sumNext)) { 48 | return { 49 | a: count.b, 50 | b: count.c, 51 | c: x, 52 | value: x, 53 | count: count.count + 1 54 | }; 55 | } else { 56 | return { 57 | a: count.b, 58 | b: count.c, 59 | c: x, 60 | value: x, 61 | count: count.count 62 | }; 63 | }; 64 | }; 65 | }; 66 | } 67 | } 68 | 69 | function getCount(iteration) { 70 | return iteration.count; 71 | } 72 | 73 | function countIncreases(xs) { 74 | return getCount(xs.reduce(countHelper, { 75 | a: 9999, 76 | b: 9999, 77 | c: 9999, 78 | value: 9999, 79 | count: 0 80 | })); 81 | } 82 | 83 | const adventInput = toNumbers(split(toString(fs.readFileSync("input.txt")))); 84 | 85 | function toInt(str) { 86 | return globalThis.parseInt(str, 10); 87 | } 88 | 89 | function toNumbers(list) { 90 | return list.map(toInt); 91 | } 92 | 93 | function split(file) { 94 | return file.split("\n"); 95 | } 96 | 97 | function toString(buffer) { 98 | return buffer.toString(); 99 | } 100 | 101 | const exampleMain = console.log(countIncreases([ 199, 200, 208, 210, 200, 207, 240, 269, 260, 263 ])); 102 | 103 | const main = globalThis.console.log(countIncreases(adventInput)); 104 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_1/part_2.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | type Iteration = { 4 | a: number; 5 | b: number; 6 | c: number; 7 | value: number; 8 | count: number; 9 | } 10 | 11 | function Iteration(args: { a: number, b: number, c: number, value: number, count: number }): Iteration { 12 | return { 13 | ...args, 14 | }; 15 | } 16 | 17 | function isIncrease(x: number, y: number): boolean { 18 | return x < y; 19 | } 20 | 21 | function sumIteration(iteration: Iteration): number { 22 | return iteration.a + iteration.b + iteration.c; 23 | } 24 | 25 | function countHelper(count: Iteration, x: number): Iteration { 26 | const sumCurrent: number = sumIteration(count); 27 | const sumNext: number = count.b + count.c + x; 28 | if (count.a === 9999) { 29 | return { 30 | a: x, 31 | b: count.b, 32 | c: count.c, 33 | value: x, 34 | count: count.count 35 | }; 36 | } else { 37 | if (count.b === 9999) { 38 | return { 39 | a: count.a, 40 | b: x, 41 | c: count.c, 42 | value: x, 43 | count: count.count 44 | }; 45 | } else { 46 | if (count.c === 9999) { 47 | return { 48 | a: count.a, 49 | b: x, 50 | c: x, 51 | value: x, 52 | count: count.count 53 | }; 54 | } else { 55 | if (isIncrease(sumCurrent, sumNext)) { 56 | return { 57 | a: count.b, 58 | b: count.c, 59 | c: x, 60 | value: x, 61 | count: count.count + 1 62 | }; 63 | } else { 64 | return { 65 | a: count.b, 66 | b: count.c, 67 | c: x, 68 | value: x, 69 | count: count.count 70 | }; 71 | }; 72 | }; 73 | }; 74 | } 75 | } 76 | 77 | function getCount(iteration: Iteration): number { 78 | return iteration.count; 79 | } 80 | 81 | function countIncreases(xs: number[]): number { 82 | return getCount(xs.reduce(countHelper, { 83 | a: 9999, 84 | b: 9999, 85 | c: 9999, 86 | value: 9999, 87 | count: 0 88 | })); 89 | } 90 | 91 | const adventInput: number[] = toNumbers(split(toString(fs.readFileSync("input.txt")))); 92 | 93 | function toInt(str: string): number { 94 | return globalThis.parseInt(str, 10); 95 | } 96 | 97 | function toNumbers(list: string[]): number[] { 98 | return list.map(toInt); 99 | } 100 | 101 | function split(file: string): string[] { 102 | return file.split("\n"); 103 | } 104 | 105 | function toString(buffer: Buffer): string { 106 | return buffer.toString(); 107 | } 108 | 109 | const exampleMain: void = console.log(countIncreases([ 199, 200, 208, 210, 200, 207, 240, 269, 260, 263 ])); 110 | 111 | const main: void = globalThis.console.log(countIncreases(adventInput)); 112 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_2/part_1.derw: -------------------------------------------------------------------------------- 1 | import fs 2 | import globalThis exposing ( Buffer ) 3 | 4 | type alias Boat = { 5 | horizontal: number, 6 | depth: number 7 | } 8 | 9 | forward: number -> Boat -> Boat 10 | forward x boat = 11 | { 12 | horizontal: x + boat.horizontal, 13 | depth: boat.depth 14 | } 15 | 16 | up: number -> Boat -> Boat 17 | up y boat = 18 | { 19 | horizontal: boat.horizontal, 20 | depth: boat.depth - y 21 | } 22 | 23 | down: number -> Boat -> Boat 24 | down y boat = 25 | { 26 | horizontal: boat.horizontal, 27 | depth: boat.depth + y 28 | } 29 | 30 | getMulti: Boat -> number 31 | getMulti boat = 32 | boat.horizontal * boat.depth 33 | 34 | run: Boat -> Command -> Boat 35 | run boat command = 36 | if command.command == "forward" then 37 | forward command.amount boat 38 | else 39 | if command.command == "up" then 40 | up command.amount boat 41 | else 42 | down command.amount boat 43 | 44 | runAll: List Command -> Boat 45 | runAll commands = 46 | commands.reduce run { 47 | horizontal: 0, 48 | depth: 0 49 | } 50 | 51 | type alias Command = { 52 | command: string, 53 | amount: number 54 | } 55 | 56 | parseLine: string -> Command 57 | parseLine str = 58 | let 59 | piece: List string 60 | piece = 61 | str.split " " 62 | 63 | left: string 64 | left = 65 | piece[0] 66 | 67 | right: number 68 | right = 69 | globalThis.parseInt piece[1] 70 | in 71 | { 72 | command: left, 73 | amount: right 74 | } 75 | 76 | parseLines: List string -> List Command 77 | parseLines lines = 78 | lines.map parseLine 79 | 80 | exampleMain: void 81 | exampleMain = 82 | [ 83 | "forward 5", 84 | "down 5", 85 | "forward 8", 86 | "up 3", 87 | "down 8", 88 | "forward 2" 89 | ] 90 | |> parseLines 91 | |> runAll 92 | |> globalThis.console.log 93 | 94 | adventInput: List Command 95 | adventInput = 96 | fs.readFileSync "input.txt" 97 | |> toString 98 | |> split 99 | |> parseLines 100 | 101 | split: string -> List string 102 | split file = 103 | file.split "\n" 104 | 105 | toString: Buffer -> string 106 | toString buffer = 107 | buffer.toString() 108 | 109 | main: void 110 | main = 111 | adventInput 112 | |> runAll 113 | |> globalThis.console.log 114 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_2/part_1.elm: -------------------------------------------------------------------------------- 1 | module Examples.advent_of_code.day_2.part_1 exposing (..) 2 | 3 | import fs 4 | 5 | import globalThis exposing ( Buffer ) 6 | 7 | type alias Boat = { 8 | horizontal: Float, 9 | depth: Float 10 | } 11 | 12 | forward: Float -> Boat -> Boat 13 | forward x boat = 14 | { 15 | horizontal = x + boat.horizontal, 16 | depth = boat.depth 17 | } 18 | 19 | up: Float -> Boat -> Boat 20 | up y boat = 21 | { 22 | horizontal = boat.horizontal, 23 | depth = boat.depth - y 24 | } 25 | 26 | down: Float -> Boat -> Boat 27 | down y boat = 28 | { 29 | horizontal = boat.horizontal, 30 | depth = boat.depth + y 31 | } 32 | 33 | getMulti: Boat -> Float 34 | getMulti boat = 35 | boat.horizontal * boat.depth 36 | 37 | run: Boat -> Command -> Boat 38 | run boat command = 39 | if command.command == "forward" then 40 | forward command.amount boat 41 | else 42 | if command.command == "up" then 43 | up command.amount boat 44 | else 45 | down command.amount boat 46 | 47 | runAll: List Command -> Boat 48 | runAll commands = 49 | commands.reduce run { 50 | horizontal = 0, 51 | depth = 0 52 | } 53 | 54 | type alias Command = { 55 | command: String, 56 | amount: Float 57 | } 58 | 59 | parseLine: String -> Command 60 | parseLine str = 61 | let 62 | piece: List String 63 | piece = 64 | str.split " " 65 | 66 | left: String 67 | left = 68 | piece[0] 69 | 70 | right: Float 71 | right = 72 | globalThis.parseInt piece[1] 73 | in 74 | { 75 | command = left, 76 | amount = right 77 | } 78 | 79 | parseLines: List String -> List Command 80 | parseLines lines = 81 | lines.map parseLine 82 | 83 | exampleMain: String 84 | exampleMain = 85 | [ 86 | "forward 5", 87 | "down 5", 88 | "forward 8", 89 | "up 3", 90 | "down 8", 91 | "forward 2" 92 | ] 93 | |> parseLines 94 | |> runAll 95 | |> globalThis.console.log 96 | 97 | adventInput: List Command 98 | adventInput = 99 | fs.readFileSync "input.txt" 100 | |> toString 101 | |> split 102 | |> parseLines 103 | 104 | split: String -> List String 105 | split file = 106 | file.split "\n" 107 | 108 | toString: Buffer -> String 109 | toString buffer = 110 | buffer.toString() 111 | 112 | main: String 113 | main = 114 | adventInput 115 | |> runAll 116 | |> globalThis.console.log 117 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_2/part_1.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | function Boat(args) { 4 | return { 5 | ...args, 6 | }; 7 | } 8 | 9 | function forward(x, boat) { 10 | return { 11 | horizontal: x + boat.horizontal, 12 | depth: boat.depth 13 | }; 14 | } 15 | 16 | function up(y, boat) { 17 | return { 18 | horizontal: boat.horizontal, 19 | depth: boat.depth - y 20 | }; 21 | } 22 | 23 | function down(y, boat) { 24 | return { 25 | horizontal: boat.horizontal, 26 | depth: boat.depth + y 27 | }; 28 | } 29 | 30 | function getMulti(boat) { 31 | return boat.horizontal * boat.depth; 32 | } 33 | 34 | function run(boat, command) { 35 | if (command.command === "forward") { 36 | return forward(command.amount, boat); 37 | } else { 38 | if (command.command === "up") { 39 | return up(command.amount, boat); 40 | } else { 41 | return down(command.amount, boat); 42 | }; 43 | } 44 | } 45 | 46 | function runAll(commands) { 47 | return commands.reduce(run, { 48 | horizontal: 0, 49 | depth: 0 50 | }); 51 | } 52 | 53 | function Command(args) { 54 | return { 55 | ...args, 56 | }; 57 | } 58 | 59 | function parseLine(str) { 60 | const piece = str.split(" "); 61 | const left = piece[0]; 62 | const right = globalThis.parseInt(piece[1]); 63 | return { 64 | command: left, 65 | amount: right 66 | }; 67 | } 68 | 69 | function parseLines(lines) { 70 | return lines.map(parseLine); 71 | } 72 | 73 | const exampleMain = globalThis.console.log(runAll(parseLines([ "forward 5", "down 5", "forward 8", "up 3", "down 8", "forward 2" ]))); 74 | 75 | const adventInput = parseLines(split(toString(fs.readFileSync("input.txt")))); 76 | 77 | function split(file) { 78 | return file.split("\n"); 79 | } 80 | 81 | function toString(buffer) { 82 | return buffer.toString(); 83 | } 84 | 85 | const main = globalThis.console.log(runAll(adventInput)); 86 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_2/part_1.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | type Boat = { 4 | horizontal: number; 5 | depth: number; 6 | } 7 | 8 | function Boat(args: { horizontal: number, depth: number }): Boat { 9 | return { 10 | ...args, 11 | }; 12 | } 13 | 14 | function forward(x: number, boat: Boat): Boat { 15 | return { 16 | horizontal: x + boat.horizontal, 17 | depth: boat.depth 18 | }; 19 | } 20 | 21 | function up(y: number, boat: Boat): Boat { 22 | return { 23 | horizontal: boat.horizontal, 24 | depth: boat.depth - y 25 | }; 26 | } 27 | 28 | function down(y: number, boat: Boat): Boat { 29 | return { 30 | horizontal: boat.horizontal, 31 | depth: boat.depth + y 32 | }; 33 | } 34 | 35 | function getMulti(boat: Boat): number { 36 | return boat.horizontal * boat.depth; 37 | } 38 | 39 | function run(boat: Boat, command: Command): Boat { 40 | if (command.command === "forward") { 41 | return forward(command.amount, boat); 42 | } else { 43 | if (command.command === "up") { 44 | return up(command.amount, boat); 45 | } else { 46 | return down(command.amount, boat); 47 | }; 48 | } 49 | } 50 | 51 | function runAll(commands: Command[]): Boat { 52 | return commands.reduce(run, { 53 | horizontal: 0, 54 | depth: 0 55 | }); 56 | } 57 | 58 | type Command = { 59 | command: string; 60 | amount: number; 61 | } 62 | 63 | function Command(args: { command: string, amount: number }): Command { 64 | return { 65 | ...args, 66 | }; 67 | } 68 | 69 | function parseLine(str: string): Command { 70 | const piece: string[] = str.split(" "); 71 | const left: string = piece[0]; 72 | const right: number = globalThis.parseInt(piece[1]); 73 | return { 74 | command: left, 75 | amount: right 76 | }; 77 | } 78 | 79 | function parseLines(lines: string[]): Command[] { 80 | return lines.map(parseLine); 81 | } 82 | 83 | const exampleMain: void = globalThis.console.log(runAll(parseLines([ "forward 5", "down 5", "forward 8", "up 3", "down 8", "forward 2" ]))); 84 | 85 | const adventInput: Command[] = parseLines(split(toString(fs.readFileSync("input.txt")))); 86 | 87 | function split(file: string): string[] { 88 | return file.split("\n"); 89 | } 90 | 91 | function toString(buffer: Buffer): string { 92 | return buffer.toString(); 93 | } 94 | 95 | const main: void = globalThis.console.log(runAll(adventInput)); 96 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_2/part_2.derw: -------------------------------------------------------------------------------- 1 | import fs 2 | import globalThis exposing ( Buffer ) 3 | 4 | type alias Boat = { 5 | horizontal: number, 6 | depth: number, 7 | aim: number 8 | } 9 | 10 | forward: number -> Boat -> Boat 11 | forward x boat = 12 | let 13 | multi: number 14 | multi = 15 | x * boat.aim 16 | in 17 | { 18 | horizontal: x + boat.horizontal, 19 | depth: boat.depth + multi, 20 | aim: boat.aim 21 | } 22 | 23 | up: number -> Boat -> Boat 24 | up y boat = 25 | { 26 | horizontal: boat.horizontal, 27 | depth: boat.depth, 28 | aim: boat.aim - y 29 | } 30 | 31 | down: number -> Boat -> Boat 32 | down y boat = 33 | { 34 | horizontal: boat.horizontal, 35 | depth: boat.depth, 36 | aim: boat.aim + y 37 | } 38 | 39 | getMulti: Boat -> number 40 | getMulti boat = 41 | boat.horizontal * boat.depth 42 | 43 | run: Boat -> Command -> Boat 44 | run boat command = 45 | if command.command == "forward" then 46 | forward command.amount boat 47 | else 48 | if command.command == "up" then 49 | up command.amount boat 50 | else 51 | down command.amount boat 52 | 53 | runAll: List Command -> Boat 54 | runAll commands = 55 | commands.reduce run { 56 | horizontal: 0, 57 | depth: 0, 58 | aim: 0 59 | } 60 | 61 | type alias Command = { 62 | command: string, 63 | amount: number 64 | } 65 | 66 | parseLine: string -> Command 67 | parseLine str = 68 | let 69 | piece: List string 70 | piece = 71 | str.split " " 72 | 73 | left: string 74 | left = 75 | piece[0] 76 | 77 | right: number 78 | right = 79 | globalThis.parseInt piece[1] 80 | in 81 | { 82 | command: left, 83 | amount: right 84 | } 85 | 86 | parseLines: List string -> List Command 87 | parseLines lines = 88 | lines.map parseLine 89 | 90 | exampleMain: void 91 | exampleMain = 92 | [ 93 | "forward 5", 94 | "down 5", 95 | "forward 8", 96 | "up 3", 97 | "down 8", 98 | "forward 2" 99 | ] 100 | |> parseLines 101 | |> runAll 102 | |> globalThis.console.log 103 | 104 | adventInput: List Command 105 | adventInput = 106 | fs.readFileSync "input.txt" 107 | |> toString 108 | |> split 109 | |> parseLines 110 | 111 | split: string -> List string 112 | split file = 113 | file.split "\n" 114 | 115 | toString: Buffer -> string 116 | toString buffer = 117 | buffer.toString() 118 | 119 | main: void 120 | main = 121 | adventInput 122 | |> runAll 123 | |> getMulti 124 | |> globalThis.console.log 125 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_2/part_2.elm: -------------------------------------------------------------------------------- 1 | module Examples.advent_of_code.day_2.part_2 exposing (..) 2 | 3 | import fs 4 | 5 | import globalThis exposing ( Buffer ) 6 | 7 | type alias Boat = { 8 | horizontal: Float, 9 | depth: Float, 10 | aim: Float 11 | } 12 | 13 | forward: Float -> Boat -> Boat 14 | forward x boat = 15 | let 16 | multi: Float 17 | multi = 18 | x * boat.aim 19 | in 20 | { 21 | horizontal = x + boat.horizontal, 22 | depth = boat.depth + multi, 23 | aim = boat.aim 24 | } 25 | 26 | up: Float -> Boat -> Boat 27 | up y boat = 28 | { 29 | horizontal = boat.horizontal, 30 | depth = boat.depth, 31 | aim = boat.aim - y 32 | } 33 | 34 | down: Float -> Boat -> Boat 35 | down y boat = 36 | { 37 | horizontal = boat.horizontal, 38 | depth = boat.depth, 39 | aim = boat.aim + y 40 | } 41 | 42 | getMulti: Boat -> Float 43 | getMulti boat = 44 | boat.horizontal * boat.depth 45 | 46 | run: Boat -> Command -> Boat 47 | run boat command = 48 | if command.command == "forward" then 49 | forward command.amount boat 50 | else 51 | if command.command == "up" then 52 | up command.amount boat 53 | else 54 | down command.amount boat 55 | 56 | runAll: List Command -> Boat 57 | runAll commands = 58 | commands.reduce run { 59 | horizontal = 0, 60 | depth = 0, 61 | aim = 0 62 | } 63 | 64 | type alias Command = { 65 | command: String, 66 | amount: Float 67 | } 68 | 69 | parseLine: String -> Command 70 | parseLine str = 71 | let 72 | piece: List String 73 | piece = 74 | str.split " " 75 | 76 | left: String 77 | left = 78 | piece[0] 79 | 80 | right: Float 81 | right = 82 | globalThis.parseInt piece[1] 83 | in 84 | { 85 | command = left, 86 | amount = right 87 | } 88 | 89 | parseLines: List String -> List Command 90 | parseLines lines = 91 | lines.map parseLine 92 | 93 | exampleMain: String 94 | exampleMain = 95 | [ 96 | "forward 5", 97 | "down 5", 98 | "forward 8", 99 | "up 3", 100 | "down 8", 101 | "forward 2" 102 | ] 103 | |> parseLines 104 | |> runAll 105 | |> globalThis.console.log 106 | 107 | adventInput: List Command 108 | adventInput = 109 | fs.readFileSync "input.txt" 110 | |> toString 111 | |> split 112 | |> parseLines 113 | 114 | split: String -> List String 115 | split file = 116 | file.split "\n" 117 | 118 | toString: Buffer -> String 119 | toString buffer = 120 | buffer.toString() 121 | 122 | main: String 123 | main = 124 | adventInput 125 | |> runAll 126 | |> getMulti 127 | |> globalThis.console.log 128 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_2/part_2.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | function Boat(args) { 4 | return { 5 | ...args, 6 | }; 7 | } 8 | 9 | function forward(x, boat) { 10 | const multi = x * boat.aim; 11 | return { 12 | horizontal: x + boat.horizontal, 13 | depth: boat.depth + multi, 14 | aim: boat.aim 15 | }; 16 | } 17 | 18 | function up(y, boat) { 19 | return { 20 | horizontal: boat.horizontal, 21 | depth: boat.depth, 22 | aim: boat.aim - y 23 | }; 24 | } 25 | 26 | function down(y, boat) { 27 | return { 28 | horizontal: boat.horizontal, 29 | depth: boat.depth, 30 | aim: boat.aim + y 31 | }; 32 | } 33 | 34 | function getMulti(boat) { 35 | return boat.horizontal * boat.depth; 36 | } 37 | 38 | function run(boat, command) { 39 | if (command.command === "forward") { 40 | return forward(command.amount, boat); 41 | } else { 42 | if (command.command === "up") { 43 | return up(command.amount, boat); 44 | } else { 45 | return down(command.amount, boat); 46 | }; 47 | } 48 | } 49 | 50 | function runAll(commands) { 51 | return commands.reduce(run, { 52 | horizontal: 0, 53 | depth: 0, 54 | aim: 0 55 | }); 56 | } 57 | 58 | function Command(args) { 59 | return { 60 | ...args, 61 | }; 62 | } 63 | 64 | function parseLine(str) { 65 | const piece = str.split(" "); 66 | const left = piece[0]; 67 | const right = globalThis.parseInt(piece[1]); 68 | return { 69 | command: left, 70 | amount: right 71 | }; 72 | } 73 | 74 | function parseLines(lines) { 75 | return lines.map(parseLine); 76 | } 77 | 78 | const exampleMain = globalThis.console.log(runAll(parseLines([ "forward 5", "down 5", "forward 8", "up 3", "down 8", "forward 2" ]))); 79 | 80 | const adventInput = parseLines(split(toString(fs.readFileSync("input.txt")))); 81 | 82 | function split(file) { 83 | return file.split("\n"); 84 | } 85 | 86 | function toString(buffer) { 87 | return buffer.toString(); 88 | } 89 | 90 | const main = globalThis.console.log(getMulti(runAll(adventInput))); 91 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_2/part_2.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | type Boat = { 4 | horizontal: number; 5 | depth: number; 6 | aim: number; 7 | } 8 | 9 | function Boat(args: { horizontal: number, depth: number, aim: number }): Boat { 10 | return { 11 | ...args, 12 | }; 13 | } 14 | 15 | function forward(x: number, boat: Boat): Boat { 16 | const multi: number = x * boat.aim; 17 | return { 18 | horizontal: x + boat.horizontal, 19 | depth: boat.depth + multi, 20 | aim: boat.aim 21 | }; 22 | } 23 | 24 | function up(y: number, boat: Boat): Boat { 25 | return { 26 | horizontal: boat.horizontal, 27 | depth: boat.depth, 28 | aim: boat.aim - y 29 | }; 30 | } 31 | 32 | function down(y: number, boat: Boat): Boat { 33 | return { 34 | horizontal: boat.horizontal, 35 | depth: boat.depth, 36 | aim: boat.aim + y 37 | }; 38 | } 39 | 40 | function getMulti(boat: Boat): number { 41 | return boat.horizontal * boat.depth; 42 | } 43 | 44 | function run(boat: Boat, command: Command): Boat { 45 | if (command.command === "forward") { 46 | return forward(command.amount, boat); 47 | } else { 48 | if (command.command === "up") { 49 | return up(command.amount, boat); 50 | } else { 51 | return down(command.amount, boat); 52 | }; 53 | } 54 | } 55 | 56 | function runAll(commands: Command[]): Boat { 57 | return commands.reduce(run, { 58 | horizontal: 0, 59 | depth: 0, 60 | aim: 0 61 | }); 62 | } 63 | 64 | type Command = { 65 | command: string; 66 | amount: number; 67 | } 68 | 69 | function Command(args: { command: string, amount: number }): Command { 70 | return { 71 | ...args, 72 | }; 73 | } 74 | 75 | function parseLine(str: string): Command { 76 | const piece: string[] = str.split(" "); 77 | const left: string = piece[0]; 78 | const right: number = globalThis.parseInt(piece[1]); 79 | return { 80 | command: left, 81 | amount: right 82 | }; 83 | } 84 | 85 | function parseLines(lines: string[]): Command[] { 86 | return lines.map(parseLine); 87 | } 88 | 89 | const exampleMain: void = globalThis.console.log(runAll(parseLines([ "forward 5", "down 5", "forward 8", "up 3", "down 8", "forward 2" ]))); 90 | 91 | const adventInput: Command[] = parseLines(split(toString(fs.readFileSync("input.txt")))); 92 | 93 | function split(file: string): string[] { 94 | return file.split("\n"); 95 | } 96 | 97 | function toString(buffer: Buffer): string { 98 | return buffer.toString(); 99 | } 100 | 101 | const main: void = globalThis.console.log(getMulti(runAll(adventInput))); 102 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_3/part_1.derw: -------------------------------------------------------------------------------- 1 | import fs 2 | import globalThis exposing ( Buffer ) 3 | 4 | type alias Common = { 5 | zero: number, 6 | one: number 7 | } 8 | 9 | reducer: number -> Common -> string -> Common 10 | reducer index common line = 11 | if line.charAt index == "0" then 12 | { 13 | zero: common.zero + 1, 14 | one: common.one 15 | } 16 | else 17 | { 18 | zero: common.zero, 19 | one: common.one + 1 20 | } 21 | 22 | mostCommon: number -> List string -> Common 23 | mostCommon index bits = 24 | let 25 | something: Common -> string -> Common 26 | something x y = 27 | reducer index x y 28 | in 29 | bits.reduce something { 30 | zero: 0, 31 | one: 0 32 | } 33 | 34 | commonBits: List string -> List Common 35 | commonBits xs = 36 | let 37 | truthy: string -> boolean 38 | truthy str = 39 | true 40 | 41 | firstElement: any 42 | firstElement = 43 | xs.find truthy 44 | 45 | length: number 46 | length = 47 | firstElement 48 | |> (\x -> x.length) 49 | 50 | lengthMinusOne: number 51 | lengthMinusOne = 52 | length - 1 53 | 54 | commoner: number -> Common 55 | commoner x = 56 | mostCommon x xs 57 | in 58 | [ 0..lengthMinusOne ] 59 | |> (\x -> x.map commoner) 60 | 61 | gammaToString: Common -> string 62 | gammaToString common = 63 | if common.zero > common.one then 64 | "0" 65 | else 66 | "1" 67 | 68 | epsilonToString: Common -> string 69 | epsilonToString common = 70 | if common.zero > common.one then 71 | "1" 72 | else 73 | "0" 74 | 75 | getNumber: string -> number 76 | getNumber str = 77 | parseInt str 2 78 | 79 | allGammaToString: List Common -> List string 80 | allGammaToString xs = 81 | xs.map gammaToString 82 | 83 | allEpsilonToString: List Common -> List string 84 | allEpsilonToString xs = 85 | xs.map epsilonToString 86 | 87 | join: List string -> string 88 | join str = 89 | str.join "" 90 | 91 | calc: List string -> number 92 | calc xs = 93 | let 94 | common: List Common 95 | common = 96 | commonBits xs 97 | 98 | gamma: number 99 | gamma = 100 | allGammaToString common 101 | |> join 102 | |> getNumber 103 | 104 | epsilon: number 105 | epsilon = 106 | allEpsilonToString common 107 | |> join 108 | |> getNumber 109 | 110 | nothing: void 111 | nothing = 112 | globalThis.console.log gamma 113 | in 114 | gamma * epsilon 115 | 116 | exampleMain: void 117 | exampleMain = 118 | [ 119 | "00100", 120 | "11110", 121 | "10110", 122 | "10111", 123 | "10101", 124 | "01111", 125 | "00111", 126 | "11100", 127 | "10000", 128 | "11001", 129 | "00010", 130 | "01010" 131 | ] 132 | |> calc 133 | |> globalThis.console.log 134 | 135 | adventInput: List string 136 | adventInput = 137 | fs.readFileSync "input.txt" 138 | |> toString 139 | |> split 140 | 141 | split: string -> List string 142 | split file = 143 | file.split "\n" 144 | 145 | toString: Buffer -> string 146 | toString buffer = 147 | buffer.toString() 148 | 149 | main: void 150 | main = 151 | adventInput 152 | |> calc 153 | |> globalThis.console.log 154 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_3/part_1.elm: -------------------------------------------------------------------------------- 1 | module Examples.advent_of_code.day_3.part_1 exposing (..) 2 | 3 | import fs 4 | 5 | import globalThis exposing ( Buffer ) 6 | 7 | type alias Common = { 8 | zero: Float, 9 | one: Float 10 | } 11 | 12 | reducer: Float -> Common -> String -> Common 13 | reducer index common line = 14 | if line.charAt index == "0" then 15 | { 16 | zero = common.zero + 1, 17 | one = common.one 18 | } 19 | else 20 | { 21 | zero = common.zero, 22 | one = common.one + 1 23 | } 24 | 25 | mostCommon: Float -> List String -> Common 26 | mostCommon index bits = 27 | let 28 | something: Common -> String -> Common 29 | something x y = 30 | reducer index x y 31 | in 32 | bits.reduce something { 33 | zero = 0, 34 | one = 0 35 | } 36 | 37 | commonBits: List String -> List Common 38 | commonBits xs = 39 | let 40 | truthy: String -> Bool 41 | truthy str = 42 | True 43 | 44 | firstElement: any 45 | firstElement = 46 | xs.find truthy 47 | 48 | length: Float 49 | length = 50 | firstElement 51 | |> \x -> x.length 52 | 53 | lengthMinusOne: Float 54 | lengthMinusOne = 55 | length - 1 56 | 57 | commoner: Float -> Common 58 | commoner x = 59 | mostCommon x xs 60 | in 61 | [ 0..lengthMinusOne ] 62 | |> \x -> x.map commoner 63 | 64 | gammaToString: Common -> String 65 | gammaToString common = 66 | if common.zero > common.one then 67 | "0" 68 | else 69 | "1" 70 | 71 | epsilonToString: Common -> String 72 | epsilonToString common = 73 | if common.zero > common.one then 74 | "1" 75 | else 76 | "0" 77 | 78 | getNumber: String -> Float 79 | getNumber str = 80 | parseInt str 2 81 | 82 | allGammaToString: List Common -> List String 83 | allGammaToString xs = 84 | xs.map gammaToString 85 | 86 | allEpsilonToString: List Common -> List String 87 | allEpsilonToString xs = 88 | xs.map epsilonToString 89 | 90 | join: List String -> String 91 | join str = 92 | str.join "" 93 | 94 | calc: List String -> Float 95 | calc xs = 96 | let 97 | common: List Common 98 | common = 99 | commonBits xs 100 | 101 | gamma: Float 102 | gamma = 103 | allGammaToString common 104 | |> join 105 | |> getNumber 106 | 107 | epsilon: Float 108 | epsilon = 109 | allEpsilonToString common 110 | |> join 111 | |> getNumber 112 | 113 | nothing: String 114 | nothing = 115 | globalThis.console.log gamma 116 | in 117 | gamma * epsilon 118 | 119 | exampleMain: String 120 | exampleMain = 121 | [ 122 | "00100", 123 | "11110", 124 | "10110", 125 | "10111", 126 | "10101", 127 | "01111", 128 | "00111", 129 | "11100", 130 | "10000", 131 | "11001", 132 | "00010", 133 | "01010" 134 | ] 135 | |> calc 136 | |> globalThis.console.log 137 | 138 | adventInput: List String 139 | adventInput = 140 | fs.readFileSync "input.txt" 141 | |> toString 142 | |> split 143 | 144 | split: String -> List String 145 | split file = 146 | file.split "\n" 147 | 148 | toString: Buffer -> String 149 | toString buffer = 150 | buffer.toString() 151 | 152 | main: String 153 | main = 154 | adventInput 155 | |> calc 156 | |> globalThis.console.log 157 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_3/part_1.js: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | function Common(args) { 4 | return { 5 | ...args, 6 | }; 7 | } 8 | 9 | function reducer(index, common, line) { 10 | if (line.charAt(index) === "0") { 11 | return { 12 | zero: common.zero + 1, 13 | one: common.one 14 | }; 15 | } else { 16 | return { 17 | zero: common.zero, 18 | one: common.one + 1 19 | }; 20 | } 21 | } 22 | 23 | function mostCommon(index, bits) { 24 | function something(x, y) { 25 | return reducer(index, x, y); 26 | } 27 | return bits.reduce(something, { 28 | zero: 0, 29 | one: 0 30 | }); 31 | } 32 | 33 | function commonBits(xs) { 34 | function truthy(str) { 35 | return true; 36 | } 37 | const firstElement = xs.find(truthy); 38 | const length = (function(x) { 39 | return x.length; 40 | })(firstElement); 41 | const lengthMinusOne = length - 1; 42 | function commoner(x) { 43 | return mostCommon(x, xs); 44 | } 45 | return (function(x) { 46 | return x.map(commoner); 47 | })(Array.from({ length: lengthMinusOne - 0 + 1 }, (_ReservedX, _ReservedI) => _ReservedI + 0)); 48 | } 49 | 50 | function gammaToString(common) { 51 | if (common.zero > common.one) { 52 | return "0"; 53 | } else { 54 | return "1"; 55 | } 56 | } 57 | 58 | function epsilonToString(common) { 59 | if (common.zero > common.one) { 60 | return "1"; 61 | } else { 62 | return "0"; 63 | } 64 | } 65 | 66 | function getNumber(str) { 67 | return parseInt(str, 2); 68 | } 69 | 70 | function allGammaToString(xs) { 71 | return xs.map(gammaToString); 72 | } 73 | 74 | function allEpsilonToString(xs) { 75 | return xs.map(epsilonToString); 76 | } 77 | 78 | function join(str) { 79 | return str.join(""); 80 | } 81 | 82 | function calc(xs) { 83 | const common = commonBits(xs); 84 | const gamma = getNumber(join(allGammaToString(common))); 85 | const epsilon = getNumber(join(allEpsilonToString(common))); 86 | const nothing = globalThis.console.log(gamma); 87 | return gamma * epsilon; 88 | } 89 | 90 | const exampleMain = globalThis.console.log(calc([ "00100", "11110", "10110", "10111", "10101", "01111", "00111", "11100", "10000", "11001", "00010", "01010" ])); 91 | 92 | const adventInput = split(toString(fs.readFileSync("input.txt"))); 93 | 94 | function split(file) { 95 | return file.split("\n"); 96 | } 97 | 98 | function toString(buffer) { 99 | return buffer.toString(); 100 | } 101 | 102 | const main = globalThis.console.log(calc(adventInput)); 103 | -------------------------------------------------------------------------------- /examples/advent_of_code/day_3/part_1.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | type Common = { 4 | zero: number; 5 | one: number; 6 | } 7 | 8 | function Common(args: { zero: number, one: number }): Common { 9 | return { 10 | ...args, 11 | }; 12 | } 13 | 14 | function reducer(index: number, common: Common, line: string): Common { 15 | if (line.charAt(index) === "0") { 16 | return { 17 | zero: common.zero + 1, 18 | one: common.one 19 | }; 20 | } else { 21 | return { 22 | zero: common.zero, 23 | one: common.one + 1 24 | }; 25 | } 26 | } 27 | 28 | function mostCommon(index: number, bits: string[]): Common { 29 | function something(x: Common, y: string): Common { 30 | return reducer(index, x, y); 31 | } 32 | return bits.reduce(something, { 33 | zero: 0, 34 | one: 0 35 | }); 36 | } 37 | 38 | function commonBits(xs: string[]): Common[] { 39 | function truthy(str: string): boolean { 40 | return true; 41 | } 42 | const firstElement: any = xs.find(truthy); 43 | const length: number = (function(x: any) { 44 | return x.length; 45 | })(firstElement); 46 | const lengthMinusOne: number = length - 1; 47 | function commoner(x: number): Common { 48 | return mostCommon(x, xs); 49 | } 50 | return (function(x: any) { 51 | return x.map(commoner); 52 | })(Array.from({ length: lengthMinusOne - 0 + 1 }, (_ReservedX, _ReservedI) => _ReservedI + 0)); 53 | } 54 | 55 | function gammaToString(common: Common): string { 56 | if (common.zero > common.one) { 57 | return "0"; 58 | } else { 59 | return "1"; 60 | } 61 | } 62 | 63 | function epsilonToString(common: Common): string { 64 | if (common.zero > common.one) { 65 | return "1"; 66 | } else { 67 | return "0"; 68 | } 69 | } 70 | 71 | function getNumber(str: string): number { 72 | return parseInt(str, 2); 73 | } 74 | 75 | function allGammaToString(xs: Common[]): string[] { 76 | return xs.map(gammaToString); 77 | } 78 | 79 | function allEpsilonToString(xs: Common[]): string[] { 80 | return xs.map(epsilonToString); 81 | } 82 | 83 | function join(str: string[]): string { 84 | return str.join(""); 85 | } 86 | 87 | function calc(xs: string[]): number { 88 | const common: Common[] = commonBits(xs); 89 | const gamma: number = getNumber(join(allGammaToString(common))); 90 | const epsilon: number = getNumber(join(allEpsilonToString(common))); 91 | const nothing: void = globalThis.console.log(gamma); 92 | return gamma * epsilon; 93 | } 94 | 95 | const exampleMain: void = globalThis.console.log(calc([ "00100", "11110", "10110", "10111", "10101", "01111", "00111", "11100", "10000", "11001", "00010", "01010" ])); 96 | 97 | const adventInput: string[] = split(toString(fs.readFileSync("input.txt"))); 98 | 99 | function split(file: string): string[] { 100 | return file.split("\n"); 101 | } 102 | 103 | function toString(buffer: Buffer): string { 104 | return buffer.toString(); 105 | } 106 | 107 | const main: void = globalThis.console.log(calc(adventInput)); 108 | -------------------------------------------------------------------------------- /examples/complex_union.derw: -------------------------------------------------------------------------------- 1 | type Animal = 2 | Dog { name: string } 3 | | Cat { lives: number } 4 | 5 | sayHiToPet: Animal -> string 6 | sayHiToPet pet = 7 | case pet of 8 | Dog { name } -> 9 | `Good boy ${name}!` 10 | 11 | Cat { lives } -> 12 | "You have " + lives + " lives remaining." 13 | 14 | main: void 15 | main = 16 | Dog { name: "roof" } 17 | |> sayHiToPet 18 | |> console.log 19 | -------------------------------------------------------------------------------- /examples/complex_union.elm: -------------------------------------------------------------------------------- 1 | module Examples.complex_union exposing (..) 2 | 3 | type Animal = 4 | Dog { name: String } 5 | | Cat { lives: Float } 6 | 7 | sayHiToPet: Animal -> String 8 | sayHiToPet pet = 9 | case pet of 10 | Dog { name } -> 11 | "Good boy ${name}!" 12 | 13 | Cat { lives } -> 14 | "You have " ++ lives ++ " lives remaining." 15 | 16 | main: String 17 | main = 18 | Dog { name = "roof" } 19 | |> sayHiToPet 20 | |> Debug.log "" 21 | -------------------------------------------------------------------------------- /examples/complex_union.js: -------------------------------------------------------------------------------- 1 | function Dog(args) { 2 | return { 3 | kind: "Dog", 4 | ...args, 5 | }; 6 | } 7 | 8 | function Cat(args) { 9 | return { 10 | kind: "Cat", 11 | ...args, 12 | }; 13 | } 14 | 15 | function sayHiToPet(pet) { 16 | switch (pet.kind) { 17 | case "Dog": { 18 | const { name } = pet; 19 | return `Good boy ${name}!`; 20 | } 21 | case "Cat": { 22 | const { lives } = pet; 23 | return "You have " + lives + " lives remaining."; 24 | } 25 | } 26 | } 27 | 28 | const main = console.log(sayHiToPet(Dog({ name: "roof" }))); 29 | -------------------------------------------------------------------------------- /examples/complex_union.ts: -------------------------------------------------------------------------------- 1 | type Dog = { 2 | kind: "Dog"; 3 | name: string; 4 | }; 5 | 6 | function Dog(args: { name: string }): Dog { 7 | return { 8 | kind: "Dog", 9 | ...args, 10 | }; 11 | } 12 | 13 | type Cat = { 14 | kind: "Cat"; 15 | lives: number; 16 | }; 17 | 18 | function Cat(args: { lives: number }): Cat { 19 | return { 20 | kind: "Cat", 21 | ...args, 22 | }; 23 | } 24 | 25 | type Animal = Dog | Cat; 26 | 27 | function sayHiToPet(pet: Animal): string { 28 | switch (pet.kind) { 29 | case "Dog": { 30 | const { name } = pet; 31 | return `Good boy ${name}!`; 32 | } 33 | case "Cat": { 34 | const { lives } = pet; 35 | return "You have " + lives + " lives remaining."; 36 | } 37 | } 38 | } 39 | 40 | const main: void = console.log(sayHiToPet(Dog({ name: "roof" }))); 41 | -------------------------------------------------------------------------------- /examples/derw_imports/main.derw: -------------------------------------------------------------------------------- 1 | import "./other" as Other 2 | import "fs/promises" exposing ( readFile ) 3 | 4 | oneThing: Other.Some 5 | oneThing = 6 | Other.None 7 | 8 | dontKnowWhy: Other.Other 9 | dontKnowWhy = 10 | Other.Other { name: "noah" } 11 | -------------------------------------------------------------------------------- /examples/derw_imports/other.derw: -------------------------------------------------------------------------------- 1 | exposing (isTrue) 2 | 3 | exposing (Some, Something, None, Other) 4 | 5 | type Some = 6 | None 7 | | Something { value: string } 8 | 9 | type alias Other = { 10 | name: string 11 | } 12 | 13 | isTrue: boolean -> boolean 14 | isTrue bool = 15 | bool == true 16 | -------------------------------------------------------------------------------- /examples/errors/mismatching_types.derw: -------------------------------------------------------------------------------- 1 | isTrue: boolean -> boolean 2 | isTrue x = 3 | 1 + 2 4 | 5 | names: List string 6 | names = 7 | [1..2] 8 | 9 | -------------------------------------------------------------------------------- /examples/errors/name_collisions.derw: -------------------------------------------------------------------------------- 1 | isTrue: boolean -> boolean 2 | isTrue x = 3 | x == true 4 | 5 | isTrue: boolean -> boolean 6 | isTrue x = 7 | x != true 8 | 9 | type Person = 10 | Person { name: string } 11 | 12 | type alias Person = { 13 | name: string 14 | } -------------------------------------------------------------------------------- /examples/nested_union.derw: -------------------------------------------------------------------------------- 1 | type Maybe a = 2 | Just { value: a } 3 | | nothing 4 | 5 | something: Maybe string -> Maybe string 6 | something x = 7 | x 8 | 9 | other: List (Maybe Binary) 10 | other = 11 | [ ] 12 | -------------------------------------------------------------------------------- /examples/nested_union.elm: -------------------------------------------------------------------------------- 1 | module Examples.nested_union exposing (..) 2 | 3 | type Maybe a = 4 | Just { value: a } 5 | | nothing 6 | 7 | something: Maybe String -> Maybe String 8 | something x = 9 | x 10 | 11 | other: List (Maybe Binary) 12 | other = 13 | [ ] 14 | -------------------------------------------------------------------------------- /examples/nested_union.js: -------------------------------------------------------------------------------- 1 | function Just(args) { 2 | return { 3 | kind: "Just", 4 | ...args, 5 | }; 6 | } 7 | 8 | function nothing(args) { 9 | return { 10 | kind: "nothing", 11 | ...args, 12 | }; 13 | } 14 | 15 | function something(x) { 16 | return x; 17 | } 18 | 19 | const other = [ ]; 20 | -------------------------------------------------------------------------------- /examples/nested_union.ts: -------------------------------------------------------------------------------- 1 | type Just = { 2 | kind: "Just"; 3 | value: a; 4 | }; 5 | 6 | function Just(args: { value: a }): Just { 7 | return { 8 | kind: "Just", 9 | ...args, 10 | }; 11 | } 12 | 13 | type nothing = { 14 | kind: "nothing"; 15 | }; 16 | 17 | function nothing(args: {}): nothing { 18 | return { 19 | kind: "nothing", 20 | ...args, 21 | }; 22 | } 23 | 24 | type Maybe = Just | nothing; 25 | 26 | function something(x: Maybe): Maybe { 27 | return x; 28 | } 29 | 30 | const other: Maybe[] = [ ]; 31 | -------------------------------------------------------------------------------- /examples/simple_union.derw: -------------------------------------------------------------------------------- 1 | type Binary = 2 | One 3 | | Zero 4 | 5 | isTruthy: Binary -> boolean 6 | isTruthy binary = 7 | case binary of 8 | One -> 9 | true 10 | 11 | Zero -> 12 | false 13 | -------------------------------------------------------------------------------- /examples/simple_union.elm: -------------------------------------------------------------------------------- 1 | module Examples.simple_union exposing (..) 2 | 3 | type Binary = 4 | One 5 | | Zero 6 | 7 | isTruthy: Binary -> Bool 8 | isTruthy binary = 9 | case binary of 10 | One -> 11 | True 12 | 13 | Zero -> 14 | False 15 | -------------------------------------------------------------------------------- /examples/simple_union.js: -------------------------------------------------------------------------------- 1 | function One(args) { 2 | return { 3 | kind: "One", 4 | ...args, 5 | }; 6 | } 7 | 8 | function Zero(args) { 9 | return { 10 | kind: "Zero", 11 | ...args, 12 | }; 13 | } 14 | 15 | function isTruthy(binary) { 16 | switch (binary.kind) { 17 | case "One": { 18 | return true; 19 | } 20 | case "Zero": { 21 | return false; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/simple_union.ts: -------------------------------------------------------------------------------- 1 | type One = { 2 | kind: "One"; 3 | }; 4 | 5 | function One(args: {}): One { 6 | return { 7 | kind: "One", 8 | ...args, 9 | }; 10 | } 11 | 12 | type Zero = { 13 | kind: "Zero"; 14 | }; 15 | 16 | function Zero(args: {}): Zero { 17 | return { 18 | kind: "Zero", 19 | ...args, 20 | }; 21 | } 22 | 23 | type Binary = One | Zero; 24 | 25 | function isTruthy(binary: Binary): boolean { 26 | switch (binary.kind) { 27 | case "One": { 28 | return true; 29 | } 30 | case "Zero": { 31 | return false; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "derw", 3 | "version": "0.0.10", 4 | "description": "An Elm-inspired language that transpiles to TypeScript", 5 | "main": "index.js", 6 | "bin": "build/cli.js", 7 | "scripts": { 8 | "postinstall": "npx ts-node src/cli.ts compile", 9 | "build": "npx derw compile", 10 | "test": "npx @eeue56/bach", 11 | "test-chromebook": "./test_on_chromebook.sh", 12 | "bench": "npx @eeue56/mainc", 13 | "format": "npx prettier --write .", 14 | "prepublish": "npx ts-node src/cli.ts compile && npx tsc -p tsconfig.json", 15 | "stdlib": "npx ts-node src/cli.ts compile --files ../derw-lang/stdlib/src/*.derw --output ../derw-lang/stdlib", 16 | "test-stdlib": "npx @eeue56/bach --file ../derw-lang/stdlib/src/*.ts" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/eeue56/derw.git" 21 | }, 22 | "keywords": [ 23 | "derw", 24 | "typescript", 25 | "hiraeth" 26 | ], 27 | "author": "eeue56", 28 | "license": "BSD-3-Clause", 29 | "bugs": { 30 | "url": "https://github.com/eeue56/derw/issues" 31 | }, 32 | "homepage": "https://github.com/eeue56/derw#readme", 33 | "devDependencies": { 34 | "@eeue56/mainc": "^0.0.6", 35 | "@eeue56/ts-assert": "^0.0.2", 36 | "@types/node": "^16.3.1", 37 | "prettier": "github:eeue56/prettier#feat-eeue56/brackets" 38 | }, 39 | "dependencies": { 40 | "@eeue56/adeilad": "^0.0.2", 41 | "@eeue56/bach": "^0.1.2", 42 | "@eeue56/baner": "^0.0.3", 43 | "@eeue56/ts-core": "^3.0.1", 44 | "@types/node-fetch": "^2.5.12", 45 | "chokidar": "^3.5.2", 46 | "esbuild": "^0.14.11", 47 | "node-fetch": "^2.6.6", 48 | "typescript": "^4.9.5", 49 | "trim": "^1.0.1" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Generator.derw: -------------------------------------------------------------------------------- 1 | import "./generators/Derw" exposing ( generateDerw ) 2 | import "./generators/Elm" exposing ( generateElm ) 3 | import "./generators/English" exposing ( generateEnglish ) 4 | import "./generators/Js" exposing ( generateJavascript ) 5 | import "./generators/Ts" exposing ( generateTypescript ) 6 | import "./types" exposing ( Module ) 7 | 8 | exposing ( Target, generate ) 9 | 10 | emptyLineAtEndOfFile: string 11 | emptyLineAtEndOfFile = 12 | "\n" 13 | 14 | type Target = 15 | "js" 16 | | "ts" 17 | | "derw" 18 | | "elm" 19 | | "english" 20 | 21 | generate: Target -> Module -> string 22 | generate target parsed = 23 | case target of 24 | "js" -> 25 | (generateJavascript parsed) + emptyLineAtEndOfFile 26 | 27 | "ts" -> 28 | (generateTypescript parsed) + emptyLineAtEndOfFile 29 | 30 | "derw" -> 31 | (generateDerw parsed) + emptyLineAtEndOfFile 32 | 33 | "elm" -> 34 | (generateElm parsed) + emptyLineAtEndOfFile 35 | 36 | "english" -> 37 | (generateEnglish parsed) + emptyLineAtEndOfFile 38 | -------------------------------------------------------------------------------- /src/Generator.ts: -------------------------------------------------------------------------------- 1 | import { generateDerw } from "./generators/Derw"; 2 | 3 | import { generateElm } from "./generators/Elm"; 4 | 5 | import { generateEnglish } from "./generators/English"; 6 | 7 | import { generateJavascript } from "./generators/Js"; 8 | 9 | import { generateTypescript } from "./generators/Ts"; 10 | 11 | import { Module } from "./types"; 12 | 13 | export { Target }; 14 | export { generate }; 15 | 16 | const emptyLineAtEndOfFile: string = "\n"; 17 | 18 | type Target = "js" | "ts" | "derw" | "elm" | "english"; 19 | 20 | function generate(target: Target, parsed: Module): string { 21 | switch (target) { 22 | case "js": { 23 | return generateJavascript(parsed) + emptyLineAtEndOfFile; 24 | } 25 | case "ts": { 26 | return generateTypescript(parsed) + emptyLineAtEndOfFile; 27 | } 28 | case "derw": { 29 | return generateDerw(parsed) + emptyLineAtEndOfFile; 30 | } 31 | case "elm": { 32 | return generateElm(parsed) + emptyLineAtEndOfFile; 33 | } 34 | case "english": { 35 | return generateEnglish(parsed) + emptyLineAtEndOfFile; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Tokens_types_kernel.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ArrowToken, 3 | CloseBracketToken, 4 | IdentifierToken, 5 | OpenBracketToken, 6 | StringToken, 7 | } from "./Tokens"; 8 | 9 | export type TypeTokenRaw = 10 | | IdentifierToken 11 | | ArrowToken 12 | | OpenBracketToken 13 | | CloseBracketToken 14 | | StringToken; 15 | 16 | export type TypeToken = 17 | | IdentifierToken 18 | | ArrowToken 19 | | OpenBracketToken 20 | | CloseBracketToken 21 | | StringToken 22 | | BaseTypeToken 23 | | FunctionTypeToken; 24 | 25 | export type BaseTypeToken = { 26 | kind: "BaseTypeToken"; 27 | body: TypeToken[]; 28 | }; 29 | 30 | export function BaseTypeToken(args: { body: TypeToken[] }): BaseTypeToken { 31 | return { 32 | kind: "BaseTypeToken", 33 | ...args, 34 | }; 35 | } 36 | 37 | export type FunctionTypeToken = { 38 | kind: "FunctionTypeToken"; 39 | body: TypeToken[]; 40 | }; 41 | 42 | export function FunctionTypeToken(args: { 43 | body: TypeToken[]; 44 | }): FunctionTypeToken { 45 | return { 46 | kind: "FunctionTypeToken", 47 | ...args, 48 | }; 49 | } 50 | 51 | export type RootTypeTokens = BaseTypeToken | FunctionTypeToken; 52 | -------------------------------------------------------------------------------- /src/Utils.derw: -------------------------------------------------------------------------------- 1 | import "./stdlib/Bitwise" as Bitwise 2 | import "./stdlib/List" as List 3 | 4 | exposing ( getNameFromPath, isTestFile, hashCode ) 5 | 6 | getNameFromPath: string -> string 7 | getNameFromPath path = 8 | let 9 | splitByPathSymbol: List string 10 | splitByPathSymbol = 11 | path.split "/" 12 | 13 | lastElement: string 14 | lastElement = 15 | case splitByPathSymbol.slice -1 of 16 | x :: [] -> 17 | x.split "." 18 | |> (\y -> y[0]) 19 | 20 | default -> 21 | "" 22 | in 23 | lastElement 24 | 25 | isTestFile: string -> boolean 26 | isTestFile name = 27 | name.endsWith "_test.derw" 28 | 29 | hashCodeStep: number -> number -> number 30 | hashCodeStep charCode hash = 31 | let 32 | added: number 33 | added = 34 | hash + charCode 35 | 36 | shift: number 37 | shift = 38 | Bitwise.leftShift hash 5 39 | 40 | subtracted: number 41 | subtracted = 42 | shift - added 43 | in 44 | Bitwise.or subtracted 0 45 | 46 | hashCode: string -> number 47 | hashCode str = 48 | List.map (\letter -> letter.charCodeAt 0) (str.split "") 49 | |> List.foldl hashCodeStep 0 50 | |> (\y -> Math.abs y) 51 | -------------------------------------------------------------------------------- /src/Utils.ts: -------------------------------------------------------------------------------- 1 | import * as Bitwise from "./stdlib/Bitwise"; 2 | 3 | import * as List from "./stdlib/List"; 4 | 5 | export { getNameFromPath }; 6 | export { isTestFile }; 7 | export { hashCode }; 8 | 9 | function getNameFromPath(path: string): string { 10 | const splitByPathSymbol: string[] = path.split("/"); 11 | const lastElement: string = (function (): any { 12 | const _res118108815 = splitByPathSymbol.slice(-1); 13 | switch (_res118108815.length) { 14 | case _res118108815.length: { 15 | if (_res118108815.length === 1) { 16 | const [ x ] = _res118108815; 17 | return (function(y: any) { 18 | return y[0]; 19 | })(x.split(".")); 20 | } 21 | } 22 | default: { 23 | return ""; 24 | } 25 | } 26 | })(); 27 | return lastElement; 28 | } 29 | 30 | function isTestFile(name: string): boolean { 31 | return name.endsWith("_test.derw"); 32 | } 33 | 34 | function hashCodeStep(charCode: number, hash: number): number { 35 | const added: number = hash + charCode; 36 | const shift: number = Bitwise.leftShift(hash, 5); 37 | const subtracted: number = shift - added; 38 | return Bitwise.or(subtracted, 0); 39 | } 40 | 41 | function hashCode(str: string): number { 42 | return (function(y: any) { 43 | return Math.abs(y); 44 | })(List.foldl(hashCodeStep, 0, List.map(function(letter: any) { 45 | return letter.charCodeAt(0); 46 | }, str.split("")))); 47 | } 48 | -------------------------------------------------------------------------------- /src/__snapshots__/src/tests/simple_union_test/snapshotGeneratedDerw.ts: -------------------------------------------------------------------------------- 1 | export const snapshotGeneratedDerw = "type Binary =\n True\n | False"; -------------------------------------------------------------------------------- /src/bench/examples_bench.ts: -------------------------------------------------------------------------------- 1 | import { readdir, readFile } from "fs/promises"; 2 | import path from "path"; 3 | import { generateDerw } from "../generators/Derw"; 4 | import { generateElm } from "../generators/Elm"; 5 | import { generateJavascript } from "../generators/Js"; 6 | import { generateTypescript } from "../generators/Ts"; 7 | import { parse } from "../parser"; 8 | 9 | const adventOfCodePath = "./examples/advent_of_code"; 10 | 11 | async function adventOfCodeFiles(): Promise { 12 | const days = await readdir(adventOfCodePath); 13 | 14 | let aocFiles: string[] = [ ]; 15 | 16 | for (const day of days) { 17 | const files = await ( 18 | await readdir(path.join(adventOfCodePath, day)) 19 | ).map((file) => path.join(day, file)); 20 | aocFiles = aocFiles.concat( 21 | files.filter((file) => file.endsWith("derw")) 22 | ); 23 | } 24 | 25 | return aocFiles; 26 | } 27 | 28 | async function getFilePairs(): Promise<{ derw: string }[]> { 29 | const exampleFiles: string[] = (await readdir("./examples")).map((file) => 30 | path.join("examples", file) 31 | ); 32 | 33 | const aocFiles = await ( 34 | await adventOfCodeFiles() 35 | ).map((file) => path.join(adventOfCodePath, file)); 36 | 37 | const files: string[] = exampleFiles.concat(aocFiles); 38 | 39 | const filePairs = files 40 | .filter((file) => file.endsWith("derw")) 41 | .map((derwFile) => { 42 | return { 43 | derw: derwFile, 44 | }; 45 | }); 46 | 47 | return filePairs; 48 | } 49 | 50 | export async function benchWarmUpCache() { 51 | for (var i = 0; i < 10; i++) { 52 | await getFilePairs(); 53 | } 54 | } 55 | 56 | export async function benchParsingExamples() { 57 | const filePairs = await getFilePairs(); 58 | await Promise.all( 59 | filePairs.map(async ({ derw }) => { 60 | const derwContents = (await readFile(derw)).toString(); 61 | parse(derwContents); 62 | }) 63 | ); 64 | } 65 | 66 | export async function benchGeneratingDerwExamples() { 67 | const filePairs = await getFilePairs(); 68 | await Promise.all( 69 | filePairs.map(async ({ derw }) => { 70 | const derwContents = (await readFile(derw)).toString(); 71 | generateDerw(parse(derwContents)); 72 | }) 73 | ); 74 | } 75 | 76 | export async function benchGeneratingTsExamples() { 77 | const filePairs = await getFilePairs(); 78 | await Promise.all( 79 | filePairs.map(async ({ derw }) => { 80 | const derwContents = (await readFile(derw)).toString(); 81 | generateTypescript(parse(derwContents)); 82 | }) 83 | ); 84 | } 85 | 86 | export async function benchGeneratingJavascriptExamples() { 87 | const filePairs = await getFilePairs(); 88 | await Promise.all( 89 | filePairs.map(async ({ derw }) => { 90 | const derwContents = (await readFile(derw)).toString(); 91 | generateJavascript(parse(derwContents)); 92 | }) 93 | ); 94 | } 95 | 96 | export async function benchGeneratingElmExamples() { 97 | const filePairs = await getFilePairs(); 98 | await Promise.all( 99 | filePairs.map(async ({ derw }) => { 100 | const derwContents = (await readFile(derw)).toString(); 101 | generateElm(parse(derwContents)); 102 | }) 103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /src/builtins.ts: -------------------------------------------------------------------------------- 1 | type BuiltinTypes = 2 | | "boolean" 3 | | "number" 4 | | "string" 5 | | "void" 6 | | "any" 7 | | "Promise"; 8 | 9 | export function isBuiltinType( 10 | potentialType: string 11 | ): potentialType is BuiltinTypes { 12 | return ( 13 | [ "boolean", "number", "string", "void", "any", "Promise" ].indexOf( 14 | potentialType 15 | ) > -1 16 | ); 17 | } 18 | 19 | export function isReservedName(potentialName: string): boolean { 20 | return [ "Object", "Function" ].indexOf(potentialName.trim()) > -1; 21 | } 22 | -------------------------------------------------------------------------------- /src/cli/init.ts: -------------------------------------------------------------------------------- 1 | import { 2 | allErrors, 3 | bothFlag, 4 | empty, 5 | help, 6 | longFlag, 7 | parse, 8 | parser, 9 | string, 10 | } from "@eeue56/baner"; 11 | import { readFile, writeFile } from "fs/promises"; 12 | import path from "path"; 13 | import { exportPackage, Package } from "../package"; 14 | import { ensureDirectoryExists, fileExists } from "./utils"; 15 | 16 | const initParser = parser([ 17 | longFlag( 18 | "dir", 19 | "name of a directory to use as package name e.g stdlib. Defaults to current directory's name", 20 | string() 21 | ), 22 | bothFlag("h", "help", "This help text", empty()), 23 | ]); 24 | 25 | function showInfoHelp() { 26 | console.log("Initialize a directory as a Derw project."); 27 | console.log(help(initParser)); 28 | } 29 | 30 | async function copyTSconfig(dir: string): Promise { 31 | const tsconfig = { 32 | compilerOptions: { 33 | target: "es2017", 34 | module: "commonjs", 35 | declaration: true, 36 | outDir: "./build/", 37 | rootDirs: [ "src" ], 38 | strict: true, 39 | moduleResolution: "node", 40 | types: [ "node" ], 41 | esModuleInterop: true, 42 | skipLibCheck: true, 43 | forceConsistentCasingInFileNames: true, 44 | }, 45 | include: [ "src/**/*" ], 46 | exclude: [ "node_modules/**" ], 47 | }; 48 | 49 | if (await fileExists(path.join(dir, "tsconfig.json"))) { 50 | console.log("Already got a tsconfig!"); 51 | process.exit(1); 52 | } 53 | 54 | await writeFile( 55 | path.join(dir, "tsconfig.json"), 56 | JSON.stringify(tsconfig, null, 4) 57 | ); 58 | } 59 | 60 | async function appendGitIgnore(dir: string): Promise { 61 | let gitIgnore = ""; 62 | const gitIgnorePath = path.join(dir, ".gitignore"); 63 | 64 | try { 65 | gitIgnore = await (await readFile(gitIgnorePath)).toString(); 66 | } catch (e) {} 67 | 68 | gitIgnore = 69 | gitIgnore + 70 | ` 71 | 72 | # derw 73 | 74 | derw-packages/ 75 | src/**/*.ts 76 | `; 77 | 78 | await writeFile(gitIgnorePath, gitIgnore); 79 | } 80 | 81 | async function appendGitAttributes(dir: string): Promise { 82 | let gitAttributes = ""; 83 | const gitAttributesPath = path.join(dir, ".gitattributes"); 84 | 85 | try { 86 | gitAttributes = await (await readFile(gitAttributesPath)).toString(); 87 | } catch (e) {} 88 | 89 | gitAttributes = 90 | gitAttributes + 91 | ` 92 | *.derw linguist-language=Elm 93 | *.derw gitlab-language=elm 94 | `.trim(); 95 | 96 | await writeFile(gitAttributesPath, gitAttributes); 97 | } 98 | 99 | export async function init( 100 | isInPackageDirectory: boolean, 101 | argv: string[] 102 | ): Promise { 103 | const program = parse(initParser, argv); 104 | 105 | if (program.flags["h/help"].isPresent) { 106 | showInfoHelp(); 107 | return; 108 | } 109 | 110 | const errors = allErrors(program); 111 | if (errors.length > 0) { 112 | console.log("Errors:"); 113 | console.log(errors.join("\n")); 114 | process.exit(1); 115 | } 116 | 117 | const dir = 118 | program.flags.dir.isPresent && program.flags.dir.arguments.kind === "Ok" 119 | ? (program.flags.dir.arguments.value as string) 120 | : process.cwd(); 121 | 122 | const packageName = path.basename(dir); 123 | 124 | const package_ = Package(packageName, [ ], [ ]); 125 | 126 | const isAlreadyAPackage = await fileExists( 127 | path.join(dir, "derw-package.json") 128 | ); 129 | if (isAlreadyAPackage) { 130 | console.log("Package already initialized!"); 131 | process.exit(-1); 132 | } 133 | 134 | await writeFile( 135 | path.join(dir, "derw-package.json"), 136 | exportPackage(package_) 137 | ); 138 | await copyTSconfig(dir); 139 | await appendGitIgnore(dir); 140 | await appendGitAttributes(dir); 141 | await ensureDirectoryExists(path.join(dir, "src")); 142 | 143 | console.log("Project initialized!"); 144 | console.log("Put your files in `src`"); 145 | console.log("Compile your project via `derw compile`"); 146 | console.log("Run tests via `derw test`"); 147 | } 148 | -------------------------------------------------------------------------------- /src/cli/template.ts: -------------------------------------------------------------------------------- 1 | import { 2 | allErrors, 3 | bothFlag, 4 | empty, 5 | help, 6 | longFlag, 7 | oneOf, 8 | parse, 9 | parser, 10 | string, 11 | } from "@eeue56/baner"; 12 | import { writeFile } from "fs/promises"; 13 | import { install } from "./install"; 14 | import { fileExists } from "./utils"; 15 | 16 | type TemplateType = "web" | "html"; 17 | const validTemplates = [ "web", "html" ]; 18 | 19 | const templateParser = parser([ 20 | longFlag("path", "path of Derw file to create", string()), 21 | longFlag("template", "Template to use", oneOf(validTemplates)), 22 | bothFlag("h", "help", "This help text", empty()), 23 | ]); 24 | 25 | function showInfoHelp() { 26 | console.log("Generate a Derw file from a template."); 27 | console.log("Also installs required packages."); 28 | console.log(help(templateParser)); 29 | } 30 | 31 | async function copyWebTemplate(path: string): Promise { 32 | const template = 33 | ` 34 | import "../derw-packages/derw-lang/html/src/Html" exposing ( HtmlNode, RunningProgram, div, text, program, attribute, class_ ) 35 | 36 | type alias Model = { 37 | } 38 | 39 | initialModel: Model 40 | initialModel = 41 | { } 42 | 43 | type Msg = 44 | Noop 45 | 46 | update: Msg -> Model -> (Msg -> void) -> Model 47 | update msg model send = 48 | case msg of 49 | Noop -> 50 | model 51 | 52 | view: Model -> HtmlNode Msg 53 | view model = 54 | div [ ] [ ] [ text "Hello" ] 55 | 56 | root: any 57 | root = 58 | document.getElementById "root" 59 | 60 | main: RunningProgram Model Msg 61 | main = 62 | program { 63 | initialModel: initialModel, 64 | view: view, 65 | update: update, 66 | root: root 67 | } 68 | `.trim() + "\n"; 69 | 70 | if (await fileExists(path)) { 71 | console.log("Already a file!"); 72 | process.exit(1); 73 | } 74 | 75 | await writeFile(path, template); 76 | } 77 | 78 | async function copyHtmlTemplate(path: string): Promise { 79 | const template = 80 | ` 81 | 82 | 83 | 84 |
85 | 86 | 87 | 88 | `.trim() + "\n"; 89 | 90 | if (await fileExists(path)) { 91 | console.log("Already a file!"); 92 | process.exit(1); 93 | } 94 | 95 | await writeFile(path, template); 96 | } 97 | 98 | export async function template( 99 | isInPackageDirectory: boolean, 100 | argv: string[] 101 | ): Promise { 102 | const program = parse(templateParser, argv); 103 | 104 | if (program.flags["h/help"].isPresent) { 105 | showInfoHelp(); 106 | return; 107 | } 108 | 109 | const errors = allErrors(program); 110 | if (errors.length > 0) { 111 | console.log("Errors:"); 112 | console.log(errors.join("\n")); 113 | process.exit(1); 114 | } 115 | 116 | const path = 117 | program.flags.path.isPresent && 118 | program.flags.path.arguments.kind === "Ok" && 119 | (program.flags.path.arguments.value as string); 120 | 121 | const template = 122 | program.flags.template.isPresent && 123 | program.flags.template.arguments.kind === "Ok" && 124 | (program.flags.template.arguments.value as TemplateType); 125 | 126 | if (!path) { 127 | console.log("You must provide a path via --path"); 128 | return; 129 | } 130 | 131 | if (template === "web") { 132 | await copyWebTemplate(path); 133 | await install(isInPackageDirectory, [ 134 | "--name", 135 | "derw-lang/html", 136 | "--version", 137 | "main", 138 | ]); 139 | } else if (template === "html") { 140 | await copyHtmlTemplate(path); 141 | } else { 142 | console.log( 143 | `Template ${template} is unknown. Try one of: ${validTemplates.join( 144 | ", " 145 | )}` 146 | ); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/cli/testing.ts: -------------------------------------------------------------------------------- 1 | import { runner } from "@eeue56/bach/build/bach"; 2 | import { 3 | allErrors, 4 | bothFlag, 5 | empty, 6 | help, 7 | longFlag, 8 | parse, 9 | parser, 10 | string, 11 | } from "@eeue56/baner"; 12 | import * as chokidar from "chokidar"; 13 | import path from "path"; 14 | import { compileFiles } from "./compile"; 15 | 16 | const testingParser = parser([ 17 | longFlag("watch", "Watch Derw files for changes", empty()), 18 | longFlag("function", "A particular function name to run", string()), 19 | longFlag("file", "A particular file name to run", string()), 20 | longFlag("only-fails", "Only log failing tests", empty()), 21 | bothFlag("h", "help", "This help text", empty()), 22 | ]); 23 | 24 | function showTestingHelp(): void { 25 | console.log("To run tests, run `derw test` from the package directory"); 26 | console.log("To watch use the --watch flag"); 27 | console.log(help(testingParser)); 28 | } 29 | 30 | export async function runTests( 31 | isInPackageDirectory: boolean, 32 | argv: string[] 33 | ): Promise { 34 | const program = parse(testingParser, argv); 35 | if (program.flags["h/help"].isPresent) { 36 | showTestingHelp(); 37 | return; 38 | } 39 | 40 | const errors = allErrors(program); 41 | if (errors.length > 0) { 42 | console.log("Errors:"); 43 | console.log(errors.join("\n")); 44 | process.exit(1); 45 | } 46 | 47 | if (!isInPackageDirectory) { 48 | console.log("Must run tests from the root of a package directory."); 49 | process.exit(1); 50 | } 51 | 52 | if (program.flags.watch.isPresent) { 53 | console.log("Watching src and derw-packages..."); 54 | argv.push("--clean-exit"); 55 | let timer: NodeJS.Timeout; 56 | chokidar 57 | .watch([ 58 | path.join(process.cwd(), "src"), 59 | path.join(process.cwd(), "derw-packages"), 60 | ]) 61 | .on("error", () => { 62 | console.log("Got an error"); 63 | }) 64 | .on("all", async (event: Event, path: string): Promise => { 65 | if (path.endsWith(".derw")) { 66 | if (timer !== null) { 67 | clearTimeout(timer); 68 | } 69 | timer = setTimeout(async () => { 70 | await compileFiles(isInPackageDirectory, argv); 71 | await runner(); 72 | }, 300); 73 | } 74 | }); 75 | } else { 76 | await compileFiles(isInPackageDirectory, argv); 77 | await runner(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/cli/utils.ts: -------------------------------------------------------------------------------- 1 | import { Err, Ok, Result } from "@eeue56/ts-core/build/main/lib/result"; 2 | import { promises } from "fs"; 3 | import { readdir } from "fs/promises"; 4 | import path from "path"; 5 | import { suggestName } from "../errors/distance"; 6 | 7 | export async function fileExists(name: string): Promise { 8 | try { 9 | await promises.access(name); 10 | } catch (e) { 11 | return false; 12 | } 13 | return true; 14 | } 15 | 16 | export async function ensureDirectoryExists(directory: string): Promise { 17 | try { 18 | const lstat = await promises.lstat(directory); 19 | if (!lstat.isDirectory()) { 20 | await promises.mkdir(directory, { recursive: true }); 21 | } 22 | } catch (error) { 23 | await promises.mkdir(directory, { recursive: true }); 24 | } 25 | } 26 | 27 | export async function getDerwFiles( 28 | dir: string 29 | ): Promise> { 30 | try { 31 | const lstat = await promises.lstat(dir); 32 | if (!lstat.isDirectory()) { 33 | return Ok([ dir ]); 34 | } 35 | } catch (error) { 36 | return Err(`${error}`); 37 | } 38 | 39 | let files: string[] = [ ]; 40 | 41 | for (const file of await readdir(dir, { withFileTypes: true })) { 42 | if (file.isFile()) { 43 | if (file.name.endsWith("derw")) { 44 | files.push(path.join(dir, file.name)); 45 | } 46 | } else if (file.isDirectory()) { 47 | if (file.name === "node_modules") { 48 | } else { 49 | const nested = await getDerwFiles(path.join(dir, file.name)); 50 | if (nested.kind === "Ok") { 51 | files = files.concat(nested.value); 52 | } else { 53 | return nested; 54 | } 55 | } 56 | } 57 | } 58 | 59 | return Ok(files); 60 | } 61 | 62 | export async function getFlatFiles( 63 | files: string[] 64 | ): Promise> { 65 | const nestedFiles = await Promise.all( 66 | files.map(async (file) => await getDerwFiles(file)) 67 | ); 68 | let returnedFiles: string[] = [ ]; 69 | 70 | for (const innerFiles of nestedFiles) { 71 | if (innerFiles.kind === "Err") { 72 | return Err(`Failed to find the file ${innerFiles.error}`); 73 | } else { 74 | returnedFiles = returnedFiles.concat(innerFiles.value); 75 | } 76 | } 77 | 78 | return Ok(returnedFiles); 79 | } 80 | 81 | export async function suggestFileNames(fullPath: string): Promise { 82 | const dir = path.dirname(fullPath); 83 | const files = await getDerwFiles(dir); 84 | 85 | if (files.kind === "Err") { 86 | return `I couldn't find a directory called ${dir}`; 87 | } 88 | 89 | const suggestions = suggestName(fullPath, files.value); 90 | 91 | if (suggestions.length === 0) { 92 | return `I couldn't find the file ${fullPath} and have no suggestions.`; 93 | } 94 | 95 | return `I couldn't find the file ${fullPath}. Maybe you meant ${suggestions.join( 96 | "," 97 | )}?`; 98 | } 99 | -------------------------------------------------------------------------------- /src/cli/version.ts: -------------------------------------------------------------------------------- 1 | export async function version( 2 | isInPackageDirectory: boolean, 3 | argv: string[] 4 | ): Promise { 5 | console.log("Version: ", process.env.npm_package_version || "main"); 6 | } 7 | -------------------------------------------------------------------------------- /src/compile.ts: -------------------------------------------------------------------------------- 1 | import { Err, Ok, Result } from "@eeue56/ts-core/build/main/lib/result"; 2 | import * as ts from "typescript"; 3 | 4 | export function compileTypescript( 5 | source: string 6 | ): Result { 7 | const options: ts.TranspileOptions = { 8 | compilerOptions: { module: ts.ModuleKind.CommonJS }, 9 | reportDiagnostics: true, 10 | }; 11 | const output = ts.transpileModule(source, options); 12 | 13 | if (output.diagnostics && output.diagnostics.length > 0) { 14 | return Err(output.diagnostics); 15 | } 16 | 17 | return Ok(output.outputText); 18 | } 19 | -------------------------------------------------------------------------------- /src/debugging/tokens.ts: -------------------------------------------------------------------------------- 1 | import { tokenize } from "../Tokens"; 2 | 3 | export function log(x: string): void { 4 | console.log(x); 5 | tokenize(x) 6 | .map((token) => { 7 | if ((token as any).body) 8 | return `${token.kind}("${(token as any).body}"),`; 9 | return `${token.kind}(),`; 10 | }) 11 | .forEach((token) => { 12 | console.log(token); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/derw.ts: -------------------------------------------------------------------------------- 1 | function main() {} 2 | -------------------------------------------------------------------------------- /src/errors/distance.ts: -------------------------------------------------------------------------------- 1 | function levDistance(a: string, b: string): number { 2 | if (a.length === 0) return b.length; 3 | if (b.length === 0) return a.length; 4 | 5 | if (a.toLowerCase() === b.toLowerCase()) return 0; 6 | 7 | const matrix: number[][] = [ ]; 8 | 9 | // increment along the first column of each row 10 | let i; 11 | for (i = 0; i <= b.length; i += 1) { 12 | matrix[i] = [ i ]; 13 | } 14 | 15 | // increment each column in the first row 16 | let j; 17 | for (j = 0; j <= a.length; j += 1) { 18 | matrix[0][j] = j; 19 | } 20 | 21 | // Fill in the rest of the matrix 22 | for (i = 1; i <= b.length; i += 1) { 23 | for (j = 1; j <= a.length; j += 1) { 24 | if (b.charAt(i - 1) === a.charAt(j - 1)) { 25 | matrix[i][j] = matrix[i - 1][j - 1]; 26 | } else { 27 | matrix[i][j] = Math.min( 28 | matrix[i - 1][j - 1] + 1, // substitution 29 | Math.min( 30 | matrix[i][j - 1] + 1, // insertion 31 | matrix[i - 1][j] + 1 32 | ) 33 | ); // deletion 34 | } 35 | } 36 | } 37 | 38 | return matrix[b.length][a.length]; 39 | } 40 | 41 | type Match = { 42 | name: string; 43 | distance: number; 44 | }; 45 | 46 | function sortByDistance(list: Match[]): Match[] { 47 | return list.slice(0).sort((a, b) => { 48 | return a.distance < b.distance ? -1 : a.distance > b.distance ? 1 : 0; 49 | }); 50 | } 51 | 52 | export function suggestName( 53 | nameToFind: string, 54 | alternatives: string[] 55 | ): string[] { 56 | const possibleMatches = alternatives 57 | .map((name) => ({ 58 | name, 59 | distance: levDistance(name, nameToFind), 60 | })) 61 | .filter((alternative) => alternative.distance < 3); 62 | 63 | return sortByDistance(possibleMatches).map( 64 | (alternative) => alternative.name 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /src/generators/Common.derw: -------------------------------------------------------------------------------- 1 | import "../stdlib/List" as List 2 | import "../types" exposing ( ListDestructure, ListDestructurePart ) 3 | 4 | exposing ( prefixLines, destructureLength, patternGapPositions, patternHasGaps ) 5 | 6 | prefixLines: string -> number -> string 7 | prefixLines body indent = 8 | let 9 | lineFn: string -> string 10 | lineFn line = 11 | if line.trim() == "" then 12 | line 13 | else 14 | `${" ".repeat(indent)}${line}` 15 | in 16 | body.split "\n" 17 | |> List.map lineFn 18 | |> (\y -> y.join "\n") 19 | 20 | partLength: ListDestructurePart -> number -> number 21 | partLength part index = 22 | case part of 23 | Destructure -> 24 | 1 25 | 26 | StringValue -> 27 | 1 28 | 29 | FormatStringValue -> 30 | 1 31 | 32 | EmptyList -> 33 | 0 34 | 35 | Value -> 36 | if index == 0 then 37 | 1 38 | else 39 | 0 40 | 41 | destructureLength: ListDestructure -> number 42 | destructureLength pattern = 43 | List.indexedMap partLength pattern.parts 44 | |> List.foldl (\x y -> x + y) 0 45 | 46 | type alias GapPositionInfo = { 47 | i: number, 48 | positions: List number 49 | } 50 | 51 | patternGapPositions: ListDestructure -> List number 52 | patternGapPositions pattern = 53 | let 54 | folder: ListDestructurePart -> GapPositionInfo -> GapPositionInfo 55 | folder part info = 56 | case part of 57 | Value -> 58 | if info.i > 0 then 59 | { 60 | i: info.i - 1, 61 | positions: info.i :: info.positions 62 | } 63 | else 64 | { ...info, i: info.i - 1 } 65 | 66 | default -> 67 | { ...info, i: info.i - 1 } 68 | in 69 | List.foldr folder { 70 | i: pattern.parts.length - 1, 71 | positions: [ ] 72 | } pattern.parts 73 | |> (\y -> y.positions) 74 | 75 | patternHasGaps: ListDestructure -> boolean 76 | patternHasGaps pattern = 77 | let 78 | hasGap: number -> List ListDestructurePart -> boolean 79 | hasGap index xs = 80 | case xs of 81 | x :: ys -> 82 | case x of 83 | Value -> 84 | if index > 0 then 85 | true 86 | else 87 | hasGap (index + 1) ys 88 | 89 | default -> 90 | hasGap (index + 1) ys 91 | 92 | default -> 93 | false 94 | in 95 | hasGap 0 pattern.parts 96 | -------------------------------------------------------------------------------- /src/generators/Common.ts: -------------------------------------------------------------------------------- 1 | import * as List from "../stdlib/List"; 2 | 3 | import { ListDestructure, ListDestructurePart } from "../types"; 4 | 5 | export { prefixLines }; 6 | export { destructureLength }; 7 | export { patternGapPositions }; 8 | export { patternHasGaps }; 9 | 10 | function prefixLines(body: string, indent: number): string { 11 | function lineFn(line: string): string { 12 | if (line.trim() === "") { 13 | return line; 14 | } else { 15 | return `${" ".repeat(indent)}${line}`; 16 | } 17 | } 18 | return (function(y: any) { 19 | return y.join("\n"); 20 | })(List.map(lineFn, body.split("\n"))); 21 | } 22 | 23 | function partLength(part: ListDestructurePart, index: number): number { 24 | switch (part.kind) { 25 | case "Destructure": { 26 | return 1; 27 | } 28 | case "StringValue": { 29 | return 1; 30 | } 31 | case "FormatStringValue": { 32 | return 1; 33 | } 34 | case "EmptyList": { 35 | return 0; 36 | } 37 | case "Value": { 38 | if (index === 0) { 39 | return 1; 40 | } else { 41 | return 0; 42 | }; 43 | } 44 | } 45 | } 46 | 47 | function destructureLength(pattern: ListDestructure): number { 48 | return List.foldl(function(x: any, y: any) { 49 | return x + y; 50 | }, 0, List.indexedMap(partLength, pattern.parts)); 51 | } 52 | 53 | type GapPositionInfo = { 54 | i: number; 55 | positions: number[]; 56 | } 57 | 58 | function GapPositionInfo(args: { i: number, positions: number[] }): GapPositionInfo { 59 | return { 60 | ...args, 61 | }; 62 | } 63 | 64 | function patternGapPositions(pattern: ListDestructure): number[] { 65 | function folder(part: ListDestructurePart, info: GapPositionInfo): GapPositionInfo { 66 | switch (part.kind) { 67 | case "Value": { 68 | if (info.i > 0) { 69 | return { 70 | i: info.i - 1, 71 | positions: [ info.i, ...info.positions ] 72 | }; 73 | } else { 74 | return { ...info, i: info.i - 1 }; 75 | }; 76 | } 77 | default: { 78 | return { ...info, i: info.i - 1 }; 79 | } 80 | } 81 | } 82 | return (function(y: any) { 83 | return y.positions; 84 | })(List.foldr(folder, { 85 | i: pattern.parts.length - 1, 86 | positions: [ ] 87 | }, pattern.parts)); 88 | } 89 | 90 | function patternHasGaps(pattern: ListDestructure): boolean { 91 | function hasGap(index: number, xs: ListDestructurePart[]): boolean { 92 | switch (xs.length) { 93 | case xs.length: { 94 | if (xs.length >= 1) { 95 | const [ x, ...ys ] = xs; 96 | switch (x.kind) { 97 | case "Value": { 98 | if (index > 0) { 99 | return true; 100 | } else { 101 | return hasGap(index + 1, ys); 102 | }; 103 | } 104 | default: { 105 | return hasGap(index + 1, ys); 106 | } 107 | }; 108 | } 109 | } 110 | default: { 111 | return false; 112 | } 113 | } 114 | } 115 | return hasGap(0, pattern.parts); 116 | } 117 | -------------------------------------------------------------------------------- /src/package.ts: -------------------------------------------------------------------------------- 1 | import { 2 | array, 3 | decode, 4 | map, 5 | pipeline, 6 | record, 7 | required, 8 | string, 9 | } from "@eeue56/adeilad"; 10 | import { Result } from "@eeue56/ts-core/build/main/lib/result"; 11 | import { readFile } from "fs/promises"; 12 | 13 | export type PackageModule = { 14 | kind: "PackageModule"; 15 | name: string; 16 | }; 17 | 18 | export function PackageModule(name: string): PackageModule { 19 | return { 20 | kind: "PackageModule", 21 | name, 22 | }; 23 | } 24 | 25 | export type Dependency = { 26 | kind: "Dependency"; 27 | name: string; 28 | version: string; 29 | }; 30 | 31 | export function Dependency(name: string, version: string): Dependency { 32 | return { 33 | kind: "Dependency", 34 | name, 35 | version, 36 | }; 37 | } 38 | 39 | export function dependenciesFromRecord( 40 | record: Record 41 | ): Dependency[] { 42 | const dependencies = [ ]; 43 | 44 | for (const entry of Object.keys(record)) { 45 | dependencies.push(Dependency(entry, record[entry])); 46 | } 47 | 48 | return dependencies; 49 | } 50 | 51 | export type Package = { 52 | kind: "Package"; 53 | name: string; 54 | exposing: PackageModule[]; 55 | dependencies: Dependency[]; 56 | }; 57 | 58 | export function Package( 59 | name: string, 60 | exposing: PackageModule[], 61 | dependencies: Dependency[] 62 | ): Package { 63 | return { 64 | kind: "Package", 65 | name, 66 | exposing, 67 | dependencies, 68 | }; 69 | } 70 | 71 | const packageDecoder = pipeline( 72 | [ 73 | required("name", string()), 74 | required("exposing", array(map(PackageModule, string()))), 75 | required("dependencies", map(dependenciesFromRecord, record(string()))), 76 | ], 77 | Package 78 | ); 79 | 80 | export function addDependency( 81 | dependency: Dependency, 82 | package_: Package 83 | ): Package { 84 | for (const dep of package_.dependencies) { 85 | if (dep.name === dependency.name) { 86 | dep.version = dependency.version; 87 | return package_; 88 | } 89 | } 90 | package_.dependencies.push(dependency); 91 | 92 | return package_; 93 | } 94 | 95 | export function exportPackage(package_: Package): string { 96 | const dependencies: Record = {}; 97 | 98 | for (const dependency of package_.dependencies) { 99 | dependencies[dependency.name] = dependency.version; 100 | } 101 | 102 | return JSON.stringify( 103 | { 104 | name: package_.name, 105 | exposing: package_.exposing.map((e) => e.name), 106 | dependencies: dependencies, 107 | }, 108 | null, 109 | 4 110 | ); 111 | } 112 | 113 | export async function loadPackageFile( 114 | path: string 115 | ): Promise> { 116 | return decodePackage(JSON.parse(await (await readFile(path)).toString())); 117 | } 118 | 119 | export function decodePackage(potentialPackage: any): Result { 120 | return decode(packageDecoder, potentialPackage); 121 | } 122 | -------------------------------------------------------------------------------- /src/stdlib/Bitwise.derw: -------------------------------------------------------------------------------- 1 | import "./Bitwise_kernel" as Kernel 2 | 3 | exposing ( leftShift, or ) 4 | 5 | leftShift: number -> number -> number 6 | leftShift a b = 7 | Kernel.leftShift a b 8 | 9 | or: number -> number -> number 10 | or a b = 11 | Kernel.or a b 12 | -------------------------------------------------------------------------------- /src/stdlib/Bitwise.ts: -------------------------------------------------------------------------------- 1 | import * as Kernel from "./Bitwise_kernel"; 2 | 3 | export { leftShift }; 4 | export { or }; 5 | 6 | function leftShift(a: number, b: number): number { 7 | return Kernel.leftShift(a, b); 8 | } 9 | 10 | function or(a: number, b: number): number { 11 | return Kernel.or(a, b); 12 | } 13 | -------------------------------------------------------------------------------- /src/stdlib/Bitwise_kernel.ts: -------------------------------------------------------------------------------- 1 | export function leftShift(a: number, b: number): number { 2 | return a << b; 3 | } 4 | 5 | export function or(a: number, b: number): number { 6 | return a | b; 7 | } 8 | -------------------------------------------------------------------------------- /src/stdlib/List.derw: -------------------------------------------------------------------------------- 1 | import "./List_kernel" exposing ( kernelLength, kernelEmptyList, kernelSort, kernelSortBy, kernelStatefulFold ) 2 | import "./Maybe" exposing ( Maybe ) 3 | 4 | exposing ( emptyList, map, indexedMap, filter, foldl, statefulFold, foldr, filterMap, append, reverse, length, take, drop, sort, sortBy ) 5 | 6 | emptyList: List any 7 | emptyList = 8 | [ ] 9 | 10 | map: (a -> b) -> List a -> List b 11 | map fn xs = 12 | xs.map fn 13 | 14 | indexedMap: (a -> number -> b) -> List a -> List b 15 | indexedMap fn xs = 16 | xs.map fn 17 | 18 | filter: (a -> boolean) -> List a -> List a 19 | filter fn xs = 20 | xs.filter fn 21 | 22 | foldl: (a -> b -> b) -> b -> List a -> b 23 | foldl fn init xs = 24 | xs.reduce (\a b -> fn b a) init 25 | 26 | statefulFold: (item -> state -> state) -> state -> List item -> state 27 | statefulFold fn init xs = 28 | kernelStatefulFold fn init xs 29 | 30 | foldr: (a -> b -> b) -> b -> List a -> b 31 | foldr fn init xs = 32 | xs.reduceRight (\a b -> fn b a) init 33 | 34 | filterMapHelp: (a -> Maybe b) -> a -> List b -> List b 35 | filterMapHelp fn a xs = 36 | let 37 | maybe: Maybe b 38 | maybe = 39 | fn a 40 | in 41 | case maybe of 42 | Just { value } -> 43 | append xs [ value ] 44 | 45 | Nothing -> 46 | xs 47 | 48 | filterMap: (a -> Maybe b) -> List a -> List b 49 | filterMap fn xs = 50 | foldl (\y ys -> filterMapHelp fn y ys) [ ] xs 51 | 52 | append: List a -> List a -> List a 53 | append xs ys = 54 | kernelEmptyList() 55 | |> (\x -> x.concat xs ys) 56 | 57 | reverse: List a -> List a 58 | reverse xs = 59 | xs.slice().reverse() 60 | 61 | length: List a -> number 62 | length xs = 63 | kernelLength xs 64 | 65 | take: number -> List a -> List a 66 | take n xs = 67 | xs.slice 0 n 68 | 69 | drop: number -> List a -> List a 70 | drop n xs = 71 | xs.slice n xs.length 72 | 73 | sort: List a -> List a 74 | sort xs = 75 | kernelSort xs 76 | 77 | sortBy: (a -> a -> number) -> List a -> List a 78 | sortBy fn xs = 79 | kernelSortBy fn xs 80 | -------------------------------------------------------------------------------- /src/stdlib/List.ts: -------------------------------------------------------------------------------- 1 | import { kernelLength, kernelEmptyList, kernelSort, kernelSortBy, kernelStatefulFold } from "./List_kernel"; 2 | 3 | import { Maybe } from "./Maybe"; 4 | 5 | export { emptyList }; 6 | export { map }; 7 | export { indexedMap }; 8 | export { filter }; 9 | export { foldl }; 10 | export { statefulFold }; 11 | export { foldr }; 12 | export { filterMap }; 13 | export { append }; 14 | export { reverse }; 15 | export { length }; 16 | export { take }; 17 | export { drop }; 18 | export { sort }; 19 | export { sortBy }; 20 | 21 | const emptyList: any[] = [ ]; 22 | 23 | function map(fn: (arg0: a) => b, xs: a[]): b[] { 24 | return xs.map(fn); 25 | } 26 | 27 | function indexedMap(fn: (arg0: a, arg1: number) => b, xs: a[]): b[] { 28 | return xs.map(fn); 29 | } 30 | 31 | function filter
(fn: (arg0: a) => boolean, xs: a[]): a[] { 32 | return xs.filter(fn); 33 | } 34 | 35 | function foldl(fn: (arg0: a, arg1: b) => b, init: b, xs: a[]): b { 36 | return xs.reduce(function(a: any, b: any) { 37 | return fn(b, a); 38 | }, init); 39 | } 40 | 41 | function statefulFold(fn: (arg0: item, arg1: state) => state, init: state, xs: item[]): state { 42 | return kernelStatefulFold(fn, init, xs); 43 | } 44 | 45 | function foldr(fn: (arg0: a, arg1: b) => b, init: b, xs: a[]): b { 46 | return xs.reduceRight(function(a: any, b: any) { 47 | return fn(b, a); 48 | }, init); 49 | } 50 | 51 | function filterMapHelp(fn: (arg0: a) => Maybe, a: a, xs: b[]): b[] { 52 | const maybe: Maybe = fn(a); 53 | switch (maybe.kind) { 54 | case "Just": { 55 | const { value } = maybe; 56 | return append(xs, [ value ]); 57 | } 58 | case "Nothing": { 59 | return xs; 60 | } 61 | } 62 | } 63 | 64 | function filterMap(fn: (arg0: a) => Maybe, xs: a[]): b[] { 65 | return foldl(function(y: any, ys: any) { 66 | return filterMapHelp(fn, y, ys); 67 | }, [ ], xs); 68 | } 69 | 70 | function append(xs: a[], ys: a[]): a[] { 71 | return (function(x: any) { 72 | return x.concat(xs, ys); 73 | })(kernelEmptyList()); 74 | } 75 | 76 | function reverse(xs: a[]): a[] { 77 | return xs.slice().reverse(); 78 | } 79 | 80 | function length(xs: a[]): number { 81 | return kernelLength(xs); 82 | } 83 | 84 | function take(n: number, xs: a[]): a[] { 85 | return xs.slice(0, n); 86 | } 87 | 88 | function drop(n: number, xs: a[]): a[] { 89 | return xs.slice(n, xs.length); 90 | } 91 | 92 | function sort(xs: a[]): a[] { 93 | return kernelSort(xs); 94 | } 95 | 96 | function sortBy(fn: (arg0: a, arg1: a) => number, xs: a[]): a[] { 97 | return kernelSortBy(fn, xs); 98 | } 99 | -------------------------------------------------------------------------------- /src/stdlib/List_kernel.ts: -------------------------------------------------------------------------------- 1 | export function kernelLength(xs: any[]): number { 2 | return xs.length; 3 | } 4 | 5 | export function kernelEmptyList(): a[] { 6 | return [ ]; 7 | } 8 | 9 | export function kernelSort(xs: a[]): a[] { 10 | const ys = [ ...xs ]; 11 | ys.sort(); 12 | return ys; 13 | } 14 | 15 | export function kernelSortBy(fn: (_0: a, _1: a) => number, xs: a[]): a[] { 16 | const ys = [ ...xs ]; 17 | ys.sort(fn); 18 | return ys; 19 | } 20 | 21 | export function kernelStatefulFold( 22 | fn: (item: item, state: state) => state, 23 | init: state, 24 | items: item[] 25 | ): state { 26 | let currentState = init; 27 | for (const item of items) { 28 | currentState = fn(item, currentState); 29 | } 30 | return currentState; 31 | } 32 | -------------------------------------------------------------------------------- /src/stdlib/Maybe.derw: -------------------------------------------------------------------------------- 1 | exposing ( Maybe, Just, Nothing, map, withDefault, andThen ) 2 | 3 | type Maybe a = 4 | Just { value: a } 5 | | Nothing 6 | 7 | map: (a -> b) -> Maybe a -> Maybe b 8 | map fn maybe = 9 | case maybe of 10 | Just { value } -> 11 | Just { value: fn value } 12 | 13 | Nothing -> 14 | Nothing 15 | 16 | withDefault: a -> Maybe a -> a 17 | withDefault defaultValue maybe = 18 | case maybe of 19 | Just { value } -> 20 | value 21 | 22 | Nothing -> 23 | defaultValue 24 | 25 | andThen: (a -> Maybe b) -> Maybe a -> Maybe b 26 | andThen fn maybe = 27 | case maybe of 28 | Just { value } -> 29 | fn value 30 | 31 | Nothing -> 32 | Nothing 33 | -------------------------------------------------------------------------------- /src/stdlib/Maybe.ts: -------------------------------------------------------------------------------- 1 | export { Maybe }; 2 | export { Just }; 3 | export { Nothing }; 4 | export { map }; 5 | export { withDefault }; 6 | export { andThen }; 7 | 8 | type Just = { 9 | kind: "Just"; 10 | value: a; 11 | }; 12 | 13 | function Just(args: { value: a }): Just { 14 | return { 15 | kind: "Just", 16 | ...args, 17 | }; 18 | } 19 | 20 | type Nothing = { 21 | kind: "Nothing"; 22 | }; 23 | 24 | function Nothing(args: {}): Nothing { 25 | return { 26 | kind: "Nothing", 27 | ...args, 28 | }; 29 | } 30 | 31 | type Maybe = Just | Nothing; 32 | 33 | function map(fn: (arg0: a) => b, maybe: Maybe): Maybe { 34 | switch (maybe.kind) { 35 | case "Just": { 36 | const { value } = maybe; 37 | return Just({ value: fn(value) }); 38 | } 39 | case "Nothing": { 40 | return Nothing({ }); 41 | } 42 | } 43 | } 44 | 45 | function withDefault(defaultValue: a, maybe: Maybe): a { 46 | switch (maybe.kind) { 47 | case "Just": { 48 | const { value } = maybe; 49 | return value; 50 | } 51 | case "Nothing": { 52 | return defaultValue; 53 | } 54 | } 55 | } 56 | 57 | function andThen(fn: (arg0: a) => Maybe, maybe: Maybe): Maybe { 58 | switch (maybe.kind) { 59 | case "Just": { 60 | const { value } = maybe; 61 | return fn(value); 62 | } 63 | case "Nothing": { 64 | return Nothing({ }); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/tests/array_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | Const, 11 | FixedType, 12 | ListValue, 13 | Module, 14 | UnparsedBlock, 15 | Value, 16 | } from "../types"; 17 | 18 | const oneLine = ` 19 | helloWorld: List number 20 | helloWorld = [ 1, 2, 3 ] 21 | `.trim(); 22 | 23 | const multiLine = ` 24 | helloWorld: List number 25 | helloWorld = 26 | [ 27 | 1, 28 | 2, 29 | 3 30 | ] 31 | `.trim(); 32 | 33 | const expectedOutput = ` 34 | const helloWorld: number[] = [ 1, 2, 3 ]; 35 | `.trim(); 36 | 37 | const expectedOutputJS = ` 38 | const helloWorld = [ 1, 2, 3 ]; 39 | `.trim(); 40 | 41 | export function testIntoBlocks() { 42 | assert.deepStrictEqual(intoBlocks(oneLine), [ 43 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 44 | ]); 45 | } 46 | 47 | export function testIntoBlocksMultiLine() { 48 | assert.deepStrictEqual(intoBlocks(multiLine), [ 49 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 50 | ]); 51 | } 52 | 53 | export function testBlockKind() { 54 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 55 | } 56 | 57 | export function testBlockKindMultiLine() { 58 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 59 | } 60 | 61 | export function testParse() { 62 | assert.deepStrictEqual( 63 | parse(oneLine), 64 | Module( 65 | "main", 66 | [ 67 | Const( 68 | "helloWorld", 69 | FixedType("List", [ FixedType("number", [ ]) ]), 70 | [ ], 71 | ListValue([ Value("1"), Value("2"), Value("3") ]) 72 | ), 73 | ], 74 | [ ] 75 | ) 76 | ); 77 | } 78 | 79 | export function testParseMultiLine() { 80 | assert.deepStrictEqual( 81 | parse(multiLine), 82 | Module( 83 | "main", 84 | [ 85 | Const( 86 | "helloWorld", 87 | FixedType("List", [ FixedType("number", [ ]) ]), 88 | [ ], 89 | ListValue([ Value("1"), Value("2"), Value("3") ]) 90 | ), 91 | ], 92 | [ ] 93 | ) 94 | ); 95 | } 96 | 97 | export function testGenerate() { 98 | const parsed = parse(multiLine); 99 | const generated = generateTypescript(parsed); 100 | assert.strictEqual(generated, expectedOutput); 101 | } 102 | 103 | export function testGenerateOneLine() { 104 | const parsed = parse(oneLine); 105 | const generated = generateTypescript(parsed); 106 | assert.strictEqual(generated, expectedOutput); 107 | } 108 | 109 | export function testCompile() { 110 | const parsed = parse(oneLine); 111 | const generated = generateTypescript(parsed); 112 | const compiled = compileTypescript(generated); 113 | 114 | assert.deepStrictEqual( 115 | compiled.kind, 116 | "Ok", 117 | (compiled.kind === "Err" && compiled.error.toString()) || "" 118 | ); 119 | } 120 | 121 | export function testCompileMultiLine() { 122 | const parsed = parse(multiLine); 123 | const generated = generateTypescript(parsed); 124 | const compiled = compileTypescript(generated); 125 | 126 | assert.deepStrictEqual( 127 | compiled.kind, 128 | "Ok", 129 | (compiled.kind === "Err" && compiled.error.toString()) || "" 130 | ); 131 | } 132 | 133 | export function testGenerateJS() { 134 | const parsed = parse(multiLine); 135 | const generated = generateJavascript(parsed); 136 | assert.strictEqual(generated, expectedOutputJS); 137 | } 138 | 139 | export function testGenerateOneLineJS() { 140 | const parsed = parse(oneLine); 141 | const generated = generateJavascript(parsed); 142 | assert.strictEqual(generated, expectedOutputJS); 143 | } 144 | 145 | export function testGenerateDerw() { 146 | const parsed = parse(multiLine); 147 | const generated = generateDerw(parsed); 148 | assert.strictEqual(generated, multiLine); 149 | } 150 | -------------------------------------------------------------------------------- /src/tests/array_with_left_pipe_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | Const, 11 | FixedType, 12 | LeftPipe, 13 | ListValue, 14 | Module, 15 | UnparsedBlock, 16 | Value, 17 | } from "../types"; 18 | 19 | const oneLine = ` 20 | helloWorld: List string 21 | helloWorld = [ 1 |> toString ] 22 | `.trim(); 23 | 24 | const multiLine = ` 25 | helloWorld: List string 26 | helloWorld = 27 | [ 1 28 | |> toString ] 29 | `.trim(); 30 | 31 | const expectedOutput = ` 32 | const helloWorld: string[] = [ toString(1) ]; 33 | `.trim(); 34 | 35 | const expectedOutputJS = ` 36 | const helloWorld = [ toString(1) ]; 37 | `.trim(); 38 | 39 | export function testIntoBlocks() { 40 | assert.deepStrictEqual(intoBlocks(oneLine), [ 41 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 42 | ]); 43 | } 44 | 45 | export function testIntoBlocksMultiLine() { 46 | assert.deepStrictEqual(intoBlocks(multiLine), [ 47 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 48 | ]); 49 | } 50 | 51 | export function testBlockKind() { 52 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 53 | } 54 | 55 | export function testBlockKindMultiLine() { 56 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 57 | } 58 | 59 | export function testParse() { 60 | assert.deepStrictEqual( 61 | parse(oneLine), 62 | Module( 63 | "main", 64 | [ 65 | Const( 66 | "helloWorld", 67 | FixedType("List", [ FixedType("string", [ ]) ]), 68 | [ ], 69 | ListValue([ LeftPipe(Value("1"), Value("toString")) ]) 70 | ), 71 | ], 72 | [ ] 73 | ) 74 | ); 75 | } 76 | 77 | export function testParseMultiLine() { 78 | assert.deepStrictEqual( 79 | parse(multiLine), 80 | Module( 81 | "main", 82 | [ 83 | Const( 84 | "helloWorld", 85 | FixedType("List", [ FixedType("string", [ ]) ]), 86 | [ ], 87 | ListValue([ LeftPipe(Value("1"), Value("toString")) ]) 88 | ), 89 | ], 90 | [ ] 91 | ) 92 | ); 93 | } 94 | 95 | export function testGenerate() { 96 | const parsed = parse(multiLine); 97 | const generated = generateTypescript(parsed); 98 | assert.strictEqual(generated, expectedOutput); 99 | } 100 | 101 | export function testGenerateOneLine() { 102 | const parsed = parse(oneLine); 103 | const generated = generateTypescript(parsed); 104 | assert.strictEqual(generated, expectedOutput); 105 | } 106 | 107 | export function testCompile() { 108 | const parsed = parse(oneLine); 109 | const generated = generateTypescript(parsed); 110 | const compiled = compileTypescript(generated); 111 | 112 | assert.deepStrictEqual( 113 | compiled.kind, 114 | "Ok", 115 | (compiled.kind === "Err" && compiled.error.toString()) || "" 116 | ); 117 | } 118 | 119 | export function testCompileMultiLine() { 120 | const parsed = parse(multiLine); 121 | const generated = generateTypescript(parsed); 122 | const compiled = compileTypescript(generated); 123 | 124 | assert.deepStrictEqual( 125 | compiled.kind, 126 | "Ok", 127 | (compiled.kind === "Err" && compiled.error.toString()) || "" 128 | ); 129 | } 130 | 131 | export function testGenerateJS() { 132 | const parsed = parse(multiLine); 133 | const generated = generateJavascript(parsed); 134 | assert.strictEqual(generated, expectedOutputJS); 135 | } 136 | 137 | export function testGenerateOneLineJS() { 138 | const parsed = parse(oneLine); 139 | const generated = generateJavascript(parsed); 140 | assert.strictEqual(generated, expectedOutputJS); 141 | } 142 | 143 | export function testGenerateDerw() { 144 | const parsed = parse(multiLine); 145 | const generated = generateDerw(parsed); 146 | assert.strictEqual(generated, multiLine); 147 | } 148 | -------------------------------------------------------------------------------- /src/tests/array_with_right_pipe_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | Const, 11 | FixedType, 12 | ListValue, 13 | Module, 14 | RightPipe, 15 | UnparsedBlock, 16 | Value, 17 | } from "../types"; 18 | 19 | const oneLine = ` 20 | helloWorld: List string 21 | helloWorld = [ toString <| 1 ] 22 | `.trim(); 23 | 24 | const multiLine = ` 25 | helloWorld: List string 26 | helloWorld = 27 | [ toString 28 | <| 1 ] 29 | `.trim(); 30 | 31 | const expectedOutput = ` 32 | const helloWorld: string[] = [ toString(1) ]; 33 | `.trim(); 34 | 35 | const expectedOutputJS = ` 36 | const helloWorld = [ toString(1) ]; 37 | `.trim(); 38 | 39 | export function testIntoBlocks() { 40 | assert.deepStrictEqual(intoBlocks(oneLine), [ 41 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 42 | ]); 43 | } 44 | 45 | export function testIntoBlocksMultiLine() { 46 | assert.deepStrictEqual(intoBlocks(multiLine), [ 47 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 48 | ]); 49 | } 50 | 51 | export function testBlockKind() { 52 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 53 | } 54 | 55 | export function testBlockKindMultiLine() { 56 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 57 | } 58 | 59 | export function testParse() { 60 | assert.deepStrictEqual( 61 | parse(oneLine), 62 | Module( 63 | "main", 64 | [ 65 | Const( 66 | "helloWorld", 67 | FixedType("List", [ FixedType("string", [ ]) ]), 68 | [ ], 69 | ListValue([ RightPipe(Value("toString"), Value("1")) ]) 70 | ), 71 | ], 72 | [ ] 73 | ) 74 | ); 75 | } 76 | 77 | export function testParseMultiLine() { 78 | assert.deepStrictEqual( 79 | parse(multiLine), 80 | Module( 81 | "main", 82 | [ 83 | Const( 84 | "helloWorld", 85 | FixedType("List", [ FixedType("string", [ ]) ]), 86 | [ ], 87 | ListValue([ RightPipe(Value("toString"), Value("1")) ]) 88 | ), 89 | ], 90 | [ ] 91 | ) 92 | ); 93 | } 94 | 95 | export function testGenerate() { 96 | const parsed = parse(multiLine); 97 | const generated = generateTypescript(parsed); 98 | assert.strictEqual(generated, expectedOutput); 99 | } 100 | 101 | export function testGenerateOneLine() { 102 | const parsed = parse(oneLine); 103 | const generated = generateTypescript(parsed); 104 | assert.strictEqual(generated, expectedOutput); 105 | } 106 | 107 | export function testCompile() { 108 | const parsed = parse(oneLine); 109 | const generated = generateTypescript(parsed); 110 | const compiled = compileTypescript(generated); 111 | 112 | assert.deepStrictEqual( 113 | compiled.kind, 114 | "Ok", 115 | (compiled.kind === "Err" && compiled.error.toString()) || "" 116 | ); 117 | } 118 | 119 | export function testCompileMultiLine() { 120 | const parsed = parse(multiLine); 121 | const generated = generateTypescript(parsed); 122 | const compiled = compileTypescript(generated); 123 | 124 | assert.deepStrictEqual( 125 | compiled.kind, 126 | "Ok", 127 | (compiled.kind === "Err" && compiled.error.toString()) || "" 128 | ); 129 | } 130 | 131 | export function testGenerateJS() { 132 | const parsed = parse(multiLine); 133 | const generated = generateJavascript(parsed); 134 | assert.strictEqual(generated, expectedOutputJS); 135 | } 136 | 137 | export function testGenerateOneLineJS() { 138 | const parsed = parse(oneLine); 139 | const generated = generateJavascript(parsed); 140 | assert.strictEqual(generated, expectedOutputJS); 141 | } 142 | 143 | export function testGenerateDerw() { 144 | const parsed = parse(multiLine); 145 | const generated = generateDerw(parsed); 146 | assert.strictEqual(generated, multiLine); 147 | } 148 | -------------------------------------------------------------------------------- /src/tests/cli_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { access, readdir, readFile, rm } from "fs/promises"; 3 | import path from "path"; 4 | import { main } from "../cli"; 5 | import { ensureDirectoryExists } from "../cli/utils"; 6 | 7 | const adventOfCodePath = "./examples/advent_of_code"; 8 | 9 | async function adventOfCodeFiles(): Promise { 10 | const days = await readdir(adventOfCodePath); 11 | 12 | let aocFiles: string[] = [ ]; 13 | 14 | for (const day of days) { 15 | const files = await ( 16 | await readdir(path.join(adventOfCodePath, day)) 17 | ).map((file) => path.join(day, file)); 18 | aocFiles = aocFiles.concat( 19 | files.filter((file) => file.endsWith("derw")) 20 | ); 21 | } 22 | 23 | return aocFiles; 24 | } 25 | 26 | async function fileExists(path: string): Promise { 27 | try { 28 | await access(path); 29 | return true; 30 | } catch { 31 | return false; 32 | } 33 | } 34 | 35 | export async function testInit() { 36 | await rm("test/package", { force: true, recursive: true }); 37 | 38 | await ensureDirectoryExists("test/package"); 39 | process.argv = [ "", "", "init", "--dir", "test/package" ]; 40 | await main(); 41 | 42 | const doesDerwPackageExist = await fileExists( 43 | "test/package/derw-package.json" 44 | ); 45 | assert.deepStrictEqual(doesDerwPackageExist, true); 46 | 47 | const doesGitAttributesExist = await fileExists( 48 | "test/package/.gitattributes" 49 | ); 50 | assert.deepStrictEqual(doesGitAttributesExist, true); 51 | 52 | const doesTsConfigExist = await fileExists("test/package/tsconfig.json"); 53 | assert.deepStrictEqual(doesTsConfigExist, true); 54 | 55 | const doesSrcExist = await fileExists("test/package/src"); 56 | assert.deepStrictEqual(doesSrcExist, true); 57 | } 58 | 59 | export async function testCompileExamples() { 60 | const exampleFiles: string[] = await ( 61 | await readdir("./examples") 62 | ).map((file) => path.join("examples", file)); 63 | 64 | const aocFiles = await ( 65 | await adventOfCodeFiles() 66 | ).map((file) => path.join(adventOfCodePath, file)); 67 | 68 | const files: string[] = exampleFiles.concat(aocFiles); 69 | 70 | const filePairs = files 71 | .filter((file) => file.endsWith("derw")) 72 | .map((derwFile) => { 73 | return { 74 | derw: derwFile, 75 | ts: derwFile.split(".")[0] + ".ts", 76 | js: derwFile.split(".")[0] + ".js", 77 | elm: derwFile.split(".")[0] + ".elm", 78 | }; 79 | }); 80 | 81 | await rm("test/temp", { force: true, recursive: true }); 82 | 83 | let index = 0; 84 | 85 | for (const pair of filePairs) { 86 | const { derw, ts, js, elm } = pair; 87 | const outputDir = `test/temp/${index}`; 88 | index++; 89 | 90 | for (const target of [ "ts", "js", "elm", "derw" ]) { 91 | process.argv = [ 92 | "", 93 | "", 94 | "compile", 95 | "--files", 96 | derw, 97 | "--output", 98 | outputDir, 99 | "--target", 100 | target, 101 | "--quiet", 102 | ]; 103 | await main(); 104 | } 105 | 106 | const derwContents = (await readFile(derw)).toString(); 107 | const tsContents = (await readFile(ts)).toString(); 108 | const jsContents = (await readFile(js)).toString(); 109 | const elmContents = (await readFile(elm)).toString(); 110 | 111 | const generatedDerw = ( 112 | await readFile(`${outputDir}/${derw}`) 113 | ).toString(); 114 | const generatedTS = (await readFile(`${outputDir}/${ts}`)).toString(); 115 | const generatedJS = (await readFile(`${outputDir}/${js}`)).toString(); 116 | const generatedElm = (await readFile(`${outputDir}/${elm}`)).toString(); 117 | 118 | try { 119 | assert.deepStrictEqual(derwContents, generatedDerw); 120 | assert.deepStrictEqual(tsContents, generatedTS); 121 | assert.deepStrictEqual(jsContents, generatedJS); 122 | assert.deepStrictEqual(elmContents, generatedElm); 123 | } catch (e) { 124 | console.log(`Failed to correctly generate ${derw}`); 125 | throw e; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/tests/collisions_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { intoBlocks } from "../Blocks"; 3 | import { parse } from "../parser"; 4 | import { UnparsedBlock } from "../types"; 5 | 6 | const oneLine = ` 7 | isEqual: boolean 8 | isEqual = 1 == 2 9 | 10 | isEqual: boolean 11 | isEqual = 1 != 2 12 | 13 | type Robot = Robot 14 | 15 | type alias Robot = { name : string } 16 | 17 | Robot: number -> number 18 | Robot x = x 19 | `.trim(); 20 | 21 | const multiLine = ` 22 | isEqual: boolean 23 | isEqual = 24 | 1 == 2 25 | 26 | isEqual: boolean 27 | isEqual = 28 | 1 != 2 29 | 30 | type Robot = Robot 31 | 32 | type alias Robot = { 33 | name : string 34 | } 35 | 36 | Robot: number -> number 37 | Robot x = 38 | x 39 | `.trim(); 40 | 41 | export function testIntoBlocks() { 42 | const lines = oneLine.split("\n"); 43 | 44 | assert.deepStrictEqual(intoBlocks(oneLine), [ 45 | UnparsedBlock("ConstBlock", 0, [ lines[0], lines[1] ]), 46 | UnparsedBlock("ConstBlock", 3, [ lines[3], lines[4] ]), 47 | UnparsedBlock("UnionTypeBlock", 6, [ lines[6] ]), 48 | UnparsedBlock("TypeAliasBlock", 8, [ lines[8] ]), 49 | UnparsedBlock("FunctionBlock", 10, [ lines[10], lines[11] ]), 50 | ]); 51 | } 52 | 53 | export function testIntoBlocksMultiLine() { 54 | const lines = multiLine.split("\n"); 55 | 56 | assert.deepStrictEqual(intoBlocks(multiLine), [ 57 | UnparsedBlock("ConstBlock", 0, lines.slice(0, 3)), 58 | UnparsedBlock("ConstBlock", 4, lines.slice(4, 7)), 59 | UnparsedBlock("UnionTypeBlock", 8, lines.slice(8, 9)), 60 | UnparsedBlock("TypeAliasBlock", 10, lines.slice(10, 13)), 61 | UnparsedBlock("FunctionBlock", 14, lines.slice(14, 17)), 62 | ]); 63 | } 64 | 65 | export function testParse() { 66 | const errors = parse(oneLine).errors; 67 | assert.deepStrictEqual(errors.length, 2); 68 | assert.deepStrictEqual(errors, [ 69 | "The name `isEqual` has been used for different things.\n0 - 2:\n```\nisEqual: boolean\nisEqual = 1 == 2\n```\n\n3 - 5:\n```\nisEqual: boolean\nisEqual = 1 != 2\n```", 70 | "The name `Robot` has been used for different things.\n6 - 7:\n```\ntype Robot = Robot\n```\n\n8 - 9:\n```\ntype alias Robot = { name : string }\n```\n\n10 - 12:\n```\nRobot: number -> number\nRobot x = x\n```", 71 | ]); 72 | } 73 | 74 | export function testParseMultiLine() { 75 | const errors = parse(multiLine).errors; 76 | assert.deepStrictEqual(errors.length, 2); 77 | assert.deepStrictEqual(errors, [ 78 | "The name `isEqual` has been used for different things.\n0 - 3:\n```\nisEqual: boolean\nisEqual =\n 1 == 2\n```\n\n4 - 7:\n```\nisEqual: boolean\nisEqual =\n 1 != 2\n```", 79 | "The name `Robot` has been used for different things.\n8 - 9:\n```\ntype Robot = Robot\n```\n\n10 - 13:\n```\ntype alias Robot = {\n name : string\n}\n```\n\n14 - 17:\n```\nRobot: number -> number\nRobot x =\n x\n```", 80 | ]); 81 | } 82 | -------------------------------------------------------------------------------- /src/tests/derw_function_call_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { generateDerw } from "../generators/Derw"; 3 | import { parse } from "../parser"; 4 | 5 | export function testBase() { 6 | const str = ` 7 | hello: string 8 | hello = 9 | List.map banana orange 10 | `.trim(); 11 | 12 | const parsed = parse(str); 13 | const generated = generateDerw(parsed); 14 | 15 | assert.deepStrictEqual(generated, str); 16 | } 17 | 18 | export function testFunctionCall() { 19 | const str = ` 20 | hello: string 21 | hello = 22 | List.map banana (fn orange) 23 | `.trim(); 24 | 25 | const parsed = parse(str); 26 | const generated = generateDerw(parsed); 27 | 28 | assert.deepStrictEqual(generated, str); 29 | } 30 | 31 | export function testMultipleFunctionCalls() { 32 | const str = ` 33 | hello: string 34 | hello = 35 | List.map banana (fn orange) (fn fruit) 36 | `.trim(); 37 | 38 | const parsed = parse(str); 39 | const generated = generateDerw(parsed); 40 | 41 | assert.deepStrictEqual(generated, str); 42 | } 43 | 44 | export function testModuleReference() { 45 | const str = ` 46 | hello: string 47 | hello = 48 | List.map banana (Other.fn orange) 49 | `.trim(); 50 | 51 | const parsed = parse(str); 52 | const generated = generateDerw(parsed); 53 | 54 | assert.deepStrictEqual(generated, str); 55 | } 56 | 57 | export function testMultipleModuleReferences() { 58 | const str = ` 59 | hello: string 60 | hello = 61 | List.map banana (Other.fn orange) (Other.fn fruit) 62 | `.trim(); 63 | 64 | const parsed = parse(str); 65 | const generated = generateDerw(parsed); 66 | 67 | assert.deepStrictEqual(generated, str); 68 | } 69 | 70 | export function testConstructor() { 71 | const str = ` 72 | hello: string 73 | hello = 74 | something (Just { value: 5 }) 75 | `.trim(); 76 | 77 | const parsed = parse(str); 78 | const generated = generateDerw(parsed); 79 | 80 | assert.deepStrictEqual(generated, str); 81 | } 82 | 83 | export function testMultipleConstructor() { 84 | const str = ` 85 | hello: string 86 | hello = 87 | something (Just { value: 5 }) Nothing 88 | `.trim(); 89 | 90 | const parsed = parse(str); 91 | const generated = generateDerw(parsed); 92 | 93 | assert.deepStrictEqual(generated, str); 94 | } 95 | -------------------------------------------------------------------------------- /src/tests/empty_array_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { Const, FixedType, ListValue, Module, UnparsedBlock } from "../types"; 10 | 11 | const oneLine = ` 12 | helloWorld: List number 13 | helloWorld = [ ] 14 | `.trim(); 15 | 16 | const multiLine = ` 17 | helloWorld: List number 18 | helloWorld = 19 | [ ] 20 | `.trim(); 21 | 22 | const expectedOutput = ` 23 | const helloWorld: number[] = [ ]; 24 | `.trim(); 25 | 26 | const expectedOutputJS = ` 27 | const helloWorld = [ ]; 28 | `.trim(); 29 | 30 | export function testIntoBlocks() { 31 | assert.deepStrictEqual(intoBlocks(oneLine), [ 32 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 33 | ]); 34 | } 35 | 36 | export function testIntoBlocksMultiLine() { 37 | assert.deepStrictEqual(intoBlocks(multiLine), [ 38 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 39 | ]); 40 | } 41 | 42 | export function testBlockKind() { 43 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 44 | } 45 | 46 | export function testBlockKindMultiLine() { 47 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 48 | } 49 | 50 | export function testParse() { 51 | assert.deepStrictEqual( 52 | parse(oneLine), 53 | Module( 54 | "main", 55 | [ 56 | Const( 57 | "helloWorld", 58 | FixedType("List", [ FixedType("number", [ ]) ]), 59 | [ ], 60 | ListValue([ ]) 61 | ), 62 | ], 63 | [ ] 64 | ) 65 | ); 66 | } 67 | 68 | export function testParseMultiLine() { 69 | assert.deepStrictEqual( 70 | parse(multiLine), 71 | Module( 72 | "main", 73 | [ 74 | Const( 75 | "helloWorld", 76 | FixedType("List", [ FixedType("number", [ ]) ]), 77 | [ ], 78 | ListValue([ ]) 79 | ), 80 | ], 81 | [ ] 82 | ) 83 | ); 84 | } 85 | 86 | export function testGenerate() { 87 | const parsed = parse(multiLine); 88 | const generated = generateTypescript(parsed); 89 | assert.strictEqual(generated, expectedOutput); 90 | } 91 | 92 | export function testGenerateOneLine() { 93 | const parsed = parse(oneLine); 94 | const generated = generateTypescript(parsed); 95 | assert.strictEqual(generated, expectedOutput); 96 | } 97 | 98 | export function testCompile() { 99 | const parsed = parse(oneLine); 100 | const generated = generateTypescript(parsed); 101 | const compiled = compileTypescript(generated); 102 | 103 | assert.deepStrictEqual( 104 | compiled.kind, 105 | "Ok", 106 | (compiled.kind === "Err" && compiled.error.toString()) || "" 107 | ); 108 | } 109 | 110 | export function testCompileMultiLine() { 111 | const parsed = parse(multiLine); 112 | const generated = generateTypescript(parsed); 113 | const compiled = compileTypescript(generated); 114 | 115 | assert.deepStrictEqual( 116 | compiled.kind, 117 | "Ok", 118 | (compiled.kind === "Err" && compiled.error.toString()) || "" 119 | ); 120 | } 121 | 122 | export function testGenerateJS() { 123 | const parsed = parse(multiLine); 124 | const generated = generateJavascript(parsed); 125 | assert.strictEqual(generated, expectedOutputJS); 126 | } 127 | 128 | export function testGenerateOneLineJS() { 129 | const parsed = parse(oneLine); 130 | const generated = generateJavascript(parsed); 131 | assert.strictEqual(generated, expectedOutputJS); 132 | } 133 | 134 | export function testGenerateDerw() { 135 | const parsed = parse(multiLine); 136 | const generated = generateDerw(parsed); 137 | assert.strictEqual(generated, multiLine); 138 | } 139 | -------------------------------------------------------------------------------- /src/tests/empty_type_alias_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | BlockKinds, 11 | FixedType, 12 | Module, 13 | TypeAlias, 14 | UnparsedBlock, 15 | } from "../types"; 16 | 17 | const oneLine = ` 18 | type alias Person = { } 19 | `.trim(); 20 | 21 | const multiLine = ` 22 | type alias Person = { 23 | } 24 | `.trim(); 25 | 26 | const expectedOutput = ` 27 | type Person = { 28 | 29 | } 30 | 31 | function Person(args: { }): Person { 32 | return { 33 | ...args, 34 | }; 35 | } 36 | `.trim(); 37 | 38 | const expectedOutputJS = ` 39 | function Person(args) { 40 | return { 41 | ...args, 42 | }; 43 | } 44 | `.trim(); 45 | 46 | export function testIntoBlocks() { 47 | assert.deepStrictEqual(intoBlocks(oneLine), [ 48 | UnparsedBlock("TypeAliasBlock", 0, oneLine.split("\n")), 49 | ]); 50 | } 51 | 52 | export function testIntoBlocksMultiLine() { 53 | assert.deepStrictEqual(intoBlocks(multiLine), [ 54 | UnparsedBlock("TypeAliasBlock", 0, multiLine.split("\n")), 55 | ]); 56 | } 57 | 58 | export function testBlockKind() { 59 | assert.deepStrictEqual( 60 | blockKind(oneLine), 61 | Ok("TypeAlias") 62 | ); 63 | } 64 | 65 | export function testBlockKindMultiLine() { 66 | assert.deepStrictEqual( 67 | blockKind(multiLine), 68 | Ok("TypeAlias") 69 | ); 70 | } 71 | 72 | export function testParse() { 73 | assert.deepStrictEqual( 74 | parse(oneLine), 75 | Module("main", [ TypeAlias(FixedType("Person", [ ]), [ ]) ], [ ]) 76 | ); 77 | } 78 | 79 | export function testParseMultiLine() { 80 | assert.deepStrictEqual( 81 | parse(multiLine), 82 | Module("main", [ TypeAlias(FixedType("Person", [ ]), [ ]) ], [ ]) 83 | ); 84 | } 85 | 86 | export function testGenerate() { 87 | const parsed = parse(oneLine); 88 | assert.strictEqual(generateTypescript(parsed), expectedOutput); 89 | } 90 | 91 | export function testGenerateMultiLine() { 92 | const parsed = parse(multiLine); 93 | assert.deepStrictEqual(generateTypescript(parsed), expectedOutput); 94 | } 95 | 96 | export function testCompile() { 97 | const parsed = parse(oneLine); 98 | const generated = generateTypescript(parsed); 99 | const compiled = compileTypescript(generated); 100 | 101 | assert.deepStrictEqual( 102 | compiled.kind, 103 | "Ok", 104 | (compiled.kind === "Err" && compiled.error.toString()) || "" 105 | ); 106 | } 107 | 108 | export function testCompileMultiLine() { 109 | const parsed = parse(multiLine); 110 | const generated = generateTypescript(parsed); 111 | const compiled = compileTypescript(generated); 112 | 113 | assert.deepStrictEqual( 114 | compiled.kind, 115 | "Ok", 116 | (compiled.kind === "Err" && compiled.error.toString()) || "" 117 | ); 118 | } 119 | 120 | export function testGenerateJS() { 121 | const parsed = parse(multiLine); 122 | const generated = generateJavascript(parsed); 123 | assert.strictEqual(generated, expectedOutputJS); 124 | } 125 | 126 | export function testGenerateOneLineJS() { 127 | const parsed = parse(oneLine); 128 | const generated = generateJavascript(parsed); 129 | assert.strictEqual(generated, expectedOutputJS); 130 | } 131 | 132 | export function testGenerateDerw() { 133 | const parsed = parse(multiLine); 134 | const generated = generateDerw(parsed); 135 | assert.strictEqual(generated, multiLine); 136 | } 137 | -------------------------------------------------------------------------------- /src/tests/errors/names_in_scope_test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from "@eeue56/ts-assert"; 2 | import { addMissingNamesSuggestions } from "../../errors/names"; 3 | import { parseWithContext } from "../../parser"; 4 | 5 | export function testSingleName() { 6 | const str = ` 7 | world: string 8 | world = 9 | "hello" 10 | 11 | hello: string 12 | hello = 13 | world 14 | `.trim(); 15 | let parsed = parseWithContext(str, "Main"); 16 | parsed = addMissingNamesSuggestions(parsed); 17 | 18 | deepStrictEqual(parsed.errors, [ ]); 19 | } 20 | 21 | export function testSingleNameWithFnCall() { 22 | const str = ` 23 | world: string 24 | world = 25 | "hello" 26 | 27 | fn: string -> string 28 | fn x = 29 | "hello" 30 | 31 | hello: string 32 | hello = 33 | fn world 34 | `.trim(); 35 | let parsed = parseWithContext(str, "Main"); 36 | parsed = addMissingNamesSuggestions(parsed); 37 | 38 | deepStrictEqual(parsed.errors, [ ]); 39 | } 40 | 41 | export function testSingleNameWithNestedFnCall() { 42 | const str = ` 43 | world: string 44 | world = 45 | "hello" 46 | 47 | one: string -> string 48 | one x = 49 | "hello" 50 | 51 | two: string -> string 52 | two x = 53 | "hello" 54 | 55 | hello: string 56 | hello = 57 | one (two world) 58 | `.trim(); 59 | let parsed = parseWithContext(str, "Main"); 60 | parsed = addMissingNamesSuggestions(parsed); 61 | 62 | deepStrictEqual(parsed.errors, [ ]); 63 | } 64 | 65 | export function testFnWithSingleArg() { 66 | const str = ` 67 | fn: string -> string 68 | fn x = 69 | "hello" 70 | 71 | hello: string -> string 72 | hello name = 73 | fn name 74 | `.trim(); 75 | let parsed = parseWithContext(str, "Main"); 76 | parsed = addMissingNamesSuggestions(parsed); 77 | 78 | deepStrictEqual(parsed.errors, [ ]); 79 | } 80 | 81 | export function testFnWithTwoArgs() { 82 | const str = ` 83 | import "./String" exposing (fromNumber) 84 | 85 | hello: string -> number -> string 86 | hello name age = 87 | name + (fromNumber age) 88 | `.trim(); 89 | let parsed = parseWithContext(str, "Main"); 90 | parsed = addMissingNamesSuggestions(parsed); 91 | 92 | deepStrictEqual(parsed.errors, [ ]); 93 | } 94 | 95 | export function testLambda() { 96 | const str = ` 97 | hello: number -> number 98 | hello age = 99 | age 100 | |> (\\x -> x + 1) 101 | `.trim(); 102 | let parsed = parseWithContext(str, "Main"); 103 | parsed = addMissingNamesSuggestions(parsed); 104 | 105 | deepStrictEqual(parsed.errors, [ ]); 106 | } 107 | 108 | export function testGlobalName() { 109 | const str = ` 110 | hello: boolean 111 | hello = 112 | globalThis.isNaN 10 113 | `.trim(); 114 | let parsed = parseWithContext(str, "Main"); 115 | parsed = addMissingNamesSuggestions(parsed); 116 | 117 | deepStrictEqual(parsed.errors, [ ]); 118 | } 119 | -------------------------------------------------------------------------------- /src/tests/errors/names_out_of_scope_test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from "@eeue56/ts-assert"; 2 | import { addMissingNamesSuggestions } from "../../errors/names"; 3 | import { parseWithContext } from "../../parser"; 4 | 5 | export function testSingleName() { 6 | const str = ` 7 | hello: string 8 | hello = 9 | world 10 | `.trim(); 11 | let parsed = parseWithContext(str, "Main"); 12 | parsed = addMissingNamesSuggestions(parsed); 13 | 14 | deepStrictEqual(parsed.errors, [ 15 | "Failed to find `world` in the scope of `hello`.", 16 | ]); 17 | } 18 | 19 | export function testSingleNameWithFnCall() { 20 | const str = ` 21 | hello: string 22 | hello = 23 | fn world 24 | `.trim(); 25 | let parsed = parseWithContext(str, "Main"); 26 | parsed = addMissingNamesSuggestions(parsed); 27 | 28 | deepStrictEqual(parsed.errors, [ 29 | "Failed to find `fn` in the scope of `hello`.", 30 | "Failed to find `world` in the scope of `hello`.", 31 | ]); 32 | } 33 | 34 | export function testSingleNameWithNestedFnCall() { 35 | const str = ` 36 | hello: string 37 | hello = 38 | one (two world) 39 | `.trim(); 40 | let parsed = parseWithContext(str, "Main"); 41 | parsed = addMissingNamesSuggestions(parsed); 42 | 43 | deepStrictEqual(parsed.errors, [ 44 | "Failed to find `one` in the scope of `hello`.", 45 | "Failed to find `two` in the scope of `hello`.", 46 | "Failed to find `world` in the scope of `hello`.", 47 | ]); 48 | } 49 | 50 | export function testFnWithSingleArg() { 51 | const str = ` 52 | hello: string -> string 53 | hello name = 54 | fn name 55 | `.trim(); 56 | let parsed = parseWithContext(str, "Main"); 57 | parsed = addMissingNamesSuggestions(parsed); 58 | 59 | deepStrictEqual(parsed.errors, [ 60 | "Failed to find `fn` in the scope of `hello`.", 61 | ]); 62 | } 63 | 64 | export function testFnWithTwoArgs() { 65 | const str = ` 66 | hello: string -> number -> string 67 | hello name age = 68 | name + (fromNumber age) 69 | `.trim(); 70 | let parsed = parseWithContext(str, "Main"); 71 | parsed = addMissingNamesSuggestions(parsed); 72 | 73 | deepStrictEqual(parsed.errors, [ 74 | "Failed to find `fromNumber` in the scope of `hello`.", 75 | ]); 76 | } 77 | -------------------------------------------------------------------------------- /src/tests/errors/names_test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { intoBlocks } from "../../Blocks"; 4 | import { namesPerBlock } from "../../errors/names"; 5 | import { parseBlock } from "../../parser"; 6 | import { Block } from "../../types"; 7 | 8 | export function testSingleName() { 9 | const str = ` 10 | hello: string 11 | hello = 12 | world 13 | `.trim(); 14 | const block = intoBlocks(str)[0]; 15 | const parsedBlock = (parseBlock(block) as Ok).value; 16 | 17 | deepStrictEqual(namesPerBlock(parsedBlock), [ "hello", "world" ]); 18 | } 19 | 20 | export function testSingleNameWithFnCall() { 21 | const str = ` 22 | hello: string 23 | hello = 24 | fn world 25 | `.trim(); 26 | const block = intoBlocks(str)[0]; 27 | const parsedBlock = (parseBlock(block) as Ok).value; 28 | 29 | deepStrictEqual(namesPerBlock(parsedBlock), [ "hello", "fn", "world" ]); 30 | } 31 | 32 | export function testSingleNameWithNestedFnCall() { 33 | const str = ` 34 | hello: string 35 | hello = 36 | one (two world) 37 | `.trim(); 38 | const block = intoBlocks(str)[0]; 39 | const parsedBlock = (parseBlock(block) as Ok).value; 40 | 41 | deepStrictEqual(namesPerBlock(parsedBlock), [ 42 | "hello", 43 | "one", 44 | "two", 45 | "world", 46 | ]); 47 | } 48 | 49 | export function testFnWithSingleArg() { 50 | const str = ` 51 | hello: string -> string 52 | hello name = 53 | name 54 | `.trim(); 55 | const block = intoBlocks(str)[0]; 56 | const parsedBlock = (parseBlock(block) as Ok).value; 57 | 58 | deepStrictEqual(namesPerBlock(parsedBlock), [ "hello", "name" ]); 59 | } 60 | 61 | export function testFnWithTwoArgs() { 62 | const str = ` 63 | hello: string -> number -> string 64 | hello name age = 65 | name + (fromNumber age) 66 | `.trim(); 67 | const block = intoBlocks(str)[0]; 68 | const parsedBlock = (parseBlock(block) as Ok).value; 69 | 70 | deepStrictEqual(namesPerBlock(parsedBlock), [ 71 | "hello", 72 | "name", 73 | "fromNumber", 74 | "age", 75 | ]); 76 | } 77 | -------------------------------------------------------------------------------- /src/tests/errors/types/int_and_string_test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from "@eeue56/ts-assert"; 2 | import { addTypeErrors, parseWithContext } from "../../../parser"; 3 | 4 | export function testIntToIntPerfect() { 5 | const str = ` 6 | doSomething: number -> number 7 | doSomething x = 8 | x + x 9 | `.trim(); 10 | let parsed = parseWithContext(str, "Main"); 11 | parsed = addTypeErrors(parsed, [ ]); 12 | 13 | deepStrictEqual(parsed.errors, [ ]); 14 | } 15 | 16 | export function testStringToStringPerfect() { 17 | const str = ` 18 | doSomething: string -> string 19 | doSomething x = 20 | x 21 | `.trim(); 22 | let parsed = parseWithContext(str, "Main"); 23 | parsed = addTypeErrors(parsed, [ ]); 24 | 25 | deepStrictEqual(parsed.errors, [ ]); 26 | } 27 | 28 | export function testIntToStringError() { 29 | const str = ` 30 | doSomething: number -> string 31 | doSomething x = 32 | x + x 33 | `.trim(); 34 | let parsed = parseWithContext(str, "Main"); 35 | parsed = addTypeErrors(parsed, [ ]); 36 | 37 | deepStrictEqual(parsed.errors, [ 38 | "Error on lines 0 - 3\n" + 39 | "Expected `string` but got `number` in the body of the function:\n" + 40 | "```\n" + 41 | "doSomething: number -> string\n" + 42 | "doSomething x =\n" + 43 | " x + x\n" + 44 | "```", 45 | ]); 46 | } 47 | 48 | export function testStringToIntError() { 49 | const str = ` 50 | doSomething: string -> number 51 | doSomething x = 52 | x + x 53 | `.trim(); 54 | let parsed = parseWithContext(str, "Main"); 55 | parsed = addTypeErrors(parsed, [ ]); 56 | 57 | deepStrictEqual(parsed.errors, [ 58 | "Error on lines 0 - 3\n" + 59 | "Expected `number` but got `string` in the body of the function:\n" + 60 | "```\n" + 61 | "doSomething: string -> number\n" + 62 | "doSomething x =\n" + 63 | " x + x\n" + 64 | "```", 65 | ]); 66 | } 67 | 68 | export function testStringPlusIntError() { 69 | const str = ` 70 | something: string 71 | something = 72 | "hello" + 99 73 | `.trim(); 74 | let parsed = parseWithContext(str, "Main"); 75 | parsed = addTypeErrors(parsed, [ ]); 76 | 77 | deepStrictEqual(parsed.errors, [ 78 | "Error on lines 0 - 3\n" + 79 | "Mismatching types between the left of the addition: string and the right of the addition: number\n" + 80 | "In Derw, types of both sides of an addition must be the same.\n" + 81 | "Try using a format string via `` instead\n" + 82 | "For example, `hello${99}`:\n" + 83 | "```\n" + 84 | "something: string\n" + 85 | "something =\n" + 86 | ' "hello" + 99\n' + 87 | "```", 88 | ]); 89 | } 90 | 91 | export function testStringPlusIntFunctionError() { 92 | const str = ` 93 | something: string -> number -> string 94 | something x y = 95 | x + y 96 | `.trim(); 97 | let parsed = parseWithContext(str, "Main"); 98 | parsed = addTypeErrors(parsed, [ ]); 99 | 100 | deepStrictEqual(parsed.errors, [ 101 | "Error on lines 0 - 3\n" + 102 | "Mismatching types between the left of the addition: string and the right of the addition: number\n" + 103 | "In Derw, types of both sides of an addition must be the same.\n" + 104 | "Try using a format string via `` instead\n" + 105 | "For example, `${x}${y}`:\n" + 106 | "```\n" + 107 | "something: string -> number -> string\n" + 108 | "something x y =\n" + 109 | " x + y\n" + 110 | "```", 111 | ]); 112 | } 113 | 114 | export function testIntPlusStringFunctionError() { 115 | const str = ` 116 | something: number -> string -> string 117 | something x y = 118 | x + y 119 | `.trim(); 120 | let parsed = parseWithContext(str, "Main"); 121 | parsed = addTypeErrors(parsed, [ ]); 122 | 123 | deepStrictEqual(parsed.errors, [ 124 | "Error on lines 0 - 3\n" + 125 | "Mismatching types between the left of the addition: number and the right of the addition: string\n" + 126 | "In Derw, types of both sides of an addition must be the same.\n" + 127 | "Try using a format string via `` instead\n" + 128 | "For example, `${x}${y}`:\n" + 129 | "```\n" + 130 | "something: number -> string -> string\n" + 131 | "something x y =\n" + 132 | " x + y\n" + 133 | "```", 134 | ]); 135 | } 136 | -------------------------------------------------------------------------------- /src/tests/format/auto_name_for_unamed_function_args_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { generateDerw } from "../../generators/Derw"; 3 | import { parse } from "../../parser"; 4 | 5 | export function testArgumentsArePreserved() { 6 | const input = ` 7 | fn: number -> number -> number 8 | fn a b = 9 | a + b 10 | `.trim(); 11 | 12 | const expected = ` 13 | fn: number -> number -> number 14 | fn a b = 15 | a + b 16 | `.trim(); 17 | 18 | const parsed = parse(input); 19 | const generated = generateDerw(parsed); 20 | assert.strictEqual(generated, expected); 21 | } 22 | 23 | export function testArgumentsAreAddedWhenMissing() { 24 | const input = ` 25 | fn: number -> Context -> number 26 | fn a = 27 | a + b 28 | `.trim(); 29 | 30 | const expected = ` 31 | fn: number -> Context -> number 32 | fn a context = 33 | a + b 34 | `.trim(); 35 | 36 | const parsed = parse(input); 37 | const generated = generateDerw(parsed); 38 | assert.strictEqual(generated, expected); 39 | } 40 | 41 | export function testArgumentsAreAddedWhenMissingWithParens() { 42 | const input = ` 43 | fn: number -> Maybe string -> number 44 | fn a = 45 | a + b 46 | `.trim(); 47 | 48 | const expected = ` 49 | fn: number -> Maybe string -> number 50 | fn a maybe_string = 51 | a + b 52 | `.trim(); 53 | 54 | const parsed = parse(input); 55 | const generated = generateDerw(parsed); 56 | assert.strictEqual(generated, expected); 57 | } 58 | 59 | export function testArgumentsAreAddedForFunctions() { 60 | const input = ` 61 | fn: number -> (a -> b) -> number 62 | fn a = 63 | a + b 64 | `.trim(); 65 | 66 | const expected = ` 67 | fn: number -> (a -> b) -> number 68 | fn a fn = 69 | a + b 70 | `.trim(); 71 | 72 | const parsed = parse(input); 73 | const generated = generateDerw(parsed); 74 | assert.strictEqual(generated, expected); 75 | } 76 | -------------------------------------------------------------------------------- /src/tests/format_string_values_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | Const, 11 | FixedType, 12 | FormatStringValue, 13 | Module, 14 | UnparsedBlock, 15 | } from "../types"; 16 | 17 | const oneLine = ` 18 | helloWorld: string 19 | helloWorld = \`Hello world\` 20 | `.trim(); 21 | 22 | const multiLine = ` 23 | helloWorld: string 24 | helloWorld = 25 | \`Hello world\` 26 | `.trim(); 27 | 28 | const expectedOutput = ` 29 | const helloWorld: string = \`Hello world\`; 30 | `.trim(); 31 | 32 | const expectedOutputJS = ` 33 | const helloWorld = \`Hello world\`; 34 | `.trim(); 35 | 36 | export function testIntoBlocks() { 37 | assert.deepStrictEqual(intoBlocks(oneLine), [ 38 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 39 | ]); 40 | } 41 | 42 | export function testIntoBlocksMultiLine() { 43 | assert.deepStrictEqual(intoBlocks(multiLine), [ 44 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 45 | ]); 46 | } 47 | 48 | export function testBlockKind() { 49 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 50 | } 51 | 52 | export function testBlockKindMultiLine() { 53 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 54 | } 55 | 56 | export function testParse() { 57 | assert.deepStrictEqual( 58 | parse(oneLine), 59 | Module( 60 | "main", 61 | [ 62 | Const( 63 | "helloWorld", 64 | FixedType("string", [ ]), 65 | [ ], 66 | FormatStringValue("Hello world") 67 | ), 68 | ], 69 | [ ] 70 | ) 71 | ); 72 | } 73 | 74 | export function testParseMultiLine() { 75 | assert.deepStrictEqual( 76 | parse(multiLine), 77 | Module( 78 | "main", 79 | [ 80 | Const( 81 | "helloWorld", 82 | FixedType("string", [ ]), 83 | [ ], 84 | FormatStringValue("Hello world") 85 | ), 86 | ], 87 | [ ] 88 | ) 89 | ); 90 | } 91 | 92 | export function testGenerate() { 93 | const parsed = parse(multiLine); 94 | const generated = generateTypescript(parsed); 95 | assert.strictEqual(generated, expectedOutput); 96 | } 97 | 98 | export function testGenerateOneLine() { 99 | const parsed = parse(oneLine); 100 | const generated = generateTypescript(parsed); 101 | assert.strictEqual(generated, expectedOutput); 102 | } 103 | 104 | export function testCompile() { 105 | const parsed = parse(oneLine); 106 | const generated = generateTypescript(parsed); 107 | const compiled = compileTypescript(generated); 108 | 109 | assert.deepStrictEqual( 110 | compiled.kind, 111 | "Ok", 112 | (compiled.kind === "Err" && compiled.error.toString()) || "" 113 | ); 114 | } 115 | 116 | export function testCompileMultiLine() { 117 | const parsed = parse(multiLine); 118 | const generated = generateTypescript(parsed); 119 | const compiled = compileTypescript(generated); 120 | 121 | assert.deepStrictEqual( 122 | compiled.kind, 123 | "Ok", 124 | (compiled.kind === "Err" && compiled.error.toString()) || "" 125 | ); 126 | } 127 | 128 | export function testGenerateJS() { 129 | const parsed = parse(multiLine); 130 | const generated = generateJavascript(parsed); 131 | assert.strictEqual(generated, expectedOutputJS); 132 | } 133 | 134 | export function testGenerateOneLineJS() { 135 | const parsed = parse(oneLine); 136 | const generated = generateJavascript(parsed); 137 | assert.strictEqual(generated, expectedOutputJS); 138 | } 139 | 140 | export function testGenerateDerw() { 141 | const parsed = parse(multiLine); 142 | const generated = generateDerw(parsed); 143 | assert.strictEqual(generated, multiLine); 144 | } 145 | -------------------------------------------------------------------------------- /src/tests/list_prepend_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | Const, 11 | FixedType, 12 | ListPrepend, 13 | ListRange, 14 | Module, 15 | UnparsedBlock, 16 | Value, 17 | } from "../types"; 18 | 19 | const oneLine = ` 20 | helloWorld: List number 21 | helloWorld = 1::[ 2..5 ] 22 | `.trim(); 23 | 24 | const multiLine = ` 25 | helloWorld: List number 26 | helloWorld = 27 | 1 :: [ 2..5 ] 28 | `.trim(); 29 | 30 | const expectedOutput = ` 31 | const helloWorld: number[] = [ 1, ...Array.from({ length: 5 - 2 + 1 }, (_ReservedX, _ReservedI) => _ReservedI + 2) ]; 32 | `.trim(); 33 | 34 | const expectedOutputJS = ` 35 | const helloWorld = [ 1, ...Array.from({ length: 5 - 2 + 1 }, (_ReservedX, _ReservedI) => _ReservedI + 2) ]; 36 | `.trim(); 37 | 38 | export function testIntoBlocks() { 39 | assert.deepStrictEqual(intoBlocks(oneLine), [ 40 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 41 | ]); 42 | } 43 | 44 | export function testIntoBlocksMultiLine() { 45 | assert.deepStrictEqual(intoBlocks(multiLine), [ 46 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 47 | ]); 48 | } 49 | 50 | export function testBlockKind() { 51 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 52 | } 53 | 54 | export function testBlockKindMultiLine() { 55 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 56 | } 57 | 58 | export function testParse() { 59 | assert.deepStrictEqual( 60 | parse(oneLine), 61 | Module( 62 | "main", 63 | [ 64 | Const( 65 | "helloWorld", 66 | FixedType("List", [ FixedType("number", [ ]) ]), 67 | [ ], 68 | ListPrepend(Value("1"), ListRange(Value("2"), Value("5"))) 69 | ), 70 | ], 71 | [ ] 72 | ) 73 | ); 74 | } 75 | 76 | export function testParseMultiLine() { 77 | assert.deepStrictEqual( 78 | parse(multiLine), 79 | Module( 80 | "main", 81 | [ 82 | Const( 83 | "helloWorld", 84 | FixedType("List", [ FixedType("number", [ ]) ]), 85 | [ ], 86 | ListPrepend(Value("1"), ListRange(Value("2"), Value("5"))) 87 | ), 88 | ], 89 | [ ] 90 | ) 91 | ); 92 | } 93 | 94 | export function testGenerate() { 95 | const parsed = parse(multiLine); 96 | const generated = generateTypescript(parsed); 97 | assert.strictEqual(generated, expectedOutput); 98 | } 99 | 100 | export function testGenerateOneLine() { 101 | const parsed = parse(oneLine); 102 | const generated = generateTypescript(parsed); 103 | assert.strictEqual(generated, expectedOutput); 104 | } 105 | 106 | export function testCompile() { 107 | const parsed = parse(oneLine); 108 | const generated = generateTypescript(parsed); 109 | const compiled = compileTypescript(generated); 110 | 111 | assert.deepStrictEqual( 112 | compiled.kind, 113 | "Ok", 114 | (compiled.kind === "Err" && compiled.error.toString()) || "" 115 | ); 116 | } 117 | 118 | export function testCompileMultiLine() { 119 | const parsed = parse(multiLine); 120 | const generated = generateTypescript(parsed); 121 | const compiled = compileTypescript(generated); 122 | 123 | assert.deepStrictEqual( 124 | compiled.kind, 125 | "Ok", 126 | (compiled.kind === "Err" && compiled.error.toString()) || "" 127 | ); 128 | } 129 | 130 | export function testGenerateJS() { 131 | const parsed = parse(multiLine); 132 | const generated = generateJavascript(parsed); 133 | assert.strictEqual(generated, expectedOutputJS); 134 | } 135 | 136 | export function testGenerateOneLineJS() { 137 | const parsed = parse(oneLine); 138 | const generated = generateJavascript(parsed); 139 | assert.strictEqual(generated, expectedOutputJS); 140 | } 141 | 142 | export function testGenerateDerw() { 143 | const parsed = parse(multiLine); 144 | const generated = generateDerw(parsed); 145 | assert.strictEqual(generated, multiLine); 146 | } 147 | -------------------------------------------------------------------------------- /src/tests/list_ranges_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | Const, 11 | FixedType, 12 | ListRange, 13 | Module, 14 | UnparsedBlock, 15 | Value, 16 | } from "../types"; 17 | 18 | const oneLine = ` 19 | helloWorld: List number 20 | helloWorld = [ 1..5 ] 21 | `.trim(); 22 | 23 | const multiLine = ` 24 | helloWorld: List number 25 | helloWorld = 26 | [ 1..5 ] 27 | `.trim(); 28 | 29 | const expectedOutput = ` 30 | const helloWorld: number[] = Array.from({ length: 5 - 1 + 1 }, (_ReservedX, _ReservedI) => _ReservedI + 1); 31 | `.trim(); 32 | 33 | const expectedOutputJS = ` 34 | const helloWorld = Array.from({ length: 5 - 1 + 1 }, (_ReservedX, _ReservedI) => _ReservedI + 1); 35 | `.trim(); 36 | 37 | export function testIntoBlocks() { 38 | assert.deepStrictEqual(intoBlocks(oneLine), [ 39 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 40 | ]); 41 | } 42 | 43 | export function testIntoBlocksMultiLine() { 44 | assert.deepStrictEqual(intoBlocks(multiLine), [ 45 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 46 | ]); 47 | } 48 | 49 | export function testBlockKind() { 50 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 51 | } 52 | 53 | export function testBlockKindMultiLine() { 54 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 55 | } 56 | 57 | export function testParse() { 58 | assert.deepStrictEqual( 59 | parse(oneLine), 60 | Module( 61 | "main", 62 | [ 63 | Const( 64 | "helloWorld", 65 | FixedType("List", [ FixedType("number", [ ]) ]), 66 | [ ], 67 | ListRange(Value("1"), Value("5")) 68 | ), 69 | ], 70 | [ ] 71 | ) 72 | ); 73 | } 74 | 75 | export function testParseMultiLine() { 76 | assert.deepStrictEqual( 77 | parse(multiLine), 78 | Module( 79 | "main", 80 | [ 81 | Const( 82 | "helloWorld", 83 | FixedType("List", [ FixedType("number", [ ]) ]), 84 | [ ], 85 | ListRange(Value("1"), Value("5")) 86 | ), 87 | ], 88 | [ ] 89 | ) 90 | ); 91 | } 92 | 93 | export function testGenerate() { 94 | const parsed = parse(multiLine); 95 | const generated = generateTypescript(parsed); 96 | assert.strictEqual(generated, expectedOutput); 97 | } 98 | 99 | export function testGenerateOneLine() { 100 | const parsed = parse(oneLine); 101 | const generated = generateTypescript(parsed); 102 | assert.strictEqual(generated, expectedOutput); 103 | } 104 | 105 | export function testCompile() { 106 | const parsed = parse(oneLine); 107 | const generated = generateTypescript(parsed); 108 | const compiled = compileTypescript(generated); 109 | 110 | assert.deepStrictEqual( 111 | compiled.kind, 112 | "Ok", 113 | (compiled.kind === "Err" && compiled.error.toString()) || "" 114 | ); 115 | } 116 | 117 | export function testCompileMultiLine() { 118 | const parsed = parse(multiLine); 119 | const generated = generateTypescript(parsed); 120 | const compiled = compileTypescript(generated); 121 | 122 | assert.deepStrictEqual( 123 | compiled.kind, 124 | "Ok", 125 | (compiled.kind === "Err" && compiled.error.toString()) || "" 126 | ); 127 | } 128 | 129 | export function testGenerateJS() { 130 | const parsed = parse(multiLine); 131 | const generated = generateJavascript(parsed); 132 | assert.strictEqual(generated, expectedOutputJS); 133 | } 134 | 135 | export function testGenerateOneLineJS() { 136 | const parsed = parse(oneLine); 137 | const generated = generateJavascript(parsed); 138 | assert.strictEqual(generated, expectedOutputJS); 139 | } 140 | 141 | export function testGenerateDerw() { 142 | const parsed = parse(multiLine); 143 | const generated = generateDerw(parsed); 144 | assert.strictEqual(generated, multiLine); 145 | } 146 | -------------------------------------------------------------------------------- /src/tests/multiline_string_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | Const, 11 | FixedType, 12 | FormatStringValue, 13 | Module, 14 | UnparsedBlock, 15 | } from "../types"; 16 | 17 | const oneLine = ` 18 | example: string 19 | example = 20 | \` 21 | Hello world 22 | \` 23 | `.trim(); 24 | 25 | const multiLine = ` 26 | example: string 27 | example = 28 | \`Hello world\` 29 | `.trim(); 30 | 31 | const expectedOutput = ` 32 | const example: string = \`Hello world\`; 33 | `.trim(); 34 | 35 | const expectedOutputJS = ` 36 | const example = \`Hello world\`; 37 | `.trim(); 38 | 39 | export function testIntoBlocks() { 40 | assert.deepStrictEqual(intoBlocks(oneLine), [ 41 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 42 | ]); 43 | } 44 | 45 | export function testIntoBlocksMultiLine() { 46 | assert.deepStrictEqual(intoBlocks(multiLine), [ 47 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 48 | ]); 49 | } 50 | 51 | export function testBlockKind() { 52 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 53 | } 54 | 55 | export function testBlockKindMultiLine() { 56 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 57 | } 58 | 59 | export function testParse() { 60 | assert.deepStrictEqual( 61 | parse(oneLine), 62 | Module( 63 | "main", 64 | [ 65 | Const( 66 | "example", 67 | FixedType("string", [ ]), 68 | [ ], 69 | FormatStringValue("Hello world") 70 | ), 71 | ], 72 | [ ] 73 | ) 74 | ); 75 | } 76 | 77 | export function testParseMultiLine() { 78 | assert.deepStrictEqual( 79 | parse(multiLine), 80 | Module( 81 | "main", 82 | [ 83 | Const( 84 | "example", 85 | FixedType("string", [ ]), 86 | [ ], 87 | FormatStringValue("Hello world") 88 | ), 89 | ], 90 | [ ] 91 | ) 92 | ); 93 | } 94 | 95 | export function testGenerate() { 96 | const parsed = parse(multiLine); 97 | const generated = generateTypescript(parsed); 98 | assert.strictEqual(generated, expectedOutput); 99 | } 100 | 101 | export function testGenerateOneLine() { 102 | const parsed = parse(oneLine); 103 | const generated = generateTypescript(parsed); 104 | assert.strictEqual(generated, expectedOutput); 105 | } 106 | 107 | export function testCompile() { 108 | const parsed = parse(oneLine); 109 | const generated = generateTypescript(parsed); 110 | const compiled = compileTypescript(generated); 111 | 112 | assert.deepStrictEqual( 113 | compiled.kind, 114 | "Ok", 115 | (compiled.kind === "Err" && compiled.error.toString()) || "" 116 | ); 117 | } 118 | 119 | export function testCompileMultiLine() { 120 | const parsed = parse(multiLine); 121 | const generated = generateTypescript(parsed); 122 | const compiled = compileTypescript(generated); 123 | 124 | assert.deepStrictEqual( 125 | compiled.kind, 126 | "Ok", 127 | (compiled.kind === "Err" && compiled.error.toString()) || "" 128 | ); 129 | } 130 | 131 | export function testGenerateJS() { 132 | const parsed = parse(multiLine); 133 | const generated = generateJavascript(parsed); 134 | assert.strictEqual(generated, expectedOutputJS); 135 | } 136 | 137 | export function testGenerateOneLineJS() { 138 | const parsed = parse(oneLine); 139 | const generated = generateJavascript(parsed); 140 | assert.strictEqual(generated, expectedOutputJS); 141 | } 142 | 143 | export function testGenerateDerw() { 144 | const parsed = parse(multiLine); 145 | const generated = generateDerw(parsed); 146 | assert.strictEqual(generated, multiLine); 147 | } 148 | -------------------------------------------------------------------------------- /src/tests/negative_number_literal_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { Const, FixedType, Module, UnparsedBlock, Value } from "../types"; 10 | 11 | const oneLine = ` 12 | helloWorld: number 13 | helloWorld = -1 14 | `.trim(); 15 | 16 | const multiLine = ` 17 | helloWorld: number 18 | helloWorld = 19 | -1 20 | `.trim(); 21 | 22 | const expectedOutput = ` 23 | const helloWorld: number = -1; 24 | `.trim(); 25 | 26 | const expectedOutputJS = ` 27 | const helloWorld = -1; 28 | `.trim(); 29 | 30 | export function testIntoBlocks() { 31 | assert.deepStrictEqual(intoBlocks(oneLine), [ 32 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 33 | ]); 34 | } 35 | 36 | export function testIntoBlocksMultiLine() { 37 | assert.deepStrictEqual(intoBlocks(multiLine), [ 38 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 39 | ]); 40 | } 41 | 42 | export function testBlockKind() { 43 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 44 | } 45 | 46 | export function testBlockKindMultiLine() { 47 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 48 | } 49 | 50 | export function testParse() { 51 | assert.deepStrictEqual( 52 | parse(oneLine), 53 | Module( 54 | "main", 55 | [ Const("helloWorld", FixedType("number", [ ]), [ ], Value("-1")) ], 56 | [ ] 57 | ) 58 | ); 59 | } 60 | 61 | export function testParseMultiLine() { 62 | assert.deepStrictEqual( 63 | parse(multiLine), 64 | Module( 65 | "main", 66 | [ Const("helloWorld", FixedType("number", [ ]), [ ], Value("-1")) ], 67 | [ ] 68 | ) 69 | ); 70 | } 71 | 72 | export function testGenerate() { 73 | const parsed = parse(multiLine); 74 | const generated = generateTypescript(parsed); 75 | assert.strictEqual(generated, expectedOutput); 76 | } 77 | 78 | export function testGenerateOneLine() { 79 | const parsed = parse(oneLine); 80 | const generated = generateTypescript(parsed); 81 | assert.strictEqual(generated, expectedOutput); 82 | } 83 | 84 | export function testCompile() { 85 | const parsed = parse(oneLine); 86 | const generated = generateTypescript(parsed); 87 | const compiled = compileTypescript(generated); 88 | 89 | assert.deepStrictEqual( 90 | compiled.kind, 91 | "Ok", 92 | (compiled.kind === "Err" && compiled.error.toString()) || "" 93 | ); 94 | } 95 | 96 | export function testCompileMultiLine() { 97 | const parsed = parse(multiLine); 98 | const generated = generateTypescript(parsed); 99 | const compiled = compileTypescript(generated); 100 | 101 | assert.deepStrictEqual( 102 | compiled.kind, 103 | "Ok", 104 | (compiled.kind === "Err" && compiled.error.toString()) || "" 105 | ); 106 | } 107 | 108 | export function testGenerateJS() { 109 | const parsed = parse(multiLine); 110 | const generated = generateJavascript(parsed); 111 | assert.strictEqual(generated, expectedOutputJS); 112 | } 113 | 114 | export function testGenerateOneLineJS() { 115 | const parsed = parse(oneLine); 116 | const generated = generateJavascript(parsed); 117 | assert.strictEqual(generated, expectedOutputJS); 118 | } 119 | 120 | export function testGenerateDerw() { 121 | const parsed = parse(multiLine); 122 | const generated = generateDerw(parsed); 123 | assert.strictEqual(generated, multiLine); 124 | } 125 | -------------------------------------------------------------------------------- /src/tests/package_test.ts: -------------------------------------------------------------------------------- 1 | import { deepStrictEqual } from "@eeue56/ts-assert"; 2 | import { Err, Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { decodePackage, Dependency, Package, PackageModule } from "../package"; 4 | 5 | export function testValidPackageWithNoModules() { 6 | const packageJson = { 7 | name: "stdlib", 8 | exposing: [ ], 9 | dependencies: {}, 10 | }; 11 | 12 | deepStrictEqual( 13 | decodePackage(packageJson), 14 | Ok(Package("stdlib", [ ], [ ])) 15 | ); 16 | } 17 | 18 | export function testValidPackageWithModules() { 19 | const packageJson = { 20 | name: "stdlib", 21 | exposing: [ "List", "Maybe", "Test" ], 22 | dependencies: {}, 23 | }; 24 | 25 | deepStrictEqual( 26 | decodePackage(packageJson), 27 | Ok( 28 | Package( 29 | "stdlib", 30 | [ 31 | PackageModule("List"), 32 | PackageModule("Maybe"), 33 | PackageModule("Test"), 34 | ], 35 | [ ] 36 | ) 37 | ) 38 | ); 39 | } 40 | 41 | export function testValidPackageWithDependencies() { 42 | const packageJson = { 43 | name: "stdlib", 44 | exposing: [ "List", "Maybe", "Test" ], 45 | dependencies: { 46 | result: "1.0.0", 47 | }, 48 | }; 49 | 50 | deepStrictEqual( 51 | decodePackage(packageJson), 52 | Ok( 53 | Package( 54 | "stdlib", 55 | [ 56 | PackageModule("List"), 57 | PackageModule("Maybe"), 58 | PackageModule("Test"), 59 | ], 60 | [ Dependency("result", "1.0.0") ] 61 | ) 62 | ) 63 | ); 64 | } 65 | 66 | export function testValidPackageWithExtraJson() { 67 | const packageJson = { 68 | name: "stdlib", 69 | exposing: [ ], 70 | dependencies: {}, 71 | somethingUnused: true, 72 | }; 73 | 74 | deepStrictEqual( 75 | decodePackage(packageJson), 76 | Ok(Package("stdlib", [ ], [ ])) 77 | ); 78 | } 79 | 80 | export function testInvalidPackageWithMissingName() { 81 | const packageJson = { 82 | exposing: [ ], 83 | }; 84 | 85 | deepStrictEqual( 86 | decodePackage(packageJson), 87 | Err('Error parsing name due to "undefined" is not a string') 88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /src/tests/promise_any_main_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | Const, 11 | FixedType, 12 | GenericType, 13 | Module, 14 | UnparsedBlock, 15 | Value, 16 | } from "../types"; 17 | 18 | const oneLine = ` 19 | main: Promise any 20 | main = null 21 | `.trim(); 22 | 23 | const multiLine = ` 24 | main: Promise any 25 | main = 26 | null 27 | `.trim(); 28 | 29 | const expectedOutput = ` 30 | const main: Promise = null; 31 | `.trim(); 32 | 33 | const expectedOutputJS = ` 34 | const main = null; 35 | `.trim(); 36 | 37 | export function testIntoBlocks() { 38 | assert.deepStrictEqual(intoBlocks(oneLine), [ 39 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 40 | ]); 41 | } 42 | 43 | export function testIntoBlocksMultiLine() { 44 | assert.deepStrictEqual(intoBlocks(multiLine), [ 45 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 46 | ]); 47 | } 48 | 49 | export function testBlockKind() { 50 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 51 | } 52 | 53 | export function testBlockKindMultiLine() { 54 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 55 | } 56 | 57 | export function testParse() { 58 | assert.deepStrictEqual( 59 | parse(oneLine), 60 | Module( 61 | "main", 62 | [ 63 | Const( 64 | "main", 65 | FixedType("Promise", [ GenericType("any") ]), 66 | [ ], 67 | Value("null") 68 | ), 69 | ], 70 | [ ] 71 | ) 72 | ); 73 | } 74 | 75 | export function testParseMultiLine() { 76 | assert.deepStrictEqual( 77 | parse(multiLine), 78 | Module( 79 | "main", 80 | [ 81 | Const( 82 | "main", 83 | FixedType("Promise", [ GenericType("any") ]), 84 | [ ], 85 | Value("null") 86 | ), 87 | ], 88 | [ ] 89 | ) 90 | ); 91 | } 92 | 93 | export function testGenerate() { 94 | const parsed = parse(multiLine); 95 | const generated = generateTypescript(parsed); 96 | assert.strictEqual(generated, expectedOutput); 97 | } 98 | 99 | export function testGenerateOneLine() { 100 | const parsed = parse(oneLine); 101 | const generated = generateTypescript(parsed); 102 | assert.strictEqual(generated, expectedOutput); 103 | } 104 | 105 | export function testCompile() { 106 | const parsed = parse(oneLine); 107 | const generated = generateTypescript(parsed); 108 | const compiled = compileTypescript(generated); 109 | 110 | assert.deepStrictEqual( 111 | compiled.kind, 112 | "Ok", 113 | (compiled.kind === "Err" && compiled.error.toString()) || "" 114 | ); 115 | } 116 | 117 | export function testCompileMultiLine() { 118 | const parsed = parse(multiLine); 119 | const generated = generateTypescript(parsed); 120 | const compiled = compileTypescript(generated); 121 | 122 | assert.deepStrictEqual( 123 | compiled.kind, 124 | "Ok", 125 | (compiled.kind === "Err" && compiled.error.toString()) || "" 126 | ); 127 | } 128 | 129 | export function testGenerateJS() { 130 | const parsed = parse(multiLine); 131 | const generated = generateJavascript(parsed); 132 | assert.strictEqual(generated, expectedOutputJS); 133 | } 134 | 135 | export function testGenerateOneLineJS() { 136 | const parsed = parse(oneLine); 137 | const generated = generateJavascript(parsed); 138 | assert.strictEqual(generated, expectedOutputJS); 139 | } 140 | 141 | export function testGenerateDerw() { 142 | const parsed = parse(multiLine); 143 | const generated = generateDerw(parsed); 144 | assert.strictEqual(generated, multiLine); 145 | } 146 | -------------------------------------------------------------------------------- /src/tests/stdlib_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { readdir, readFile } from "fs/promises"; 3 | import path from "path"; 4 | import { generateDerw } from "../generators/Derw"; 5 | import { parse } from "../parser"; 6 | 7 | const emptyLineAtEndOfFile = "\n"; 8 | 9 | export async function testStdlibAreConsistentlyParsed() { 10 | const exampleFiles: string[] = await ( 11 | await readdir("../derw-lang/stdlib/src") 12 | ).map((file) => path.join("../derw-lang/stdlib/src", file)); 13 | 14 | const files: string[] = exampleFiles; 15 | 16 | const filePairs = files.filter((file) => file.endsWith("derw")); 17 | 18 | await Promise.all( 19 | filePairs.map(async (derw) => { 20 | const derwContents = (await readFile(derw)).toString(); 21 | 22 | const parsed = parse(derwContents); 23 | const generated = generateDerw(parsed) + emptyLineAtEndOfFile; 24 | const secondParsed = parse(generated); 25 | const secondGenerated = 26 | generateDerw(secondParsed) + emptyLineAtEndOfFile; 27 | 28 | try { 29 | assert.deepStrictEqual(parsed.errors, [ ]); 30 | assert.deepStrictEqual(generated, secondGenerated); 31 | assert.deepStrictEqual(parsed, secondParsed); 32 | } catch (e) { 33 | console.log("generated", generated); 34 | console.log("second generated", secondGenerated); 35 | console.log(`Failed to correctly generate ${derw}`); 36 | throw e; 37 | } 38 | }) 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/tests/type_alias_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | BlockKinds, 11 | FixedType, 12 | Module, 13 | Property, 14 | TypeAlias, 15 | UnparsedBlock, 16 | } from "../types"; 17 | 18 | const oneLine = ` 19 | type alias Person = { name: string, age: number } 20 | `.trim(); 21 | 22 | const multiLine = ` 23 | type alias Person = { 24 | name: string, 25 | age: number 26 | } 27 | `.trim(); 28 | 29 | const expectedOutput = ` 30 | type Person = { 31 | name: string; 32 | age: number; 33 | } 34 | 35 | function Person(args: { name: string, age: number }): Person { 36 | return { 37 | ...args, 38 | }; 39 | } 40 | `.trim(); 41 | 42 | const expectedOutputJS = ` 43 | function Person(args) { 44 | return { 45 | ...args, 46 | }; 47 | } 48 | `.trim(); 49 | 50 | export function testIntoBlocks() { 51 | assert.deepStrictEqual(intoBlocks(oneLine), [ 52 | UnparsedBlock("TypeAliasBlock", 0, oneLine.split("\n")), 53 | ]); 54 | } 55 | 56 | export function testIntoBlocksMultiLine() { 57 | assert.deepStrictEqual(intoBlocks(multiLine), [ 58 | UnparsedBlock("TypeAliasBlock", 0, multiLine.split("\n")), 59 | ]); 60 | } 61 | 62 | export function testBlockKind() { 63 | assert.deepStrictEqual( 64 | blockKind(oneLine), 65 | Ok("TypeAlias") 66 | ); 67 | } 68 | 69 | export function testBlockKindMultiLine() { 70 | assert.deepStrictEqual( 71 | blockKind(multiLine), 72 | Ok("TypeAlias") 73 | ); 74 | } 75 | 76 | export function testParse() { 77 | assert.deepStrictEqual( 78 | parse(oneLine), 79 | Module( 80 | "main", 81 | [ 82 | TypeAlias(FixedType("Person", [ ]), [ 83 | Property("name", FixedType("string", [ ])), 84 | Property("age", FixedType("number", [ ])), 85 | ]), 86 | ], 87 | [ ] 88 | ) 89 | ); 90 | } 91 | 92 | export function testParseMultiLine() { 93 | assert.deepStrictEqual( 94 | parse(multiLine), 95 | Module( 96 | "main", 97 | [ 98 | TypeAlias(FixedType("Person", [ ]), [ 99 | Property("name", FixedType("string", [ ])), 100 | Property("age", FixedType("number", [ ])), 101 | ]), 102 | ], 103 | [ ] 104 | ) 105 | ); 106 | } 107 | 108 | export function testGenerate() { 109 | const parsed = parse(oneLine); 110 | assert.strictEqual(generateTypescript(parsed), expectedOutput); 111 | } 112 | 113 | export function testGenerateMultiLine() { 114 | const parsed = parse(multiLine); 115 | assert.deepStrictEqual(generateTypescript(parsed), expectedOutput); 116 | } 117 | 118 | export function testCompile() { 119 | const parsed = parse(oneLine); 120 | const generated = generateTypescript(parsed); 121 | const compiled = compileTypescript(generated); 122 | 123 | assert.deepStrictEqual( 124 | compiled.kind, 125 | "Ok", 126 | (compiled.kind === "Err" && compiled.error.toString()) || "" 127 | ); 128 | } 129 | 130 | export function testCompileMultiLine() { 131 | const parsed = parse(multiLine); 132 | const generated = generateTypescript(parsed); 133 | const compiled = compileTypescript(generated); 134 | 135 | assert.deepStrictEqual( 136 | compiled.kind, 137 | "Ok", 138 | (compiled.kind === "Err" && compiled.error.toString()) || "" 139 | ); 140 | } 141 | 142 | export function testGenerateJS() { 143 | const parsed = parse(multiLine); 144 | const generated = generateJavascript(parsed); 145 | assert.strictEqual(generated, expectedOutputJS); 146 | } 147 | 148 | export function testGenerateOneLineJS() { 149 | const parsed = parse(oneLine); 150 | const generated = generateJavascript(parsed); 151 | assert.strictEqual(generated, expectedOutputJS); 152 | } 153 | 154 | export function testGenerateDerw() { 155 | const parsed = parse(multiLine); 156 | const generated = generateDerw(parsed); 157 | assert.strictEqual(generated, multiLine); 158 | } 159 | -------------------------------------------------------------------------------- /src/tests/type_alias_with_type_property_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | BlockKinds, 11 | FixedType, 12 | Module, 13 | Property, 14 | TypeAlias, 15 | UnparsedBlock, 16 | } from "../types"; 17 | 18 | const oneLine = ` 19 | type alias Person = { name: string, type: number } 20 | `.trim(); 21 | 22 | const multiLine = ` 23 | type alias Person = { 24 | name: string, 25 | type: number 26 | } 27 | `.trim(); 28 | 29 | const expectedOutput = ` 30 | type Person = { 31 | name: string; 32 | type: number; 33 | } 34 | 35 | function Person(args: { name: string, type: number }): Person { 36 | return { 37 | ...args, 38 | }; 39 | } 40 | `.trim(); 41 | 42 | const expectedOutputJS = ` 43 | function Person(args) { 44 | return { 45 | ...args, 46 | }; 47 | } 48 | `.trim(); 49 | 50 | export function testIntoBlocks() { 51 | assert.deepStrictEqual(intoBlocks(oneLine), [ 52 | UnparsedBlock("TypeAliasBlock", 0, oneLine.split("\n")), 53 | ]); 54 | } 55 | 56 | export function testIntoBlocksMultiLine() { 57 | assert.deepStrictEqual(intoBlocks(multiLine), [ 58 | UnparsedBlock("TypeAliasBlock", 0, multiLine.split("\n")), 59 | ]); 60 | } 61 | 62 | export function testBlockKind() { 63 | assert.deepStrictEqual( 64 | blockKind(oneLine), 65 | Ok("TypeAlias") 66 | ); 67 | } 68 | 69 | export function testBlockKindMultiLine() { 70 | assert.deepStrictEqual( 71 | blockKind(multiLine), 72 | Ok("TypeAlias") 73 | ); 74 | } 75 | 76 | export function testParse() { 77 | assert.deepStrictEqual( 78 | parse(oneLine), 79 | Module( 80 | "main", 81 | [ 82 | TypeAlias(FixedType("Person", [ ]), [ 83 | Property("name", FixedType("string", [ ])), 84 | Property("type", FixedType("number", [ ])), 85 | ]), 86 | ], 87 | [ ] 88 | ) 89 | ); 90 | } 91 | 92 | export function testParseMultiLine() { 93 | assert.deepStrictEqual( 94 | parse(multiLine), 95 | Module( 96 | "main", 97 | [ 98 | TypeAlias(FixedType("Person", [ ]), [ 99 | Property("name", FixedType("string", [ ])), 100 | Property("type", FixedType("number", [ ])), 101 | ]), 102 | ], 103 | [ ] 104 | ) 105 | ); 106 | } 107 | 108 | export function testGenerate() { 109 | const parsed = parse(oneLine); 110 | assert.strictEqual(generateTypescript(parsed), expectedOutput); 111 | } 112 | 113 | export function testGenerateMultiLine() { 114 | const parsed = parse(multiLine); 115 | assert.deepStrictEqual(generateTypescript(parsed), expectedOutput); 116 | } 117 | 118 | export function testCompile() { 119 | const parsed = parse(oneLine); 120 | const generated = generateTypescript(parsed); 121 | const compiled = compileTypescript(generated); 122 | 123 | assert.deepStrictEqual( 124 | compiled.kind, 125 | "Ok", 126 | (compiled.kind === "Err" && compiled.error.toString()) || "" 127 | ); 128 | } 129 | 130 | export function testCompileMultiLine() { 131 | const parsed = parse(multiLine); 132 | const generated = generateTypescript(parsed); 133 | const compiled = compileTypescript(generated); 134 | 135 | assert.deepStrictEqual( 136 | compiled.kind, 137 | "Ok", 138 | (compiled.kind === "Err" && compiled.error.toString()) || "" 139 | ); 140 | } 141 | 142 | export function testGenerateJS() { 143 | const parsed = parse(multiLine); 144 | const generated = generateJavascript(parsed); 145 | assert.strictEqual(generated, expectedOutputJS); 146 | } 147 | 148 | export function testGenerateOneLineJS() { 149 | const parsed = parse(oneLine); 150 | const generated = generateJavascript(parsed); 151 | assert.strictEqual(generated, expectedOutputJS); 152 | } 153 | 154 | export function testGenerateDerw() { 155 | const parsed = parse(multiLine); 156 | const generated = generateDerw(parsed); 157 | assert.strictEqual(generated, multiLine); 158 | } 159 | -------------------------------------------------------------------------------- /src/tests/union_type_with_type_property_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | FixedType, 11 | Module, 12 | Tag, 13 | TagArg, 14 | UnionType, 15 | UnparsedBlock, 16 | } from "../types"; 17 | 18 | const oneLine = ` 19 | type CustomList = Leaf { type: string } 20 | `.trim(); 21 | 22 | const multiLine = ` 23 | type CustomList = 24 | Leaf { type: string } 25 | `.trim(); 26 | 27 | const expectedOutput = ` 28 | type Leaf = { 29 | kind: "Leaf"; 30 | type: string; 31 | }; 32 | 33 | function Leaf(args: { type: string }): Leaf { 34 | return { 35 | kind: "Leaf", 36 | ...args, 37 | }; 38 | } 39 | 40 | type CustomList = Leaf; 41 | `.trim(); 42 | 43 | const expectedOutputJS = ` 44 | function Leaf(args) { 45 | return { 46 | kind: "Leaf", 47 | ...args, 48 | }; 49 | } 50 | `.trim(); 51 | 52 | export function testIntoBlocks() { 53 | assert.deepStrictEqual(intoBlocks(oneLine), [ 54 | UnparsedBlock("UnionTypeBlock", 0, oneLine.split("\n")), 55 | ]); 56 | } 57 | 58 | export function testIntoBlocksMultiLine() { 59 | assert.deepStrictEqual(intoBlocks(multiLine), [ 60 | UnparsedBlock("UnionTypeBlock", 0, multiLine.split("\n")), 61 | ]); 62 | } 63 | 64 | export function testBlockKind() { 65 | assert.deepStrictEqual(blockKind(oneLine), Ok("UnionType")); 66 | } 67 | 68 | export function testBlockKindMultiLine() { 69 | assert.deepStrictEqual(blockKind(multiLine), Ok("UnionType")); 70 | } 71 | 72 | export function testParse() { 73 | assert.deepStrictEqual( 74 | parse(oneLine), 75 | Module( 76 | "main", 77 | [ 78 | UnionType(FixedType("CustomList", [ ]), [ 79 | Tag("Leaf", [ TagArg("type", FixedType("string", [ ])) ]), 80 | ]), 81 | ], 82 | [ ] 83 | ) 84 | ); 85 | } 86 | 87 | export function testParseMultiLine() { 88 | assert.deepStrictEqual( 89 | parse(multiLine), 90 | Module( 91 | "main", 92 | [ 93 | UnionType(FixedType("CustomList", [ ]), [ 94 | Tag("Leaf", [ TagArg("type", FixedType("string", [ ])) ]), 95 | ]), 96 | ], 97 | [ ] 98 | ) 99 | ); 100 | } 101 | 102 | export function testGenerate() { 103 | const parsed = parse(oneLine); 104 | 105 | assert.deepStrictEqual(generateTypescript(parsed), expectedOutput); 106 | } 107 | 108 | export function testGenerateMultiLine() { 109 | const parsed = parse(multiLine); 110 | 111 | assert.deepStrictEqual(generateTypescript(parsed), expectedOutput); 112 | } 113 | 114 | export function testCompile() { 115 | const parsed = parse(oneLine); 116 | const generated = generateTypescript(parsed); 117 | const compiled = compileTypescript(generated); 118 | 119 | assert.deepStrictEqual( 120 | compiled.kind, 121 | "Ok", 122 | (compiled.kind === "Err" && compiled.error.toString()) || "" 123 | ); 124 | } 125 | 126 | export function testCompileMultiLine() { 127 | const parsed = parse(multiLine); 128 | const generated = generateTypescript(parsed); 129 | const compiled = compileTypescript(generated); 130 | 131 | assert.deepStrictEqual( 132 | compiled.kind, 133 | "Ok", 134 | (compiled.kind === "Err" && compiled.error.toString()) || "" 135 | ); 136 | } 137 | 138 | export function testGenerateJS() { 139 | const parsed = parse(multiLine); 140 | const generated = generateJavascript(parsed); 141 | assert.strictEqual(generated, expectedOutputJS); 142 | } 143 | 144 | export function testGenerateOneLineJS() { 145 | const parsed = parse(oneLine); 146 | const generated = generateJavascript(parsed); 147 | assert.strictEqual(generated, expectedOutputJS); 148 | } 149 | 150 | export function testGenerateDerw() { 151 | const parsed = parse(multiLine); 152 | const generated = generateDerw(parsed); 153 | assert.strictEqual(generated, multiLine); 154 | } 155 | -------------------------------------------------------------------------------- /src/tests/union_untagged_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { 10 | FixedType, 11 | Module, 12 | StringValue, 13 | UnionUntaggedType, 14 | UnparsedBlock, 15 | } from "../types"; 16 | 17 | const rawOneLine = ` 18 | type Result = "Err" | "Ok" 19 | `.trim(); 20 | 21 | const oneLine = ` 22 | ${rawOneLine} 23 | `.trim(); 24 | 25 | const rawMultiLine = ` 26 | type Result = 27 | "Err" 28 | | "Ok" 29 | `.trim(); 30 | 31 | const multiLine = ` 32 | ${rawMultiLine} 33 | `.trim(); 34 | 35 | const expectedOutput = ` 36 | type Result = "Err" | "Ok"; 37 | `.trim(); 38 | 39 | const expectedOutputJS = ` 40 | `.trim(); 41 | 42 | export function testIntoBlocks() { 43 | assert.deepStrictEqual(intoBlocks(oneLine), [ 44 | UnparsedBlock("UnionUntaggedTypeBlock", 0, rawOneLine.split("\n")), 45 | ]); 46 | } 47 | 48 | export function testIntoBlocksMultiLine() { 49 | assert.deepStrictEqual(intoBlocks(multiLine), [ 50 | UnparsedBlock("UnionUntaggedTypeBlock", 0, rawMultiLine.split("\n")), 51 | ]); 52 | } 53 | 54 | export function testBlockKind() { 55 | const blocks = intoBlocks(oneLine); 56 | 57 | assert.deepStrictEqual( 58 | blocks.map((block) => blockKind(block.lines.join("\n"))), 59 | [ Ok("UnionUntaggedType") ] 60 | ); 61 | } 62 | 63 | export function testBlockKindMultiLine() { 64 | const blocks = intoBlocks(multiLine); 65 | 66 | assert.deepStrictEqual( 67 | blocks.map((block) => blockKind(block.lines.join("\n"))), 68 | [ Ok("UnionUntaggedType") ] 69 | ); 70 | } 71 | 72 | export function testParse() { 73 | assert.deepStrictEqual( 74 | parse(oneLine), 75 | Module( 76 | "main", 77 | [ 78 | UnionUntaggedType(FixedType("Result", [ ]), [ 79 | StringValue("Err"), 80 | StringValue("Ok"), 81 | ]), 82 | ], 83 | [ ] 84 | ) 85 | ); 86 | } 87 | 88 | export function testParseMultiLine() { 89 | assert.deepStrictEqual( 90 | parse(multiLine), 91 | Module( 92 | "main", 93 | [ 94 | UnionUntaggedType(FixedType("Result", [ ]), [ 95 | StringValue("Err"), 96 | StringValue("Ok"), 97 | ]), 98 | ], 99 | [ ] 100 | ) 101 | ); 102 | } 103 | 104 | export function testGenerate() { 105 | const parsed = parse(oneLine); 106 | 107 | assert.deepStrictEqual(generateTypescript(parsed), expectedOutput); 108 | } 109 | 110 | export function testGenerateMultiLine() { 111 | const parsed = parse(multiLine); 112 | 113 | assert.deepStrictEqual(generateTypescript(parsed), expectedOutput); 114 | } 115 | 116 | export function testCompile() { 117 | const parsed = parse(oneLine); 118 | const generated = generateTypescript(parsed); 119 | const compiled = compileTypescript(generated); 120 | 121 | assert.deepStrictEqual( 122 | compiled.kind, 123 | "Ok", 124 | (compiled.kind === "Err" && compiled.error.toString()) || "" 125 | ); 126 | } 127 | 128 | export function testCompileMultiLine() { 129 | const parsed = parse(multiLine); 130 | const generated = generateTypescript(parsed); 131 | const compiled = compileTypescript(generated); 132 | 133 | assert.deepStrictEqual( 134 | compiled.kind, 135 | "Ok", 136 | (compiled.kind === "Err" && compiled.error.toString()) || "" 137 | ); 138 | } 139 | 140 | export function testGenerateJS() { 141 | const parsed = parse(multiLine); 142 | const generated = generateJavascript(parsed); 143 | assert.strictEqual(generated, expectedOutputJS); 144 | } 145 | 146 | export function testGenerateOneLineJS() { 147 | const parsed = parse(oneLine); 148 | const generated = generateJavascript(parsed); 149 | assert.strictEqual(generated, expectedOutputJS); 150 | } 151 | 152 | export function testGenerateDerw() { 153 | const parsed = parse(multiLine); 154 | const generated = generateDerw(parsed); 155 | assert.strictEqual(generated, multiLine); 156 | } 157 | -------------------------------------------------------------------------------- /src/tests/values_test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "@eeue56/ts-assert"; 2 | import { Ok } from "@eeue56/ts-core/build/main/lib/result"; 3 | import { blockKind, intoBlocks } from "../Blocks"; 4 | import { compileTypescript } from "../compile"; 5 | import { generateDerw } from "../generators/Derw"; 6 | import { generateJavascript } from "../generators/Js"; 7 | import { generateTypescript } from "../generators/Ts"; 8 | import { parse } from "../parser"; 9 | import { Const, FixedType, Module, StringValue, UnparsedBlock } from "../types"; 10 | 11 | const oneLine = ` 12 | helloWorld: string 13 | helloWorld = "Hello world" 14 | `.trim(); 15 | 16 | const multiLine = ` 17 | helloWorld: string 18 | helloWorld = 19 | "Hello world" 20 | `.trim(); 21 | 22 | const expectedOutput = ` 23 | const helloWorld: string = "Hello world"; 24 | `.trim(); 25 | 26 | const expectedOutputJS = ` 27 | const helloWorld = "Hello world"; 28 | `.trim(); 29 | 30 | export function testIntoBlocks() { 31 | assert.deepStrictEqual(intoBlocks(oneLine), [ 32 | UnparsedBlock("ConstBlock", 0, oneLine.split("\n")), 33 | ]); 34 | } 35 | 36 | export function testIntoBlocksMultiLine() { 37 | assert.deepStrictEqual(intoBlocks(multiLine), [ 38 | UnparsedBlock("ConstBlock", 0, multiLine.split("\n")), 39 | ]); 40 | } 41 | 42 | export function testBlockKind() { 43 | assert.deepStrictEqual(blockKind(oneLine), Ok("Const")); 44 | } 45 | 46 | export function testBlockKindMultiLine() { 47 | assert.deepStrictEqual(blockKind(multiLine), Ok("Const")); 48 | } 49 | 50 | export function testParse() { 51 | assert.deepStrictEqual( 52 | parse(oneLine), 53 | Module( 54 | "main", 55 | [ 56 | Const( 57 | "helloWorld", 58 | FixedType("string", [ ]), 59 | [ ], 60 | StringValue("Hello world") 61 | ), 62 | ], 63 | [ ] 64 | ) 65 | ); 66 | } 67 | 68 | export function testParseMultiLine() { 69 | assert.deepStrictEqual( 70 | parse(multiLine), 71 | Module( 72 | "main", 73 | [ 74 | Const( 75 | "helloWorld", 76 | FixedType("string", [ ]), 77 | [ ], 78 | StringValue("Hello world") 79 | ), 80 | ], 81 | [ ] 82 | ) 83 | ); 84 | } 85 | 86 | export function testGenerate() { 87 | const parsed = parse(multiLine); 88 | const generated = generateTypescript(parsed); 89 | assert.strictEqual(generated, expectedOutput); 90 | } 91 | 92 | export function testGenerateOneLine() { 93 | const parsed = parse(oneLine); 94 | const generated = generateTypescript(parsed); 95 | assert.strictEqual(generated, expectedOutput); 96 | } 97 | 98 | export function testCompile() { 99 | const parsed = parse(oneLine); 100 | const generated = generateTypescript(parsed); 101 | const compiled = compileTypescript(generated); 102 | 103 | assert.deepStrictEqual( 104 | compiled.kind, 105 | "Ok", 106 | (compiled.kind === "Err" && compiled.error.toString()) || "" 107 | ); 108 | } 109 | 110 | export function testCompileMultiLine() { 111 | const parsed = parse(multiLine); 112 | const generated = generateTypescript(parsed); 113 | const compiled = compileTypescript(generated); 114 | 115 | assert.deepStrictEqual( 116 | compiled.kind, 117 | "Ok", 118 | (compiled.kind === "Err" && compiled.error.toString()) || "" 119 | ); 120 | } 121 | 122 | export function testGenerateJS() { 123 | const parsed = parse(multiLine); 124 | const generated = generateJavascript(parsed); 125 | assert.strictEqual(generated, expectedOutputJS); 126 | } 127 | 128 | export function testGenerateOneLineJS() { 129 | const parsed = parse(oneLine); 130 | const generated = generateJavascript(parsed); 131 | assert.strictEqual(generated, expectedOutputJS); 132 | } 133 | 134 | export function testGenerateDerw() { 135 | const parsed = parse(multiLine); 136 | const generated = generateDerw(parsed); 137 | assert.strictEqual(generated, multiLine); 138 | } 139 | -------------------------------------------------------------------------------- /test_on_chromebook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | for i in {0..250..30} 4 | do 5 | npx @eeue56/bach -- --in-chunks 30 --chunk-start $i 6 | done -------------------------------------------------------------------------------- /translation_progress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ts_files=$(find src | grep -v '[[:upper:]]' | grep -v src/tests | grep .ts) 4 | 5 | generated_ts_files=$(find src | grep '[[:upper:]]' | grep -v src/tests | grep .ts) 6 | 7 | derw_files=$(find src | grep '[[:upper:]]' | grep -v src/tests | grep .derw) 8 | 9 | cloc $ts_files $derw_files --force-lang=elm,derw | sed s/'Elm '/Derw/g 10 | # cloc $generated_ts_files 11 | # cloc $derw_files --force-lang=elm,derw | sed s/Elm/Derw/g --------------------------------------------------------------------------------