master d7647fbaa7b1 cached
52 files
168.9 KB
45.3k tokens
114 symbols
1 requests
Download .txt
Repository: kraklin/elm-debug-transformer
Branch: master
Commit: d7647fbaa7b1
Files: 52
Total size: 168.9 KB

Directory structure:
gitextract_lexnr1a5/

├── .gitignore
├── .mocharc.json
├── .npmignore
├── .prettierrc
├── .tool-versions
├── CHANGELOG.md
├── LICENSE
├── README.md
├── example/
│   ├── .gitignore
│   ├── .tool-versions
│   ├── README.md
│   ├── elm-tooling.json
│   ├── elm.json
│   ├── index.html
│   ├── package.json
│   ├── src/
│   │   ├── Main.elm
│   │   └── index.js
│   └── vite.config.js
├── package.json
├── src/
│   ├── CommonTypes.ts
│   ├── JsonML.ts
│   ├── elm-debug-parser.ts
│   ├── elm-debug.pegjs
│   ├── formatters/
│   │   ├── JsonMLFormatter.ts
│   │   ├── SimpleFormatter.ts
│   │   └── elements/
│   │       ├── BooleanElement.ts
│   │       ├── BytesElement.ts
│   │       ├── CustomTypeElement.ts
│   │       ├── DebugElement.ts
│   │       ├── DictElement.ts
│   │       ├── Elements.ts
│   │       ├── EllipsisElement.ts
│   │       ├── FilesElement.ts
│   │       ├── InternalsElement.ts
│   │       ├── ListElement.ts
│   │       ├── NumberElement.ts
│   │       ├── RecordElement.ts
│   │       ├── StringElement.ts
│   │       ├── Styles.ts
│   │       ├── TupleElement.ts
│   │       └── TypeElement.ts
│   ├── index.ts
│   └── peg-file.d.ts
├── test/
│   ├── builders.ts
│   ├── jsonml-formatter.spec.ts
│   ├── jsonml.spec.ts
│   ├── parser.spec.ts
│   └── simple-formatter.spec.ts
├── tsconfig.json
├── tslint.json
├── vite.config.js
└── vitest.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules
dist

yarn.lock
yarn-error.lock
yarn-error.log
 
.DS_Store


================================================
FILE: .mocharc.json
================================================
{
  "diff": true,
  "extension": ["ts"],
  "package": "./package.json",
  "reporter": "spec",
  "slow": 75,
  "timeout": 2000,
  "ui": "bdd",
  "watch-files": ["test/**/*.ts"]
}


================================================
FILE: .npmignore
================================================
elm-app/
node_modules/
src/
test/
img/

webpack.config.js
.babelrc


================================================
FILE: .prettierrc
================================================
{
  "trailingComma": "es5",
  "tabWidth": 4,
  "semi": true,
  "singleQuote": true
}


================================================
FILE: .tool-versions
================================================
nodejs 20.5.0


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- switch to vitest

## [1.2.1] - 2023-08-05

### Removed

- Wrongly placed `prettier` that was set as dependency instead of dev-dependency has been Removed

## [1.2.0] - 2023-08-04

### Added

- add support for Custom formatters in Firefox

### Changed

- drop building with webpack in favour of vite
- clean out the code to get pass the checks from new version of TS


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Tomáš Látal

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<div align="center">
    <h1>Elm Debug Transformer</h1>
    <a href="https://badge.fury.io/js/elm-debug-transformer">
      <img src="https://badge.fury.io/js/elm-debug-transformer.svg" alt="version">
    </a>
    <p>Transform Elm Debug.log output into nice log object with custom formatters</p>
</div>

The standard Elm `Debug.log` console output:

![Elm Debug.log in console without formatter](img/ugly_output.png)

and the same output with this package 

![Elm Debug.log with this package and custom formatter enabled in Chrome](img/nice_output.png)

The main module exposes `register()` function that replaces your `console.log()` and try to parse each incoming message with Elm parser. If it fails, it would pass the original message.


## Installation

Just install this module with Yarn:

```
yarn add -D elm-debug-transformer
```

or NPM:

```
npm install elm-debug-transformer
```

### Get it directly from CDN

[Roman Potashow](https://github.com/justgook) pointed out on Elm Slack that you can use the NPM package directly without the need of installing it.

```
<script src="https://unpkg.com/elm-debug-transformer@<VERSION>/dist/elm-console-debug.js"></script>

<script>ElmConsoleDebug.register()</script>
```

#### Bookmarklet
[Ale Grilli](https://github.com/agj) created a bookmarklet for enabling transformer on any page. Just add this to your bookmark
URL and run it on the pages you want the transformer to work on.


Bookmarklet below is for version 1.2.1

```
javascript:(function()%7Bconsole.info(%22Loading%20Elm%20Debug%20Transformer%E2%80%A6%22)%3Bimport(%22%2F%2Funpkg.com%2Felm-debug-transformer%401.2.1%2Fdist%2Findex.mjs%22).then((ElmDebugger)%20%3D%3E%20%7BElmDebugger.register()%3Bconsole.info(%22Elm%20Debug%20Transformer%20loaded!%22)%3B%7D)%7D)()
```

## Usage

There is a nice summary of the usage in Alex Korban's article [Get improved Debug.log output in the browser console](https://korban.net/posts/elm/2019-07-02-improved-debug-log-output-browser-console/)

### `register()`
Register the console debugger in your main JS file before you initialize Elm application:

```
import * as ElmDebugger from 'elm-debug-transformer';

ElmDebugger.register();

// rest of application
```

Here's a sample HTML for your reference:
```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Elm App</title>
</head>
<body>
  <main></main>
  <script src="https://unpkg.com/elm-debug-transformer@latest/dist/elm-console-debug.js"></script>
  <script>ElmConsoleDebug.register({simple_mode: false, debug: false, limit: 10000});</script>
  <script src="elm.js"></script>
  <script>
    var app = Elm.Main.init({ node: document.querySelector('main') });
  </script>
</body>
</html>
```

### `parse()`
Since version 1.1.0 the parser function is exposed as well, so you can use it to parse Debug.log output into JSON structure and work with it later as you wish.

```
import {parse} ElmDebugger from 'elm-debug-transformer';

const parsedValue = parse("debug tag: [1,2,3]");
```


### Enable custom formatters in Chrome and Firefox dev tools
Available in Chrome 47 and higher and in Firefox 116 and higher.

The output object is kind of chatty right now (it carries information about parsed type etc. - less verbose version is worked on right now). 

If your browser is Firefox 116 and above or if it have have Chrome dev tools, you can enable custom formatters so you get less noise and more nice output.

#### How to enable custom formatters in Chrome:
  - Open DevTools
  - Go to Preferences ("cog wheel" icon in the upper right corner of DevTools > Preferences > Console)
  - Check-in "Enable custom formatters"
  - Close DevTools
  - Open DevTools

#### How to enable custom formatters in Firefox:
  - Open DevTools
  - Go to Settings ("three dots" icon in the upper right corner of DevTools > Settings F1 > Advanced settings)
  - Check-in "Enable custom formatters"
  - Close DevTools
  - Open DevTools

Note: You might need to refresh the page first time you open Console panel with existing logs - custom formatters are applied only to newly printed console messages.

That way the `Debug.log` would output simpler JS object without `type` information. `Tuple`, `Set`, `Array` and `List` would become arrays and `Dict` would become JS object with keys and values.

### Options
Options object can be provided to `register` function:

```
import * as ElmDebugger from 'elm-debug-transformer';

ElmDebugger.register({simple_mode: true, debug: false, limit: 10000});
```

| parameter     | type    | description                                                                                                                               | default value   |
|---------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------|----------------:|
| `limit`       | number  | number of message characters after which the parser won't parse the message. (Helpful for bypass the parsing of large datastructures)     | `1 000 000`     |
| `debug`       | boolean | include original message and parser error with the message                                                                                | `false`         |
| `simple_mode` | boolean | force output to be in simple object format                                                                                                | `false`         |
| `theme` | `light`|`dark` | sets theme colour of debug output. It is useful for switching on the dark mode of devtools.                                                                                                | `light`         |



## Credits

This would probably not see the light of the day without [Matt Zeunert](https://github.com/mattzeunert) and his [blogpost](https://www.mattzeunert.com/2016/02/19/custom-chrome-devtools-object-formatters.html) about writing custom formatters. Thank you!



================================================
FILE: example/.gitignore
================================================
# Distribution
build/

# elm-package generated files
elm-stuff

# elm-repl generated files
repl-temp-*

# Dependency directories
node_modules

# Desktop Services Store on macOS
.DS_Store


================================================
FILE: example/.tool-versions
================================================
nodejs 20.5.0


================================================
FILE: example/README.md
================================================
# Tesing Elm App

First build the elm-debug-parser from root with

```
yarn start
```


================================================
FILE: example/elm-tooling.json
================================================
{
    "tools": {
        "elm": "0.19.1",
        "elm-format": "0.8.7",
        "elm-json": "0.2.13"
    }
}


================================================
FILE: example/elm.json
================================================
{
    "type": "application",
    "source-directories": [
        "src"
    ],
    "elm-version": "0.19.1",
    "dependencies": {
        "direct": {
            "elm/browser": "1.0.0",
            "elm/core": "1.0.0",
            "elm/html": "1.0.0",
            "turboMaCk/any-dict": "1.1.0"
        },
        "indirect": {
            "elm/json": "1.0.0",
            "elm/time": "1.0.0",
            "elm/url": "1.0.0",
            "elm/virtual-dom": "1.0.0"
        }
    },
    "test-dependencies": {
        "direct": {
            "elm-explorations/test": "1.0.0"
        },
        "indirect": {
            "elm/random": "1.0.0"
        }
    }
}


================================================
FILE: example/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <title>Elm Debugger testing app</title>
</head>
<body>
    <noscript>
        You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
    <script type="module" src="./src/index.js"></script>
</body>
</html>


================================================
FILE: example/package.json
================================================
{
  "name": "elm-app",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "start": "vite"
  },
  "devDependencies": {
    "elm-debug-transformer": "^1.2.1",
    "elm-tooling": "^1.14.0",
    "vite": "^4.4.8",
    "vite-plugin-elm": "^2.8.0",
    "vite-plugin-elm-debug-transformer": "^0.0.1"
  }
}


================================================
FILE: example/src/Main.elm
================================================
module Main exposing (Model, Msg(..), init, main, update, view)

import Array exposing (Array)
import Browser
import Dict exposing (Dict)
import Html exposing (Html, div, h1, img, text)
import Html.Attributes exposing (src, style)
import Html.Events exposing (onClick)
import Set exposing (Set)



---- MODEL ----


type CustomType
    = None
    | Some String Int
    | Recursive CustomType
    | Complex (List ( Int, CustomType ))
    | With_Underscore1


type alias User =
    { name : String, age : Int }


type CustomRecord
    = WithRecord (List User)


type Tree a
    = Leaf a
    | Node (Tree a) (Tree a)


type alias Model =
    { array : Array Int
    , set : Set String
    , dict : Dict Int String
    , dictWithTuples : Dict ( Int, String, Int ) String
    , bools : ( Bool, Bool )
    , binaryTree : Tree CustomType
    , custom : CustomType
    , customRecord : CustomRecord
    , tuple : ( Int, Int )
    , triplet : ( Int, String, Int )
    , nonEmptyList : ( Int, List Int )
    , complexTuple : ( Int, ( String, ( String, Int ) ) )
    , int : Int
    , unit : ()
    , string : String
    , list : List (Maybe (List String))
    , listSingleton : List String
    , listOfLists : List (List (List String))
    , function : Int -> String
    }


init : ( Model, Cmd Msg )
init =
    ( { array = Array.fromList [ 1, 2, 3, 4, 5678, 3464637, 893145, -29 ]
      , set = Set.fromList [ "a", "b", "Some really long string with some nonsense" ]
      , bools = ( True, False )
      , dict = Dict.fromList [ ( 1, "a" ), ( 2, "b" ), ( 234, "String longer than one char" ) ]
      , dictWithTuples = Dict.fromList [ ( ( 0, "b", 1 ), "a" ), ( ( 0, "c", 1 ), "b" ), ( ( 4, "d", 1 ), "String longer than one char" ) ]
      , custom = Complex [ ( 1, Some "text" 1 ), ( 2, Recursive (Complex []) ), ( 3, None ), ( 4, With_Underscore1 ) ]
      , tuple = ( 1, 2 )
      , triplet = ( 1, "b", 1 )
      , complexTuple = ( 1, ( "longer string", ( "much longer string", 1 ) ) )
      , int = 123
      , string = "Some string"
      , list = [ Nothing, Just [ "String" ], Nothing, Nothing ]
      , listSingleton = [ "Singleton" ]
      , listOfLists = [ [ [ "a", "b" ], [ "c", "d" ] ], [ [ "e", "f" ], [ "g", "h" ] ] ]
      , nonEmptyList = ( 1, [] )
      , function = String.fromInt
      , binaryTree = Node (Node (Leaf None) (Leaf None)) (Node (Leaf None) (Leaf None))
      , unit = ()
      , customRecord = WithRecord [ { name = "Joe", age = 21 } ]
      }
    , Cmd.none
    )



---- UPDATE ----


type Msg
    = DebugOutputClicked


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    let
        _ =
            Debug.log "Debug with 2 numbers 7 chars like !_+))($ and emojis 💪 : " model
    in
    ( model, Cmd.none )



---- VIEW ----


view : Model -> Html Msg
view model =
    div [ style "text-align" "center" ]
        [ img [ src "/logo.svg", style "max-width" "200px" ] []
        , h1 [] [ text "Your Elm App is working!" ]
        , Html.button [ onClick DebugOutputClicked ] [ Html.text "Debug Model to console" ]
        ]



---- PROGRAM ----


main : Program () Model Msg
main =
    Browser.element
        { view = view
        , init = \_ -> init
        , update = update
        , subscriptions = always Sub.none
        }


================================================
FILE: example/src/index.js
================================================
import { Elm } from './Main.elm';
//import * as ElmDebugger from 'elm-debug-transformer';

//ElmDebugger.register({simple_mode: false, debug: true, limit: 10000});

Elm.Main.init({
    node: document.getElementById('root'),
});


================================================
FILE: example/vite.config.js
================================================
import { defineConfig } from 'vite'
import plugin from 'vite-plugin-elm'
import transformer from 'vite-plugin-elm-debug-transformer'

export default defineConfig({
  plugins: [plugin(), transformer({theme: 'dark'}) ]
})


================================================
FILE: package.json
================================================
{
  "name": "elm-debug-transformer",
  "version": "1.2.1",
  "description": "Transform Elm Debug.log output into nice log object with custom formatter",
  "author": "Tomas Latal <tom@tomaslatal.cz>",
  "repository": "https://github.com/kraklin/elm-debug-transformer",
  "keywords": [
    "elm",
    "debug",
    "console",
    "parser",
    "formatter"
  ],
  "main": "./dist/index.js",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    }
  },
  "module": "./dist/index.mjs",
  "typings": "./dist/index.d.ts",
  "files": [
    "dist"
  ],
  "license": "MIT",
  "private": false,
  "scripts": {
    "pegjs": "pegjs --plugin ./node_modules/ts-pegjs/src/tspegjs -o src/elm-debug-parser.ts src/elm-debug.pegjs",
    "test": "vitest run",
    "build": "npm run pegjs && tsc && vite build",
    "build:watch": "npm run pegjs && tsc && vite build --watch",
    "prepublishOnly": "npm run test && npm run build"
  },
  "devDependencies": {
    "@rollup/plugin-typescript": "^11.1.2",
    "@types/lodash": "^4.14.134",
    "@types/node": "^20.4.7",
    "jsdom": "^22.1.0",
    "lodash": "^4.17.15",
    "pegjs": "^0.10.0",
    "prettier": "^3.0.1",
    "rollup-plugin-typescript-paths": "^1.4.0",
    "ts-pegjs": "^0.2.5",
    "tslib": "^2.6.1",
    "typescript": "^5.1.6",
    "vite": "^4.4.8",
    "vite-plugin-peggy-loader": "^1.0.1",
    "vitest": "^0.34.1"
  },
  "prettier": {
    "trailingComma": "es5",
    "tabWidth": 4,
    "semi": true,
    "singleQuote": true
  }
}


================================================
FILE: src/CommonTypes.ts
================================================
import JsonML from './JsonML';

export interface IFormatter {
    format(obj: IElmDebugValue): any;
}

export interface IJsonMLFormatter {
    theme: ITheme;
    handleHeader(obj: ElmDebugValueType, config?: IConfig): JsonML;
    handleBody(obj: ElmDebugValueType, config?: IConfig): JsonML | null;
}

export interface IDevToolsFormatter {
    header(obj: ElmDebugValueType, config?: IConfig): any[] | null;
    hasBody(obj: ElmDebugValueType, config?: IConfig): boolean;
    body(obj: ElmDebugValueType, config?: IConfig): any[] | null;
}

export interface IFormatterElement {
    header(config?: IConfig): JsonML;
    body?(config?: IConfig): JsonML | null;
}

export type IThemeOption = "dark" | "light"

export interface ITheme {
  booleanStyle: string;
  stringStyle: string;
  numberStyle: string;
  debugTagStyle: string;
  greyedStyle: string;
  greyedItalicsStyle: string;
  customTypeNameStyle: string;
  typeNameStyle: string;
  dataStructureNameStyle: string;
  keyElementStyle: string;
  bytesStyle: string;
  expandableBorderStyle: string;
  elmLogoElementStyle: string;
}

export interface IConfig {
    elmFormat: boolean;
    level: number;
    misc?: any;
    key?: JsonML;
}

export type ElmDebugValueType =
    | IElmDebugValue
    | IElmDebugCustomValue
    | IElmDebugRecordValue
    | IElmDebugListValue
    | IElmDebugDictValue
    | IElmDebugTypeValueType
    | IElmDebugNumberValue
    | IElmDebugStringValue
    | IElmDebugBoolValue;

export interface IElmDebugValue {
    type: string;
    name?: string;
    value?: ElmDebugValueType;
}

export interface IElmDebugCustomValue {
    type: string;
    name: string;
    value: ElmDebugValueType[];
}

export interface IElmDebugTypeValueType {
    type: string;
    name: string;
}

export interface IElmDebugListValue {
    type: string;
    value: ElmDebugValueType[];
}

export interface IElmDebugRecordValue {
    type: string;
    value: { [key: string]: ElmDebugValueType };
}

export interface IElmDebugDictValue {
    type: string;
    value: Array<{ key: ElmDebugValueType; value: ElmDebugValueType }>;
}

export interface IElmDebugNumberValue {
    type: string;
    value: number | string;
}

export interface IElmDebugStringValue {
    type: string;
    value: string;
}

export interface IElmDebugBoolValue {
    type: string;
    value: boolean;
}

export function isElmValue(value: any): value is IElmDebugValue {
    return (value as IElmDebugValue).type !== undefined;
}

export function isElmCustomValue(value: any): value is IElmDebugCustomValue {
    return value.type === 'Custom';
}

export function isElmRecordValue(value: any): value is IElmDebugRecordValue {
    return value.type === 'Record';
}

export function isElmListValue(value: any): value is IElmDebugListValue {
    return (
        value.type === 'List' ||
        value.type === 'Array' ||
        value.type === 'Set' ||
        value.type === 'Tuple'
    );
}

export function isElmNumberType(value: any): value is IElmDebugNumberValue {
    return value.type === 'Number';
}

export function isElmTypeValue(value: any): value is IElmDebugTypeValueType {
    return value.type === 'Type';
}

export function isElmDictValue(value: any): value is IElmDebugDictValue {
    return value.type === 'Dict';
}


================================================
FILE: src/JsonML.ts
================================================
import { IConfig } from './CommonTypes';
export type TagName = 'div' | 'span';

export default class JSONMLElement {
    private attributes: { [key: string]: any };
    private jsonML: any[];

    constructor(tagName: TagName) {
        this.attributes = {};
        this.jsonML = [tagName, this.attributes];
    }

    public toJSONML(): any {
        return this.jsonML;
    }

    public withChild(element: JSONMLElement) {
        this.jsonML.push(element.toJSONML());
        return this;
    }

    public withChildren(elements: JSONMLElement[]) {
        const children = elements.map(child => child.toJSONML());
        this.jsonML = this.jsonML.concat(children);
        return this;
    }

    public withObject(
        key: JSONMLElement,
        object: any,
        config: IConfig = { elmFormat: true, level: 0 }
    ) {
        config.key = key;
        this.jsonML.push(['object', { object, config }]);
        return this;
    }

    public withStyle(style: string) {
        this.attributes.style =
            this.attributes.style === undefined
                ? style
                : this.attributes.style + style;
        return this;
    }

    public withAttribute(key: string, value: any) {
        this.attributes[key] = value;
        return this;
    }

    public withText(value: any) {
        this.jsonML.push(value + '');
        return this;
    }

    public toStr(): string {
        return this.toStrInner(this.jsonML);
    }

    private toStrInner(jsonML: any[]): string {
        return jsonML
            .map((item, index) => {
                if (index < 2) {
                    return '';
                } else if (typeof item === 'string') {
                    return item;
                } else if (item instanceof Array) {
                    return this.toStrInner(item);
                } else {
                    return '';
                }
            })
            .join('');
    }
}


================================================
FILE: src/elm-debug-parser.ts
================================================
// tslint:disable:only-arrow-functions
// tslint:disable:object-literal-shorthand
// tslint:disable:trailing-comma
// tslint:disable:object-literal-sort-keys
// tslint:disable:one-variable-per-declaration
// tslint:disable:max-line-length
// tslint:disable:no-consecutive-blank-lines
// tslint:disable:align


// Generated by PEG.js v. 0.10.0 (ts-pegjs plugin v. 0.2.7 )
//
// https://pegjs.org/   https://github.com/metadevpro/ts-pegjs

"use strict";

export interface IFilePosition {
  offset: number;
  line: number;
  column: number;
}

export interface IFileRange {
  start: IFilePosition;
  end: IFilePosition;
}

export interface ILiteralExpectation {
  type: "literal";
  text: string;
  ignoreCase: boolean;
}

export interface IClassParts extends Array<string | IClassParts> {}

export interface IClassExpectation {
  type: "class";
  parts: IClassParts;
  inverted: boolean;
  ignoreCase: boolean;
}

export interface IAnyExpectation {
  type: "any";
}

export interface IEndExpectation {
  type: "end";
}

export interface IOtherExpectation {
  type: "other";
  description: string;
}

export type Expectation = ILiteralExpectation | IClassExpectation | IAnyExpectation | IEndExpectation | IOtherExpectation;

export class SyntaxError extends Error {
  public static buildMessage(expected: Expectation[], found: string | null) {
    function hex(ch: string): string {
      return ch.charCodeAt(0).toString(16).toUpperCase();
    }

    function literalEscape(s: string): string {
      return s
        .replace(/\\/g, "\\\\")
        .replace(/"/g,  "\\\"")
        .replace(/\0/g, "\\0")
        .replace(/\t/g, "\\t")
        .replace(/\n/g, "\\n")
        .replace(/\r/g, "\\r")
        .replace(/[\x00-\x0F]/g,            (ch) => "\\x0" + hex(ch) )
        .replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => "\\x"  + hex(ch) );
    }

    function classEscape(s: string): string {
      return s
        .replace(/\\/g, "\\\\")
        .replace(/\]/g, "\\]")
        .replace(/\^/g, "\\^")
        .replace(/-/g,  "\\-")
        .replace(/\0/g, "\\0")
        .replace(/\t/g, "\\t")
        .replace(/\n/g, "\\n")
        .replace(/\r/g, "\\r")
        .replace(/[\x00-\x0F]/g,            (ch) => "\\x0" + hex(ch) )
        .replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => "\\x"  + hex(ch) );
    }

    function describeExpectation(expectation: Expectation) {
      switch (expectation.type) {
        case "literal":
          return "\"" + literalEscape(expectation.text) + "\"";
        case "class":
          const escapedParts = expectation.parts.map((part) => {
            return Array.isArray(part)
              ? classEscape(part[0] as string) + "-" + classEscape(part[1] as string)
              : classEscape(part);
          });

          return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
        case "any":
          return "any character";
        case "end":
          return "end of input";
        case "other":
          return expectation.description;
      }
    }

    function describeExpected(expected1: Expectation[]) {
      const descriptions = expected1.map(describeExpectation);
      let i: number;
      let j: number;

      descriptions.sort();

      if (descriptions.length > 0) {
        for (i = 1, j = 1; i < descriptions.length; i++) {
          if (descriptions[i - 1] !== descriptions[i]) {
            descriptions[j] = descriptions[i];
            j++;
          }
        }
        descriptions.length = j;
      }

      switch (descriptions.length) {
        case 1:
          return descriptions[0];

        case 2:
          return descriptions[0] + " or " + descriptions[1];

        default:
          return descriptions.slice(0, -1).join(", ")
            + ", or "
            + descriptions[descriptions.length - 1];
      }
    }

    function describeFound(found1: string | null) {
      return found1 ? "\"" + literalEscape(found1) + "\"" : "end of input";
    }

    return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
  }

  public message: string;
  public expected: Expectation[];
  public found: string | null;
  public location: IFileRange;
  public name: string;

  constructor(message: string, expected: Expectation[], found: string | null, location: IFileRange) {
    super();
    this.message = message;
    this.expected = expected;
    this.found = found;
    this.location = location;
    this.name = "SyntaxError";

    if (typeof (Error as any).captureStackTrace === "function") {
      (Error as any).captureStackTrace(this, SyntaxError);
    }
  }
}

function peg$parse(input: string, options?: IParseOptions) {
  options = options !== undefined ? options : {};

  const peg$FAILED: Readonly<{}> = {};

  const peg$startRuleFunctions: {[id: string]: any} = { DebugString: peg$parseDebugString };
  let peg$startRuleFunction: () => any = peg$parseDebugString;

  const peg$c0 = ":";
  const peg$c1 = peg$literalExpectation(":", false);
  const peg$c2 = function(tag: any, value: any): any {return {type: "ElmDebug", name: tag, value: value};};
  const peg$c3 = function(value: any): any {return {type: "ElmDebug", name: "", value: value};};
  const peg$c4 = "{}";
  const peg$c5 = peg$literalExpectation("{}", false);
  const peg$c6 = function(): any {return {type: "Record", value: {}};};
  const peg$c7 = "{ ";
  const peg$c8 = peg$literalExpectation("{ ", false);
  const peg$c9 = " = ";
  const peg$c10 = peg$literalExpectation(" = ", false);
  const peg$c11 = " }";
  const peg$c12 = peg$literalExpectation(" }", false);
  const peg$c13 = function(key: any, value: any): any {return {type: "Record", value: {[key]: value}}};
  const peg$c14 = ", ";
  const peg$c15 = peg$literalExpectation(", ", false);
  const peg$c16 = function(key: any, value: any, tag: any, otherVal: any): any {return {[tag]: otherVal};};
  const peg$c17 = function(key: any, value: any, values: any): any { var composed = [{[key]: value},...values].reduce((item, obj) => {return {...item,...obj} },{}); return {type: "Record", value: composed}};
  const peg$c18 = "Dict.fromList ";
  const peg$c19 = peg$literalExpectation("Dict.fromList ", false);
  const peg$c20 = function(values: any): any {return {type: "Dict", value: values.map((tuple) => { return {key: tuple.value[0], value: tuple.value[1]};})}};
  const peg$c21 = "Set.fromList ";
  const peg$c22 = peg$literalExpectation("Set.fromList ", false);
  const peg$c23 = function(values: any): any {return {type: "Set", value: values};};
  const peg$c24 = "Array.fromList ";
  const peg$c25 = peg$literalExpectation("Array.fromList ", false);
  const peg$c26 = function(values: any): any {return {type: "Array", value: values};};
  const peg$c27 = "()";
  const peg$c28 = peg$literalExpectation("()", false);
  const peg$c29 = function(): any {return {type:"Unit"}};
  const peg$c30 = "(";
  const peg$c31 = peg$literalExpectation("(", false);
  const peg$c32 = ",";
  const peg$c33 = peg$literalExpectation(",", false);
  const peg$c34 = function(head: any, item: any): any {return item;};
  const peg$c35 = ")";
  const peg$c36 = peg$literalExpectation(")", false);
  const peg$c37 = function(head: any, others: any): any {return {type: "Tuple", value: [head,...others]};};
  const peg$c38 = function(customType: any): any {return customType;};
  const peg$c39 = function(mainType: any, value: any): any {return value;};
  const peg$c40 = function(mainType: any, values: any): any {return {type: "Custom", name: mainType.name, value: values};};
  const peg$c41 = function(mainType: any, customType: any): any {return {type: "Custom", name: mainType.name, value: customType};};
  const peg$c42 = function(list: any): any {return {type: "List", value: list};};
  const peg$c43 = /^[0-9.]/;
  const peg$c44 = peg$classExpectation([["0", "9"], "."], false, false);
  const peg$c45 = function(digits: any): any {return {type: "Number", value: parseFloat(toStr(digits))};};
  const peg$c46 = "-";
  const peg$c47 = peg$literalExpectation("-", false);
  const peg$c48 = function(digits: any): any {return {type: "Number", value: parseFloat("-"+toStr(digits))};};
  const peg$c49 = "NaN";
  const peg$c50 = peg$literalExpectation("NaN", false);
  const peg$c51 = function(): any {return {type: "Number", value: "NaN"};};
  const peg$c52 = "Infinity";
  const peg$c53 = peg$literalExpectation("Infinity", false);
  const peg$c54 = function(): any {return {type: "Number", value: "Infinity"};};
  const peg$c55 = "-Infinity";
  const peg$c56 = peg$literalExpectation("-Infinity", false);
  const peg$c57 = function(): any {return {type: "Number", value: "-Infinity"};};
  const peg$c58 = "True";
  const peg$c59 = peg$literalExpectation("True", false);
  const peg$c60 = function(): any {return {type: "Boolean", value: true};};
  const peg$c61 = "False";
  const peg$c62 = peg$literalExpectation("False", false);
  const peg$c63 = function(): any {return {type: "Boolean", value: false};};
  const peg$c64 = "<function>";
  const peg$c65 = peg$literalExpectation("<function>", false);
  const peg$c66 = function(): any {return {type: "Function"};};
  const peg$c67 = "<internals>";
  const peg$c68 = peg$literalExpectation("<internals>", false);
  const peg$c69 = function(): any {return {type: "Internals"};};
  const peg$c70 = "<";
  const peg$c71 = peg$literalExpectation("<", false);
  const peg$c72 = /^[0-9]/;
  const peg$c73 = peg$classExpectation([["0", "9"]], false, false);
  const peg$c74 = " bytes>";
  const peg$c75 = peg$literalExpectation(" bytes>", false);
  const peg$c76 = function(digits: any): any {return {type: "Bytes", value: parseInt(toStr(digits), 10)};};
  const peg$c77 = "\"";
  const peg$c78 = peg$literalExpectation("\"", false);
  const peg$c79 = "\\";
  const peg$c80 = peg$literalExpectation("\\", false);
  const peg$c81 = ">";
  const peg$c82 = peg$literalExpectation(">", false);
  const peg$c83 = peg$anyExpectation();
  const peg$c84 = function(char: any): any { return char; };
  const peg$c85 = function(chars: any): any {return { type: "File", value: chars.join('') };};
  const peg$c86 = /^[a-zA-Z0-9_]/;
  const peg$c87 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"], "_"], false, false);
  const peg$c88 = function(chars: any): any {return toStr(chars);};
  const peg$c89 = /^[A-Z]/;
  const peg$c90 = peg$classExpectation([["A", "Z"]], false, false);
  const peg$c91 = function(type: any): any {return {type: "Type", name: toStr(flat(type))};};
  const peg$c92 = function(tag: any): any { return toStr(tag); };
  const peg$c93 = function(chars: any): any { return {type: "String", value: chars.join('')}; };
  const peg$c94 = "'";
  const peg$c95 = peg$literalExpectation("'", false);
  const peg$c96 = function(chars: any): any { return { type: "String", value: chars.join('')}; };
  const peg$c97 = "[]";
  const peg$c98 = peg$literalExpectation("[]", false);
  const peg$c99 = function(): any {return [];};
  const peg$c100 = "[";
  const peg$c101 = peg$literalExpectation("[", false);
  const peg$c102 = "]";
  const peg$c103 = peg$literalExpectation("]", false);
  const peg$c104 = function(singleton: any): any {return singleton;};
  const peg$c105 = function(list: any): any {return [list];};
  const peg$c106 = function(head: any, value: any): any {return value;};
  const peg$c107 = function(head: any, tail: any): any {return [head, ...tail]};
  const peg$c108 = function(sequence: any): any { return sequence; };
  const peg$c109 = "b";
  const peg$c110 = peg$literalExpectation("b", false);
  const peg$c111 = function(): any { return "\b";   };
  const peg$c112 = "f";
  const peg$c113 = peg$literalExpectation("f", false);
  const peg$c114 = function(): any { return "\f";   };
  const peg$c115 = "n";
  const peg$c116 = peg$literalExpectation("n", false);
  const peg$c117 = function(): any { return "\n";   };
  const peg$c118 = "r";
  const peg$c119 = peg$literalExpectation("r", false);
  const peg$c120 = function(): any { return "\r";   };
  const peg$c121 = "t";
  const peg$c122 = peg$literalExpectation("t", false);
  const peg$c123 = function(): any { return "\t";   };
  const peg$c124 = "v";
  const peg$c125 = peg$literalExpectation("v", false);
  const peg$c126 = function(): any { return "\x0B"; };
  const peg$c127 = peg$otherExpectation("whitespace");
  const peg$c128 = /^[ \t\n\r]/;
  const peg$c129 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false);

  let peg$currPos = 0;
  let peg$savedPos = 0;
  const peg$posDetailsCache = [{ line: 1, column: 1 }];
  let peg$maxFailPos = 0;
  let peg$maxFailExpected: Expectation[] = [];
  let peg$silentFails = 0;

  let peg$result;

  if (options.startRule !== undefined) {
    if (!(options.startRule in peg$startRuleFunctions)) {
      throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
    }

    peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
  }

  function text(): string {
    return input.substring(peg$savedPos, peg$currPos);
  }

  function location(): IFileRange {
    return peg$computeLocation(peg$savedPos, peg$currPos);
  }

  function expected(description: string, location1?: IFileRange) {
    location1 = location1 !== undefined
      ? location1
      : peg$computeLocation(peg$savedPos, peg$currPos);

    throw peg$buildStructuredError(
      [peg$otherExpectation(description)],
      input.substring(peg$savedPos, peg$currPos),
      location1
    );
  }

  function error(message: string, location1?: IFileRange) {
    location1 = location1 !== undefined
      ? location1
      : peg$computeLocation(peg$savedPos, peg$currPos);

    throw peg$buildSimpleError(message, location1);
  }

  function peg$literalExpectation(text1: string, ignoreCase: boolean): ILiteralExpectation {
    return { type: "literal", text: text1, ignoreCase: ignoreCase };
  }

  function peg$classExpectation(parts: IClassParts, inverted: boolean, ignoreCase: boolean): IClassExpectation {
    return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
  }

  function peg$anyExpectation(): IAnyExpectation {
    return { type: "any" };
  }

  function peg$endExpectation(): IEndExpectation {
    return { type: "end" };
  }

  function peg$otherExpectation(description: string): IOtherExpectation {
    return { type: "other", description: description };
  }

  function peg$computePosDetails(pos: number) {
    let details = peg$posDetailsCache[pos];
    let p;

    if (details) {
      return details;
    } else {
      p = pos - 1;
      while (!peg$posDetailsCache[p]) {
        p--;
      }

      details = peg$posDetailsCache[p];
      details = {
        line: details.line,
        column: details.column
      };

      while (p < pos) {
        if (input.charCodeAt(p) === 10) {
          details.line++;
          details.column = 1;
        } else {
          details.column++;
        }

        p++;
      }

      peg$posDetailsCache[pos] = details;

      return details;
    }
  }

  function peg$computeLocation(startPos: number, endPos: number): IFileRange {
    const startPosDetails = peg$computePosDetails(startPos);
    const endPosDetails = peg$computePosDetails(endPos);

    return {
      start: {
        offset: startPos,
        line: startPosDetails.line,
        column: startPosDetails.column
      },
      end: {
        offset: endPos,
        line: endPosDetails.line,
        column: endPosDetails.column
      }
    };
  }

  function peg$fail(expected1: Expectation) {
    if (peg$currPos < peg$maxFailPos) { return; }

    if (peg$currPos > peg$maxFailPos) {
      peg$maxFailPos = peg$currPos;
      peg$maxFailExpected = [];
    }

    peg$maxFailExpected.push(expected1);
  }

  function peg$buildSimpleError(message: string, location1: IFileRange) {
    return new SyntaxError(message, [], "", location1);
  }

  function peg$buildStructuredError(expected1: Expectation[], found: string | null, location1: IFileRange) {
    return new SyntaxError(
      SyntaxError.buildMessage(expected1, found),
      expected1,
      found,
      location1
    );
  }

  function peg$parseDebugString(): any {
    let s0, s1, s2, s3, s4, s5;

    s0 = peg$currPos;
    s1 = peg$parseTag();
    if (s1 !== peg$FAILED) {
      s2 = [];
      s3 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 58) {
        s4 = peg$c0;
        peg$currPos++;
      } else {
        s4 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c1); }
      }
      if (s4 !== peg$FAILED) {
        s5 = peg$parse_();
        if (s5 !== peg$FAILED) {
          s4 = [s4, s5];
          s3 = s4;
        } else {
          peg$currPos = s3;
          s3 = peg$FAILED;
        }
      } else {
        peg$currPos = s3;
        s3 = peg$FAILED;
      }
      if (s3 !== peg$FAILED) {
        while (s3 !== peg$FAILED) {
          s2.push(s3);
          s3 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 58) {
            s4 = peg$c0;
            peg$currPos++;
          } else {
            s4 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c1); }
          }
          if (s4 !== peg$FAILED) {
            s5 = peg$parse_();
            if (s5 !== peg$FAILED) {
              s4 = [s4, s5];
              s3 = s4;
            } else {
              peg$currPos = s3;
              s3 = peg$FAILED;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$FAILED;
          }
        }
      } else {
        s2 = peg$FAILED;
      }
      if (s2 !== peg$FAILED) {
        s3 = peg$parseValue();
        if (s3 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c2(s1, s3);
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 58) {
        s1 = peg$c0;
        peg$currPos++;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c1); }
      }
      if (s1 !== peg$FAILED) {
        s2 = peg$parse_();
        if (s2 !== peg$FAILED) {
          s3 = peg$parseValue();
          if (s3 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c3(s3);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    }

    return s0;
  }

  function peg$parseValue(): any {
    let s0;

    s0 = peg$parseRecord();
    if (s0 === peg$FAILED) {
      s0 = peg$parseArray();
      if (s0 === peg$FAILED) {
        s0 = peg$parseSet();
        if (s0 === peg$FAILED) {
          s0 = peg$parseDict();
          if (s0 === peg$FAILED) {
            s0 = peg$parseList();
            if (s0 === peg$FAILED) {
              s0 = peg$parseCustomTypeWithParens();
              if (s0 === peg$FAILED) {
                s0 = peg$parseCustomType();
                if (s0 === peg$FAILED) {
                  s0 = peg$parseTuple();
                  if (s0 === peg$FAILED) {
                    s0 = peg$parseNumber();
                    if (s0 === peg$FAILED) {
                      s0 = peg$parseBoolean();
                      if (s0 === peg$FAILED) {
                        s0 = peg$parseType();
                        if (s0 === peg$FAILED) {
                          s0 = peg$parseInternals();
                          if (s0 === peg$FAILED) {
                            s0 = peg$parseBytes();
                            if (s0 === peg$FAILED) {
                              s0 = peg$parseFile();
                              if (s0 === peg$FAILED) {
                                s0 = peg$parseString();
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }

    return s0;
  }

  function peg$parseCustomTypeValue(): any {
    let s0;

    s0 = peg$parseRecord();
    if (s0 === peg$FAILED) {
      s0 = peg$parseArray();
      if (s0 === peg$FAILED) {
        s0 = peg$parseSet();
        if (s0 === peg$FAILED) {
          s0 = peg$parseDict();
          if (s0 === peg$FAILED) {
            s0 = peg$parseList();
            if (s0 === peg$FAILED) {
              s0 = peg$parseCustomTypeWithParens();
              if (s0 === peg$FAILED) {
                s0 = peg$parseTuple();
                if (s0 === peg$FAILED) {
                  s0 = peg$parseNumber();
                  if (s0 === peg$FAILED) {
                    s0 = peg$parseBoolean();
                    if (s0 === peg$FAILED) {
                      s0 = peg$parseType();
                      if (s0 === peg$FAILED) {
                        s0 = peg$parseInternals();
                        if (s0 === peg$FAILED) {
                          s0 = peg$parseBytes();
                          if (s0 === peg$FAILED) {
                            s0 = peg$parseFile();
                            if (s0 === peg$FAILED) {
                              s0 = peg$parseString();
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }

    return s0;
  }

  function peg$parseRecord(): any {
    let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;

    s0 = peg$currPos;
    if (input.substr(peg$currPos, 2) === peg$c4) {
      s1 = peg$c4;
      peg$currPos += 2;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c5); }
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c6();
    }
    s0 = s1;
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.substr(peg$currPos, 2) === peg$c7) {
        s1 = peg$c7;
        peg$currPos += 2;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c8); }
      }
      if (s1 !== peg$FAILED) {
        s2 = peg$parseVariableName();
        if (s2 !== peg$FAILED) {
          if (input.substr(peg$currPos, 3) === peg$c9) {
            s3 = peg$c9;
            peg$currPos += 3;
          } else {
            s3 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c10); }
          }
          if (s3 !== peg$FAILED) {
            s4 = peg$parseValue();
            if (s4 !== peg$FAILED) {
              if (input.substr(peg$currPos, 2) === peg$c11) {
                s5 = peg$c11;
                peg$currPos += 2;
              } else {
                s5 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c12); }
              }
              if (s5 !== peg$FAILED) {
                peg$savedPos = s0;
                s1 = peg$c13(s2, s4);
                s0 = s1;
              } else {
                peg$currPos = s0;
                s0 = peg$FAILED;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
      if (s0 === peg$FAILED) {
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 2) === peg$c7) {
          s1 = peg$c7;
          peg$currPos += 2;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c8); }
        }
        if (s1 !== peg$FAILED) {
          s2 = peg$parseVariableName();
          if (s2 !== peg$FAILED) {
            if (input.substr(peg$currPos, 3) === peg$c9) {
              s3 = peg$c9;
              peg$currPos += 3;
            } else {
              s3 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c10); }
            }
            if (s3 !== peg$FAILED) {
              s4 = peg$parseValue();
              if (s4 !== peg$FAILED) {
                s5 = [];
                s6 = peg$currPos;
                if (input.substr(peg$currPos, 2) === peg$c14) {
                  s7 = peg$c14;
                  peg$currPos += 2;
                } else {
                  s7 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c15); }
                }
                if (s7 !== peg$FAILED) {
                  s8 = peg$parseVariableName();
                  if (s8 !== peg$FAILED) {
                    if (input.substr(peg$currPos, 3) === peg$c9) {
                      s9 = peg$c9;
                      peg$currPos += 3;
                    } else {
                      s9 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c10); }
                    }
                    if (s9 !== peg$FAILED) {
                      s10 = peg$parseValue();
                      if (s10 !== peg$FAILED) {
                        peg$savedPos = s6;
                        s7 = peg$c16(s2, s4, s8, s10);
                        s6 = s7;
                      } else {
                        peg$currPos = s6;
                        s6 = peg$FAILED;
                      }
                    } else {
                      peg$currPos = s6;
                      s6 = peg$FAILED;
                    }
                  } else {
                    peg$currPos = s6;
                    s6 = peg$FAILED;
                  }
                } else {
                  peg$currPos = s6;
                  s6 = peg$FAILED;
                }
                while (s6 !== peg$FAILED) {
                  s5.push(s6);
                  s6 = peg$currPos;
                  if (input.substr(peg$currPos, 2) === peg$c14) {
                    s7 = peg$c14;
                    peg$currPos += 2;
                  } else {
                    s7 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c15); }
                  }
                  if (s7 !== peg$FAILED) {
                    s8 = peg$parseVariableName();
                    if (s8 !== peg$FAILED) {
                      if (input.substr(peg$currPos, 3) === peg$c9) {
                        s9 = peg$c9;
                        peg$currPos += 3;
                      } else {
                        s9 = peg$FAILED;
                        if (peg$silentFails === 0) { peg$fail(peg$c10); }
                      }
                      if (s9 !== peg$FAILED) {
                        s10 = peg$parseValue();
                        if (s10 !== peg$FAILED) {
                          peg$savedPos = s6;
                          s7 = peg$c16(s2, s4, s8, s10);
                          s6 = s7;
                        } else {
                          peg$currPos = s6;
                          s6 = peg$FAILED;
                        }
                      } else {
                        peg$currPos = s6;
                        s6 = peg$FAILED;
                      }
                    } else {
                      peg$currPos = s6;
                      s6 = peg$FAILED;
                    }
                  } else {
                    peg$currPos = s6;
                    s6 = peg$FAILED;
                  }
                }
                if (s5 !== peg$FAILED) {
                  if (input.substr(peg$currPos, 2) === peg$c11) {
                    s6 = peg$c11;
                    peg$currPos += 2;
                  } else {
                    s6 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c12); }
                  }
                  if (s6 !== peg$FAILED) {
                    peg$savedPos = s0;
                    s1 = peg$c17(s2, s4, s5);
                    s0 = s1;
                  } else {
                    peg$currPos = s0;
                    s0 = peg$FAILED;
                  }
                } else {
                  peg$currPos = s0;
                  s0 = peg$FAILED;
                }
              } else {
                peg$currPos = s0;
                s0 = peg$FAILED;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      }
    }

    return s0;
  }

  function peg$parseDict(): any {
    let s0, s1, s2;

    s0 = peg$currPos;
    if (input.substr(peg$currPos, 14) === peg$c18) {
      s1 = peg$c18;
      peg$currPos += 14;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c19); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$parseListValue();
      if (s2 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c20(s2);
        s0 = s1;
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseSet(): any {
    let s0, s1, s2;

    s0 = peg$currPos;
    if (input.substr(peg$currPos, 13) === peg$c21) {
      s1 = peg$c21;
      peg$currPos += 13;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c22); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$parseListValue();
      if (s2 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c23(s2);
        s0 = s1;
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseArray(): any {
    let s0, s1, s2;

    s0 = peg$currPos;
    if (input.substr(peg$currPos, 15) === peg$c24) {
      s1 = peg$c24;
      peg$currPos += 15;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c25); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$parseListValue();
      if (s2 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c26(s2);
        s0 = s1;
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseTuple(): any {
    let s0, s1, s2, s3, s4, s5, s6, s7, s8;

    s0 = peg$currPos;
    if (input.substr(peg$currPos, 2) === peg$c27) {
      s1 = peg$c27;
      peg$currPos += 2;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c28); }
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c29();
    }
    s0 = s1;
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 40) {
        s1 = peg$c30;
        peg$currPos++;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c31); }
      }
      if (s1 !== peg$FAILED) {
        s2 = peg$parseValue();
        if (s2 !== peg$FAILED) {
          s3 = [];
          s4 = peg$currPos;
          s5 = peg$parse_();
          if (s5 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 44) {
              s6 = peg$c32;
              peg$currPos++;
            } else {
              s6 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c33); }
            }
            if (s6 !== peg$FAILED) {
              s7 = peg$parse_();
              if (s7 !== peg$FAILED) {
                s8 = peg$parseValue();
                if (s8 !== peg$FAILED) {
                  peg$savedPos = s4;
                  s5 = peg$c34(s2, s8);
                  s4 = s5;
                } else {
                  peg$currPos = s4;
                  s4 = peg$FAILED;
                }
              } else {
                peg$currPos = s4;
                s4 = peg$FAILED;
              }
            } else {
              peg$currPos = s4;
              s4 = peg$FAILED;
            }
          } else {
            peg$currPos = s4;
            s4 = peg$FAILED;
          }
          if (s4 !== peg$FAILED) {
            while (s4 !== peg$FAILED) {
              s3.push(s4);
              s4 = peg$currPos;
              s5 = peg$parse_();
              if (s5 !== peg$FAILED) {
                if (input.charCodeAt(peg$currPos) === 44) {
                  s6 = peg$c32;
                  peg$currPos++;
                } else {
                  s6 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c33); }
                }
                if (s6 !== peg$FAILED) {
                  s7 = peg$parse_();
                  if (s7 !== peg$FAILED) {
                    s8 = peg$parseValue();
                    if (s8 !== peg$FAILED) {
                      peg$savedPos = s4;
                      s5 = peg$c34(s2, s8);
                      s4 = s5;
                    } else {
                      peg$currPos = s4;
                      s4 = peg$FAILED;
                    }
                  } else {
                    peg$currPos = s4;
                    s4 = peg$FAILED;
                  }
                } else {
                  peg$currPos = s4;
                  s4 = peg$FAILED;
                }
              } else {
                peg$currPos = s4;
                s4 = peg$FAILED;
              }
            }
          } else {
            s3 = peg$FAILED;
          }
          if (s3 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 41) {
              s4 = peg$c35;
              peg$currPos++;
            } else {
              s4 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c36); }
            }
            if (s4 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c37(s2, s3);
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    }

    return s0;
  }

  function peg$parseCustomTypeWithParens(): any {
    let s0, s1, s2, s3, s4, s5;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 40) {
      s1 = peg$c30;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c31); }
    }
    if (s1 !== peg$FAILED) {
      s2 = peg$parse_();
      if (s2 !== peg$FAILED) {
        s3 = peg$parseCustomType();
        if (s3 !== peg$FAILED) {
          s4 = peg$parse_();
          if (s4 !== peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 41) {
              s5 = peg$c35;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c36); }
            }
            if (s5 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c38(s3);
              s0 = s1;
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseCustomType(): any {
    let s0, s1, s2, s3, s4, s5;

    s0 = peg$currPos;
    s1 = peg$parseType();
    if (s1 !== peg$FAILED) {
      s2 = [];
      s3 = peg$currPos;
      s4 = peg$parse_();
      if (s4 !== peg$FAILED) {
        s5 = peg$parseCustomTypeValue();
        if (s5 !== peg$FAILED) {
          peg$savedPos = s3;
          s4 = peg$c39(s1, s5);
          s3 = s4;
        } else {
          peg$currPos = s3;
          s3 = peg$FAILED;
        }
      } else {
        peg$currPos = s3;
        s3 = peg$FAILED;
      }
      if (s3 !== peg$FAILED) {
        while (s3 !== peg$FAILED) {
          s2.push(s3);
          s3 = peg$currPos;
          s4 = peg$parse_();
          if (s4 !== peg$FAILED) {
            s5 = peg$parseCustomTypeValue();
            if (s5 !== peg$FAILED) {
              peg$savedPos = s3;
              s4 = peg$c39(s1, s5);
              s3 = s4;
            } else {
              peg$currPos = s3;
              s3 = peg$FAILED;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$FAILED;
          }
        }
      } else {
        s2 = peg$FAILED;
      }
      if (s2 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c40(s1, s2);
        s0 = s1;
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      s1 = peg$parseType();
      if (s1 !== peg$FAILED) {
        s2 = peg$parse_();
        if (s2 !== peg$FAILED) {
          s3 = peg$parseCustomTypeWithParens();
          if (s3 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c41(s1, s3);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    }

    return s0;
  }

  function peg$parseList(): any {
    let s0, s1;

    s0 = peg$currPos;
    s1 = peg$parseListValue();
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c42(s1);
    }
    s0 = s1;

    return s0;
  }

  function peg$parseNumber(): any {
    let s0, s1, s2, s3, s4;

    s0 = peg$currPos;
    s1 = [];
    if (peg$c43.test(input.charAt(peg$currPos))) {
      s2 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c44); }
    }
    if (s2 !== peg$FAILED) {
      while (s2 !== peg$FAILED) {
        s1.push(s2);
        if (peg$c43.test(input.charAt(peg$currPos))) {
          s2 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s2 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c44); }
        }
      }
    } else {
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c45(s1);
    }
    s0 = s1;
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 45) {
        s1 = peg$c46;
        peg$currPos++;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c47); }
      }
      if (s1 !== peg$FAILED) {
        s2 = peg$parse_();
        if (s2 !== peg$FAILED) {
          s3 = [];
          if (peg$c43.test(input.charAt(peg$currPos))) {
            s4 = input.charAt(peg$currPos);
            peg$currPos++;
          } else {
            s4 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c44); }
          }
          if (s4 !== peg$FAILED) {
            while (s4 !== peg$FAILED) {
              s3.push(s4);
              if (peg$c43.test(input.charAt(peg$currPos))) {
                s4 = input.charAt(peg$currPos);
                peg$currPos++;
              } else {
                s4 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c44); }
              }
            }
          } else {
            s3 = peg$FAILED;
          }
          if (s3 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c48(s3);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
      if (s0 === peg$FAILED) {
        s0 = peg$currPos;
        if (input.substr(peg$currPos, 3) === peg$c49) {
          s1 = peg$c49;
          peg$currPos += 3;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c50); }
        }
        if (s1 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c51();
        }
        s0 = s1;
        if (s0 === peg$FAILED) {
          s0 = peg$currPos;
          if (input.substr(peg$currPos, 8) === peg$c52) {
            s1 = peg$c52;
            peg$currPos += 8;
          } else {
            s1 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c53); }
          }
          if (s1 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c54();
          }
          s0 = s1;
          if (s0 === peg$FAILED) {
            s0 = peg$currPos;
            if (input.substr(peg$currPos, 9) === peg$c55) {
              s1 = peg$c55;
              peg$currPos += 9;
            } else {
              s1 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c56); }
            }
            if (s1 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c57();
            }
            s0 = s1;
          }
        }
      }
    }

    return s0;
  }

  function peg$parseBoolean(): any {
    let s0, s1;

    s0 = peg$currPos;
    if (input.substr(peg$currPos, 4) === peg$c58) {
      s1 = peg$c58;
      peg$currPos += 4;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c59); }
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c60();
    }
    s0 = s1;
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.substr(peg$currPos, 5) === peg$c61) {
        s1 = peg$c61;
        peg$currPos += 5;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c62); }
      }
      if (s1 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c63();
      }
      s0 = s1;
    }

    return s0;
  }

  function peg$parseInternals(): any {
    let s0, s1;

    s0 = peg$currPos;
    if (input.substr(peg$currPos, 10) === peg$c64) {
      s1 = peg$c64;
      peg$currPos += 10;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c65); }
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c66();
    }
    s0 = s1;
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.substr(peg$currPos, 11) === peg$c67) {
        s1 = peg$c67;
        peg$currPos += 11;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c68); }
      }
      if (s1 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c69();
      }
      s0 = s1;
    }

    return s0;
  }

  function peg$parseBytes(): any {
    let s0, s1, s2, s3;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 60) {
      s1 = peg$c70;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c71); }
    }
    if (s1 !== peg$FAILED) {
      s2 = [];
      if (peg$c72.test(input.charAt(peg$currPos))) {
        s3 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s3 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c73); }
      }
      if (s3 !== peg$FAILED) {
        while (s3 !== peg$FAILED) {
          s2.push(s3);
          if (peg$c72.test(input.charAt(peg$currPos))) {
            s3 = input.charAt(peg$currPos);
            peg$currPos++;
          } else {
            s3 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c73); }
          }
        }
      } else {
        s2 = peg$FAILED;
      }
      if (s2 !== peg$FAILED) {
        if (input.substr(peg$currPos, 7) === peg$c74) {
          s3 = peg$c74;
          peg$currPos += 7;
        } else {
          s3 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c75); }
        }
        if (s3 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c76(s2);
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseFile(): any {
    let s0, s1, s2, s3, s4, s5;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 60) {
      s1 = peg$c70;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c71); }
    }
    if (s1 !== peg$FAILED) {
      s2 = [];
      s3 = peg$currPos;
      s4 = peg$currPos;
      peg$silentFails++;
      if (input.charCodeAt(peg$currPos) === 34) {
        s5 = peg$c77;
        peg$currPos++;
      } else {
        s5 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c78); }
      }
      if (s5 === peg$FAILED) {
        if (input.charCodeAt(peg$currPos) === 92) {
          s5 = peg$c79;
          peg$currPos++;
        } else {
          s5 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c80); }
        }
        if (s5 === peg$FAILED) {
          if (input.charCodeAt(peg$currPos) === 60) {
            s5 = peg$c70;
            peg$currPos++;
          } else {
            s5 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c71); }
          }
          if (s5 === peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 62) {
              s5 = peg$c81;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c82); }
            }
          }
        }
      }
      peg$silentFails--;
      if (s5 === peg$FAILED) {
        s4 = undefined;
      } else {
        peg$currPos = s4;
        s4 = peg$FAILED;
      }
      if (s4 !== peg$FAILED) {
        if (input.length > peg$currPos) {
          s5 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s5 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c83); }
        }
        if (s5 !== peg$FAILED) {
          peg$savedPos = s3;
          s4 = peg$c84(s5);
          s3 = s4;
        } else {
          peg$currPos = s3;
          s3 = peg$FAILED;
        }
      } else {
        peg$currPos = s3;
        s3 = peg$FAILED;
      }
      if (s3 !== peg$FAILED) {
        while (s3 !== peg$FAILED) {
          s2.push(s3);
          s3 = peg$currPos;
          s4 = peg$currPos;
          peg$silentFails++;
          if (input.charCodeAt(peg$currPos) === 34) {
            s5 = peg$c77;
            peg$currPos++;
          } else {
            s5 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c78); }
          }
          if (s5 === peg$FAILED) {
            if (input.charCodeAt(peg$currPos) === 92) {
              s5 = peg$c79;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c80); }
            }
            if (s5 === peg$FAILED) {
              if (input.charCodeAt(peg$currPos) === 60) {
                s5 = peg$c70;
                peg$currPos++;
              } else {
                s5 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c71); }
              }
              if (s5 === peg$FAILED) {
                if (input.charCodeAt(peg$currPos) === 62) {
                  s5 = peg$c81;
                  peg$currPos++;
                } else {
                  s5 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c82); }
                }
              }
            }
          }
          peg$silentFails--;
          if (s5 === peg$FAILED) {
            s4 = undefined;
          } else {
            peg$currPos = s4;
            s4 = peg$FAILED;
          }
          if (s4 !== peg$FAILED) {
            if (input.length > peg$currPos) {
              s5 = input.charAt(peg$currPos);
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c83); }
            }
            if (s5 !== peg$FAILED) {
              peg$savedPos = s3;
              s4 = peg$c84(s5);
              s3 = s4;
            } else {
              peg$currPos = s3;
              s3 = peg$FAILED;
            }
          } else {
            peg$currPos = s3;
            s3 = peg$FAILED;
          }
        }
      } else {
        s2 = peg$FAILED;
      }
      if (s2 !== peg$FAILED) {
        if (input.charCodeAt(peg$currPos) === 62) {
          s3 = peg$c81;
          peg$currPos++;
        } else {
          s3 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c82); }
        }
        if (s3 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c85(s2);
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseVariableName(): any {
    let s0, s1, s2;

    s0 = peg$currPos;
    s1 = [];
    if (peg$c86.test(input.charAt(peg$currPos))) {
      s2 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c87); }
    }
    if (s2 !== peg$FAILED) {
      while (s2 !== peg$FAILED) {
        s1.push(s2);
        if (peg$c86.test(input.charAt(peg$currPos))) {
          s2 = input.charAt(peg$currPos);
          peg$currPos++;
        } else {
          s2 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c87); }
        }
      }
    } else {
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c88(s1);
    }
    s0 = s1;

    return s0;
  }

  function peg$parseType(): any {
    let s0, s1, s2, s3, s4;

    s0 = peg$currPos;
    s1 = peg$currPos;
    if (peg$c89.test(input.charAt(peg$currPos))) {
      s2 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c90); }
    }
    if (s2 !== peg$FAILED) {
      s3 = [];
      if (peg$c86.test(input.charAt(peg$currPos))) {
        s4 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s4 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c87); }
      }
      if (s4 !== peg$FAILED) {
        while (s4 !== peg$FAILED) {
          s3.push(s4);
          if (peg$c86.test(input.charAt(peg$currPos))) {
            s4 = input.charAt(peg$currPos);
            peg$currPos++;
          } else {
            s4 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c87); }
          }
        }
      } else {
        s3 = peg$FAILED;
      }
      if (s3 !== peg$FAILED) {
        s2 = [s2, s3];
        s1 = s2;
      } else {
        peg$currPos = s1;
        s1 = peg$FAILED;
      }
    } else {
      peg$currPos = s1;
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c91(s1);
    }
    s0 = s1;

    return s0;
  }

  function peg$parseTag(): any {
    let s0, s1, s2;

    s0 = peg$currPos;
    s1 = [];
    s2 = peg$parseTagChar();
    if (s2 !== peg$FAILED) {
      while (s2 !== peg$FAILED) {
        s1.push(s2);
        s2 = peg$parseTagChar();
      }
    } else {
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c92(s1);
    }
    s0 = s1;

    return s0;
  }

  function peg$parseString(): any {
    let s0, s1, s2, s3;

    s0 = peg$currPos;
    if (input.charCodeAt(peg$currPos) === 34) {
      s1 = peg$c77;
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c78); }
    }
    if (s1 !== peg$FAILED) {
      s2 = [];
      s3 = peg$parseDoubleStringCharacter();
      while (s3 !== peg$FAILED) {
        s2.push(s3);
        s3 = peg$parseDoubleStringCharacter();
      }
      if (s2 !== peg$FAILED) {
        if (input.charCodeAt(peg$currPos) === 34) {
          s3 = peg$c77;
          peg$currPos++;
        } else {
          s3 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c78); }
        }
        if (s3 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c93(s2);
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 39) {
        s1 = peg$c94;
        peg$currPos++;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c95); }
      }
      if (s1 !== peg$FAILED) {
        s2 = [];
        s3 = peg$parseSingleStringCharacter();
        while (s3 !== peg$FAILED) {
          s2.push(s3);
          s3 = peg$parseSingleStringCharacter();
        }
        if (s2 !== peg$FAILED) {
          if (input.charCodeAt(peg$currPos) === 39) {
            s3 = peg$c94;
            peg$currPos++;
          } else {
            s3 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c95); }
          }
          if (s3 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c96(s2);
            s0 = s1;
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    }

    return s0;
  }

  function peg$parseListValue(): any {
    let s0, s1, s2, s3, s4, s5, s6, s7;

    s0 = peg$currPos;
    if (input.substr(peg$currPos, 2) === peg$c97) {
      s1 = peg$c97;
      peg$currPos += 2;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c98); }
    }
    if (s1 !== peg$FAILED) {
      peg$savedPos = s0;
      s1 = peg$c99();
    }
    s0 = s1;
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      s1 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 91) {
        s2 = peg$c100;
        peg$currPos++;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c101); }
      }
      if (s2 !== peg$FAILED) {
        s3 = peg$parseValue();
        if (s3 !== peg$FAILED) {
          if (input.charCodeAt(peg$currPos) === 93) {
            s4 = peg$c102;
            peg$currPos++;
          } else {
            s4 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c103); }
          }
          if (s4 !== peg$FAILED) {
            peg$savedPos = s1;
            s2 = peg$c104(s3);
            s1 = s2;
          } else {
            peg$currPos = s1;
            s1 = peg$FAILED;
          }
        } else {
          peg$currPos = s1;
          s1 = peg$FAILED;
        }
      } else {
        peg$currPos = s1;
        s1 = peg$FAILED;
      }
      if (s1 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c105(s1);
      }
      s0 = s1;
      if (s0 === peg$FAILED) {
        s0 = peg$currPos;
        if (input.charCodeAt(peg$currPos) === 91) {
          s1 = peg$c100;
          peg$currPos++;
        } else {
          s1 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c101); }
        }
        if (s1 !== peg$FAILED) {
          s2 = peg$parseValue();
          if (s2 !== peg$FAILED) {
            s3 = [];
            s4 = peg$currPos;
            if (input.charCodeAt(peg$currPos) === 44) {
              s5 = peg$c32;
              peg$currPos++;
            } else {
              s5 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c33); }
            }
            if (s5 !== peg$FAILED) {
              s6 = peg$parse_();
              if (s6 !== peg$FAILED) {
                s7 = peg$parseValue();
                if (s7 !== peg$FAILED) {
                  peg$savedPos = s4;
                  s5 = peg$c106(s2, s7);
                  s4 = s5;
                } else {
                  peg$currPos = s4;
                  s4 = peg$FAILED;
                }
              } else {
                peg$currPos = s4;
                s4 = peg$FAILED;
              }
            } else {
              peg$currPos = s4;
              s4 = peg$FAILED;
            }
            if (s4 !== peg$FAILED) {
              while (s4 !== peg$FAILED) {
                s3.push(s4);
                s4 = peg$currPos;
                if (input.charCodeAt(peg$currPos) === 44) {
                  s5 = peg$c32;
                  peg$currPos++;
                } else {
                  s5 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c33); }
                }
                if (s5 !== peg$FAILED) {
                  s6 = peg$parse_();
                  if (s6 !== peg$FAILED) {
                    s7 = peg$parseValue();
                    if (s7 !== peg$FAILED) {
                      peg$savedPos = s4;
                      s5 = peg$c106(s2, s7);
                      s4 = s5;
                    } else {
                      peg$currPos = s4;
                      s4 = peg$FAILED;
                    }
                  } else {
                    peg$currPos = s4;
                    s4 = peg$FAILED;
                  }
                } else {
                  peg$currPos = s4;
                  s4 = peg$FAILED;
                }
              }
            } else {
              s3 = peg$FAILED;
            }
            if (s3 !== peg$FAILED) {
              if (input.charCodeAt(peg$currPos) === 93) {
                s4 = peg$c102;
                peg$currPos++;
              } else {
                s4 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c103); }
              }
              if (s4 !== peg$FAILED) {
                peg$savedPos = s0;
                s1 = peg$c107(s2, s3);
                s0 = s1;
              } else {
                peg$currPos = s0;
                s0 = peg$FAILED;
              }
            } else {
              peg$currPos = s0;
              s0 = peg$FAILED;
            }
          } else {
            peg$currPos = s0;
            s0 = peg$FAILED;
          }
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      }
    }

    return s0;
  }

  function peg$parseDoubleStringCharacter(): any {
    let s0, s1, s2;

    s0 = peg$currPos;
    s1 = peg$currPos;
    peg$silentFails++;
    if (input.charCodeAt(peg$currPos) === 34) {
      s2 = peg$c77;
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c78); }
    }
    if (s2 === peg$FAILED) {
      if (input.charCodeAt(peg$currPos) === 92) {
        s2 = peg$c79;
        peg$currPos++;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c80); }
      }
    }
    peg$silentFails--;
    if (s2 === peg$FAILED) {
      s1 = undefined;
    } else {
      peg$currPos = s1;
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      if (input.length > peg$currPos) {
        s2 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c83); }
      }
      if (s2 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c84(s2);
        s0 = s1;
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 92) {
        s1 = peg$c79;
        peg$currPos++;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c80); }
      }
      if (s1 !== peg$FAILED) {
        s2 = peg$parseEscapeSequence();
        if (s2 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c108(s2);
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    }

    return s0;
  }

  function peg$parseSingleStringCharacter(): any {
    let s0, s1, s2;

    s0 = peg$currPos;
    s1 = peg$currPos;
    peg$silentFails++;
    if (input.charCodeAt(peg$currPos) === 39) {
      s2 = peg$c94;
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c95); }
    }
    if (s2 === peg$FAILED) {
      if (input.charCodeAt(peg$currPos) === 92) {
        s2 = peg$c79;
        peg$currPos++;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c80); }
      }
    }
    peg$silentFails--;
    if (s2 === peg$FAILED) {
      s1 = undefined;
    } else {
      peg$currPos = s1;
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      if (input.length > peg$currPos) {
        s2 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c83); }
      }
      if (s2 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c84(s2);
        s0 = s1;
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }
    if (s0 === peg$FAILED) {
      s0 = peg$currPos;
      if (input.charCodeAt(peg$currPos) === 92) {
        s1 = peg$c79;
        peg$currPos++;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c80); }
      }
      if (s1 !== peg$FAILED) {
        s2 = peg$parseEscapeSequence();
        if (s2 !== peg$FAILED) {
          peg$savedPos = s0;
          s1 = peg$c108(s2);
          s0 = s1;
        } else {
          peg$currPos = s0;
          s0 = peg$FAILED;
        }
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    }

    return s0;
  }

  function peg$parseTagChar(): any {
    let s0, s1, s2;

    s0 = peg$currPos;
    s1 = peg$currPos;
    peg$silentFails++;
    if (input.charCodeAt(peg$currPos) === 58) {
      s2 = peg$c0;
      peg$currPos++;
    } else {
      s2 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c1); }
    }
    peg$silentFails--;
    if (s2 === peg$FAILED) {
      s1 = undefined;
    } else {
      peg$currPos = s1;
      s1 = peg$FAILED;
    }
    if (s1 !== peg$FAILED) {
      if (input.length > peg$currPos) {
        s2 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s2 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c83); }
      }
      if (s2 !== peg$FAILED) {
        peg$savedPos = s0;
        s1 = peg$c84(s2);
        s0 = s1;
      } else {
        peg$currPos = s0;
        s0 = peg$FAILED;
      }
    } else {
      peg$currPos = s0;
      s0 = peg$FAILED;
    }

    return s0;
  }

  function peg$parseEscapeSequence(): any {
    let s0, s1;

    if (input.charCodeAt(peg$currPos) === 39) {
      s0 = peg$c94;
      peg$currPos++;
    } else {
      s0 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c95); }
    }
    if (s0 === peg$FAILED) {
      if (input.charCodeAt(peg$currPos) === 34) {
        s0 = peg$c77;
        peg$currPos++;
      } else {
        s0 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c78); }
      }
      if (s0 === peg$FAILED) {
        if (input.charCodeAt(peg$currPos) === 92) {
          s0 = peg$c79;
          peg$currPos++;
        } else {
          s0 = peg$FAILED;
          if (peg$silentFails === 0) { peg$fail(peg$c80); }
        }
        if (s0 === peg$FAILED) {
          s0 = peg$currPos;
          if (input.charCodeAt(peg$currPos) === 98) {
            s1 = peg$c109;
            peg$currPos++;
          } else {
            s1 = peg$FAILED;
            if (peg$silentFails === 0) { peg$fail(peg$c110); }
          }
          if (s1 !== peg$FAILED) {
            peg$savedPos = s0;
            s1 = peg$c111();
          }
          s0 = s1;
          if (s0 === peg$FAILED) {
            s0 = peg$currPos;
            if (input.charCodeAt(peg$currPos) === 102) {
              s1 = peg$c112;
              peg$currPos++;
            } else {
              s1 = peg$FAILED;
              if (peg$silentFails === 0) { peg$fail(peg$c113); }
            }
            if (s1 !== peg$FAILED) {
              peg$savedPos = s0;
              s1 = peg$c114();
            }
            s0 = s1;
            if (s0 === peg$FAILED) {
              s0 = peg$currPos;
              if (input.charCodeAt(peg$currPos) === 110) {
                s1 = peg$c115;
                peg$currPos++;
              } else {
                s1 = peg$FAILED;
                if (peg$silentFails === 0) { peg$fail(peg$c116); }
              }
              if (s1 !== peg$FAILED) {
                peg$savedPos = s0;
                s1 = peg$c117();
              }
              s0 = s1;
              if (s0 === peg$FAILED) {
                s0 = peg$currPos;
                if (input.charCodeAt(peg$currPos) === 114) {
                  s1 = peg$c118;
                  peg$currPos++;
                } else {
                  s1 = peg$FAILED;
                  if (peg$silentFails === 0) { peg$fail(peg$c119); }
                }
                if (s1 !== peg$FAILED) {
                  peg$savedPos = s0;
                  s1 = peg$c120();
                }
                s0 = s1;
                if (s0 === peg$FAILED) {
                  s0 = peg$currPos;
                  if (input.charCodeAt(peg$currPos) === 116) {
                    s1 = peg$c121;
                    peg$currPos++;
                  } else {
                    s1 = peg$FAILED;
                    if (peg$silentFails === 0) { peg$fail(peg$c122); }
                  }
                  if (s1 !== peg$FAILED) {
                    peg$savedPos = s0;
                    s1 = peg$c123();
                  }
                  s0 = s1;
                  if (s0 === peg$FAILED) {
                    s0 = peg$currPos;
                    if (input.charCodeAt(peg$currPos) === 118) {
                      s1 = peg$c124;
                      peg$currPos++;
                    } else {
                      s1 = peg$FAILED;
                      if (peg$silentFails === 0) { peg$fail(peg$c125); }
                    }
                    if (s1 !== peg$FAILED) {
                      peg$savedPos = s0;
                      s1 = peg$c126();
                    }
                    s0 = s1;
                  }
                }
              }
            }
          }
        }
      }
    }

    return s0;
  }

  function peg$parse_(): any {
    let s0, s1;

    peg$silentFails++;
    s0 = [];
    if (peg$c128.test(input.charAt(peg$currPos))) {
      s1 = input.charAt(peg$currPos);
      peg$currPos++;
    } else {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c129); }
    }
    while (s1 !== peg$FAILED) {
      s0.push(s1);
      if (peg$c128.test(input.charAt(peg$currPos))) {
        s1 = input.charAt(peg$currPos);
        peg$currPos++;
      } else {
        s1 = peg$FAILED;
        if (peg$silentFails === 0) { peg$fail(peg$c129); }
      }
    }
    peg$silentFails--;
    if (s0 === peg$FAILED) {
      s1 = peg$FAILED;
      if (peg$silentFails === 0) { peg$fail(peg$c127); }
    }

    return s0;
  }


    function flat(arr) { return arr.reduce((acc, val) => acc.concat(val), []);}
    function toStr(chars) {return chars.join("")};


  peg$result = peg$startRuleFunction();

  if (peg$result !== peg$FAILED && peg$currPos === input.length) {
    return peg$result;
  } else {
    if (peg$result !== peg$FAILED && peg$currPos < input.length) {
      peg$fail(peg$endExpectation());
    }

    throw peg$buildStructuredError(
      peg$maxFailExpected,
      peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
      peg$maxFailPos < input.length
        ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
        : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
    );
  }
}

export interface IParseOptions {
  filename?: string;
  startRule?: string;
  tracer?: any;
  [key: string]: any;
}
export type ParseFunction = (input: string, options?: IParseOptions) => any;
export const parse: ParseFunction = peg$parse;



================================================
FILE: src/elm-debug.pegjs
================================================
// Set.toList, Array.toList
{
  function flat(arr) { return arr.reduce((acc, val) => acc.concat(val), []);}
  function toStr(chars) {return chars.join("")};
}

DebugString 
  = tag:Tag (":" _ )+ value:Value {return {type: "ElmDebug", name: tag, value: value};}
  / ":" _ value:Value {return {type: "ElmDebug", name: "", value: value};}

Value
  = Record / Array / Set / Dict / List / CustomTypeWithParens / CustomType / Tuple / Number / Boolean / Type / Internals / Bytes / File / String

CustomTypeValue
  = Record / Array / Set / Dict / List / CustomTypeWithParens / Tuple / Number / Boolean / Type / Internals / Bytes / File / String

Record
  = "{}" {return {type: "Record", value: {}};}
  / "{ " key:VariableName " = " value:Value " }" {return {type: "Record", value: {[key]: value}}}
  / "{ " key:VariableName " = " value:Value values:(", " tag:VariableName " = " otherVal:Value {return {[tag]: otherVal};})* " }" { var composed = [{[key]: value},...values].reduce((item, obj) => {return {...item,...obj} },{}); return {type: "Record", value: composed}}

Dict
  = "Dict.fromList " values:ListValue {return {type: "Dict", value: values.map((tuple) => { return {key: tuple.value[0], value: tuple.value[1]};})}}

Set
  = "Set.fromList " values:ListValue {return {type: "Set", value: values};}

Array
  = "Array.fromList " values:ListValue {return {type: "Array", value: values};}

Tuple
  = "()" {return {type:"Unit"}}
  / "(" head:Value others:(_ "," _ item:Value {return item;})+ ")" {return {type: "Tuple", value: [head,...others]};}

CustomTypeWithParens
  = "(" _ customType:CustomType _ ")" {return customType;}

CustomType 
  = mainType:Type values:(_ value:CustomTypeValue {return value;})+ {return {type: "Custom", name: mainType.name, value: values};}
  / mainType:Type _ customType:CustomTypeWithParens {return {type: "Custom", name: mainType.name, value: customType};}

List 
  = list:ListValue {return {type: "List", value: list};}


Number =
  digits:[0-9\.]+ {return {type: "Number", value: parseFloat(toStr(digits))};}
	/ "-" _ digits:[0-9\.]+ {return {type: "Number", value: parseFloat("-"+toStr(digits))};}
	/ "NaN" {return {type: "Number", value: "NaN"};}
	/ "Infinity" {return {type: "Number", value: "Infinity"};}
	/ "-Infinity" {return {type: "Number", value: "-Infinity"};}

Boolean
  = "True" {return {type: "Boolean", value: true};}
  / "False" {return {type: "Boolean", value: false};}

Internals
  = "<function>" {return {type: "Function"};}
  / "<internals>" {return {type: "Internals"};}

Bytes 
  = "<" digits:[0-9]+ " bytes>" {return {type: "Bytes", value: parseInt(toStr(digits), 10)};}

File
  = "<" chars:(!('"' / "\\" / "<" / ">") char:. { return char; })+ ">" {return { type: "File", value: chars.join('') };}

VariableName =
	chars:[a-zA-Z0-9_]+ {return toStr(chars);}
    
Type = 
  // type returns array in array -> "Yes" -> ["Y",["e", "s"]] -- need to flat that array
	type: ([A-Z][a-zA-Z0-9_]+) {return {type: "Type", name: toStr(flat(type))};}

Tag =
  tag:TagChar+ { return toStr(tag); }

String
  = '"' chars:DoubleStringCharacter* '"' { return {type: "String", value: chars.join('')}; }
  / "'" chars:SingleStringCharacter* "'" { return { type: "String", value: chars.join('')}; }


ListValue
  = "[]" {return [];} 
  / list:("[" singleton:Value "]" {return singleton;}) {return [list];}
  / "[" head:Value tail:("," _ value:Value {return value;})+ "]" {return [head, ...tail]}
  

DoubleStringCharacter
  = !('"' / "\\") char:. { return char; }
  / "\\" sequence:EscapeSequence { return sequence; }

SingleStringCharacter
  = !("'" / "\\") char:. { return char; }
  / "\\" sequence:EscapeSequence { return sequence; }

TagChar
  = !(":") char:. { return char; }

EscapeSequence
  = "'"
  / '"'
  / "\\"
  / "b"  { return "\b";   }
  / "f"  { return "\f";   }
  / "n"  { return "\n";   }
  / "r"  { return "\r";   }
  / "t"  { return "\t";   }
  / "v"  { return "\x0B"; }

_ "whitespace"
  = [ \t\n\r]*



================================================
FILE: src/formatters/JsonMLFormatter.ts
================================================
import * as _ from 'lodash';
import * as T from '../CommonTypes';
import JsonML from '../JsonML';
import { toElement } from './elements/Elements';
import EllipsisElement from './elements/EllipsisElement';

/* tslint:disable */
declare global {
    interface Window {
        devtoolsFormatters: any;
    }
}
/* tslint:enable*/

export default class JsonMLFormatter
    implements T.IFormatter, T.IJsonMLFormatter, T.IDevToolsFormatter {

    public theme:T.ITheme;

    constructor(theme: T.ITheme) {
        window.devtoolsFormatters = [this];
        this.theme = theme;
    }

    public format = (obj: T.IElmDebugValue): any => {
        return obj;
    }

    public header = (obj: T.IElmDebugValue, config: T.IConfig): any[] | null => {
        if (!!config && !!config.key && config.elmFormat) {
            return new JsonML('div')
                .withChild(config.key)
                .withChild(this.handleHeader(obj, config))
                .toJSONML();
        }
        if (!!obj.type && obj.type === 'ElmDebug') {
            return new JsonML('div')
                .withChild(this.handleHeader(obj))
                .toJSONML();
        } else {
            return null;
        }
    }

    public hasBody = (obj: T.IElmDebugValue, config: T.IConfig): boolean =>{
        const element = toElement(obj, this as T.IJsonMLFormatter);
        return (
            element !== null &&
            element.body !== undefined &&
            element.body() !== null
        );
    }

    public body = (obj: T.IElmDebugValue, config: T.IConfig): any[] | null => {
        if (this.handleBody(obj) === null) {
            return null;
        }

        return this.handleBody(obj)?.toJSONML();
    }

    public handleHeader = (
        obj: T.ElmDebugValueType | undefined,
        config: T.IConfig = { elmFormat: true, level: 0 }
    ): JsonML => {
        if(obj === undefined) {
            return new JsonML('span').withText('undefined value');
        }

        const newConfig = _.clone(config);
        const element = toElement(obj, this as T.IJsonMLFormatter);

        newConfig.level = config.level + 1;

        if (element) {
            return element.header(newConfig);
        } else {
            return new JsonML('span').withText('UNPARSED: ').withText(obj);
        }
    }

    public handleBody = (
        obj: T.ElmDebugValueType,
        config?: T.IConfig
    ): JsonML | null => {
        const element = toElement(obj, this as T.IJsonMLFormatter);
        if (element) {
            return element.body !== undefined ? element.body(config) : null;
        } else {
            return new JsonML('div').withText('UNPARSED body: ').withText(obj);
        }
    }
}


================================================
FILE: src/formatters/SimpleFormatter.ts
================================================
import { defaults, mapValues } from 'lodash';
import * as T from '../CommonTypes';

export default class SimpleFormatter implements T.IFormatter {
    public format(obj: T.IElmDebugValue): object {
      if(obj.name !== undefined && obj.value !== undefined){
        return { [obj.name]: this.formatValue(obj.value) };
      }
      else { 
        return {}
      }
    }

    public formatArray(array: T.ElmDebugValueType[]): object[] {
        return array.map(v => this.formatValue(v));
    }

    public formatCustom(
        custom: T.IElmDebugCustomValue
    ): { [key: string]: any } {
        return {
            [custom.name]:
                custom.value.length === 1
                    ? this.formatValue(custom.value[0])
                    : this.formatArray(custom.value),
        };
    }

    public formatValue(formatee: T.ElmDebugValueType): any {
        if (T.isElmCustomValue(formatee)) {
            return this.formatCustom(formatee);
        }

        if (T.isElmRecordValue(formatee)) {
            return mapValues(formatee.value, v => this.formatValue(v));
        }

        if (T.isElmListValue(formatee)) {
            return this.formatArray(formatee.value);
        }

        if (T.isElmDictValue(formatee)) {
            return formatee.value.reduce(
                (result, dictItem: { [k: string]: any }) => {
                    return defaults(
                        {
                            [this.formatValue(dictItem.key)]: this.formatValue(
                                dictItem.value
                            ),
                        },
                        result
                    );
                },
                {}
            );
        }

        if (T.isElmTypeValue(formatee)) {
            return formatee.name;
        }

        if (T.isElmNumberType(formatee)) {
            return formatee.value;
        }

        switch (formatee.type) {
            case 'Function':
                return '<function>';

            case 'Internals':
                return '<internals>';

            case 'Bytes':
                return formatee.value + ' B';

            case 'File':
                return formatee.value;

            case 'Unit':
                return '()';

            case 'String':
                return formatee.value;

            case 'Boolean':
                return formatee.value;

            default:
                return formatee.value !== undefined &&
                    T.isElmValue(formatee.value)
                    ? this.formatValue(formatee.value)
                    : formatee;
        }
    }
}


================================================
FILE: src/formatters/elements/BooleanElement.ts
================================================
import { 
  IElmDebugBoolValue, 
  IFormatterElement,
  IJsonMLFormatter

} from '../../CommonTypes';
import JsonML from '../../JsonML';

export default class BooleanElement implements IFormatterElement {
    private elmObj: IElmDebugBoolValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugBoolValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = () => {
        return new JsonML('span')
            .withStyle(this.formatter.theme.booleanStyle)
            .withText(this.elmObj.value ? 'True' : 'False');
    }
}


================================================
FILE: src/formatters/elements/BytesElement.ts
================================================
import { 
  IElmDebugValue, 
  IFormatterElement,
  IJsonMLFormatter
} from '../../CommonTypes';

import JsonML from '../../JsonML';

export default class BytesElement implements IFormatterElement {
    private elmObj: IElmDebugValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = () => {
        return new JsonML('span')
            .withStyle(this.formatter.theme.bytesStyle)
            .withText(this.elmObj.value + ' B');
    }
}


================================================
FILE: src/formatters/elements/CustomTypeElement.ts
================================================
import * as _ from 'lodash';
import {
    IConfig,
    IElmDebugCustomValue,
    IFormatterElement,
    IJsonMLFormatter,
} from '../../CommonTypes';

import JsonML from '../../JsonML';
import EllipsisElement from './EllipsisElement';

export default class CustomTypeElement implements IFormatterElement {
    private elmObj: IElmDebugCustomValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugCustomValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = (config: IConfig = { elmFormat: true, level: 0 }) => {
        if (this.elmObj.value.length === 0) {
            return new JsonML('span')
                .withStyle(this.formatter.theme.customTypeNameStyle)
                .withText(this.elmObj.name);
        }
        if (this.elmObj.value.length === 1) {
            return this.wrappedHeader(
                new JsonML('span')
                    .withStyle(this.formatter.theme.customTypeNameStyle)
                    .withText(this.elmObj.name + ' ')
                    .withChild(
                        this.formatter.handleHeader(
                            this.elmObj.value[0],
                            config
                        )
                    ),
                config
            );
        } else {
            if (config !== null && config.level > 1) {
                return this.wrappedHeader(
                    new JsonML('span')
                        .withText(this.elmObj.name + ' ')
                        .withStyle(this.formatter.theme.customTypeNameStyle)
                        .withChild(new EllipsisElement().header()),
                    config
                );
            } else {
                const children = this.elmObj.value
                    .map(child => {
                        return {
                            child,
                            jsonml: new JsonML('span').withChild(
                                this.formatter.handleHeader(child, config)
                            ),
                        };
                    })

                    .reduce((acc, child) => {
                        acc.push(child.jsonml);
                        acc.push(new JsonML('span').withText(' '));
                        return acc;
                    }, [] as any[]);

                children.splice(-1, 1);

                return this.wrappedHeader(
                    new JsonML('span')
                        .withText(this.elmObj.name + ' ')
                        .withStyle(this.formatter.theme.customTypeNameStyle)
                        .withChildren(children),
                    config
                );
            }
        }
    }

    public body = (): JsonML | null => {
        if (
            this.elmObj.value.length === 1 &&
            this.formatter.handleBody(this.elmObj.value[0]) === null
        ) {
            return null;
        }

        const children = this.elmObj.value.map((child, index) => {
            const element = new JsonML('span')
                .withChild(
                    new JsonML('span')
                        .withStyle(this.formatter.theme.keyElementStyle)
                        .withText(`${index}`)
                )
                .withText(': ');

            if (this.formatter.handleBody(child) === null) {
                element.withStyle('margin-left: 13px;');
            }

            return new JsonML('div').withObject(element, child);
        });

        return new JsonML('div')
            .withStyle('margin-left: 15px;')
            .withStyle(this.formatter.theme.expandableBorderStyle)
            .withChildren(children);
    }

    private wrappedHeader = (obj: JsonML, config?: IConfig): JsonML => {
        if (config && config.level > 1) {
            return new JsonML('span')
                .withText('( ')
                .withChild(obj)
                .withText(' )');
        } else {
            return obj;
        }
    }
}


================================================
FILE: src/formatters/elements/DebugElement.ts
================================================
import {
    IElmDebugValue,
    IFormatterElement,
    IJsonMLFormatter,
} from '../../CommonTypes';
import JsonML from '../../JsonML';

export default class DebugElment implements IFormatterElement {
    private elmObj: IElmDebugValue;
    private formatter: IJsonMLFormatter;
    private elmLogoElement: JsonML;

    constructor(obj: IElmDebugValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
        this.elmLogoElement = new JsonML('span').withStyle(this.formatter.theme.elmLogoElementStyle);
    }

    public header = () => {
      if (this.elmObj?.value) {
        return new JsonML('span')
            .withChild(this.elmLogoElement)
            .withChild(
                new JsonML('span')
                    .withChild(
                        new JsonML('span')
                            .withStyle(this.formatter.theme.debugTagStyle)
                            .withText(this.elmObj.name)
                    )
                    .withText(': ')
            )
            .withChild(this.formatter.handleHeader(this.elmObj.value));
      }

      return new JsonML('span').withText("WTF");
    }

    public body = () => {
      if (this.elmObj?.value) {
        return this.formatter.handleBody(this.elmObj.value);
      }

      return null;
    }
}


================================================
FILE: src/formatters/elements/DictElement.ts
================================================
import {
    IConfig,
    IElmDebugDictValue,
    IFormatterElement,
    IJsonMLFormatter,
} from '../../CommonTypes';
import JsonML from '../../JsonML';

export default class DictElement implements IFormatterElement {
    private elmObj: IElmDebugDictValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugDictValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = () => {
        if (this.elmObj.value.length === 0) {
            return new JsonML('span')
                .withStyle(this.formatter.theme.greyedStyle)
                .withText('Dict.empty');
        }

        return new JsonML('span')
            .withStyle(this.formatter.theme.dataStructureNameStyle)
            .withText('Dict')
            .withChild(
                new JsonML('span').withText(`(${this.elmObj.value.length})`)
            );
    }

    public body = (config?: IConfig) => {
        const children = this.elmObj.value.map(child => {
            const key = this.formatter.handleHeader(child.key, config);
            const element = new JsonML('span')
                .withChild(
                    new JsonML('span').withStyle(this.formatter.theme.keyElementStyle).withChild(key)
                )
                .withText(': ');

            if (this.formatter.handleBody(child.value, config) === null) {
                element.withStyle('margin-left: 13px;');
            }

            return new JsonML('div').withObject(element, child.value, config);
        });

        return new JsonML('div')
            .withStyle(this.formatter.theme.expandableBorderStyle)
            .withChildren(children);
    }
}


================================================
FILE: src/formatters/elements/Elements.ts
================================================
import * as T from '../../CommonTypes';

import BooleanElement from './BooleanElement';
import BytesElement from './BytesElement';
import CustomTypeElement from './CustomTypeElement';
import DebugElement from './DebugElement';
import DictElement from './DictElement';
import FilesElement from './FilesElement';
import InternalsElement from './InternalsElement';
import ListElement from './ListElement';
import NumberElement from './NumberElement';
import RecordElement from './RecordElement';
import StringElement from './StringElement';
import TupleElement from './TupleElement';
import TypeElement from './TypeElement';

export function toElement(
    obj: T.ElmDebugValueType,
    formatter: T.IJsonMLFormatter
): T.IFormatterElement | null {
    if (obj.type === 'String') {
        return new StringElement(obj as T.IElmDebugStringValue, formatter);
    } else if (obj.type === 'Boolean') {
        return new BooleanElement(obj as T.IElmDebugBoolValue, formatter);
    } else if (T.isElmValue(obj) && obj.type === 'ElmDebug') {
        return new DebugElement(obj, formatter);
    } else if (T.isElmNumberType(obj)) {
        return new NumberElement(obj, formatter);
    } else if (T.isElmTypeValue(obj)) {
        return new TypeElement(obj, formatter);
    } else if (T.isElmCustomValue(obj)) {
        return new CustomTypeElement(obj, formatter);
    } else if (T.isElmDictValue(obj)) {
        return new DictElement(obj, formatter);
    } else if (T.isElmListValue(obj)) {
        return obj.type === 'Tuple'
            ? new TupleElement(obj, formatter)
            : new ListElement(obj, formatter);
    } else if (T.isElmRecordValue(obj)) {
        return new RecordElement(obj, formatter);
    } else if (
        obj.type === 'Function' ||
        obj.type === 'Internals' ||
        obj.type === 'Unit'
    ) {
        return new InternalsElement(obj.type as string, formatter);
    } else if (obj.type === 'Bytes') {
        return new BytesElement(obj as T.IElmDebugValue, formatter);
    } else if (obj.type === 'File') {
        return new FilesElement(obj as T.IElmDebugValue, formatter);
    }

    return null;
}


================================================
FILE: src/formatters/elements/EllipsisElement.ts
================================================
import {
    IElmDebugRecordValue,
    IFormatterElement,
    IJsonMLFormatter,
} from '../../CommonTypes';
import JsonML from '../../JsonML';
import { GreyedOutStyle } from './Styles';

export default class EllipsisElement implements IFormatterElement {
    public header = () => {
        return new JsonML('span').withStyle(GreyedOutStyle).withText('…');
    }
}


================================================
FILE: src/formatters/elements/FilesElement.ts
================================================
import { 
  IElmDebugValue, 
  IFormatterElement,
  IJsonMLFormatter
} from '../../CommonTypes';

import JsonML from '../../JsonML';

export default class FilesElement implements IFormatterElement {
    private elmObj: IElmDebugValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = () => {
        return new JsonML('span')
            .withStyle(this.formatter.theme.bytesStyle)
            .withText(this.elmObj.value);
    }
}


================================================
FILE: src/formatters/elements/InternalsElement.ts
================================================
import { 
  IFormatterElement,
  IJsonMLFormatter
} from '../../CommonTypes';

import JsonML from '../../JsonML';

export default class InternalsElement implements IFormatterElement {
    private elmObj: string;
    private formatter: IJsonMLFormatter;

    constructor(obj: string, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = () => {
        let value = '';

        switch (this.elmObj) {
            case 'Internals':
                value = '<internals>';
                break;
            case 'Function':
                value = '<function>';
                break;
            case 'Unit':
                value = '()';
                break;
        }

        return new JsonML('span')
          .withStyle(this.formatter.theme.greyedItalicsStyle)
          .withText(value);
    }
}


================================================
FILE: src/formatters/elements/ListElement.ts
================================================
import {
    IConfig,
    IElmDebugListValue,
    IFormatterElement,
    IJsonMLFormatter,
} from '../../CommonTypes';
import JsonML from '../../JsonML';

export default class ListElement implements IFormatterElement {
    private elmObj: IElmDebugListValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugListValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = (config?: IConfig) => {
        if (this.elmObj.value.length === 0) {
            return new JsonML('span').withStyle(this.formatter.theme.greyedStyle).withText('[]');
        }
        if (this.elmObj.value.length === 1) {
            return new JsonML('span')
                .withStyle(this.formatter.theme.greyedStyle)
                .withText('[')
                .withChild(
                    new JsonML('span').withChild(
                        this.formatter.handleHeader(
                            this.elmObj.value[0],
                            config
                        )
                    )
                )
                .withText(']');
        }
        return new JsonML('span')
            .withStyle(this.formatter.theme.dataStructureNameStyle)
            .withText(this.elmObj.type)
            .withChild(
                new JsonML('span').withText(`(${this.elmObj.value.length})`)
            );
    }

    public body = (config?: IConfig): JsonML | null => {
        if (this.elmObj.value.length === 0) {
            return null;
        }

        const children = this.elmObj.value.map((child, index) => {
            const element = new JsonML('span')
                .withChild(
                    new JsonML('span')
                        .withStyle(this.formatter.theme.keyElementStyle)
                        .withText(`${index}`)
                )
                .withText(': ');

            if (this.formatter.handleBody(child, config) === null) {
                element.withStyle('margin-left: 13px');
            }

            return new JsonML('div').withObject(element, child);
        });

        return new JsonML('div')
            .withStyle(this.formatter.theme.expandableBorderStyle)
            .withChildren(children);
    }
}


================================================
FILE: src/formatters/elements/NumberElement.ts
================================================
import { 
  IElmDebugNumberValue, 
  IFormatterElement,
  IJsonMLFormatter
} from '../../CommonTypes';
import JsonML from '../../JsonML';

export default class NumberElement implements IFormatterElement {
    private elmObj: IElmDebugNumberValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugNumberValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = () => {
        return new JsonML('span')
            .withStyle(this.formatter.theme.numberStyle)
            .withText(this.elmObj.value);
    }
}


================================================
FILE: src/formatters/elements/RecordElement.ts
================================================
import {
    IConfig,
    IElmDebugRecordValue,
    IFormatterElement,
    IJsonMLFormatter,
} from '../../CommonTypes';
import JsonML from '../../JsonML';
import EllipsisElement from './EllipsisElement';

export default class RecordElement implements IFormatterElement {
    private elmObj: IElmDebugRecordValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugRecordValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = (config: IConfig) => {
        if (!!config && config.level > 2) {
            return new JsonML('span')
                .withText('{ ')
                .withChild(new EllipsisElement().header())
                .withText(' }');
        }
        const keys = Object.keys(this.elmObj.value);
        const children = keys
            .map(k => {
                return new JsonML('span')
                    .withStyle(this.formatter.theme.keyElementStyle)
                    .withText(k +': ')
                    .withChild(
                        this.formatter.handleHeader(
                            this.elmObj.value[k],
                            config
                        )
                    );
            })
            .reduce(
                (accObj, child) => {
                    const lengthWithChild = accObj.size + child.toStr().length;
                    if (accObj.hasEllipsis) {
                        return accObj;
                    }

                    if (lengthWithChild < 50) {
                        accObj.acc.push(child);
                        accObj.size = lengthWithChild;
                    } else {
                        accObj.acc.push(new EllipsisElement().header());
                        accObj.hasEllipsis = true;
                    }

                    return accObj;
                },
                { acc: [] as any[], size: 0, hasEllipsis: false }
            )
            .acc.reduce((acc, child) => {
                acc.push(new JsonML('span').withText(', '));
                acc.push(child);
                return acc;
            }, [] as any[]);

        children.splice(0, 1);

        return new JsonML('span')
            .withText('{ ')
            .withChildren(children)
            .withText(' }');
    }

    public body = (config: IConfig) => {
        const keys = Object.keys(this.elmObj.value);
        const objects = keys.map(k => {
            const keyElement = new JsonML('span')
                .withStyle(this.formatter.theme.keyElementStyle)
                .withText(k)
                .withText(': ');
            if (
                this.formatter.handleBody(this.elmObj.value[k], config) === null
            ) {
                keyElement.withStyle('margin-left: 13px;');
            }

            return new JsonML('div').withObject(
                keyElement,
                this.elmObj.value[k]
            );
        });

        return new JsonML('div')
            .withStyle('margin-left: 15px;')
            .withChildren(objects);
    }
}


================================================
FILE: src/formatters/elements/StringElement.ts
================================================
import { 
  IElmDebugStringValue, 
  IFormatterElement,
  IJsonMLFormatter
} from '../../CommonTypes';

import JsonML from '../../JsonML';

export default class StringElement implements IFormatterElement {
    private elmObj: IElmDebugStringValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugStringValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = () => {
        return new JsonML('span')
            .withStyle(this.formatter.theme.stringStyle)
            .withText(`"${this.elmObj.value}"`);
    }
}


================================================
FILE: src/formatters/elements/Styles.ts
================================================
import {ITheme} from '../../CommonTypes'

// elm colors
// ---------------
// yellow - da9e26
// green  - 81cf46
// darkgreen - 70b53c
// dark   - 34495e
// blue   - da9e26


export const GreyedOutStyle =  'color: grey; font-weight: normal;';

const ElmLogoElementStyle =
    'width: 12px; height: 12px; display: inline-block; position: relative; top: 2px;background-size: contain; background-repeat: no-repeat; margin-right: 0.5em; background-position: center center; background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjI1NnB4IiBoZWlnaHQ9IjI1NnB4IiB2aWV3Qm94PSIwIDAgMjU2IDI1NiIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+Cgk8Zz4KCQk8cG9seWdvbiBmaWxsPSIjNUZCNENCIiBwb2ludHM9IjEyOCAxMzUuMDIyMDI5IDcuMDIyODIxMDUgMjU2IDI0OC45Nzc5NzEgMjU2Ij48L3BvbHlnb24+CgkJPHBvbHlnb24gZmlsbD0iI0VFQTQwMCIgcG9pbnRzPSIyMDIuMzMyMzEzIDE5NS4zMTEwNzUgMjU2IDI0OC45Nzk1NTQgMjU2IDE0MS42NDMzODgiPjwvcG9seWdvbj4KCQk8cG9seWdvbiBmaWxsPSIjNTk2Mjc3IiBwb2ludHM9IjEyMC45Nzc5NzEgMTI4IDAgNy4wMjIwMjkyMSAwIDI0OC45Nzk1NTQiPjwvcG9seWdvbj4KCQk8cG9seWdvbiBmaWxsPSIjNUZCNENCIiBwb2ludHM9IjI1NiAxMTMuODA2Mjg0IDI1NiAwIDE0Mi4xOTI5MjQgMCI+PC9wb2x5Z29uPgoJCTxwb2x5Z29uIGZpbGw9IiM4Q0Q2MzYiIHBvaW50cz0iMTk1LjU4MzUwNCA2Ny40MzQyMTU5IDI1NS44NzIzMTIgMTI3LjcyMzAyNCAxOTUuMzA4NTg5IDE4OC4yODY3NDggMTM1LjAxOTc4IDEyNy45OTc5NCI+PC9wb2x5Z29uPgoJCTxwb2x5Z29uIGZpbGw9IiM4Q0Q2MzYiIHBvaW50cz0iNy4wMjEyMzczNyAwIDYyLjc0NjEyMjggNTUuNzI1Njc3MiAxODMuODc1MzM1IDU1LjcyNTY3NzIgMTI4LjE0OTY1OCAwIj48L3BvbHlnb24+CgkJPHBvbHlnb24gZmlsbD0iI0VFQTQwMCIgcG9pbnRzPSIxMjggMTIwLjk3ODc2MyAxODMuMzIxODM5IDY1LjY1NjEzMTUgNzIuNjc3MzY4OSA2NS42NTYxMzE1Ij48L3BvbHlnb24+Cgk8L2c+Cjwvc3ZnPgo=);';

export const lightTheme: ITheme = {
  booleanStyle: 'color: #65b5ca; font-weight: normal;',
  bytesStyle: 'color: #34495e; font-weight: normal;',
  customTypeNameStyle: 'color: #70b53c; font-weight: bold;',
  dataStructureNameStyle: 'color: #70b53c; font-weight: normal;',
  debugTagStyle: 'color: grey; font-weight: normal;',
  elmLogoElementStyle: ElmLogoElementStyle,
  expandableBorderStyle: 'margin-left: 4px; padding-left: 11px; border-left: 1px solid grey;',
  greyedItalicsStyle: 'color: grey; font-weight: normal; font-style: italic;',
  greyedStyle: 'color: grey; font-weight: normal;',
  keyElementStyle: 'color: #34495e; font-weight: normal; font-style: italic;',
  numberStyle: 'color: #da9e26; font-weight: normal;',
  stringStyle: 'color: #65b5ca; font-weight: normal;',
  typeNameStyle: 'color: #70b53c; font-weight: bold;',
}

export const darkTheme: ITheme = {
  ...lightTheme, 
  debugTagStyle: 'color: white; font-weight: normal;',
  keyElementStyle: 'color: #ed78e6; font-weight: normal; font-style: italic;'
}




================================================
FILE: src/formatters/elements/TupleElement.ts
================================================
import {
    IConfig,
    IElmDebugListValue,
    IFormatterElement,
    IJsonMLFormatter,
} from '../../CommonTypes';
import JsonML from '../../JsonML';
import EllipsisElement from './EllipsisElement';

export default class TupleElement implements IFormatterElement {
    private elmObj: IElmDebugListValue;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugListValue, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = (config?: IConfig) => {
        if (!!config && config.level > 1) {
            return new JsonML('span')
                .withText('( ')
                .withChild(
                    this.formatter.handleHeader(this.elmObj.value[0], config)
                )
                .withChild(new JsonML('span').withText(', '))
                .withChild(new EllipsisElement().header())
                .withText(' )');
        }
        const children = this.elmObj.value
            .map(child => this.formatter.handleHeader(child, config))
            .reduce((acc, child) => {
                acc.push(new JsonML('span').withText(', '));
                acc.push(child);
                return acc;
            }, [] as any[]);

        children.splice(0, 1);

        return new JsonML('span')
            .withText('( ')
            .withChildren(children)
            .withText(' )');
    }

    public body = (config?: IConfig): JsonML | null => {
        if (this.elmObj.value.length <= 1) {
            return null;
        }

        const children = this.elmObj.value.map((child, index) => {
            const element = new JsonML('span')
                .withChild(
                    new JsonML('span')
                        .withStyle(this.formatter.theme.keyElementStyle)
                        .withText(`${index}`)
                )
                .withText(': ');

            if (this.formatter.handleBody(child, config) === null) {
                element.withStyle('margin-left: 13px;');
            }

            return new JsonML('div').withObject(element, child);
        });

        return new JsonML('div')
            .withStyle(this.formatter.theme.expandableBorderStyle)
            .withChildren(children);
    }
}


================================================
FILE: src/formatters/elements/TypeElement.ts
================================================
import { 
  IElmDebugTypeValueType, 
  IFormatterElement,
  IJsonMLFormatter
} from '../../CommonTypes';

import JsonML from '../../JsonML';

export default class TypeElement implements IFormatterElement {
    private elmObj: IElmDebugTypeValueType;
    private formatter: IJsonMLFormatter;

    constructor(obj: IElmDebugTypeValueType, formatter: IJsonMLFormatter) {
        this.elmObj = obj;
        this.formatter = formatter;
    }

    public header = () => {
        return new JsonML('span')
            .withStyle(this.formatter.theme.typeNameStyle)
            .withText(this.elmObj.name);
    }
}


================================================
FILE: src/index.ts
================================================
import * as _ from 'lodash';
import { IElmDebugValue, IThemeOption } from './CommonTypes';
import { parse as elmDebugParse } from './elm-debug-parser';
import { darkTheme, lightTheme  } from './formatters/elements/Styles';
import JsonMLFormatter from './formatters/JsonMLFormatter';
import SimpleFormatter from './formatters/SimpleFormatter';

/* tslint:disable */
declare global {
    interface Window {
        chrome: any;
        __ELM_DEBUG_TRANSFORM_OPTIONS__?: IOptions;
    }
}
/* tslint:enable*/

interface IOptions {
    active?: boolean;
    debug?: boolean;
    simple_mode?: boolean;
    limit?: number;
    theme?: IThemeOption;
}

const defaultOptions: IOptions = {
    active: true,
    debug: false,
    limit: 1000000,
    simple_mode: false,
    theme: "light"
}

export function parse(message: string) : IElmDebugValue {
  return elmDebugParse(message) as IElmDebugValue;
}

export function register(opts: IOptions | undefined): IOptions {

    if(window.__ELM_DEBUG_TRANSFORM_OPTIONS__){
      return window.__ELM_DEBUG_TRANSFORM_OPTIONS__;
    }
    
    const log = console.log;

    if (opts && opts.theme === undefined) {
      opts.theme = window.matchMedia("(prefers-color-scheme: dark)").matches 
        ? "dark" 
        : "light";
    }

    let currentOpts = _.merge(defaultOptions, opts);

    console.log = function() {
        if (!arguments || arguments.length > 1) {
            log.apply(console, arguments as any);
            return;
        }
        const msg = arguments[0];

        if (!msg || !currentOpts.limit || msg.length > currentOpts.limit) {
            log.call(console, msg);
            return;
        }

        if (!currentOpts.limit || msg.length > currentOpts.limit) {
            log.call(console, msg);
            return;
        }
        
        const themeStyle = (currentOpts.theme === "dark") 
          ? darkTheme 
          : lightTheme;

        const formatter =
            !!currentOpts.simple_mode 
                ? new SimpleFormatter()
                : new JsonMLFormatter(themeStyle);
        try {
            if (!!currentOpts.debug) {
                log.call(console, 'Original message:', msg);
            }

            const parsed = parse(msg);

            log.call(
                console,
                JSON.parse(JSON.stringify(formatter.format(parsed)))
            );
        } catch (err) {
            if (!!currentOpts.debug) {
                console.error(`Parsing error: ${err}`);
            }
            log.call(console, msg);
        }
    };

    window.__ELM_DEBUG_TRANSFORM_OPTIONS__ = currentOpts;
    return currentOpts;
}


================================================
FILE: src/peg-file.d.ts
================================================
declare module '*.pegjs' {
    const content: {
        default: string;
    };
    export = content;
}


================================================
FILE: test/builders.ts
================================================
import * as _ from 'lodash';
import {
    IElmDebugListValue,
    IElmDebugRecordValue,
    IElmDebugValue,
    IFormatter,
} from '../src/CommonTypes';
import * as Styles from '../src/formatters/elements/Styles';

export function elmDebug(values: any): IElmDebugValue {
    return { type: 'ElmDebug', name: 'Debug', value: values };
}

export function list(
    values: any[],
    typeName: string = 'List'
): IElmDebugListValue {
    return { type: typeName, value: values };
}

export function record(values: { [key: string]: any }): IElmDebugRecordValue {
    return { type: 'Record', value: values };
}

export function n(val: number) {
    return { type: 'Number', value: val };
}

export function customType(name: string, values: any[]) {
    return { type: 'Custom', name, value: values };
}

export function type(name: string) {
    return { type: 'Type', name };
}

export function dict(dictionary: object) {
    return {
        type: 'Dict',
        value: _.toPairs(dictionary).map(item => {
            return { key: item[0], value: item[1] };
        }),
    };
}
export function tuple(values: any[]): IElmDebugListValue {
    return { type: 'Tuple', value: values };
}

export function bool(value: boolean) {
    return { type: 'Boolean', value };
}

export function str(value: string) {
    return { type: 'String', value };
}

export function MLDebug(values: any[]): any[] {
    return [
        'span',
        {},
        ['span', { style: Styles.lightTheme.elmLogoElementStyle }],
        ['span', {}, ['span', { style: Styles.lightTheme.debugTagStyle }, 'Debug'], ': '],
        ...values,
    ];
}
export function MLString(value: string): any[] {
    return ['span', { style: Styles.lightTheme.stringStyle }, `"${value}"`];
}

export function MLNumber(num: number): any[] {
    return ['span', { style: Styles.lightTheme.numberStyle }, num.toString()];
}

export function MLBool(value: boolean): any[] {
    return ['span', { style: Styles.lightTheme.booleanStyle }, value ? 'True' : 'False'];
}

export function MLList(typeName: string, length: number): any[] {
    if (length === 0) {
        return ['span', { style: 'color: grey; font-weight: normal;' }, '[]'];
    } else {
        return [
            'span',
            { style: Styles.lightTheme.dataStructureNameStyle },
            typeName,
            ['span', {}, `(${length})`],
        ];
    }
}

export function MLCustomType(name: string, value?: any[]): any[] {
    if (value === undefined) {
        return ['span', { style: Styles.lightTheme.customTypeNameStyle }, name];
    }

    return [
        'span',
        { style: Styles.lightTheme.customTypeNameStyle },
        name + ' ',
        ...value,
    ];
}

export function MLTuple(values: any[]): any[] {
    const valuesWithCommas = values.reduce((acc: any[], v) => {
        acc.push(['span', {}, ', ']);
        acc.push(v);
        return acc;
    }, []);
    valuesWithCommas.splice(0, 1);
    valuesWithCommas.push(' )');

    return ['span', {}, '( ', ...valuesWithCommas];
}

export function MLEllipsis(): any[] {
    return ['span', { style: Styles.lightTheme.greyedStyle }, '…'];
}

export function MLRecord(values: any[]) {
    const valuesWithCommas = values.reduce((acc, item) => {
        acc.push(['span', {}, ', ']);
        acc.push(item);
        return acc;
    }, []);

    valuesWithCommas.splice(0, 1);

    return ['span', {}, '{ '].concat(valuesWithCommas).concat([' }']);
}

export function MLRecordValue(name: string, value: any): any[] {
    return ['span', { style: Styles.lightTheme.keyElementStyle }, name + ': ', value];
}
export function MLKeyValueBody(keyName: string, object: any): any[] {
    const jsonML = [
        'span',
        { style: Styles.lightTheme.keyElementStyle + 'margin-left: 13px;' },
        keyName,
        ': ',
    ];

    const key = {
        attributes: {
            style: Styles.lightTheme.keyElementStyle + 'margin-left: 13px;',
        },
        jsonML,
    };

    return [
        'div',
        {},
        ['object', { config: { elmFormat: true, key }, object }],
    ];
}

export function MLBody(children: any[]) {
    return ['div', { style: 'margin-left: 15px;' }, ...children];
}

export function MLObject(child: any, key: any) {
    return ['object', { object: child, config: { elmFormat: true, key } }];
}


================================================
FILE: test/jsonml-formatter.spec.ts
================================================
import { describe, expect, it, assert, beforeEach } from 'vitest';
import { IJsonMLFormatter } from '../src/CommonTypes';
import * as Styles from '../src/formatters/elements/Styles';
import JsonMLFormatter from '../src/formatters/JsonMLFormatter';
import * as B from './builders';

let formatter: IJsonMLFormatter;

beforeEach(() => {
    formatter = new JsonMLFormatter(Styles.lightTheme);
});

describe('JSONML formatting', () => {
    describe('Header values', () => {
        describe('should return values for simple values', () => {
            it('string', () => {
                const value = B.str('string');
                const expected = [B.MLString('string')];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });

            it('number', () => {
                const value = B.n(1);
                const expected = [B.MLNumber(1)];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });

            it('boolean True', () => {
                const value = B.bool(true);
                const expected = [B.MLBool(true)];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });

            it('boolean False', () => {
                const value = B.bool(false);
                const expected = [B.MLBool(false)];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
        });
        describe('should return values for array like structures', () => {
            it('Empty list', () => {
                const value = B.list([], 'List');
                const expected = [B.MLList('List', 0)];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });

            it('Array with one value', () => {
                const value = B.list([B.n(1)], 'Array');
                const expected = [
                    [
                        'span',
                        { style: Styles.lightTheme.greyedStyle },
                        '[',
                        ['span', {}, B.MLNumber(1)],
                        ']',
                    ],
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });

            it('Set with more values', () => {
                const value = B.list(['a', 'b', 'c'], 'Set');
                const expected = [B.MLList('Set', 3)];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });

            it('Tuple with three values', () => {
                const value = B.tuple([B.str('a'), B.str('b'), B.str('c')]);
                const expected = [
                    B.MLTuple([
                        B.MLString('a'),
                        B.MLString('b'),
                        B.MLString('c'),
                    ]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('Nested tuple should be truncated', () => {
                const value = B.tuple([
                    B.tuple([B.tuple([B.str('a'), B.str('b')]), B.str('c')]),
                    B.str('d'),
                ]);
                const expected = [
                    B.MLTuple([
                        B.MLTuple([
                            B.MLTuple([B.MLString('a'), B.MLEllipsis()]),
                            B.MLEllipsis(),
                        ]),
                        B.MLString('d'),
                    ]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
        });
        describe('should handle Types and Custom types', () => {
            it('Type', () => {
                const value = B.type('Nothing');
                const expected = [B.MLCustomType('Nothing')];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('CustomType without values', () => {
                const value = B.customType('CustomType', []);
                const expected = [B.MLCustomType('CustomType')];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('CustomType with one value', () => {
                const value = B.customType('CustomType', [B.n(1)]);
                const expected = [
                    B.MLCustomType('CustomType', [B.MLNumber(1)]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('CustomType with two terminal values', () => {
                const value = B.customType('CustomType', [B.n(1), B.n(1)]);
                const expected = [
                    B.MLCustomType('CustomType', [
                        ['span', {}, B.MLNumber(1)],
                        ['span', {}, ' '],
                        ['span', {}, B.MLNumber(1)],
                    ]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('CustomType with variants as custom types with values', () => {
                const value = B.customType('Tree', [
                    B.customType('Left', [B.n(1), B.n(2)]),
                    B.customType('Right', [B.n(3), B.n(4)]),
                ]);
                const expected = [
                    B.MLCustomType('Tree', [
                        [
                            'span',
                            {},
                            [
                                'span',
                                {},
                                '( ',
                                B.MLCustomType('Left', [B.MLEllipsis()]),
                                ' )',
                            ],
                        ],
                        ['span', {}, ' '],
                        [
                            'span',
                            {},
                            [
                                'span',
                                {},
                                '( ',
                                B.MLCustomType('Right', [B.MLEllipsis()]),
                                ' )',
                            ],
                        ],
                    ]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
        });
        describe('should handle Records', () => {
            it('simple record', () => {
                const value = B.record({ name: B.str('Name') });
                const expected = [
                    B.MLRecord([B.MLRecordValue('name', B.MLString('Name'))]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('record with two values', () => {
                const value = B.record({ name: B.str('Name'), age: B.n(12) });
                const expected = [
                    B.MLRecord([
                        B.MLRecordValue('name', B.MLString('Name')),
                        B.MLRecordValue('age', B.MLNumber(12)),
                    ]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('record with long child content should be truncated to 50 chars', () => {
                const value = B.record({
                    name: B.str('Name'),
                    string: B.str(
                        'Some really long string to simulate long content for the record'
                    ),
                });
                const expected = [
                    B.MLRecord([
                        B.MLRecordValue('name', B.MLString('Name')),
                        B.MLEllipsis(),
                    ]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('record with long child content continued with short child content shoul not show the short one', () => {
                const value = B.record({
                    a: B.str('a'),
                    b: B.str(
                        'Some really long string to simulate long content for the record'
                    ),
                    c: B.str('c'),
                });
                const expected = [
                    B.MLRecord([
                        B.MLRecordValue('a', B.MLString('a')),
                        B.MLEllipsis(),
                    ]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });

            it('record nested in the custom value should be shown with ellipsis', () => {
                const value = B.customType('Tree', [
                    B.list([B.record({ name: B.str('Name'), age: B.n(12) })]),
                ]);

                const expected = [
                    B.MLCustomType('Tree', [
                        [
                            'span',
                            { style: Styles.lightTheme.greyedStyle },
                            '[',
                            [
                                'span',
                                {},
                                ['span', {}, '{ ', B.MLEllipsis(), ' }'],
                            ],
                            ']',
                        ],
                    ]),
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
        });

        describe('should return values for Dict', () => {
            it('Empty dict', () => {
                const value = B.dict({});
                const expected = [
                    [
                        'span',
                        { style: 'color: grey; font-weight: normal;' },
                        'Dict.empty',
                    ],
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('Nonempty dict should show Dict(#count)', () => {
                const value = B.dict({ a: 'b', b: 'a' });

                const expected = [
                    [
                        'span',
                        { style: Styles.lightTheme.dataStructureNameStyle },
                        'Dict',
                        ['span', {}, '(2)'],
                    ],
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
        });
        describe('should return values for internals', () => {
            it('Function', () => {
                const value = { type: 'Function' };
                const expected = [
                    [
                        'span',
                        {
                            style:
                                'color: grey; font-weight: normal; font-style: italic;',
                        },
                        '<function>',
                    ],
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('Internals', () => {
                const value = { type: 'Internals' };
                const expected = [
                    [
                        'span',
                        {
                            style:
                                'color: grey; font-weight: normal; font-style: italic;',
                        },
                        '<internals>',
                    ],
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
            it('Unit', () => {
                const value = { type: 'Unit' };
                const expected = [
                    [
                        'span',
                        {
                            style:
                                'color: grey; font-weight: normal; font-style: italic;',
                        },
                        '()',
                    ],
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
        });
        describe('should handle values for bytes and files', () => {
            it('Bytes', () => {
                const value = { type: 'Bytes', value: 1234 };
                const expected = [
                    [
                        'span',
                        {
                            style: Styles.lightTheme.bytesStyle,
                        },
                        '1234 B',
                    ],
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });

            it('Files', () => {
                const value = { type: 'File', value: 'Name-of_the.file' };
                const expected = [
                    [
                        'span',
                        {
                            style: Styles.lightTheme.bytesStyle,
                        },
                        'Name-of_the.file',
                    ],
                ];

                expect(
                    formatter.handleHeader(B.elmDebug(value)).toJSONML()
                ).to.deep.equal(B.MLDebug(expected));
            });
        });
    });
    describe('Body values', () => {
        describe('Values without body should return null', () => {
            it('String', () => {
                const value = B.str('String');

                assert.isNull(formatter.handleBody(value));
            });

            it('Number', () => {
                const value = B.n(12);

                assert.isNull(formatter.handleBody(value));
            });

            it('Boolean', () => {
                const value = B.bool(true);

                assert.isNull(formatter.handleBody(value));
            });

            it('Unit', () => {
                const value = { type: 'Unit' };

                assert.isNull(formatter.handleBody(value));
            });
            it('Function', () => {
                const value = { type: 'Function' };

                assert.isNull(formatter.handleBody(value));
            });
            it('Internals', () => {
                const value = { type: 'Internals' };

                assert.isNull(formatter.handleBody(value));
            });
            it('Bytes', () => {
                const value = { type: 'Bytes', value: 1234 };

                assert.isNull(formatter.handleBody(value));
            });
            it('Files', () => {
                const value = { type: 'File', value: 'Some_file.name' };

                assert.isNull(formatter.handleBody(value));
            });
        });

        describe('List values', () => {
            it('Empty list should not be expandable', () => {
                const value = B.list([]);

                assert.isNull(formatter.handleBody(value));
            });

            it('List with value should be expandable', () => {
                const value = B.list([B.n(1)]);

                assert.isNotNull(formatter.handleBody(value));
            });
        });

        describe.skip('should handle body of the record values', () => {
            it('Record with one value', () => {
                const value = B.record({ age: B.n(12) });
                const expected = [B.MLKeyValueBody('age', B.n(12))];

                expect(formatter.handleBody(value)?.toJSONML()).to.deep.equal(
                    B.MLBody(expected)
                );
            });
        });
        describe.skip('should return values for array like structures', () => {
            it('Empty list', () => {
                const value = B.list([], 'List');
                const expected: any = null;

                expect(formatter.handleBody(value)).to.deep.equal(expected);
            });
            it('Array with 2 values', () => {
                const value = B.list([B.n(2), B.n(3)], 'Array');
                const expected: any = [
                    B.MLKeyValueBody('[0]', B.MLNumber(2)),
                    B.MLKeyValueBody('[1]', B.MLNumber(3)),
                ];

                expect(formatter.handleBody(value)?.toJSONML()).to.deep.equal(
                    B.MLBody(expected)
                );
            });
        });
    });
});


================================================
FILE: test/jsonml.spec.ts
================================================
import { describe, expect, it} from 'vitest';
import JsonML from '../src/JsonML';

describe('JsonML', () => {
    it('should return span with empty values after construction', () => {
        expect(new JsonML('span').toJSONML()).to.deep.equal(['span', {}]);
    });

    it('should return span with style attributes', () => {
        expect(
            new JsonML('span').withStyle('color: red;').toJSONML()
        ).to.deep.equal(['span', { style: 'color: red;' }]);
    });
    it('should return div with text child node', () => {
        expect(new JsonML('div').withText('child').toJSONML()).to.deep.equal([
            'div',
            {},
            'child',
        ]);
    });
    it('should return div with two child nodes', () => {
        const child = new JsonML('span');
        const parent = new JsonML('div').withChild(child);
        expect(parent.toJSONML()).to.deep.equal(['div', {}, ['span', {}]]);
    });
});


================================================
FILE: test/parser.spec.ts
================================================
import { describe, expect, should, it} from 'vitest';
import { readFileSync } from 'fs';
import { generate } from 'pegjs';
import * as B from './builders';

const parser = generate(readFileSync('src/elm-debug.pegjs', 'utf8'));

describe('Parsing', () => {
    describe('Basic type parsing', () => {
        it('without tag tag', () => {
            expect(parser.parse(': True').value).to.deep.equal(B.bool(true));
        });

        it('with multiple `:` in the tag', () => {
            expect(parser.parse('tag string :: : True').value).to.deep.equal(
                B.bool(true)
            );
        });

        it('with numbers in the tag', () => {
          expect(parser.parse('1234567890: True').value).to.deep.equal(
                B.bool(true)
            );
        });

        it('with non alphabetic characters', () => {
          expect(parser.parse('!@#$%^&*()_+-=[]{}|\\,./<>?~`: True').value).to.deep.equal(
                B.bool(true)
            );
        });

        it('with emojis in tag', () => {
          expect(parser.parse('with 1 Emoji 👍 : False').value).to.deep.equal(
                B.bool(false)
            );
        });


        it('Boolean', () => {
            expect(parser.parse('tag string: True').value).to.deep.equal(
                B.bool(true)
            );
        });

        it('Integer', () => {
            expect(parser.parse('integer: 123').value).to.deep.equal({
                type: 'Number',
                value: 123,
            });
        });

        it('Negative Integer', () => {
            expect(parser.parse('integer: - 123').value).to.deep.equal({
                type: 'Number',
                value: -123,
            });
        });

        describe('Non number values', () => {
            it('Infinity', () => {
                expect(parser.parse('integer: Infinity').value).to.deep.equal({
                    type: 'Number',
                    value: 'Infinity',
                });
            });
            it('-Infinity', () => {
                expect(parser.parse('integer: -Infinity').value).to.deep.equal({
                    type: 'Number',
                    value: '-Infinity',
                });
            });
            it('NaN', () => {
                expect(parser.parse('integer: NaN').value).to.deep.equal({
                    type: 'Number',
                    value: 'NaN',
                });
            });
        });

        it('Float', () => {
            expect(parser.parse('float: 123.45').value).to.deep.equal({
                type: 'Number',
                value: 123.45,
            });
        });

        it('Negative Float', () => {
            expect(parser.parse('float: - 123.45').value).to.deep.equal({
                type: 'Number',
                value: -123.45,
            });
        });

        it('String', () => {
            expect(parser.parse('string: "Lorem Ipsum."').value).to.deep.equal(
                B.str('Lorem Ipsum.')
            );
        });

        it('String with escaped quotes', () => {
            expect(
                parser.parse('string: "Lorem Ipsum. \\"with some quotes\\""')
                    .value
            ).to.deep.equal(B.str('Lorem Ipsum. "with some quotes"'));
        });
    });

    describe('Tuples', () => {
        it('Empty tuple', () => {
            expect(parser.parse('tuple: ()').value).to.deep.equal({
                type: 'Unit',
            });
        });

        it('Basic tuple', () => {
            expect(parser.parse('tuple: (123, False)').value).to.deep.equal({
                type: 'Tuple',
                value: [{ type: 'Number', value: 123 }, B.bool(false)],
            });
        });

        it('Arbitrary long tuple', () => {
            expect(
                parser.parse('tuple: (123, "Some string.", True, 12.34)').value
            ).to.deep.equal({
                type: 'Tuple',
                value: [
                    { type: 'Number', value: 123 },
                    B.str('Some string.'),
                    B.bool(true),
                    { type: 'Number', value: 12.34 },
                ],
            });
        });

        it('Tuple in tuple', () => {
            expect(
                parser.parse('tuple in tuple: (123, (True, 12.34))').value
            ).to.deep.equal({
                type: 'Tuple',
                value: [
                    { type: 'Number', value: 123 },
                    {
                        type: 'Tuple',
                        value: [B.bool(true), { type: 'Number', value: 12.34 }],
                    },
                ],
            });
        });
        it('Tuple with one item is not a tuple', () => new Promise(done => { 
          try {
                parser.parse('not tuple: (123)');
          }
          catch (err: any){
            expect(err.name).to.eql('SyntaxError')
          }
          done();
        }));
    });

    describe('List', () => {
        it('Empty list', () => {
            expect(parser.parse('list: []').value).to.deep.equal({
                type: 'List',
                value: [],
            });
        });

        it('Singleton', () => {
            expect(parser.parse('singleton: [1]').value).to.deep.equal({
                type: 'List',
                value: [{ type: 'Number', value: 1 }],
            });
        });
        it('List', () => {
            expect(
                parser.parse('list: ["s1","s2","s3","s4"]').value
            ).to.deep.equal({
                type: 'List',
                value: [B.str('s1'), B.str('s2'), B.str('s3'), B.str('s4')],
            });
        });
        it('List of tuples', () => {
            expect(
                parser.parse('list: [("s1","s2"),("s3","s4")]').value
            ).to.deep.equal({
                type: 'List',
                value: [
                    { type: 'Tuple', value: [B.str('s1'), B.str('s2')] },
                    { type: 'Tuple', value: [B.str('s3'), B.str('s4')] },
                ],
            });
        });
    });

    describe('Dict', () => {
        it('Empty dict', () => {
            expect(parser.parse('dict: Dict.fromList []').value).to.deep.equal({
                type: 'Dict',
                value: [],
            });
        });
        it('Filled dict', () => {
            expect(
                parser.parse('dict: Dict.fromList [(1,"a"),(2,"b")]').value
            ).to.deep.equal({
                type: 'Dict',
                value: [
                    { key: { type: 'Number', value: 1 }, value: B.str('a') },
                    { key: { type: 'Number', value: 2 }, value: B.str('b') },
                ],
            });
        });
    });

    describe('Set', () => {
        it('Empty Set', () => {
            expect(parser.parse('Set: Set.fromList []').value).to.deep.equal({
                type: 'Set',
                value: [],
            });
        });
        it('Filled set', () => {
            expect(
                parser.parse('Set: Set.fromList ["1","2","3"]').value
            ).to.deep.equal({
                type: 'Set',
                value: [B.str('1'), B.str('2'), B.str('3')],
            });
        });
    });

    describe('Array', () => {
        it('Empty Array', () => {
            expect(
                parser.parse('Array: Array.fromList []').value
            ).to.deep.equal({ type: 'Array', value: [] });
        });
        it('Filled Array', () => {
            expect(
                parser.parse('Array: Array.fromList ["1","2","3"]').value
            ).to.deep.equal({
                type: 'Array',
                value: [B.str('1'), B.str('2'), B.str('3')],
            });
        });
    });

    describe('Record', () => {
        it('Empty record', () => {
            expect(parser.parse('record: {}').value).to.deep.equal({
                type: 'Record',
                value: {},
            });
        });
        it('Record', () => {
            expect(
                parser.parse('record: { name = "Name" }').value
            ).to.deep.equal({
                type: 'Record',
                value: { name: B.str('Name') },
            });
        });
        it('Record with more values', () => {
            expect(
                parser.parse(
                    'record: { name = "Name", warning = Nothing, waves = [] }'
                ).value
            ).to.deep.equal({
                type: 'Record',
                value: {
                    name: B.str('Name'),
                    warning: { type: 'Type', name: 'Nothing' },
                    waves: { type: 'List', value: [] },
                },
            });
        });
        it('Nested records', () => {
            expect(
                parser.parse(
                    'record: { name = "Name", warning = { name = Nothing, waves = [] } }'
                ).value
            ).to.deep.equal({
                type: 'Record',
                value: {
                    name: B.str('Name'),
                    warning: {
                        type: 'Record',
                        value: {
                            name: { type: 'Type', name: 'Nothing' },
                            waves: { type: 'List', value: [] },
                        },
                    },
                },
            });
        });
        it('Vector', () => {
            expect(
                parser.parse('vec: { 0 = 0, 1 = 0, 2 = 0 }').value
            ).to.deep.equal({
                type: 'Record',
                value: {
                    '0': { type: 'Number', value: 0 },
                    '1': { type: 'Number', value: 0 },
                    '2': { type: 'Number', value: 0 },
                },
            });
        });
    });

    describe('Custom types', () => {
        it('Custom type name with number', () => {
            expect(
                parser.parse('custom type: User1 "Adam"').value
            ).to.deep.equal({
                name: 'User1',
                type: 'Custom',
                value: [B.str('Adam')],
            });
        });
        it('Custom type name with underscore', () => {
            expect(
                parser.parse('custom type: User_name "Adam"').value
            ).to.deep.equal({
                name: 'User_name',
                type: 'Custom',
                value: [B.str('Adam')],
            });
        });
        it('Custom type with one value', () => {
            expect(
                parser.parse('custom type: User "Adam"').value
            ).to.deep.equal({
                name: 'User',
                type: 'Custom',
                value: [B.str('Adam')],
            });
        });
        it('Custom type with more values', () => {
            expect(
                parser.parse('custom type: User "Adam" 123 (1,False)').value
            ).to.deep.equal({
                name: 'User',
                type: 'Custom',
                value: [
                    B.str('Adam'),
                    { type: 'Number', value: 123 },
                    {
                        type: 'Tuple',
                        value: [{ type: 'Number', value: 1 }, B.bool(false)],
                    },
                ],
            });
        });
        it('Custom type in parenthesis', () => {
            expect(
                parser.parse('custom type: (User (Data "Adam" 123 (1,False)))')
                    .value
            ).to.deep.equal({
                name: 'User',
                type: 'Custom',
                value: [
                    {
                        name: 'Data',
                        type: 'Custom',
                        value: [
                            B.str('Adam'),
                            { type: 'Number', value: 123 },
                            {
                                type: 'Tuple',
                                value: [
                                    { type: 'Number', value: 1 },
                                    B.bool(false),
                                ],
                            },
                        ],
                    },
                ],
            });
        });
        it('Custom type in parenthesis with record', () => {
            expect(
                parser.parse('custom type: (User { age = 23 })').value
            ).to.deep.equal({
                name: 'User',
                type: 'Custom',
                value: [
                    {
                        type: 'Record',
                        value: { age: { type: 'Number', value: 23 } },
                    },
                ],
            });
        });
        it('Custom type with two Nothings next to each other', () => {
            expect(
                parser.parse(
                    'custom type: CacheKey (Id "cache_id") Nothing Nothing'
                ).value
            ).to.deep.equal({
                name: 'CacheKey',
                type: 'Custom',
                value: [
                    {
                      name: 'Id',
                      type: 'Custom',
                      value: [
                        {type: 'String',
                        value: "cache_id"
                        }
                      ]
                    }
                    ,{
                      name: 'Nothing',
                      type: 'Type',
                    }
                    ,{
                      name: 'Nothing',
                      type: 'Type',
                    }
                ],

            });
        });
        it('Custom type with more values in parenthesis', () => {
            expect(
                parser.parse(
                    'custom type: User (Data "(tuple, in, string)" 123 (1,False))'
                ).value
            ).to.deep.equal({
                name: 'User',
                type: 'Custom',
                value: [
                    {
                        name: 'Data',
                        type: 'Custom',
                        value: [
                            B.str('(tuple, in, string)'),
                            { type: 'Number', value: 123 },
                            {
                                type: 'Tuple',
                                value: [
                                    { type: 'Number', value: 1 },
                                    B.bool(false),
                                ],
                            },
                        ],
                    },
                ],
            });
        });
    });

    describe('Internals', () => {
        it('Function value', () => {
            expect(parser.parse('custom type: <function>').value).to.deep.equal(
                { type: 'Function' }
            );
        });
        it('Internals value', () => {
            expect(
                parser.parse('custom type: <internals>').value
            ).to.deep.equal({ type: 'Internals' });
        });
    });

    describe('Bytes', () => {
        it('Bytes', () => {
            expect(parser.parse('bytes: <24294 bytes>').value).to.deep.equal({
                type: 'Bytes',
                value: 24294,
            });
        });
    });

    describe('File', () => {
        it('File', () => {
            expect(
                parser.parse(
                    'file: <Some-name-[and]_extrachars(0000239649).extension>'
                ).value
            ).to.deep.equal({
                type: 'File',
                value: 'Some-name-[and]_extrachars(0000239649).extension',
            });
        });
    });
});


================================================
FILE: test/simple-formatter.spec.ts
================================================
import { beforeEach, describe, expect, should, it} from 'vitest';
import { IFormatter } from '../src/CommonTypes';
import SimpleFormatter from '../src/formatters/SimpleFormatter';
import * as B from './builders';

let formatter: IFormatter;

beforeEach(() => {
    formatter = new SimpleFormatter();
});

describe('Simple formatting', () => {
    describe('should return values for simple values', () => {
        it('string', () => {
            expect(formatter.format(B.elmDebug(B.str('string')))).to.deep.equal(
                {
                    Debug: 'string',
                }
            );
        });

        it('int', () => {
            expect(formatter.format(B.elmDebug(B.n(123)))).to.deep.equal({
                Debug: 123,
            });
        });

        it('bool', () => {
            expect(formatter.format(B.elmDebug(B.bool(false)))).to.deep.equal({
                Debug: false,
            });
        });
        it('unit', () => {
            expect(
                formatter.format(B.elmDebug({ type: 'Unit' }))
            ).to.deep.equal({
                Debug: '()',
            });
        });
    });

    describe('should return values for lists', () => {
        it('empty list', () => {
            expect(
                formatter.format(
                    B.elmDebug(
                        B.record({
                            list: B.list([]),
                        })
                    )
                )
            ).to.deep.equal({ Debug: { list: [] } });
        });

        it('numbers list', () => {
            expect(
                formatter.format(
                    B.elmDebug(
                        B.record({
                            list: B.list([B.n(1), B.n(2), B.n(3)]),
                        })
                    )
                )
            ).to.deep.equal({ Debug: { list: [1, 2, 3] } });
        });
    });

    describe('should return values for custom types', () => {
        it('Simple type', () => {
            expect(
                formatter.format(
                    B.elmDebug(
                        B.record({
                            list: B.list([]),
                        })
                    )
                )
            ).to.deep.equal({ Debug: { list: [] } });
        });

        it('list of Maybe types', () => {
            expect(
                formatter.format(
                    B.elmDebug(
                        B.record({
                            list: B.list([
                                B.type('Nothing'),
                                B.customType('Just', [
                                    B.list([B.str('String')]),
                                ]),
                                B.type('Nothing'),
                                B.type('Nothing'),
                            ]),
                        })
                    )
                )
            ).to.deep.equal({
                Debug: {
                    list: [
                        'Nothing',
                        { Just: ['String'] },
                        'Nothing',
                        'Nothing',
                    ],
                },
            });
        });

        it('Custom with just one value', () => {
            expect(
                formatter.format(
                    B.elmDebug(B.customType('Just', [B.str('String')]))
                )
            ).to.deep.equal({ Debug: { Just: 'String' } });
        });

        it('Nested custom', () => {
            expect(
                formatter.format(
                    B.elmDebug(
                        B.customType('Just', [
                            B.customType('Node', [
                                B.customType('Leaf', [B.n(1)]),
                                B.customType('Leaf', [B.n(2)]),
                            ]),
                        ])
                    )
                )
            ).to.deep.equal({
                Debug: { Just: { Node: [{ Leaf: 1 }, { Leaf: 2 }] } },
            });
        });
    });

    describe('should return values for records', () => {
        it('simple record', () => {
            expect(
                formatter.format(
                    B.elmDebug(B.record({ name: B.str('Name'), age: B.n(12) }))
                )
            ).to.deep.equal({ Debug: { name: 'Name', age: 12 } });
        });
    });

    describe('should return values for dictionaries', () => {
        it('handles simple record', () => {
            const value = B.elmDebug(
                B.dict({ name: B.str('Name'), age: B.str('12') })
            );

            expect(formatter.format(value)).to.deep.equal({
                Debug: {
                    age: '12',
                    name: 'Name',
                },
            });
        });
    });

    describe('should return values for internals', () => {
        it('Function', () => {
            const value = B.elmDebug({ type: 'Function' });

            expect(formatter.format(value)).to.deep.equal({
                Debug: '<function>',
            });
        });

        it('Internals', () => {
            const value = B.elmDebug({ type: 'Internals' });

            expect(formatter.format(value)).to.deep.equal({
                Debug: '<internals>',
            });
        });
    });

    describe('should return values for bytes and files', () => {
        it('Bytes', () => {
            const value = B.elmDebug({ type: 'Bytes', value: 1234 });

            expect(formatter.format(value)).to.deep.equal({ Debug: '1234 B' });
        });

        it('Files', () => {
            const value = B.elmDebug({
                type: 'File',
                value: 'Name-of_the.file',
            });

            expect(formatter.format(value)).to.deep.equal({
                Debug: 'Name-of_the.file',
            });
        });
    });
});


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "ESNext",
    "lib": [
      "DOM",
      "DOM.Iterable",
      "ESNext"
    ],
    "types": [
      "vite/client",
      "node",
    ],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noImplicitAny": false,
    "noEmit": true,
    "jsx": "preserve",
    "baseUrl": "./",
    "paths": {
      "~/*": [
        "src/*"
      ]
    }
  },
  "include": [
    "./src"
  ],
  "exclude": [
    "src/elm-debug-parser.ts",
    "node_modules",
    "elm-app"
  ]
}


================================================
FILE: tslint.json
================================================
{
  "extends": [
    "tslint:latest",
    "tslint-config-prettier"
  ],
  "linterOptions": {
    "exclude": [
      "src/**/elm-debug-parser.ts",
      "elm-app"
    ]
  },
  "jsRules": {},
  "rules": {
    "no-console": [
      false
    ],
    "max-classes-per-file": [
      false
    ]
  },
  "rulesDirectory": []
}


================================================
FILE: vite.config.js
================================================
// vite.config.ts
import { defineConfig } from "vite";

import typescript from "@rollup/plugin-typescript";
import path from "path";
import { typescriptPaths } from "rollup-plugin-typescript-paths";

export default defineConfig({
  plugins: [],
  resolve: {
    alias: [
      {
        find: "~",
        replacement: path.resolve(__dirname, "./src"),
      },
    ],
  },
  server: {
    port: 3000,
  },
  build: {
    manifest: true,
    minify: true,
    reportCompressedSize: true,
    lib: {
      entry: path.resolve(__dirname, "src/index.ts"),
      fileName: "index",
      formats: ["es", "cjs"],
    },
    rollupOptions: {
      external: [],
      plugins: [
        typescriptPaths({
          preserveExtensions: true,
        }),
        typescript({
          sourceMap: false,
          declaration: true,
          outDir: "dist",
        }),
      ],
    },
  },
});


================================================
FILE: vitest.config.js
================================================
import { defineConfig } from 'vitest/config';

export default defineConfig({
  define: { global: "window", "process.env": process.env },
  test: {
    environment: "jsdom"
  }
});
Download .txt
gitextract_lexnr1a5/

├── .gitignore
├── .mocharc.json
├── .npmignore
├── .prettierrc
├── .tool-versions
├── CHANGELOG.md
├── LICENSE
├── README.md
├── example/
│   ├── .gitignore
│   ├── .tool-versions
│   ├── README.md
│   ├── elm-tooling.json
│   ├── elm.json
│   ├── index.html
│   ├── package.json
│   ├── src/
│   │   ├── Main.elm
│   │   └── index.js
│   └── vite.config.js
├── package.json
├── src/
│   ├── CommonTypes.ts
│   ├── JsonML.ts
│   ├── elm-debug-parser.ts
│   ├── elm-debug.pegjs
│   ├── formatters/
│   │   ├── JsonMLFormatter.ts
│   │   ├── SimpleFormatter.ts
│   │   └── elements/
│   │       ├── BooleanElement.ts
│   │       ├── BytesElement.ts
│   │       ├── CustomTypeElement.ts
│   │       ├── DebugElement.ts
│   │       ├── DictElement.ts
│   │       ├── Elements.ts
│   │       ├── EllipsisElement.ts
│   │       ├── FilesElement.ts
│   │       ├── InternalsElement.ts
│   │       ├── ListElement.ts
│   │       ├── NumberElement.ts
│   │       ├── RecordElement.ts
│   │       ├── StringElement.ts
│   │       ├── Styles.ts
│   │       ├── TupleElement.ts
│   │       └── TypeElement.ts
│   ├── index.ts
│   └── peg-file.d.ts
├── test/
│   ├── builders.ts
│   ├── jsonml-formatter.spec.ts
│   ├── jsonml.spec.ts
│   ├── parser.spec.ts
│   └── simple-formatter.spec.ts
├── tsconfig.json
├── tslint.json
├── vite.config.js
└── vitest.config.js
Download .txt
SYMBOL INDEX (114 symbols across 22 files)

FILE: src/CommonTypes.ts
  type IFormatter (line 3) | interface IFormatter {
  type IJsonMLFormatter (line 7) | interface IJsonMLFormatter {
  type IDevToolsFormatter (line 13) | interface IDevToolsFormatter {
  type IFormatterElement (line 19) | interface IFormatterElement {
  type IThemeOption (line 24) | type IThemeOption = "dark" | "light"
  type ITheme (line 26) | interface ITheme {
  type IConfig (line 42) | interface IConfig {
  type ElmDebugValueType (line 49) | type ElmDebugValueType =
  type IElmDebugValue (line 60) | interface IElmDebugValue {
  type IElmDebugCustomValue (line 66) | interface IElmDebugCustomValue {
  type IElmDebugTypeValueType (line 72) | interface IElmDebugTypeValueType {
  type IElmDebugListValue (line 77) | interface IElmDebugListValue {
  type IElmDebugRecordValue (line 82) | interface IElmDebugRecordValue {
  type IElmDebugDictValue (line 87) | interface IElmDebugDictValue {
  type IElmDebugNumberValue (line 92) | interface IElmDebugNumberValue {
  type IElmDebugStringValue (line 97) | interface IElmDebugStringValue {
  type IElmDebugBoolValue (line 102) | interface IElmDebugBoolValue {
  function isElmValue (line 107) | function isElmValue(value: any): value is IElmDebugValue {
  function isElmCustomValue (line 111) | function isElmCustomValue(value: any): value is IElmDebugCustomValue {
  function isElmRecordValue (line 115) | function isElmRecordValue(value: any): value is IElmDebugRecordValue {
  function isElmListValue (line 119) | function isElmListValue(value: any): value is IElmDebugListValue {
  function isElmNumberType (line 128) | function isElmNumberType(value: any): value is IElmDebugNumberValue {
  function isElmTypeValue (line 132) | function isElmTypeValue(value: any): value is IElmDebugTypeValueType {
  function isElmDictValue (line 136) | function isElmDictValue(value: any): value is IElmDebugDictValue {

FILE: src/JsonML.ts
  type TagName (line 2) | type TagName = 'div' | 'span';
  class JSONMLElement (line 4) | class JSONMLElement {
    method constructor (line 8) | constructor(tagName: TagName) {
    method toJSONML (line 13) | public toJSONML(): any {
    method withChild (line 17) | public withChild(element: JSONMLElement) {
    method withChildren (line 22) | public withChildren(elements: JSONMLElement[]) {
    method withObject (line 28) | public withObject(
    method withStyle (line 38) | public withStyle(style: string) {
    method withAttribute (line 46) | public withAttribute(key: string, value: any) {
    method withText (line 51) | public withText(value: any) {
    method toStr (line 56) | public toStr(): string {
    method toStrInner (line 60) | private toStrInner(jsonML: any[]): string {

FILE: src/elm-debug-parser.ts
  type IFilePosition (line 17) | interface IFilePosition {
  type IFileRange (line 23) | interface IFileRange {
  type ILiteralExpectation (line 28) | interface ILiteralExpectation {
  type IClassParts (line 34) | interface IClassParts extends Array<string | IClassParts> {}
  type IClassExpectation (line 36) | interface IClassExpectation {
  type IAnyExpectation (line 43) | interface IAnyExpectation {
  type IEndExpectation (line 47) | interface IEndExpectation {
  type IOtherExpectation (line 51) | interface IOtherExpectation {
  type Expectation (line 56) | type Expectation = ILiteralExpectation | IClassExpectation | IAnyExpecta...
  class SyntaxError (line 58) | class SyntaxError extends Error {
    method buildMessage (line 59) | public static buildMessage(expected: Expectation[], found: string | nu...
    method constructor (line 155) | constructor(message: string, expected: Expectation[], found: string | ...
  function peg$parse (line 169) | function peg$parse(input: string, options?: IParseOptions) {
  type IParseOptions (line 2430) | interface IParseOptions {
  type ParseFunction (line 2436) | type ParseFunction = (input: string, options?: IParseOptions) => any;

FILE: src/formatters/JsonMLFormatter.ts
  type Window (line 9) | interface Window {
  class JsonMLFormatter (line 15) | class JsonMLFormatter
    method constructor (line 20) | constructor(theme: T.ITheme) {

FILE: src/formatters/SimpleFormatter.ts
  class SimpleFormatter (line 4) | class SimpleFormatter implements T.IFormatter {
    method format (line 5) | public format(obj: T.IElmDebugValue): object {
    method formatArray (line 14) | public formatArray(array: T.ElmDebugValueType[]): object[] {
    method formatCustom (line 18) | public formatCustom(
    method formatValue (line 29) | public formatValue(formatee: T.ElmDebugValueType): any {

FILE: src/formatters/elements/BooleanElement.ts
  class BooleanElement (line 9) | class BooleanElement implements IFormatterElement {
    method constructor (line 13) | constructor(obj: IElmDebugBoolValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/BytesElement.ts
  class BytesElement (line 9) | class BytesElement implements IFormatterElement {
    method constructor (line 13) | constructor(obj: IElmDebugValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/CustomTypeElement.ts
  class CustomTypeElement (line 12) | class CustomTypeElement implements IFormatterElement {
    method constructor (line 16) | constructor(obj: IElmDebugCustomValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/DebugElement.ts
  class DebugElment (line 8) | class DebugElment implements IFormatterElement {
    method constructor (line 13) | constructor(obj: IElmDebugValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/DictElement.ts
  class DictElement (line 9) | class DictElement implements IFormatterElement {
    method constructor (line 13) | constructor(obj: IElmDebugDictValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/Elements.ts
  function toElement (line 17) | function toElement(

FILE: src/formatters/elements/EllipsisElement.ts
  class EllipsisElement (line 9) | class EllipsisElement implements IFormatterElement {

FILE: src/formatters/elements/FilesElement.ts
  class FilesElement (line 9) | class FilesElement implements IFormatterElement {
    method constructor (line 13) | constructor(obj: IElmDebugValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/InternalsElement.ts
  class InternalsElement (line 8) | class InternalsElement implements IFormatterElement {
    method constructor (line 12) | constructor(obj: string, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/ListElement.ts
  class ListElement (line 9) | class ListElement implements IFormatterElement {
    method constructor (line 13) | constructor(obj: IElmDebugListValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/NumberElement.ts
  class NumberElement (line 8) | class NumberElement implements IFormatterElement {
    method constructor (line 12) | constructor(obj: IElmDebugNumberValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/RecordElement.ts
  class RecordElement (line 10) | class RecordElement implements IFormatterElement {
    method constructor (line 14) | constructor(obj: IElmDebugRecordValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/StringElement.ts
  class StringElement (line 9) | class StringElement implements IFormatterElement {
    method constructor (line 13) | constructor(obj: IElmDebugStringValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/TupleElement.ts
  class TupleElement (line 10) | class TupleElement implements IFormatterElement {
    method constructor (line 14) | constructor(obj: IElmDebugListValue, formatter: IJsonMLFormatter) {

FILE: src/formatters/elements/TypeElement.ts
  class TypeElement (line 9) | class TypeElement implements IFormatterElement {
    method constructor (line 13) | constructor(obj: IElmDebugTypeValueType, formatter: IJsonMLFormatter) {

FILE: src/index.ts
  type Window (line 10) | interface Window {
  type IOptions (line 17) | interface IOptions {
  function parse (line 33) | function parse(message: string) : IElmDebugValue {
  function register (line 37) | function register(opts: IOptions | undefined): IOptions {

FILE: test/builders.ts
  function elmDebug (line 10) | function elmDebug(values: any): IElmDebugValue {
  function list (line 14) | function list(
  function record (line 21) | function record(values: { [key: string]: any }): IElmDebugRecordValue {
  function n (line 25) | function n(val: number) {
  function customType (line 29) | function customType(name: string, values: any[]) {
  function type (line 33) | function type(name: string) {
  function dict (line 37) | function dict(dictionary: object) {
  function tuple (line 45) | function tuple(values: any[]): IElmDebugListValue {
  function bool (line 49) | function bool(value: boolean) {
  function str (line 53) | function str(value: string) {
  function MLDebug (line 57) | function MLDebug(values: any[]): any[] {
  function MLString (line 66) | function MLString(value: string): any[] {
  function MLNumber (line 70) | function MLNumber(num: number): any[] {
  function MLBool (line 74) | function MLBool(value: boolean): any[] {
  function MLList (line 78) | function MLList(typeName: string, length: number): any[] {
  function MLCustomType (line 91) | function MLCustomType(name: string, value?: any[]): any[] {
  function MLTuple (line 104) | function MLTuple(values: any[]): any[] {
  function MLEllipsis (line 116) | function MLEllipsis(): any[] {
  function MLRecord (line 120) | function MLRecord(values: any[]) {
  function MLRecordValue (line 132) | function MLRecordValue(name: string, value: any): any[] {
  function MLKeyValueBody (line 135) | function MLKeyValueBody(keyName: string, object: any): any[] {
  function MLBody (line 157) | function MLBody(children: any[]) {
  function MLObject (line 161) | function MLObject(child: any, key: any) {
Condensed preview — 52 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (183K chars).
[
  {
    "path": ".gitignore",
    "chars": 72,
    "preview": "node_modules\ndist\n\nyarn.lock\nyarn-error.lock\nyarn-error.log\n \n.DS_Store\n"
  },
  {
    "path": ".mocharc.json",
    "chars": 178,
    "preview": "{\n  \"diff\": true,\n  \"extension\": [\"ts\"],\n  \"package\": \"./package.json\",\n  \"reporter\": \"spec\",\n  \"slow\": 75,\n  \"timeout\":"
  },
  {
    "path": ".npmignore",
    "chars": 67,
    "preview": "elm-app/\nnode_modules/\nsrc/\ntest/\nimg/\n\nwebpack.config.js\n.babelrc\n"
  },
  {
    "path": ".prettierrc",
    "chars": 85,
    "preview": "{\n  \"trailingComma\": \"es5\",\n  \"tabWidth\": 4,\n  \"semi\": true,\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": ".tool-versions",
    "chars": 14,
    "preview": "nodejs 20.5.0\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 637,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2019 Tomáš Látal\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 6027,
    "preview": "<div align=\"center\">\n    <h1>Elm Debug Transformer</h1>\n    <a href=\"https://badge.fury.io/js/elm-debug-transformer\">\n  "
  },
  {
    "path": "example/.gitignore",
    "chars": 187,
    "preview": "# Distribution\nbuild/\n\n# elm-package generated files\nelm-stuff\n\n# elm-repl generated files\nrepl-temp-*\n\n# Dependency dir"
  },
  {
    "path": "example/.tool-versions",
    "chars": 14,
    "preview": "nodejs 20.5.0\n"
  },
  {
    "path": "example/README.md",
    "chars": 86,
    "preview": "# Tesing Elm App\n\nFirst build the elm-debug-parser from root with\n\n```\nyarn start\n```\n"
  },
  {
    "path": "example/elm-tooling.json",
    "chars": 110,
    "preview": "{\n    \"tools\": {\n        \"elm\": \"0.19.1\",\n        \"elm-format\": \"0.8.7\",\n        \"elm-json\": \"0.2.13\"\n    }\n}\n"
  },
  {
    "path": "example/elm.json",
    "chars": 657,
    "preview": "{\n    \"type\": \"application\",\n    \"source-directories\": [\n        \"src\"\n    ],\n    \"elm-version\": \"0.19.1\",\n    \"dependen"
  },
  {
    "path": "example/index.html",
    "chars": 508,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"x-ua-compatible\" content=\"ie=ed"
  },
  {
    "path": "example/package.json",
    "chars": 307,
    "preview": "{\n  \"name\": \"elm-app\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"vite\"\n  },\n  \"devDependenci"
  },
  {
    "path": "example/src/Main.elm",
    "chars": 3272,
    "preview": "module Main exposing (Model, Msg(..), init, main, update, view)\n\nimport Array exposing (Array)\nimport Browser\nimport Dic"
  },
  {
    "path": "example/src/index.js",
    "chars": 228,
    "preview": "import { Elm } from './Main.elm';\n//import * as ElmDebugger from 'elm-debug-transformer';\n\n//ElmDebugger.register({simpl"
  },
  {
    "path": "example/vite.config.js",
    "chars": 220,
    "preview": "import { defineConfig } from 'vite'\nimport plugin from 'vite-plugin-elm'\nimport transformer from 'vite-plugin-elm-debug-"
  },
  {
    "path": "package.json",
    "chars": 1520,
    "preview": "{\n  \"name\": \"elm-debug-transformer\",\n  \"version\": \"1.2.1\",\n  \"description\": \"Transform Elm Debug.log output into nice lo"
  },
  {
    "path": "src/CommonTypes.ts",
    "chars": 3267,
    "preview": "import JsonML from './JsonML';\n\nexport interface IFormatter {\n    format(obj: IElmDebugValue): any;\n}\n\nexport interface "
  },
  {
    "path": "src/JsonML.ts",
    "chars": 1946,
    "preview": "import { IConfig } from './CommonTypes';\nexport type TagName = 'div' | 'span';\n\nexport default class JSONMLElement {\n   "
  },
  {
    "path": "src/elm-debug-parser.ts",
    "chars": 68893,
    "preview": "// tslint:disable:only-arrow-functions\n// tslint:disable:object-literal-shorthand\n// tslint:disable:trailing-comma\n// ts"
  },
  {
    "path": "src/elm-debug.pegjs",
    "chars": 3952,
    "preview": "// Set.toList, Array.toList\n{\n  function flat(arr) { return arr.reduce((acc, val) => acc.concat(val), []);}\n  function t"
  },
  {
    "path": "src/formatters/JsonMLFormatter.ts",
    "chars": 2706,
    "preview": "import * as _ from 'lodash';\nimport * as T from '../CommonTypes';\nimport JsonML from '../JsonML';\nimport { toElement } f"
  },
  {
    "path": "src/formatters/SimpleFormatter.ts",
    "chars": 2620,
    "preview": "import { defaults, mapValues } from 'lodash';\nimport * as T from '../CommonTypes';\n\nexport default class SimpleFormatter"
  },
  {
    "path": "src/formatters/elements/BooleanElement.ts",
    "chars": 618,
    "preview": "import { \n  IElmDebugBoolValue, \n  IFormatterElement,\n  IJsonMLFormatter\n\n} from '../../CommonTypes';\nimport JsonML from"
  },
  {
    "path": "src/formatters/elements/BytesElement.ts",
    "chars": 590,
    "preview": "import { \n  IElmDebugValue, \n  IFormatterElement,\n  IJsonMLFormatter\n} from '../../CommonTypes';\n\nimport JsonML from '.."
  },
  {
    "path": "src/formatters/elements/CustomTypeElement.ts",
    "chars": 4021,
    "preview": "import * as _ from 'lodash';\nimport {\n    IConfig,\n    IElmDebugCustomValue,\n    IFormatterElement,\n    IJsonMLFormatter"
  },
  {
    "path": "src/formatters/elements/DebugElement.ts",
    "chars": 1323,
    "preview": "import {\n    IElmDebugValue,\n    IFormatterElement,\n    IJsonMLFormatter,\n} from '../../CommonTypes';\nimport JsonML from"
  },
  {
    "path": "src/formatters/elements/DictElement.ts",
    "chars": 1708,
    "preview": "import {\n    IConfig,\n    IElmDebugDictValue,\n    IFormatterElement,\n    IJsonMLFormatter,\n} from '../../CommonTypes';\ni"
  },
  {
    "path": "src/formatters/elements/Elements.ts",
    "chars": 2140,
    "preview": "import * as T from '../../CommonTypes';\n\nimport BooleanElement from './BooleanElement';\nimport BytesElement from './Byte"
  },
  {
    "path": "src/formatters/elements/EllipsisElement.ts",
    "chars": 366,
    "preview": "import {\n    IElmDebugRecordValue,\n    IFormatterElement,\n    IJsonMLFormatter,\n} from '../../CommonTypes';\nimport JsonM"
  },
  {
    "path": "src/formatters/elements/FilesElement.ts",
    "chars": 583,
    "preview": "import { \n  IElmDebugValue, \n  IFormatterElement,\n  IJsonMLFormatter\n} from '../../CommonTypes';\n\nimport JsonML from '.."
  },
  {
    "path": "src/formatters/elements/InternalsElement.ts",
    "chars": 871,
    "preview": "import { \n  IFormatterElement,\n  IJsonMLFormatter\n} from '../../CommonTypes';\n\nimport JsonML from '../../JsonML';\n\nexpor"
  },
  {
    "path": "src/formatters/elements/ListElement.ts",
    "chars": 2260,
    "preview": "import {\n    IConfig,\n    IElmDebugListValue,\n    IFormatterElement,\n    IJsonMLFormatter,\n} from '../../CommonTypes';\ni"
  },
  {
    "path": "src/formatters/elements/NumberElement.ts",
    "chars": 602,
    "preview": "import { \n  IElmDebugNumberValue, \n  IFormatterElement,\n  IJsonMLFormatter\n} from '../../CommonTypes';\nimport JsonML fro"
  },
  {
    "path": "src/formatters/elements/RecordElement.ts",
    "chars": 3081,
    "preview": "import {\n    IConfig,\n    IElmDebugRecordValue,\n    IFormatterElement,\n    IJsonMLFormatter,\n} from '../../CommonTypes';"
  },
  {
    "path": "src/formatters/elements/StringElement.ts",
    "chars": 610,
    "preview": "import { \n  IElmDebugStringValue, \n  IFormatterElement,\n  IJsonMLFormatter\n} from '../../CommonTypes';\n\nimport JsonML fr"
  },
  {
    "path": "src/formatters/elements/Styles.ts",
    "chars": 2841,
    "preview": "import {ITheme} from '../../CommonTypes'\n\n// elm colors\n// ---------------\n// yellow - da9e26\n// green  - 81cf46\n// dark"
  },
  {
    "path": "src/formatters/elements/TupleElement.ts",
    "chars": 2262,
    "preview": "import {\n    IConfig,\n    IElmDebugListValue,\n    IFormatterElement,\n    IJsonMLFormatter,\n} from '../../CommonTypes';\ni"
  },
  {
    "path": "src/formatters/elements/TypeElement.ts",
    "chars": 608,
    "preview": "import { \n  IElmDebugTypeValueType, \n  IFormatterElement,\n  IJsonMLFormatter\n} from '../../CommonTypes';\n\nimport JsonML "
  },
  {
    "path": "src/index.ts",
    "chars": 2639,
    "preview": "import * as _ from 'lodash';\nimport { IElmDebugValue, IThemeOption } from './CommonTypes';\nimport { parse as elmDebugPar"
  },
  {
    "path": "src/peg-file.d.ts",
    "chars": 104,
    "preview": "declare module '*.pegjs' {\n    const content: {\n        default: string;\n    };\n    export = content;\n}\n"
  },
  {
    "path": "test/builders.ts",
    "chars": 4334,
    "preview": "import * as _ from 'lodash';\nimport {\n    IElmDebugListValue,\n    IElmDebugRecordValue,\n    IElmDebugValue,\n    IFormatt"
  },
  {
    "path": "test/jsonml-formatter.spec.ts",
    "chars": 18196,
    "preview": "import { describe, expect, it, assert, beforeEach } from 'vitest';\nimport { IJsonMLFormatter } from '../src/CommonTypes'"
  },
  {
    "path": "test/jsonml.spec.ts",
    "chars": 937,
    "preview": "import { describe, expect, it} from 'vitest';\nimport JsonML from '../src/JsonML';\n\ndescribe('JsonML', () => {\n    it('sh"
  },
  {
    "path": "test/parser.spec.ts",
    "chars": 15623,
    "preview": "import { describe, expect, should, it} from 'vitest';\nimport { readFileSync } from 'fs';\nimport { generate } from 'pegjs"
  },
  {
    "path": "test/simple-formatter.spec.ts",
    "chars": 5901,
    "preview": "import { beforeEach, describe, expect, should, it} from 'vitest';\nimport { IFormatter } from '../src/CommonTypes';\nimpor"
  },
  {
    "path": "tsconfig.json",
    "chars": 753,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ESNext\"\n    ],\n "
  },
  {
    "path": "tslint.json",
    "chars": 320,
    "preview": "{\n  \"extends\": [\n    \"tslint:latest\",\n    \"tslint-config-prettier\"\n  ],\n  \"linterOptions\": {\n    \"exclude\": [\n      \"src"
  },
  {
    "path": "vite.config.js",
    "chars": 888,
    "preview": "// vite.config.ts\nimport { defineConfig } from \"vite\";\n\nimport typescript from \"@rollup/plugin-typescript\";\nimport path "
  },
  {
    "path": "vitest.config.js",
    "chars": 180,
    "preview": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  define: { global: \"window\", \"process.env\""
  }
]

About this extraction

This page contains the full source code of the kraklin/elm-debug-transformer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 52 files (168.9 KB), approximately 45.3k tokens, and a symbol index with 114 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!