Full Code of scottrogowski/code2flow for AI

master c2c22afe5e12 cached
174 files
658.0 KB
157.2k tokens
1209 symbols
2 requests
Download .txt
Showing preview only (709K chars total). Download the full file or copy to clipboard to get everything.
Repository: scottrogowski/code2flow
Branch: master
Commit: c2c22afe5e12
Files: 174
Total size: 658.0 KB

Directory structure:
gitextract_mss9r7uy/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── c2f
├── code2flow/
│   ├── __init__.py
│   ├── engine.py
│   ├── get_ast.js
│   ├── get_ast.php
│   ├── javascript.py
│   ├── model.py
│   ├── package.json
│   ├── php.py
│   ├── python.py
│   └── ruby.py
├── make_expected.py
├── requirements_dev.txt
├── setup.py
└── tests/
    ├── __init__.py
    ├── test_code/
    │   ├── js/
    │   │   ├── ambiguous_names/
    │   │   │   └── ambiguous_names.js
    │   │   ├── bad_parse/
    │   │   │   ├── file_a_good.js
    │   │   │   └── file_b_bad.js
    │   │   ├── chained/
    │   │   │   └── chained.js
    │   │   ├── class_in_function/
    │   │   │   └── class_in_function.js
    │   │   ├── classes/
    │   │   │   └── classes.js
    │   │   ├── complex_ownership/
    │   │   │   └── complex_ownership.js
    │   │   ├── exclude_modules/
    │   │   │   └── exclude_modules.js
    │   │   ├── exclude_modules_es6/
    │   │   │   └── exclude_modules_es6.js
    │   │   ├── globals/
    │   │   │   └── globals.js
    │   │   ├── inheritance/
    │   │   │   └── inheritance.js
    │   │   ├── inheritance_attr/
    │   │   │   └── inheritance_attr.js
    │   │   ├── moment/
    │   │   │   └── moment.js
    │   │   ├── scoping/
    │   │   │   └── scoping.js
    │   │   ├── simple_a_js/
    │   │   │   └── simple_a.js
    │   │   ├── simple_b_js/
    │   │   │   └── simple_b.js
    │   │   ├── ternary_new/
    │   │   │   └── ternary_new.js
    │   │   ├── two_file_imports/
    │   │   │   ├── imported.js
    │   │   │   └── importer.js
    │   │   ├── two_file_simple/
    │   │   │   ├── file_a.js
    │   │   │   ├── file_b.js
    │   │   │   └── shouldntberead
    │   │   └── weird_assignments/
    │   │       └── weird_assignments.js
    │   ├── mjs/
    │   │   └── two_file_imports_es6/
    │   │       ├── imported_es6.mjs
    │   │       └── importer_es6.mjs
    │   ├── php/
    │   │   ├── ambiguous_resolution/
    │   │   │   └── ambiguous_resolution.php
    │   │   ├── anon/
    │   │   │   └── anonymous_function.php
    │   │   ├── anon2/
    │   │   │   └── anonymous_function2.php
    │   │   ├── bad_php/
    │   │   │   ├── bad_php_a.php
    │   │   │   └── bad_php_b.php
    │   │   ├── branch/
    │   │   │   └── branch.php
    │   │   ├── chains/
    │   │   │   └── chains.php
    │   │   ├── factory/
    │   │   │   ├── currency.php
    │   │   │   └── factory.php
    │   │   ├── inheritance/
    │   │   │   └── inheritance.php
    │   │   ├── inheritance2/
    │   │   │   └── inheritance2.php
    │   │   ├── instance_methods/
    │   │   │   └── instance_methods.php
    │   │   ├── money/
    │   │   │   ├── Calculator/
    │   │   │   │   ├── BcMathCalculator.php
    │   │   │   │   └── GmpCalculator.php
    │   │   │   ├── Calculator.php
    │   │   │   ├── Converter.php
    │   │   │   ├── Currencies/
    │   │   │   │   ├── AggregateCurrencies.php
    │   │   │   │   ├── BitcoinCurrencies.php
    │   │   │   │   ├── CachedCurrencies.php
    │   │   │   │   ├── CurrencyList.php
    │   │   │   │   └── ISOCurrencies.php
    │   │   │   ├── Currencies.php
    │   │   │   ├── Currency.php
    │   │   │   ├── CurrencyPair.php
    │   │   │   ├── Exception/
    │   │   │   │   ├── FormatterException.php
    │   │   │   │   ├── InvalidArgumentException.php
    │   │   │   │   ├── ParserException.php
    │   │   │   │   ├── UnknownCurrencyException.php
    │   │   │   │   └── UnresolvableCurrencyPairException.php
    │   │   │   ├── Exception.php
    │   │   │   ├── Exchange/
    │   │   │   │   ├── ExchangerExchange.php
    │   │   │   │   ├── FixedExchange.php
    │   │   │   │   ├── IndirectExchange.php
    │   │   │   │   ├── IndirectExchangeQueuedItem.php
    │   │   │   │   ├── ReversedCurrenciesExchange.php
    │   │   │   │   └── SwapExchange.php
    │   │   │   ├── Exchange.php
    │   │   │   ├── Formatter/
    │   │   │   │   ├── AggregateMoneyFormatter.php
    │   │   │   │   ├── BitcoinMoneyFormatter.php
    │   │   │   │   ├── DecimalMoneyFormatter.php
    │   │   │   │   ├── IntlLocalizedDecimalFormatter.php
    │   │   │   │   └── IntlMoneyFormatter.php
    │   │   │   ├── Money.php
    │   │   │   ├── MoneyFactory.php
    │   │   │   ├── MoneyFormatter.php
    │   │   │   ├── MoneyParser.php
    │   │   │   ├── Number.php
    │   │   │   ├── PHPUnit/
    │   │   │   │   └── Comparator.php
    │   │   │   └── Parser/
    │   │   │       ├── AggregateMoneyParser.php
    │   │   │       ├── BitcoinMoneyParser.php
    │   │   │       ├── DecimalMoneyParser.php
    │   │   │       ├── IntlLocalizedDecimalParser.php
    │   │   │       └── IntlMoneyParser.php
    │   │   ├── namespace_a/
    │   │   │   └── namespace_a.php
    │   │   ├── namespace_b/
    │   │   │   ├── namespace_b1.php
    │   │   │   └── namespace_b2.php
    │   │   ├── namespace_c/
    │   │   │   ├── namespace_c1.php
    │   │   │   └── namespace_c2.php
    │   │   ├── nested/
    │   │   │   └── nested.php
    │   │   ├── nested_calls/
    │   │   │   └── nested_calls.php
    │   │   ├── publicprivateprotected/
    │   │   │   └── publicprivateprotected.php
    │   │   ├── resolve_correct_class/
    │   │   │   └── rcc.php
    │   │   ├── simple_a/
    │   │   │   └── simple_a.php
    │   │   ├── simple_b/
    │   │   │   └── simple_b.php
    │   │   ├── static/
    │   │   │   └── static.php
    │   │   ├── traits/
    │   │   │   └── traits.php
    │   │   ├── two_file_simple/
    │   │   │   ├── file_a.php
    │   │   │   └── file_b.php
    │   │   └── weird_assign/
    │   │       └── weird_assign.php
    │   ├── py/
    │   │   ├── ambiguous_resolution/
    │   │   │   └── ambiguous_resolution.py
    │   │   ├── async_basic/
    │   │   │   └── async_basic.py
    │   │   ├── chained/
    │   │   │   └── chained.py
    │   │   ├── exclude_modules/
    │   │   │   └── exclude_modules.py
    │   │   ├── exclude_modules_two_files/
    │   │   │   ├── exclude_modules_a.py
    │   │   │   └── exclude_modules_b.py
    │   │   ├── import_paths/
    │   │   │   ├── abra.py
    │   │   │   ├── cadabra.py
    │   │   │   └── import_paths.py
    │   │   ├── inherits/
    │   │   │   ├── inherits.py
    │   │   │   └── inherits_import.py
    │   │   ├── init/
    │   │   │   ├── init.py
    │   │   │   └── the_import.py
    │   │   ├── nested_calls/
    │   │   │   └── nested_calls.py
    │   │   ├── nested_class/
    │   │   │   └── nested_class.py
    │   │   ├── pytz/
    │   │   │   ├── __init__.py
    │   │   │   ├── exceptions.py
    │   │   │   ├── lazy.py
    │   │   │   ├── reference.py
    │   │   │   ├── tzfile.py
    │   │   │   └── tzinfo.py
    │   │   ├── resolve_correct_class/
    │   │   │   └── rcc.py
    │   │   ├── simple_a/
    │   │   │   └── simple_a.py
    │   │   ├── simple_b/
    │   │   │   └── simple_b.py
    │   │   ├── subset_find_exception/
    │   │   │   ├── two.py
    │   │   │   └── zero.py
    │   │   ├── two_file_simple/
    │   │   │   ├── file_a.py
    │   │   │   ├── file_b.py
    │   │   │   └── shouldntberead
    │   │   ├── weird_calls/
    │   │   │   └── weird_calls.py
    │   │   ├── weird_encoding/
    │   │   │   └── weird_encoding.py
    │   │   └── weird_imports/
    │   │       └── weird_imports.py
    │   └── rb/
    │       ├── ambiguous_resolution/
    │       │   └── ambiguous_resolution.rb
    │       ├── chains/
    │       │   └── chains.rb
    │       ├── doublecolon/
    │       │   └── doublecolon.rb
    │       ├── inheritance_2/
    │       │   └── inheritance_2.rb
    │       ├── instance_methods/
    │       │   └── instance_methods.rb
    │       ├── modules/
    │       │   └── modules.rb
    │       ├── nested/
    │       │   └── nested.rb
    │       ├── nested_classes/
    │       │   └── nested_classes.rb
    │       ├── onelinefile/
    │       │   └── onelinefile.rb
    │       ├── public_suffix/
    │       │   ├── public_suffix/
    │       │   │   ├── domain.rb
    │       │   │   ├── errors.rb
    │       │   │   ├── list.rb
    │       │   │   ├── rule.rb
    │       │   │   └── version.rb
    │       │   └── public_suffix.rb
    │       ├── resolve_correct_class/
    │       │   └── rcc.rb
    │       ├── simple_a/
    │       │   └── simple_a.rb
    │       ├── simple_b/
    │       │   └── simple_b.rb
    │       ├── split_modules/
    │       │   ├── split_modules_a.rb
    │       │   └── split_modules_b.rb
    │       ├── two_file_simple/
    │       │   ├── file_a.rb
    │       │   └── file_b.rb
    │       └── weird_chains/
    │           └── weird_chains.rb
    ├── test_graphs.py
    ├── test_interface.py
    └── testdata.py

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

================================================
FILE: .github/FUNDING.yml
================================================
github: scottrogowski


================================================
FILE: .gitignore
================================================
.DS_Store
__pycache__
out.*
.coverage*
htmlcov
build/
*.egg-info
dist
composer.json
composer.lock
vendor


================================================
FILE: CHANGELOG.md
================================================
# Code2flow CHANGELOG

## [2.5.1] - 2023-01-08
- Minor fix for installing code2flow in windows environments
- Minor README updates and typo corrections

## [2.5.0] - 2022-03-25
- Add async/await functionality to Python
- Add --include-only-* CLI options
- Minor README updates
- Minor logging updates

## [2.4.0] - 2021-12-26
- Implement subsets

## [2.3.1] - 2021-12-13
- Colored edges
- Improve dependency robustness
- Fixes for two Python bugs
- Small textual improvements

## [2.3.0] - 2021-10-11
Fix a few rare javascript bugs. Fix non-UTF8 encoding issue for Python. Better debugging.

## [2.2.0] - 2021-06-21
Ruby + PHP support

## [2.1.1] - 2021-06-15
Updates to the CLI that allow code2flow to hypothetically run in Windows

## [2.1.0] - 2021-06-15
Javascript support

## [2.0.1] - 2021-05-30
Add support for constructors to Python

## [2.0.0] - 2021-04-28
Almost complete rewrite / refactor. Update to Python3. Use ASTs

## [0.2] - 2013-06-08
Cleaned up code. Remove comments runs much faster. Test scripts. Template.py implementation file. More to come!

## [0.1] - 2013-05-23
Initial release


================================================
FILE: LICENSE
================================================
Copyright 2021 Scott Rogowski

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: MANIFEST.in
================================================
include LICENSE, CHANGELOG.md
include code2flow/get_ast.js
include code2flow/get_ast.php


================================================
FILE: Makefile
================================================
build:
	rm -rf dist
	python3 setup.py sdist

test:
	pytest -n=4 --cov-report=html --cov-report=term --cov=code2flow -x

clean:
	rm -rf build
	rm -rf dist
	rm -f out.*
	rm -rf *.egg-info
	rm -rf htmlcov


================================================
FILE: README.md
================================================
![code2flow logo](https://raw.githubusercontent.com/scottrogowski/code2flow/master/assets/code2flowlogo.png)

![Version 2.5.1](https://img.shields.io/badge/version-2.5.1-brightgreen) ![Build passing](https://img.shields.io/badge/build-passing-brightgreen) ![Coverage 100%](https://img.shields.io/badge/coverage-100%25-brightgreen) ![License MIT](https://img.shields.io/badge/license-MIT-green])

Code2flow generates [call graphs](https://en.wikipedia.org/wiki/Call_graph) for dynamic programming language. Code2flow supports Python, JavaScript, Ruby, and PHP.

The basic algorithm is simple:

1. Translate your source files into ASTs.
1. Find all function definitions.
1. Determine where those functions are called.
1. Connect the dots.

Code2flow is useful for:
- Untangling spaghetti code.
- Identifying orphaned functions.
- Getting new developers up to speed.

Code2flow provides a *pretty good estimate* of your project's structure. No algorithm can generate a perfect call graph for a [dynamic language](https://en.wikipedia.org/wiki/Dynamic_programming_language) – even less so if that language is [duck-typed](https://en.wikipedia.org/wiki/Duck_typing). See the known limitations in the section below.

*(Below: Code2flow running against a subset of itself `code2flow code2flow/engine.py code2flow/python.py --target-function=code2flow --downstream-depth=3`)*

![code2flow running against a subset of itself](https://raw.githubusercontent.com/scottrogowski/code2flow/master/assets/code2flow_output.png)

Installation
------------

```bash
pip3 install code2flow
```

If you don't have it already, you will also need to install graphviz. Installation instructions can be found [here](https://graphviz.org/download/).

Additionally, depending on the language you want to parse, you may need to install additional dependencies:
- JavaScript: [Acorn](https://www.npmjs.com/package/acorn)
- Ruby: [Parser](https://github.com/whitequark/parser)
- PHP: [PHP-Parser](https://github.com/nikic/PHP-Parser)
- Python: No extra dependencies needed

Usage
-----

To generate a DOT file, run something like:

```bash
code2flow mypythonfile.py
```

Or, for Javascript:

```bash
code2flow myjavascriptfile.js
```

You can specify multiple files or import directories:

```bash
code2flow project/directory/source_a.js project/directory/source_b.js
```

```bash
code2flow project/directory/*.js
```

```bash
code2flow project/directory --language js
```

To pull out a subset of the graph, try something like:

```bash
code2flow mypythonfile.py --target-function my_func --upstream-depth=1 --downstream-depth=1
```


There are a ton of command line options, to see them all, run:

```bash
code2flow --help
```

How code2flow works
------------

Code2flow approximates the structure of projects in dynamic languages. It is *not possible* to generate a perfect callgraph for a dynamic language.

Detailed algorithm:

1. Generate an AST of the source code
2. Recursively separate groups and nodes. Groups are files, modules, or classes. More precisely, groups are namespaces where functions live. Nodes are the functions themselves.
3. For all nodes, identify function calls in those nodes.
4. For all nodes, identify in-scope variables. Attempt to connect those variables to specific nodes and groups. This is where there is some ambiguity in the algorithm because it is impossible to know the types of variables in dynamic languages. So, instead, heuristics must be used.
5. For all calls in all nodes, attempt to find a match from the in-scope variables. This will be an edge.
6. If a definitive match from in-scope variables cannot be found, attempt to find a single match from all other groups and nodes.
7. Trim orphaned nodes and groups.
8. Output results.

Why is it impossible to generate a perfect call graph?
----------------

Consider this toy example in Python
```python
def func_factory(param):
    if param < .5:
        return func_a
    else:
        return func_b

func = func_factory(important_variable)
func()
```

We have no way of knowing whether `func` will point to `func_a` or `func_b` until runtime. In practice, ambiguity like this is common and is present in most non-trivial applications.

Known limitations
-----------------

Code2flow is internally powered by ASTs. Most limitations stem from a token not being named what code2flow expects it to be named.

* All functions without definitions are skipped. This most often happens when a file is not included.
* Functions with identical names in different namespaces are (loudly) skipped. E.g. If you have two classes with identically named methods, code2flow cannot distinguish between these and skips them.
* Imported functions from outside your project directory (including from standard libraries) which share names with your defined functions may not be handled correctly. Instead, when you call the imported function, code2flow will link to your local functions. For example, if you have a function `search()` and call, `import searcher; searcher.search()`, code2flow may link (incorrectly) to your defined function.
* Anonymous or generated functions are skipped. This includes lambdas and factories.
* If a function is renamed, either explicitly or by being passed around as a parameter, it will be skipped.


As an imported library
-----------------

You can work with code2flow as an imported Python library in much the same way as you work with it
from the CLI.

```python
import code2flow
code2flow.code2flow(['path/to/filea', 'path/to/fileb'], 'path/to/outputfile')
```

The keyword arguments to `code2flow.code2flow` are roughly the same as the CLI
parameters. To see all available parameters, refer to the code2flow function in [engine.py](https://github.com/scottrogowski/code2flow/blob/master/code2flow/engine.py).


How to contribute
-----------------------

1. **Open an issue**: Code2flow is not perfect and there is a lot that can be improved. If you find a problem parsing your source that you can identify with a simplified example, please open an issue.
2. **Create a PR**: Even better, if you have a fix for the issue you identified that passes unit tests, please open a PR.
3. **Add a language**: While dense, each language implementation is between 250-400 lines of code including comments. If you want to implement another language, the existing implementations can be your guide.


Unit tests
------------------

Test coverage is 100%. To run:

```bash
    pip install -r requirements_dev.txt
    make test
```

License
-----------------------------

Code2flow is licensed under the MIT license.
Prior to the rewrite in April 2021, code2flow was licensed under LGPL. The last commit under that license was 24b2cb854c6a872ba6e17409fbddb6659bf64d4c.
The April 2021 rewrite was substantial, so it's probably reasonable to treat code2flow as completely MIT-licensed.


Acknowledgements
-----------------------------


* In mid 2021, Code2flow was rewritten, and two new languages were added. This was prompted and financially supported by the [Sider Corporation](https://siderlabs.com/).
* The code2flow pip name was graciously transferred to this project from [Dheeraj Nair](https://github.com/Dheeraj1998). He was using it for his own (unrelated) [code2flow](https://github.com/Dheeraj1998/code2flow) project.
* Many others have contributed through bug fixes, cleanups, and identifying issues. Thank you!!!


Unrelated projects
-----------------------

The name, "code2flow", has been used for several unrelated projects. Specifically, the domain, code2flow.com, has no association with this project. I've never heard anything from them and it doesn't appear like they use anything from here.


Feedback / Issues / Contact
-----------------------------

If you have an issue using code2flow or a feature request, please post it in the issues tab. In general, I don't provide help over email. Answering a question publicly helps way more people. For everything else, please do email! scottmrogowski@gmail.com


Feature Requests
----------------

Email me. Usually, I'm spread thin across a lot of projects, so I will, unfortunately, turn down most requests. However, I am open to paid development for compelling features.


================================================
FILE: c2f
================================================
#!/usr/bin/env python3

import sys
from code2flow.engine import main

if __name__ == "__main__":
    main(sys.argv[1:])


================================================
FILE: code2flow/__init__.py
================================================
from .engine import code2flow, VERSION

code2flow = code2flow
VERSION = VERSION


================================================
FILE: code2flow/engine.py
================================================
import argparse
import collections
import json
import logging
import os
import subprocess
import sys
import time

from .python import Python
from .javascript import Javascript
from .ruby import Ruby
from .php import PHP
from .model import (TRUNK_COLOR, LEAF_COLOR, NODE_COLOR, GROUP_TYPE, OWNER_CONST,
                    Edge, Group, Node, Variable, is_installed, flatten)

VERSION = '2.5.1'

IMAGE_EXTENSIONS = ('png', 'svg')
TEXT_EXTENSIONS = ('dot', 'gv', 'json')
VALID_EXTENSIONS = IMAGE_EXTENSIONS + TEXT_EXTENSIONS

DESCRIPTION = "Generate flow charts from your source code. " \
              "See the README at https://github.com/scottrogowski/code2flow."


LEGEND = """subgraph legend{
    rank = min;
    label = "legend";
    Legend [shape=none, margin=0, label = <
        <table cellspacing="0" cellpadding="0" border="1"><tr><td>Code2flow Legend</td></tr><tr><td>
        <table cellspacing="0">
        <tr><td>Regular function</td><td width="50px" bgcolor='%s'></td></tr>
        <tr><td>Trunk function (nothing calls this)</td><td bgcolor='%s'></td></tr>
        <tr><td>Leaf function (this calls nothing else)</td><td bgcolor='%s'></td></tr>
        <tr><td>Function call</td><td><font color='black'>&#8594;</font></td></tr>
        </table></td></tr></table>
        >];
}""" % (NODE_COLOR, TRUNK_COLOR, LEAF_COLOR)


LANGUAGES = {
    'py': Python,
    'js': Javascript,
    'mjs': Javascript,
    'rb': Ruby,
    'php': PHP,
}


class LanguageParams():
    """
    Shallow structure to make storing language-specific parameters cleaner
    """
    def __init__(self, source_type='script', ruby_version='27'):
        self.source_type = source_type
        self.ruby_version = ruby_version


class SubsetParams():
    """
    Shallow structure to make storing subset-specific parameters cleaner.
    """
    def __init__(self, target_function, upstream_depth, downstream_depth):
        self.target_function = target_function
        self.upstream_depth = upstream_depth
        self.downstream_depth = downstream_depth

    @staticmethod
    def generate(target_function, upstream_depth, downstream_depth):
        """
        :param target_function str:
        :param upstream_depth int:
        :param downstream_depth int:
        :rtype: SubsetParams|Nonetype
        """
        if upstream_depth and not target_function:
            raise AssertionError("--upstream-depth requires --target-function")

        if downstream_depth and not target_function:
            raise AssertionError("--downstream-depth requires --target-function")

        if not target_function:
            return None

        if not (upstream_depth or downstream_depth):
            raise AssertionError("--target-function requires --upstream-depth or --downstream-depth")

        if upstream_depth < 0:
            raise AssertionError("--upstream-depth must be >= 0. Exclude argument for complete depth.")

        if downstream_depth < 0:
            raise AssertionError("--downstream-depth must be >= 0. Exclude argument for complete depth.")

        return SubsetParams(target_function, upstream_depth, downstream_depth)



def _find_target_node(subset_params, all_nodes):
    """
    Find the node referenced by subset_params.target_function
    :param subset_params SubsetParams:
    :param all_nodes list[Node]:
    :rtype: Node
    """
    target_nodes = []
    for node in all_nodes:
        if node.token == subset_params.target_function or \
           node.token_with_ownership() == subset_params.target_function or \
           node.name() == subset_params.target_function:
            target_nodes.append(node)
    if not target_nodes:
        raise AssertionError("Could not find node %r to build a subset." % subset_params.target_function)
    if len(target_nodes) > 1:
        raise AssertionError("Found multiple nodes for %r: %r. Try either a `class.func` or "
                             "`filename::class.func`." % (subset_params.target_function, target_nodes))
    return target_nodes[0]


def _filter_nodes_for_subset(subset_params, all_nodes, edges):
    """
    Given subset_params, return a set of all nodes upstream and downstream of the target node.
    :param subset_params SubsetParams:
    :param all_nodes list[Node]:
    :param edges list[Edge]:
    :rtype: set[Node]
    """
    target_node = _find_target_node(subset_params, all_nodes)
    downstream_dict = collections.defaultdict(set)
    upstream_dict = collections.defaultdict(set)
    for edge in edges:
        upstream_dict[edge.node1].add(edge.node0)
        downstream_dict[edge.node0].add(edge.node1)

    include_nodes = {target_node}
    step_nodes = {target_node}
    next_step_nodes = set()

    for _ in range(subset_params.downstream_depth):
        for node in step_nodes:
            next_step_nodes.update(downstream_dict[node])
        include_nodes.update(next_step_nodes)
        step_nodes = next_step_nodes
        next_step_nodes = set()

    step_nodes = {target_node}
    next_step_nodes = set()

    for _ in range(subset_params.upstream_depth):
        for node in step_nodes:
            next_step_nodes.update(upstream_dict[node])
        include_nodes.update(next_step_nodes)
        step_nodes = next_step_nodes
        next_step_nodes = set()

    return include_nodes


def _filter_edges_for_subset(new_nodes, edges):
    """
    Given the subset of nodes, filter for edges within this subset
    :param new_nodes set[Node]:
    :param edges list[Edge]:
    :rtype: list[Edge]
    """
    new_edges = []
    for edge in edges:
        if edge.node0 in new_nodes and edge.node1 in new_nodes:
            new_edges.append(edge)
    return new_edges


def _filter_groups_for_subset(new_nodes, file_groups):
    """
    Given the subset of nodes, do housekeeping and filter out for groups within this subset
    :param new_nodes set[Node]:
    :param file_groups list[Group]:
    :rtype: list[Group]
    """
    for file_group in file_groups:
        for node in file_group.all_nodes():
            if node not in new_nodes:
                node.remove_from_parent()

    new_file_groups = [g for g in file_groups if g.all_nodes()]

    for file_group in new_file_groups:
        for group in file_group.all_groups():
            if not group.all_nodes():
                group.remove_from_parent()

    return new_file_groups


def _filter_for_subset(subset_params, all_nodes, edges, file_groups):
    """
    Given subset_params, return the subset of nodes, edges, and groups
    upstream and downstream of the target node.
    :param subset_params SubsetParams:
    :param all_nodes list[Node]:
    :param edges list[Edge]:
    :param file_groups list[Group]:
    :rtype: list[Group], list[Node], list[Edge]
    """
    new_nodes = _filter_nodes_for_subset(subset_params, all_nodes, edges)
    new_edges = _filter_edges_for_subset(new_nodes, edges)
    new_file_groups = _filter_groups_for_subset(new_nodes, file_groups)
    return new_file_groups, list(new_nodes), new_edges


def generate_json(nodes, edges):
    '''
    Generate a json string from nodes and edges
    See https://github.com/jsongraph/json-graph-specification

    :param nodes list[Node]: functions
    :param edges list[Edge]: function calls
    :rtype: str
    '''
    nodes = [n.to_dict() for n in nodes]
    nodes = {n['uid']: n for n in nodes}
    edges = [e.to_dict() for e in edges]

    return json.dumps({"graph": {
        "directed": True,
        "nodes": nodes,
        "edges": edges,
    }})


def write_file(outfile, nodes, edges, groups, hide_legend=False,
               no_grouping=False, as_json=False):
    '''
    Write a dot file that can be read by graphviz

    :param outfile File:
    :param nodes list[Node]: functions
    :param edges list[Edge]: function calls
    :param groups list[Group]: classes and files
    :param hide_legend bool:
    :rtype: None
    '''

    if as_json:
        content = generate_json(nodes, edges)
        outfile.write(content)
        return

    splines = "polyline" if len(edges) >= 500 else "ortho"

    content = "digraph G {\n"
    content += "concentrate=true;\n"
    content += f'splines="{splines}";\n'
    content += 'rankdir="LR";\n'
    if not hide_legend:
        content += LEGEND
    for node in nodes:
        content += node.to_dot() + ';\n'
    for edge in edges:
        content += edge.to_dot() + ';\n'
    if not no_grouping:
        for group in groups:
            content += group.to_dot()
    content += '}\n'
    outfile.write(content)


def determine_language(individual_files):
    """
    Given a list of filepaths, determine the language from the first
    valid extension

    :param list[str] individual_files:
    :rtype: str
    """
    for source, _ in individual_files:
        suffix = source.rsplit('.', 1)[-1]
        if suffix in LANGUAGES:
            logging.info("Implicitly detected language as %r.", suffix)
            return suffix
    raise AssertionError(f"Language could not be detected from input {individual_files}. ",
                         "Try explicitly passing the language flag.")


def get_sources_and_language(raw_source_paths, language):
    """
    Given a list of files and directories, return just files.
    If we are not passed a language, determine it.
    Filter out files that are not of that language

    :param list[str] raw_source_paths: file or directory paths
    :param str|None language: Input language
    :rtype: (list, str)
    """

    individual_files = []
    for source in sorted(raw_source_paths):
        if os.path.isfile(source):
            individual_files.append((source, True))
            continue
        for root, _, files in os.walk(source):
            for f in files:
                individual_files.append((os.path.join(root, f), False))

    if not individual_files:
        raise AssertionError("No source files found from %r" % raw_source_paths)
    logging.info("Found %d files from sources argument.", len(individual_files))

    if not language:
        language = determine_language(individual_files)

    sources = set()
    for source, explicity_added in individual_files:
        if explicity_added or source.endswith('.' + language):
            sources.add(source)
        else:
            logging.info("Skipping %r which is not a %s file. "
                         "If this is incorrect, include it explicitly.",
                         source, language)

    if not sources:
        raise AssertionError("Could not find any source files given {raw_source_paths} "
                             "and language {language}.")

    sources = sorted(list(sources))
    logging.info("Processing %d source file(s)." % (len(sources)))
    for source in sources:
        logging.info("  " + source)

    return sources, language


def make_file_group(tree, filename, extension):
    """
    Given an AST for the entire file, generate a file group complete with
    subgroups, nodes, etc.

    :param tree ast:
    :param filename str:
    :param extension str:

    :rtype: Group
    """
    language = LANGUAGES[extension]

    subgroup_trees, node_trees, body_trees = language.separate_namespaces(tree)
    group_type = GROUP_TYPE.FILE
    token = os.path.split(filename)[-1].rsplit('.' + extension, 1)[0]
    line_number = 0
    display_name = 'File'
    import_tokens = language.file_import_tokens(filename)

    file_group = Group(token, group_type, display_name, import_tokens,
                       line_number, parent=None)
    for node_tree in node_trees:
        for new_node in language.make_nodes(node_tree, parent=file_group):
            file_group.add_node(new_node)

    file_group.add_node(language.make_root_node(body_trees, parent=file_group), is_root=True)

    for subgroup_tree in subgroup_trees:
        file_group.add_subgroup(language.make_class_group(subgroup_tree, parent=file_group))
    return file_group


def _find_link_for_call(call, node_a, all_nodes):
    """
    Given a call that happened on a node (node_a), return the node
    that the call links to and the call itself if >1 node matched.

    :param call Call:
    :param node_a Node:
    :param all_nodes list[Node]:

    :returns: The node it links to and the call if >1 node matched.
    :rtype: (Node|None, Call|None)
    """

    all_vars = node_a.get_variables(call.line_number)

    for var in all_vars:
        var_match = call.matches_variable(var)
        if var_match:
            # Unknown modules (e.g. third party) we don't want to match)
            if var_match == OWNER_CONST.UNKNOWN_MODULE:
                return None, None
            assert isinstance(var_match, Node)
            return var_match, None

    possible_nodes = []
    if call.is_attr():
        for node in all_nodes:
            # checking node.parent != node_a.file_group() prevents self linkage in cases like
            # function a() {b = Obj(); b.a()}
            if call.token == node.token and node.parent != node_a.file_group():
                possible_nodes.append(node)
    else:
        for node in all_nodes:
            if call.token == node.token \
               and isinstance(node.parent, Group)  \
               and node.parent.group_type == GROUP_TYPE.FILE:
                possible_nodes.append(node)
            elif call.token == node.parent.token and node.is_constructor:
                possible_nodes.append(node)

    if len(possible_nodes) == 1:
        return possible_nodes[0], None
    if len(possible_nodes) > 1:
        return None, call
    return None, None


def _find_links(node_a, all_nodes):
    """
    Iterate through the calls on node_a to find everything the node links to.
    This will return a list of tuples of nodes and calls that were ambiguous.

    :param Node node_a:
    :param list[Node] all_nodes:
    :param BaseLanguage language:
    :rtype: list[(Node, Call)]
    """

    links = []
    for call in node_a.calls:
        lfc = _find_link_for_call(call, node_a, all_nodes)
        assert not isinstance(lfc, Group)
        links.append(lfc)
    return list(filter(None, links))


def map_it(sources, extension, no_trimming, exclude_namespaces, exclude_functions,
           include_only_namespaces, include_only_functions,
           skip_parse_errors, lang_params):
    '''
    Given a language implementation and a list of filenames, do these things:
    1. Read/parse source ASTs
    2. Find all groups (classes/modules) and nodes (functions) (a lot happens here)
    3. Trim namespaces / functions that we don't want
    4. Consolidate groups / nodes given all we know so far
    5. Attempt to resolve the variables (point them to a node or group)
    6. Find all calls between all nodes
    7. Loudly complain about duplicate edges that were skipped
    8. Trim nodes that didn't connect to anything

    :param list[str] sources:
    :param str extension:
    :param bool no_trimming:
    :param list exclude_namespaces:
    :param list exclude_functions:
    :param list include_only_namespaces:
    :param list include_only_functions:
    :param bool skip_parse_errors:
    :param LanguageParams lang_params:

    :rtype: (list[Group], list[Node], list[Edge])
    '''

    language = LANGUAGES[extension]

    # 0. Assert dependencies
    language.assert_dependencies()

    # 1. Read/parse source ASTs
    file_ast_trees = []
    for source in sources:
        try:
            file_ast_trees.append((source, language.get_tree(source, lang_params)))
        except Exception as ex:
            if skip_parse_errors:
                logging.warning("Could not parse %r. (%r) Skipping...", source, ex)
            else:
                raise ex

    # 2. Find all groups (classes/modules) and nodes (functions) (a lot happens here)
    file_groups = []
    for source, file_ast_tree in file_ast_trees:
        file_group = make_file_group(file_ast_tree, source, extension)
        file_groups.append(file_group)

    # 3. Trim namespaces / functions to exactly what we want
    if exclude_namespaces or include_only_namespaces:
        file_groups = _limit_namespaces(file_groups, exclude_namespaces, include_only_namespaces)
    if exclude_functions or include_only_functions:
        file_groups = _limit_functions(file_groups, exclude_functions, include_only_functions)

    # 4. Consolidate structures
    all_subgroups = flatten(g.all_groups() for g in file_groups)
    all_nodes = flatten(g.all_nodes() for g in file_groups)

    nodes_by_subgroup_token = collections.defaultdict(list)
    for subgroup in all_subgroups:
        if subgroup.token in nodes_by_subgroup_token:
            logging.warning("Duplicate group name %r. Naming collision possible.",
                            subgroup.token)
        nodes_by_subgroup_token[subgroup.token] += subgroup.nodes

    for group in file_groups:
        for subgroup in group.all_groups():
            subgroup.inherits = [nodes_by_subgroup_token.get(g) for g in subgroup.inherits]
            subgroup.inherits = list(filter(None, subgroup.inherits))
            for inherit_nodes in subgroup.inherits:
                for node in subgroup.nodes:
                    node.variables += [Variable(n.token, n, n.line_number) for n in inherit_nodes]

    # 5. Attempt to resolve the variables (point them to a node or group)
    for node in all_nodes:
        node.resolve_variables(file_groups)

    # Not a step. Just log what we know so far
    logging.info("Found groups %r." % [g.label() for g in all_subgroups])
    logging.info("Found nodes %r." % sorted(n.token_with_ownership() for n in all_nodes))
    logging.info("Found calls %r." % sorted(list(set(c.to_string() for c in
                                                     flatten(n.calls for n in all_nodes)))))
    logging.info("Found variables %r." % sorted(list(set(v.to_string() for v in
                                                         flatten(n.variables for n in all_nodes)))))

    # 6. Find all calls between all nodes
    bad_calls = []
    edges = []
    for node_a in list(all_nodes):
        links = _find_links(node_a, all_nodes)
        for node_b, bad_call in links:
            if bad_call:
                bad_calls.append(bad_call)
            if not node_b:
                continue
            edges.append(Edge(node_a, node_b))

    # 7. Loudly complain about duplicate edges that were skipped
    bad_calls_strings = set()
    for bad_call in bad_calls:
        bad_calls_strings.add(bad_call.to_string())
    bad_calls_strings = list(sorted(list(bad_calls_strings)))
    if bad_calls_strings:
        logging.info("Skipped processing these calls because the algorithm "
                     "linked them to multiple function definitions: %r." % bad_calls_strings)

    if no_trimming:
        return file_groups, all_nodes, edges

    # 8. Trim nodes that didn't connect to anything
    nodes_with_edges = set()
    for edge in edges:
        nodes_with_edges.add(edge.node0)
        nodes_with_edges.add(edge.node1)

    for node in all_nodes:
        if node not in nodes_with_edges:
            node.remove_from_parent()

    for file_group in file_groups:
        for group in file_group.all_groups():
            if not group.all_nodes():
                group.remove_from_parent()

    file_groups = [g for g in file_groups if g.all_nodes()]
    all_nodes = list(nodes_with_edges)

    if not all_nodes:
        logging.warning("No functions found! Most likely, your file(s) do not have "
                        "functions that call each other. Note that to generate a flowchart, "
                        "you need to have both the function calls and the function "
                        "definitions. Or, you might be excluding too many "
                        "with --exclude-* / --include-* / --target-function arguments. ")
        logging.warning("Code2flow will generate an empty output file.")

    return file_groups, all_nodes, edges


def _limit_namespaces(file_groups, exclude_namespaces, include_only_namespaces):
    """
    Exclude namespaces (classes/modules) which match any of the exclude_namespaces

    :param list[Group] file_groups:
    :param list exclude_namespaces:
    :param list include_only_namespaces:
    :rtype: list[Group]
    """

    removed_namespaces = set()

    for group in list(file_groups):
        if group.token in exclude_namespaces:
            for node in group.all_nodes():
                node.remove_from_parent()
            removed_namespaces.add(group.token)
        if include_only_namespaces and group.token not in include_only_namespaces:
            for node in group.nodes:
                node.remove_from_parent()
            removed_namespaces.add(group.token)

        for subgroup in group.all_groups():
            print(subgroup, subgroup.all_parents())
            if subgroup.token in exclude_namespaces:
                for node in subgroup.all_nodes():
                    node.remove_from_parent()
                removed_namespaces.add(subgroup.token)
            if include_only_namespaces and \
               subgroup.token not in include_only_namespaces and \
               all(p.token not in include_only_namespaces for p in subgroup.all_parents()):
                for node in subgroup.nodes:
                    node.remove_from_parent()
                removed_namespaces.add(group.token)

    for namespace in exclude_namespaces:
        if namespace not in removed_namespaces:
            logging.warning(f"Could not exclude namespace '{namespace}' "
                             "because it was not found.")
    return file_groups


def _limit_functions(file_groups, exclude_functions, include_only_functions):
    """
    Exclude nodes (functions) which match any of the exclude_functions

    :param list[Group] file_groups:
    :param list exclude_functions:
    :param list include_only_functions:
    :rtype: list[Group]
    """

    removed_functions = set()

    for group in list(file_groups):
        for node in group.all_nodes():
            if node.token in exclude_functions or \
               (include_only_functions and node.token not in include_only_functions):
                node.remove_from_parent()
                removed_functions.add(node.token)

    for function_name in exclude_functions:
        if function_name not in removed_functions:
            logging.warning(f"Could not exclude function '{function_name}' "
                             "because it was not found.")
    return file_groups


def _generate_graphviz(output_file, extension, final_img_filename):
    """
    Write the graphviz file
    :param str output_file:
    :param str extension:
    :param str final_img_filename:
    """
    start_time = time.time()
    logging.info("Running graphviz to make the image...")
    command = ["dot", "-T" + extension, output_file]
    with open(final_img_filename, 'w') as f:
        try:
            subprocess.run(command, stdout=f, check=True)
            logging.info("Graphviz finished in %.2f seconds." % (time.time() - start_time))
        except subprocess.CalledProcessError:
            logging.warning("*** Graphviz returned non-zero exit code! "
                            "Try running %r for more detail ***", ' '.join(command + ['-v', '-O']))


def _generate_final_img(output_file, extension, final_img_filename, num_edges):
    """
    Write the graphviz file
    :param str output_file:
    :param str extension:
    :param str final_img_filename:
    :param int num_edges:
    """
    _generate_graphviz(output_file, extension, final_img_filename)
    logging.info("Completed your flowchart! To see it, open %r.",
                 final_img_filename)


def code2flow(raw_source_paths, output_file, language=None, hide_legend=True,
              exclude_namespaces=None, exclude_functions=None,
              include_only_namespaces=None, include_only_functions=None,
              no_grouping=False, no_trimming=False, skip_parse_errors=False,
              lang_params=None, subset_params=None, level=logging.INFO):
    """
    Top-level function. Generate a diagram based on source code.
    Can generate either a dotfile or an image.

    :param list[str] raw_source_paths: file or directory paths
    :param str|file output_file: path to the output file. SVG/PNG will generate an image.
    :param str language: input language extension
    :param bool hide_legend: Omit the legend from the output
    :param list exclude_namespaces: List of namespaces to exclude
    :param list exclude_functions: List of functions to exclude
    :param list include_only_namespaces: List of namespaces to include
    :param list include_only_functions: List of functions to include
    :param bool no_grouping: Don't group functions into namespaces in the final output
    :param bool no_trimming: Don't trim orphaned functions / namespaces
    :param bool skip_parse_errors: If a language parser fails to parse a file, skip it
    :param lang_params LanguageParams: Object to store lang-specific params
    :param subset_params SubsetParams: Object to store subset-specific params
    :param int level: logging level
    :rtype: None
    """
    start_time = time.time()

    if not isinstance(raw_source_paths, list):
        raw_source_paths = [raw_source_paths]
    lang_params = lang_params or LanguageParams()

    exclude_namespaces = exclude_namespaces or []
    assert isinstance(exclude_namespaces, list)
    exclude_functions = exclude_functions or []
    assert isinstance(exclude_functions, list)
    include_only_namespaces = include_only_namespaces or []
    assert isinstance(include_only_namespaces, list)
    include_only_functions = include_only_functions or []
    assert isinstance(include_only_functions, list)

    logging.basicConfig(format="Code2Flow: %(message)s", level=level)

    sources, language = get_sources_and_language(raw_source_paths, language)

    output_ext = None
    if isinstance(output_file, str):
        assert '.' in output_file, "Output filename must end in one of: %r." % set(VALID_EXTENSIONS)
        output_ext = output_file.rsplit('.', 1)[1] or ''
        assert output_ext in VALID_EXTENSIONS, "Output filename must end in one of: %r." % \
                                               set(VALID_EXTENSIONS)

    final_img_filename = None
    if output_ext and output_ext in IMAGE_EXTENSIONS:
        if not is_installed('dot') and not is_installed('dot.exe'):
            raise AssertionError(
                "Can't generate a flowchart image because neither `dot` nor "
                "`dot.exe` was found. Either install graphviz (see the README) "
                "or, if you just want an intermediate text file, set your --output "
                "file to use a supported text extension: %r" % set(TEXT_EXTENSIONS))
        final_img_filename = output_file
        output_file, extension = output_file.rsplit('.', 1)
        output_file += '.gv'

    file_groups, all_nodes, edges = map_it(sources, language, no_trimming,
                                           exclude_namespaces, exclude_functions,
                                           include_only_namespaces, include_only_functions,
                                           skip_parse_errors, lang_params)

    if subset_params:
        logging.info("Filtering into subset...")
        file_groups, all_nodes, edges = _filter_for_subset(subset_params, all_nodes, edges, file_groups)

    file_groups.sort()
    all_nodes.sort()
    edges.sort()

    logging.info("Generating output file...")

    if isinstance(output_file, str):
        with open(output_file, 'w') as fh:
            as_json = output_ext == 'json'
            write_file(fh, nodes=all_nodes, edges=edges,
                       groups=file_groups, hide_legend=hide_legend,
                       no_grouping=no_grouping, as_json=as_json)
    else:
        write_file(output_file, nodes=all_nodes, edges=edges,
                   groups=file_groups, hide_legend=hide_legend,
                   no_grouping=no_grouping)

    logging.info("Wrote output file %r with %d nodes and %d edges.",
                 output_file, len(all_nodes), len(edges))
    if not output_ext == 'json':
        logging.info("For better machine readability, you can also try outputting in a json format.")
    logging.info("Code2flow finished processing in %.2f seconds." % (time.time() - start_time))

    # translate to an image if that was requested
    if final_img_filename:
        _generate_final_img(output_file, extension, final_img_filename, len(edges))


def main(sys_argv=None):
    """
    CLI interface. Sys_argv is a parameter for the sake of unittest coverage.
    :param sys_argv list:
    :rtype: None
    """
    parser = argparse.ArgumentParser(
        description=DESCRIPTION,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(
        'sources', metavar='sources', nargs='+',
        help='source code file/directory paths.')
    parser.add_argument(
        '--output', '-o', default='out.png',
        help=f'output file path. Supported types are {VALID_EXTENSIONS}.')
    parser.add_argument(
        '--language', choices=['py', 'js', 'rb', 'php'],
        help='process this language and ignore all other files.'
             'If omitted, use the suffix of the first source file.')
    parser.add_argument(
        '--target-function',
        help='output a subset of the graph centered on this function. '
             'Valid formats include `func`, `class.func`, and `file::class.func`. '
             'Requires --upstream-depth and/or --downstream-depth. ')
    parser.add_argument(
        '--upstream-depth', type=int, default=0,
        help='include n nodes upstream of --target-function.')
    parser.add_argument(
        '--downstream-depth', type=int, default=0,
        help='include n nodes downstream of --target-function.')
    parser.add_argument(
        '--exclude-functions',
        help='exclude functions from the output. Comma delimited.')
    parser.add_argument(
        '--exclude-namespaces',
        help='exclude namespaces (Classes, modules, etc) from the output. Comma delimited.')
    parser.add_argument(
        '--include-only-functions',
        help='include only functions in the output. Comma delimited.')
    parser.add_argument(
        '--include-only-namespaces',
        help='include only namespaces (Classes, modules, etc) in the output. Comma delimited.')
    parser.add_argument(
        '--no-grouping', action='store_true',
        help='instead of grouping functions into namespaces, let functions float.')
    parser.add_argument(
        '--no-trimming', action='store_true',
        help='show all functions/namespaces whether or not they connect to anything.')
    parser.add_argument(
        '--hide-legend', action='store_true',
        help='by default, Code2flow generates a small legend. This flag hides it.')
    parser.add_argument(
        '--skip-parse-errors', action='store_true',
        help='skip files that the language parser fails on.')
    parser.add_argument(
        '--source-type', choices=['script', 'module'], default='script',
        help='js only. Parse the source as scripts (commonJS) or modules (es6)')
    parser.add_argument(
        '--ruby-version', default='27',
        help='ruby only. Which ruby version to parse? This is passed directly into ruby-parse. '
             'Use numbers like 25, 27, or 31.')
    parser.add_argument(
        '--quiet', '-q', action='store_true',
        help='suppress most logging')
    parser.add_argument(
        '--verbose', '-v', action='store_true',
        help='add more logging')
    parser.add_argument(
        '--version', action='version', version='%(prog)s ' + VERSION)

    sys_argv = sys_argv or sys.argv[1:]
    args = parser.parse_args(sys_argv)
    level = logging.INFO
    if args.verbose and args.quiet:
        raise AssertionError("Passed both --verbose and --quiet flags")
    if args.verbose:
        level = logging.DEBUG
    if args.quiet:
        level = logging.WARNING

    exclude_namespaces = list(filter(None, (args.exclude_namespaces or "").split(',')))
    exclude_functions = list(filter(None, (args.exclude_functions or "").split(',')))
    include_only_namespaces = list(filter(None, (args.include_only_namespaces or "").split(',')))
    include_only_functions = list(filter(None, (args.include_only_functions or "").split(',')))

    lang_params = LanguageParams(args.source_type, args.ruby_version)
    subset_params = SubsetParams.generate(args.target_function, args.upstream_depth,
                                          args.downstream_depth)

    code2flow(
        raw_source_paths=args.sources,
        output_file=args.output,
        language=args.language,
        hide_legend=args.hide_legend,
        exclude_namespaces=exclude_namespaces,
        exclude_functions=exclude_functions,
        include_only_namespaces=include_only_namespaces,
        include_only_functions=include_only_functions,
        no_grouping=args.no_grouping,
        no_trimming=args.no_trimming,
        skip_parse_errors=args.skip_parse_errors,
        lang_params=lang_params,
        subset_params=subset_params,
        level=level,
    )


================================================
FILE: code2flow/get_ast.js
================================================
const fs = require('fs');
const {Parser} = require("acorn")

const sourceType = process.argv[2]
const src = fs.readFileSync(process.argv[3], 'utf8')
const tree = Parser.parse(src, {'locations': true, 'sourceType': sourceType,
                                'ecmaVersion': '2020'})

process.stdout.write(JSON.stringify(tree))


================================================
FILE: code2flow/get_ast.php
================================================
<?php
require_once __DIR__ . '/vendor/autoload.php';

use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;

$code = file_get_contents($argv[1]);
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);

try {
    $stmts = $parser->parse($code);
    echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";
} catch (PhpParser\Error $e) {
    echo 'Parse Error: ', $e->getMessage();
    exit(1);
}

?>


================================================
FILE: code2flow/javascript.py
================================================
import logging
import os
import json
import subprocess

from .model import (Group, Node, Call, Variable, BaseLanguage,
                    OWNER_CONST, GROUP_TYPE, is_installed, djoin, flatten)


def lineno(el):
    """
    Get the first line number of ast element

    :param ast el:
    :rtype: int
    """
    if isinstance(el, list):
        el = el[0]
    ret = el['loc']['start']['line']
    assert type(ret) == int
    return ret


def walk(tree):
    """
    Walk through the ast tree and return all nodes
    :param ast tree:
    :rtype: list[ast]
    """
    ret = []
    if type(tree) == list:
        for el in tree:
            if el.get('type'):
                ret.append(el)
                ret += walk(el)
    elif type(tree) == dict:
        for k, v in tree.items():
            if type(v) == dict and v.get('type'):
                ret.append(v)
                ret += walk(v)
            if type(v) == list:
                ret += walk(v)
    return ret


def resolve_owner(callee):
    """
    Resolve who owns the call object.
    So if the expression is i_ate.pie(). And i_ate is a Person, the callee is Person.
    This is returned as a string and eventually set to the owner_token in the call

    :param ast callee:
    :rtype: str
    """

    if callee['object']['type'] == 'ThisExpression':
        return 'this'
    if callee['object']['type'] == 'Identifier':
        return callee['object']['name']
    if callee['object']['type'] == 'MemberExpression':
        if 'object' in callee['object'] and 'name' in callee['object']['property']:
            return djoin((resolve_owner(callee['object']) or ''),
                         callee['object']['property']['name'])
        return OWNER_CONST.UNKNOWN_VAR
    if callee['object']['type'] == 'CallExpression':
        return OWNER_CONST.UNKNOWN_VAR

    if callee['object']['type'] == 'NewExpression':
        if 'name' in callee['object']['callee']:
            return callee['object']['callee']['name']
        return djoin(callee['object']['callee']['object']['name'],
                     callee['object']['callee']['property']['name'])

    return OWNER_CONST.UNKNOWN_VAR


def get_call_from_func_element(func):
    """
    Given a javascript ast that represents a function call, clear and create our
    generic Call object. Some calls have no chance at resolution (e.g. array[2](param))
    so we return nothing instead.

    :param func dict:
    :rtype: Call|None
    """
    callee = func['callee']
    if callee['type'] == 'MemberExpression' and 'name' in callee['property']:
        owner_token = resolve_owner(callee)
        return Call(token=callee['property']['name'],
                    line_number=lineno(callee),
                    owner_token=owner_token)
    if callee['type'] == 'Identifier':
        return Call(token=callee['name'], line_number=lineno(callee))
    return None


def make_calls(body):
    """
    Given a list of lines, find all calls in this list.

    :param list|dict body:
    :rtype: list[Call]
    """
    calls = []
    for element in walk(body):
        if element['type'] == 'CallExpression':
            call = get_call_from_func_element(element)
            if call:
                calls.append(call)
        elif element['type'] == 'NewExpression' and element['callee']['type'] == 'Identifier':
            calls.append(Call(token=element['callee']['name'],
                              line_number=lineno(element)))
    return calls


def process_assign(element):
    """
    Given an element from the ast which is an assignment statement, return a
    Variable that points_to the type of object being assigned. The
    points_to is often a string but that is resolved later.

    :param element ast:
    :rtype: Variable
    """

    if len(element['declarations']) > 1:
        return []
    target = element['declarations'][0]
    assert target['type'] == 'VariableDeclarator'
    if target['init'] is None:
        return []

    if target['init']['type'] == 'NewExpression':
        token = target['id']['name']
        call = get_call_from_func_element(target['init'])
        if call:
            return [Variable(token, call, lineno(element))]

    # this block is for require (as in: import) expressions
    if target['init']['type'] == 'CallExpression' \
       and target['init']['callee'].get('name') == 'require':
        import_src_str = target['init']['arguments'][0]['value']
        if 'name' in target['id']:
            imported_name = target['id']['name']
            points_to_str = djoin(import_src_str, imported_name)
            return [Variable(imported_name, points_to_str, lineno(element))]
        ret = []
        for prop in target['id'].get('properties', []):
            imported_name = prop['key']['name']
            points_to_str = djoin(import_src_str, imported_name)
            ret.append(Variable(imported_name, points_to_str, lineno(element)))
        return ret

    # For the other type of import expressions
    if target['init']['type'] == 'ImportExpression':
        import_src_str = target['init']['source']['raw']
        imported_name = target['id']['name']
        points_to_str = djoin(import_src_str, imported_name)
        return [Variable(imported_name, points_to_str, lineno(element))]

    if target['init']['type'] == 'CallExpression':
        if 'name' not in target['id']:
            return []
        call = get_call_from_func_element(target['init'])
        if call:
            return [Variable(target['id']['name'], call, lineno(element))]

    if target['init']['type'] == 'ThisExpression':
        assert set(target['init'].keys()) == {'start', 'end', 'loc', 'type'}
        return []
    return []


def make_local_variables(tree, parent):
    """
    Given an ast of all the lines in a function, generate a list of
    variables in that function. Variables are tokens and what they link to.
    In this case, what it links to is just a string. However, that is resolved
    later.

    Also return variables for the outer scope parent

    :param tree list|dict:
    :param parent Group:
    :rtype: list[Variable]
    """
    if not tree:
        return []

    variables = []
    for element in walk(tree):
        if element['type'] == 'VariableDeclaration':
            variables += process_assign(element)

    # Make a 'this' variable for use anywhere we need it that points to the class
    if isinstance(parent, Group) and parent.group_type == GROUP_TYPE.CLASS:
        variables.append(Variable('this', parent, lineno(tree)))

    variables = list(filter(None, variables))
    return variables


def children(tree):
    """
    The acorn AST is tricky. This returns all the children of an element
    :param ast tree:
    :rtype: list[ast]
    """
    assert type(tree) == dict
    ret = []
    for k, v in tree.items():
        if type(v) == dict and v.get('type'):
            ret.append(v)
        if type(v) == list:
            ret += filter(None, v)
    return ret


def get_inherits(tree):
    """
    Gets the superclass of the class. In js, this will be max 1 element
    :param ast tree:
    :rtype: list[str]
    """
    if tree['superClass']:
        if 'name' in tree['superClass']:
            return [tree['superClass']['name']]
        return [djoin(tree['superClass']['object']['name'], tree['superClass']['property']['name'])]
    return []


def get_acorn_version():
    """
    Get the version of installed acorn
    :rtype: str
    """
    proc = subprocess.Popen(['node', '-p', 'require(\'acorn/package.json\').version'],
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                            cwd=os.path.dirname(os.path.realpath(__file__)))
    assert proc.wait() == 0, "Acorn is required to parse javascript files. " \
                             "It was found on the path but could not be imported " \
                             "in node.\n" + proc.stderr.read().decode()
    return proc.stdout.read().decode().strip()


class Javascript(BaseLanguage):
    @staticmethod
    def assert_dependencies():
        """Assert that acorn is installed and the correct version"""
        assert is_installed('acorn'), "Acorn is required to parse javascript files " \
                                      "but was not found on the path. Install it " \
                                      "from npm and try again."
        version = get_acorn_version()
        if not version.startswith('8.'):
            logging.warning("Acorn is required to parse javascript files. "
                            "Version %r was found but code2flow has only been "
                            "tested on 8.*", version)
        logging.info("Using Acorn %s" % version)

    @staticmethod
    def get_tree(filename, lang_params):
        """
        Get the entire AST for this file

        :param filename str:
        :param lang_params LanguageParams:
        :rtype: ast
        """
        script_loc = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                  "get_ast.js")
        cmd = ["node", script_loc, lang_params.source_type, filename]
        try:
            output = subprocess.check_output(cmd, stderr=subprocess.PIPE)
        except subprocess.CalledProcessError:
            raise AssertionError(
                "Acorn could not parse file %r. You may have a JS syntax error or "
                "if this is an es6-style source, you may need to run code2flow "
                "with --source-type=module. "
                "For more detail, try running the command "
                "\n  acorn %s\n"
                "Warning: Acorn CANNOT parse all javascript files. See their docs. " %
                (filename, filename)) from None
        tree = json.loads(output)
        assert isinstance(tree, dict)
        assert tree['type'] == 'Program'
        return tree

    @staticmethod
    def separate_namespaces(tree):
        """
        Given an AST, recursively separate that AST into lists of ASTs for the
        subgroups, nodes, and body. This is an intermediate step to allow for
        cleaner processing downstream

        :param tree ast:
        :returns: tuple of group, node, and body trees. These are processed
                  downstream into real Groups and Nodes.
        :rtype: (list[ast], list[ast], list[ast])
        """

        groups = []
        nodes = []
        body = []
        for el in children(tree):
            if el['type'] in ('MethodDefinition', 'FunctionDeclaration'):
                nodes.append(el)
            elif el['type'] == 'ClassDeclaration':
                groups.append(el)
            else:
                tup = Javascript.separate_namespaces(el)
                if tup[0] or tup[1]:
                    groups += tup[0]
                    nodes += tup[1]
                    body += tup[2]
                else:
                    body.append(el)
        return groups, nodes, body

    @staticmethod
    def make_nodes(tree, parent):
        """
        Given an ast of all the lines in a function, create the node along with the
        calls and variables internal to it.
        Also make the nested subnodes

        :param tree ast:
        :param parent Group:
        :rtype: list[Node]
        """
        is_constructor = False
        if tree.get('kind') == 'constructor':
            token = '(constructor)'
            is_constructor = True
        elif tree['type'] == 'FunctionDeclaration':
            token = tree['id']['name']
        elif tree['type'] == 'MethodDefinition':
            token = tree['key']['name']

        if tree['type'] == 'FunctionDeclaration':
            full_node_body = tree['body']
        else:
            full_node_body = tree['value']

        subgroup_trees, subnode_trees, this_scope_body = Javascript.separate_namespaces(full_node_body)
        if subgroup_trees:
            # TODO - this is when a class is defined within a function
            # It's unusual but should probably be handled in the future.
            # Handling this use case would require some code reorganziation.
            # Take a look at class_in_function.js to better understand.
            logging.warning("Skipping class defined within a function!")

        line_number = lineno(tree)
        calls = make_calls(this_scope_body)
        variables = make_local_variables(this_scope_body, parent)
        node = Node(token, calls, variables, parent=parent, line_number=line_number,
                    is_constructor=is_constructor)
        subnodes = flatten([Javascript.make_nodes(t, node) for t in subnode_trees])

        return [node] + subnodes

    @staticmethod
    def make_root_node(lines, parent):
        """
        The "root_node" is an implict node of lines which are executed in the global
        scope on the file itself and not otherwise part of any function.

        :param lines list[ast]:
        :param parent Group:
        :rtype: Node
        """
        token = "(global)"
        calls = make_calls(lines)
        variables = make_local_variables(lines, parent)
        root_node = Node(token, calls, variables,
                         line_number=0, parent=parent)
        return root_node

    @staticmethod
    def make_class_group(tree, parent):
        """
        Given an AST for the subgroup (a class), generate that subgroup.
        In this function, we will also need to generate all of the nodes internal
        to the group.

        :param tree ast:
        :param parent Group:
        :rtype: Group
        """
        assert tree['type'] == 'ClassDeclaration'
        subgroup_trees, node_trees, body_trees = Javascript.separate_namespaces(tree)
        assert not subgroup_trees

        group_type = GROUP_TYPE.CLASS
        token = tree['id']['name']
        display_name = 'Class'
        line_number = lineno(tree)
        inherits = get_inherits(tree)
        class_group = Group(token, group_type, display_name,
                            inherits=inherits, line_number=line_number, parent=parent)

        for node_tree in node_trees:
            for new_node in Javascript.make_nodes(node_tree, parent=class_group):
                class_group.add_node(new_node)

        return class_group

    @staticmethod
    def file_import_tokens(filename):
        """
        Returns the token(s) we would use if importing this file from another.

        :param filename str:
        :rtype: list[str]
        """
        return []


================================================
FILE: code2flow/model.py
================================================
import abc
import os


TRUNK_COLOR = '#966F33'
LEAF_COLOR = '#6db33f'
EDGE_COLORS = ["#000000", "#E69F00", "#56B4E9", "#009E73",
               "#F0E442", "#0072B2", "#D55E00", "#CC79A7"]
NODE_COLOR = "#cccccc"


class Namespace(dict):
    """
    Abstract constants class
    Constants can be accessed via .attribute or [key] and can be iterated over.
    """
    def __init__(self, *args, **kwargs):
        d = {k: k for k in args}
        d.update(dict(kwargs.items()))
        super().__init__(d)

    def __getattr__(self, item):
        return self[item]


OWNER_CONST = Namespace("UNKNOWN_VAR", "UNKNOWN_MODULE")
GROUP_TYPE = Namespace("FILE", "CLASS", "NAMESPACE")


def is_installed(executable_cmd):
    """
    Determine whether a command can be run or not

    :param list[str] individual_files:
    :rtype: str
    """
    for path in os.environ["PATH"].split(os.pathsep):
        path = path.strip('"')
        exe_file = os.path.join(path, executable_cmd)
        if os.path.isfile(exe_file) and os.access(exe_file, os.X_OK):
            return True
    return False


def djoin(*tup):
    """
    Convenience method to join strings with dots
    :rtype: str
    """
    if len(tup) == 1 and isinstance(tup[0], list):
        return '.'.join(tup[0])
    return '.'.join(tup)


def flatten(list_of_lists):
    """
    Return a list from a list of lists
    :param list[list[Value]] list_of_lists:
    :rtype: list[Value]
    """
    return [el for sublist in list_of_lists for el in sublist]


def _resolve_str_variable(variable, file_groups):
    """
    String variables are when variable.points_to is a string
    This happens ONLY when we have imports that we delayed processing for

    This function looks through all files to see if any particular node matches
    the variable.points_to string

    :param Variable variable:
    :param list[Group] file_groups:
    :rtype: Node|Group|str
    """
    for file_group in file_groups:
        for node in file_group.all_nodes():
            if any(ot == variable.points_to for ot in node.import_tokens):
                return node
        for group in file_group.all_groups():
            if any(ot == variable.points_to for ot in group.import_tokens):
                return group
    return OWNER_CONST.UNKNOWN_MODULE


class BaseLanguage(abc.ABC):
    """
    Languages are individual implementations for different dynamic languages.
    This is the superclass of Python, Javascript, PHP, and Ruby.
    Every implementation must implement all of these methods.
    For more detail, see the individual implementations.
    Note that the 'Tree' type is generic and will be a different
    type for different languages. In Python, it is an ast.AST.
    """

    @staticmethod
    @abc.abstractmethod
    def assert_dependencies():
        """
        :rtype: None
        """

    @staticmethod
    @abc.abstractmethod
    def get_tree(filename, lang_params):
        """
        :param filename str:
        :rtype: Tree
        """

    @staticmethod
    @abc.abstractmethod
    def separate_namespaces(tree):
        """
        :param tree Tree:
        :rtype: (list[tree], list[tree], list[tree])
        """

    @staticmethod
    @abc.abstractmethod
    def make_nodes(tree, parent):
        """
        :param tree Tree:
        :param parent Group:
        :rtype: list[Node]
        """

    @staticmethod
    @abc.abstractmethod
    def make_root_node(lines, parent):
        """
        :param lines list[Tree]:
        :param parent Group:
        :rtype: Node
        """

    @staticmethod
    @abc.abstractmethod
    def make_class_group(tree, parent):
        """
        :param tree Tree:
        :param parent Group:
        :rtype: Group
        """


class Variable():
    """
    Variables represent named tokens that are accessible to their scope.
    They may either point to a string or, once resolved, a Group/Node.
    Not all variables can be resolved
    """
    def __init__(self, token, points_to, line_number=None):
        """
        :param str token:
        :param str|Call|Node|Group points_to: (str/Call is eventually resolved to Nodes|Groups)
        :param int|None line_number:
        """
        assert token
        assert points_to
        self.token = token
        self.points_to = points_to
        self.line_number = line_number

    def __repr__(self):
        return f"<Variable token={self.token} points_to={repr(self.points_to)}"

    def to_string(self):
        """
        For logging
        :rtype: str
        """
        if self.points_to and isinstance(self.points_to, (Group, Node)):
            return f'{self.token}->{self.points_to.token}'
        return f'{self.token}->{self.points_to}'


class Call():
    """
    Calls represent function call expressions.
    They can be an attribute call like
        object.do_something()
    Or a "naked" call like
        do_something()

    """
    def __init__(self, token, line_number=None, owner_token=None, definite_constructor=False):
        self.token = token
        self.owner_token = owner_token
        self.line_number = line_number
        self.definite_constructor = definite_constructor

    def __repr__(self):
        return f"<Call owner_token={self.owner_token} token={self.token}>"

    def to_string(self):
        """
        Returns a representation of this call to be printed by the engine
        in logging.
        :rtype: str
        """
        if self.owner_token:
            return f"{self.owner_token}.{self.token}()"
        return f"{self.token}()"

    def is_attr(self):
        """
        Attribute calls are like `a.do_something()` rather than `do_something()`
        :rtype: bool
        """
        return self.owner_token is not None

    def matches_variable(self, variable):
        """
        Check whether this variable is what the call is acting on.
        For example, if we had 'obj' from
            obj = Obj()
        as a variable and a call of
            obj.do_something()
        Those would match and we would return the "do_something" node from obj.

        :param variable Variable:
        :rtype: Node
        """

        if self.is_attr():
            if self.owner_token == variable.token:
                for node in getattr(variable.points_to, 'nodes', []):
                    if self.token == node.token:
                        return node
                for inherit_nodes in getattr(variable.points_to, 'inherits', []):
                    for node in inherit_nodes:
                        if self.token == node.token:
                            return node
                if variable.points_to in OWNER_CONST:
                    return variable.points_to

            # This section is specifically for resolving namespace variables
            if isinstance(variable.points_to, Group) \
               and variable.points_to.group_type == GROUP_TYPE.NAMESPACE:
                parts = self.owner_token.split('.')
                if len(parts) != 2:
                    return None
                if parts[0] != variable.token:
                    return None
                for node in variable.points_to.all_nodes():
                    if parts[1] == node.namespace_ownership() \
                       and self.token == node.token:
                        return node

            return None
        if self.token == variable.token:
            if isinstance(variable.points_to, Node):
                return variable.points_to
            if isinstance(variable.points_to, Group) \
               and variable.points_to.group_type == GROUP_TYPE.CLASS \
               and variable.points_to.get_constructor():
                return variable.points_to.get_constructor()
        return None


class Node():
    def __init__(self, token, calls, variables, parent, import_tokens=None,
                 line_number=None, is_constructor=False):
        self.token = token
        self.line_number = line_number
        self.calls = calls
        self.variables = variables
        self.import_tokens = import_tokens or []
        self.parent = parent
        self.is_constructor = is_constructor

        self.uid = "node_" + os.urandom(4).hex()

        # Assume it is a leaf and a trunk. These are modified later
        self.is_leaf = True  # it calls nothing else
        self.is_trunk = True  # nothing calls it

    def __repr__(self):
        return f"<Node token={self.token} parent={self.parent}>"

    def __lt__(self, other):
            return self.name() < other.name()

    def name(self):
        """
        Names exist largely for unit tests and deterministic node sorting
        :rtype: str
        """
        return f"{self.first_group().filename()}::{self.token_with_ownership()}"

    def first_group(self):
        """
        The first group that contains this node.
        :rtype: Group
        """
        parent = self.parent
        while not isinstance(parent, Group):
            parent = parent.parent
        return parent

    def file_group(self):
        """
        Get the file group that this node is in.
        :rtype: Group
        """
        parent = self.parent
        while parent.parent:
            parent = parent.parent
        return parent

    def is_attr(self):
        """
        Whether this node is attached to something besides the file
        :rtype: bool
        """
        return (self.parent
                and isinstance(self.parent, Group)
                and self.parent.group_type in (GROUP_TYPE.CLASS, GROUP_TYPE.NAMESPACE))

    def token_with_ownership(self):
        """
        Token which includes what group this is a part of
        :rtype: str
        """
        if self.is_attr():
            return djoin(self.parent.token, self.token)
        return self.token

    def namespace_ownership(self):
        """
        Get the ownership excluding namespace
        :rtype: str
        """
        parent = self.parent
        ret = []
        while parent and parent.group_type == GROUP_TYPE.CLASS:
            ret = [parent.token] + ret
            parent = parent.parent
        return djoin(ret)

    def label(self):
        """
        Labels are what you see on the graph
        :rtype: str
        """
        if self.line_number is not None:
            return f"{self.line_number}: {self.token}()"
        return f"{self.token}()"

    def remove_from_parent(self):
        """
        Remove this node from it's parent. This effectively deletes the node.
        :rtype: None
        """
        self.first_group().nodes = [n for n in self.first_group().nodes if n != self]

    def get_variables(self, line_number=None):
        """
        Get variables in-scope on the line number.
        This includes all local variables as-well-as outer-scope variables
        :rtype: list[Variable]
        """
        if line_number is None:
            ret = list(self.variables)
        else:
            # TODO variables should be sorted by scope before line_number
            ret = list([v for v in self.variables if v.line_number <= line_number])
        if any(v.line_number for v in ret):
            ret.sort(key=lambda v: v.line_number, reverse=True)

        parent = self.parent
        while parent:
            ret += parent.get_variables()
            parent = parent.parent
        return ret

    def resolve_variables(self, file_groups):
        """
        For all variables, attempt to resolve the Node/Group on points_to.
        There is a good chance this will be unsuccessful.

        :param list[Group] file_groups:
        :rtype: None
        """
        for variable in self.variables:
            if isinstance(variable.points_to, str):
                variable.points_to = _resolve_str_variable(variable, file_groups)
            elif isinstance(variable.points_to, Call):
                # else, this is a call variable
                call = variable.points_to
                # Only process Class(); Not a.Class()
                if call.is_attr() and not call.definite_constructor:
                    continue
                # Else, assume the call is a constructor.
                # iterate through to find the right group
                for file_group in file_groups:
                    for group in file_group.all_groups():
                        if group.token == call.token:
                            variable.points_to = group
            else:
                assert isinstance(variable.points_to, (Node, Group))

    def to_dot(self):
        """
        Output for graphviz (.dot) files
        :rtype: str
        """
        attributes = {
            'label': self.label(),
            'name': self.name(),
            'shape': "rect",
            'style': 'rounded,filled',
            'fillcolor': NODE_COLOR,
        }
        if self.is_trunk:
            attributes['fillcolor'] = TRUNK_COLOR
        elif self.is_leaf:
            attributes['fillcolor'] = LEAF_COLOR

        ret = self.uid + ' ['
        for k, v in attributes.items():
            ret += f'{k}="{v}" '
        ret += ']'
        return ret

    def to_dict(self):
        """
        Output for json files (json graph specification)
        :rtype: dict
        """
        return {
            'uid': self.uid,
            'label': self.label(),
            'name': self.name(),
        }


def _wrap_as_variables(sequence):
    """
    Given a list of either Nodes or Groups, wrap them in variables.
    This is used in the get_variables method to allow all defined
    functions and classes to be defined as variables
    :param list[Group|Node] sequence:
    :rtype: list[Variable]
    """
    return [Variable(el.token, el, el.line_number) for el in sequence]


class Edge():
    def __init__(self, node0, node1):
        self.node0 = node0
        self.node1 = node1

        # When we draw the edge, we know the calling function is definitely not a leaf...
        # and the called function is definitely not a trunk
        node0.is_leaf = False
        node1.is_trunk = False

    def __repr__(self):
        return f"<Edge {self.node0} -> {self.node1}"

    def __lt__(self, other):
        if self.node0 == other.node0:
            return self.node1 < other.node1
        return self.node0 < other.node0

    def to_dot(self):
        '''
        Returns string format for embedding in a dotfile. Example output:
        node_uid_a -> node_uid_b [color='#aaa' penwidth='2']
        :rtype: str
        '''
        ret = self.node0.uid + ' -> ' + self.node1.uid
        source_color = int(self.node0.uid.split("_")[-1], 16) % len(EDGE_COLORS)
        ret += f' [color="{EDGE_COLORS[source_color]}" penwidth="2"]'
        return ret

    def to_dict(self):
        """
        :rtype: dict
        """
        return {
            'source': self.node0.uid,
            'target': self.node1.uid,
            'directed': True,
        }


class Group():
    """
    Groups represent namespaces (classes and modules/files)
    """
    def __init__(self, token, group_type, display_type, import_tokens=None,
                 line_number=None, parent=None, inherits=None):
        self.token = token
        self.line_number = line_number
        self.nodes = []
        self.root_node = None
        self.subgroups = []
        self.parent = parent
        self.group_type = group_type
        self.display_type = display_type
        self.import_tokens = import_tokens or []
        self.inherits = inherits or []
        assert group_type in GROUP_TYPE

        self.uid = "cluster_" + os.urandom(4).hex()  # group doesn't work by syntax rules

    def __repr__(self):
        return f"<Group token={self.token} type={self.display_type}>"

    def __lt__(self, other):
        return self.label() < other.label()

    def label(self):
        """
        Labels are what you see on the graph
        :rtype: str
        """
        return f"{self.display_type}: {self.token}"

    def filename(self):
        """
        The ultimate filename of this group.
        :rtype: str
        """
        if self.group_type == GROUP_TYPE.FILE:
            return self.token
        return self.parent.filename()

    def add_subgroup(self, sg):
        """
        Subgroups are found after initialization. This is how they are added.
        :param sg Group:
        """
        self.subgroups.append(sg)

    def add_node(self, node, is_root=False):
        """
        Nodes are found after initialization. This is how they are added.
        :param node Node:
        :param is_root bool:
        """
        self.nodes.append(node)
        if is_root:
            self.root_node = node

    def all_nodes(self):
        """
        List of nodes that are part of this group + all subgroups
        :rtype: list[Node]
        """
        ret = list(self.nodes)
        for subgroup in self.subgroups:
            ret += subgroup.all_nodes()
        return ret

    def get_constructor(self):
        """
        Return the first constructor for this group - if any
        TODO, this excludes the possibility of multiple constructors like
        __init__ vs __new__
        :rtype: Node|None
        """
        assert self.group_type == GROUP_TYPE.CLASS
        constructors = [n for n in self.nodes if n.is_constructor]
        if constructors:
            return constructors[0]

    def all_groups(self):
        """
        List of groups that are part of this group + all subgroups
        :rtype: list[Group]
        """
        ret = [self]
        for subgroup in self.subgroups:
            ret += subgroup.all_groups()
        return ret

    def get_variables(self, line_number=None):
        """
        Get in-scope variables from this group.
        This assumes every variable will be in-scope in nested functions
        line_number is included for compatibility with Node.get_variables but is not used

        :param int line_number:
        :rtype: list[Variable]
        """

        if self.root_node:
            variables = (self.root_node.variables
                         + _wrap_as_variables(self.subgroups)
                         + _wrap_as_variables(n for n in self.nodes if n != self.root_node))
            if any(v.line_number for v in variables):
                return sorted(variables, key=lambda v: v.line_number, reverse=True)
            return variables
        else:
            return []

    def remove_from_parent(self):
        """
        Remove this group from it's parent. This is effectively a deletion
        :rtype: None
        """
        if self.parent:
            self.parent.subgroups = [g for g in self.parent.subgroups if g != self]

    def all_parents(self):
        """
        Recursively get the entire inheritance tree of this group
        :rtype: list[Group]
        """
        if self.parent:
            return [self.parent] + self.parent.all_parents()
        return []

    def to_dot(self):
        """
        Returns string format for embedding in a dotfile. Example output:
        subgraph group_uid_a {
            node_uid_b node_uid_c;
            label='class_name';
            ...
            subgraph group_uid_z {
                ...
            }
            ...
        }
        :rtype: str
        """

        ret = 'subgraph ' + self.uid + ' {\n'
        if self.nodes:
            ret += '    '
            ret += ' '.join(node.uid for node in self.nodes)
            ret += ';\n'
        attributes = {
            'label': self.label(),
            'name': self.token,
            'style': 'filled',
        }
        for k, v in attributes.items():
            ret += f'    {k}="{v}";\n'
        ret += '    graph[style=dotted];\n'
        for subgroup in self.subgroups:
            ret += '    ' + ('\n'.join('    ' + ln for ln in
                                       subgroup.to_dot().split('\n'))).strip() + '\n'
        ret += '};\n'
        return ret


================================================
FILE: code2flow/package.json
================================================
{
  "dependencies": {
    "acorn": "^8.6.0"
  }
}


================================================
FILE: code2flow/php.py
================================================
import json
import os
import subprocess

from .model import (Group, Node, Call, Variable, BaseLanguage,
                    OWNER_CONST, GROUP_TYPE, is_installed, flatten, djoin)


def lineno(tree):
    """
    Return the line number of the AST
    :param tree ast:
    :rtype: int
    """
    return tree['attributes']['startLine']


def get_name(tree, from_='name'):
    """
    Get the name (token) of the AST node.
    :param tree ast:
    :rtype: str|None
    """
    # return tree['name']['name']
    if 'name' in tree and isinstance(tree['name'], str):
        return tree['name']

    if 'parts' in tree:
        return djoin(tree['parts'])

    if from_ in tree:
        return get_name(tree[from_])

    return None


def get_call_from_expr(func_expr):
    """
    Given an ast that represents a send call, clear and create our
    generic Call object. Some calls have no chance at resolution (e.g. array[2](param))
    so we return nothing instead.

    :param func_expr ast:
    :rtype: Call|None
    """
    if func_expr['nodeType'] == 'Expr_FuncCall':
        token = get_name(func_expr)
        owner_token = None
    elif func_expr['nodeType'] == 'Expr_New' and func_expr['class'].get('parts'):
        token = '__construct'
        owner_token = get_name(func_expr['class'])
    elif func_expr['nodeType'] == 'Expr_MethodCall':
        # token = func_expr['name']['name']
        token = get_name(func_expr)
        if 'var' in func_expr['var']:
            owner_token = OWNER_CONST.UNKNOWN_VAR
        else:
            owner_token = get_name(func_expr['var'])
    elif func_expr['nodeType'] == 'Expr_BinaryOp_Concat' and func_expr['right']['nodeType'] == 'Expr_FuncCall':
        token = get_name(func_expr['right'])
        if 'class' in func_expr['left']:
            owner_token = get_name(func_expr['left']['class'])
        else:
            owner_token = get_name(func_expr['left'])
    elif func_expr['nodeType'] == 'Expr_StaticCall':
        token = get_name(func_expr)
        owner_token = get_name(func_expr['class'])
    else:
        return None

    if owner_token and token == '__construct':
        # Taking out owner_token for constructors as a little hack to make it work
        return Call(token=owner_token,
                    line_number=lineno(func_expr))
    ret = Call(token=token,
               owner_token=owner_token,
               line_number=lineno(func_expr))
    return ret


def walk(tree):
    """
    Given an ast tree walk it to get every node. For PHP, the exception
    is that we return Expr_BinaryOp_Concat which has internal nodes but
    is important to process as a whole.

    :param tree_el ast:
    :rtype: list[ast]
    """

    if isinstance(tree, list):
        ret = []
        for el in tree:
            if isinstance(el, dict) and el.get('nodeType'):
                ret += walk(el)
        return ret

    assert isinstance(tree, dict)
    assert tree['nodeType']
    ret = [tree]

    if tree['nodeType'] == 'Expr_BinaryOp_Concat':
        return ret

    for v in tree.values():
        if isinstance(v, list) or (isinstance(v, dict) and v.get('nodeType')):
            ret += walk(v)
    return ret


def children(tree):
    """
    Given an ast tree get all children. For PHP, children are anything
    with a nodeType.

    :param tree_el ast:
    :rtype: list[ast]
    """
    assert isinstance(tree, dict)
    ret = []
    for v in tree.values():
        if isinstance(v, list):
            for el in v:
                if isinstance(el, dict) and el.get('nodeType'):
                    ret.append(el)
        elif isinstance(v, dict) and v.get('nodeType'):
            ret.append(v)
    return ret


def make_calls(body_el):
    """
    Given a list of lines, find all calls in this list.

    :param body_el ast:
    :rtype: list[Call]
    """
    calls = []
    for expr in walk(body_el):
        call = get_call_from_expr(expr)
        calls.append(call)
    ret = list(filter(None, calls))

    return ret


def process_assign(assignment_el):
    """
    Given an assignment statement, return a
    Variable that points_to the type of object being assigned.

    :param assignment_el ast:
    :rtype: Variable
    """
    assert assignment_el['nodeType'] == 'Expr_Assign'
    if 'name' not in assignment_el['var']:
        return None

    varname = assignment_el['var']['name']
    call = get_call_from_expr(assignment_el['expr'])
    if call:
        return Variable(varname, call, line_number=lineno(assignment_el))
    # else is something like `varname = num`
    return None


def make_local_variables(tree_el, parent):
    """
    Given an ast of all the lines in a function, generate a list of
    variables in that function. Variables are tokens and what they link to.

    :param tree_el ast:
    :param parent Group:
    :rtype: list[Variable]
    """
    variables = []
    for el in walk(tree_el):
        if el['nodeType'] == 'Expr_Assign':
            variables.append(process_assign(el))
        if el['nodeType'] == 'Stmt_Use':
            for use in el['uses']:
                owner_token = djoin(use['name']['parts'])
                token = use['alias']['name'] if use['alias'] else owner_token
                variables.append(Variable(token, points_to=owner_token,
                                          line_number=lineno(el)))

    # Make a 'this'/'self' variable for use anywhere we need it that points to the class
    if isinstance(parent, Group) and parent.group_type in GROUP_TYPE.CLASS:
        variables.append(Variable('this', parent, line_number=parent.line_number))
        variables.append(Variable('self', parent, line_number=parent.line_number))

    return list(filter(None, variables))


def get_inherits(tree):
    """
    Get the various types of inheritances this class/namespace/trait can have

    :param tree ast:
    :rtype: list[str]
    """
    ret = []

    if tree.get('extends', {}):
        ret.append(djoin(tree['extends']['parts']))

    for stmt in tree.get('stmts', []):
        if stmt['nodeType'] == 'Stmt_TraitUse':
            for trait in stmt['traits']:
                ret.append(djoin(trait['parts']))
    return ret


def run_ast_parser(filename):
    """
    Parse the php file and return the output + the returncode
    Separate function b/c unittesting and asserting php-parser installation.
    :param filename str:
    :type: str, int
    """

    script_loc = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                              "get_ast.php")
    cmd = ["php", script_loc, filename]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    return proc.communicate()[0], proc.returncode


class PHP(BaseLanguage):
    @staticmethod
    def assert_dependencies():
        """Assert that php and php-parser are installed"""
        assert is_installed('php'), "No php installation could be found"
        self_ref = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                "get_ast.php")
        outp, returncode = run_ast_parser(self_ref)
        path = os.path.dirname(os.path.realpath(__file__))
        assert_msg = 'Error running the PHP parser. From the `%s` directory, run ' \
                     '`composer require nikic/php-parser "^4.10"`.' % path
        assert not returncode, assert_msg
        return outp

    @staticmethod
    def get_tree(filename, lang_params):
        """
        Get the entire AST for this file

        :param filename str:
        :param lang_params LanguageParams:
        :rtype: ast
        """

        outp, returncode = run_ast_parser(filename)
        if returncode:
            raise AssertionError(
                "Could not parse file %r. You may have a syntax error. "
                "For more detail, try running with `php %s`. " %
                (filename, filename))

        tree = json.loads(outp)
        assert isinstance(tree, list)
        if len(tree) == 1 and tree[0]['nodeType'] == 'Stmt_InlineHTML':
            raise AssertionError("Tried to parse a file that is not likely PHP")
        return tree

    @staticmethod
    def separate_namespaces(tree):
        """
        Given a tree element, recursively separate that AST into lists of ASTs for the
        subgroups, nodes, and body. This is an intermediate step to allow for
        cleaner processing downstream

        :param tree ast:
        :returns: tuple of group, node, and body trees. These are processed
                  downstream into real Groups and Nodes.
        :rtype: (list[ast], list[ast], list[ast])
        """
        tree = tree or []  # if its abstract, it comes in with no body

        groups = []
        nodes = []
        body = []
        for el in tree:
            if el['nodeType'] in ('Stmt_Function', 'Stmt_ClassMethod', 'Expr_Closure'):
                nodes.append(el)
            elif el['nodeType'] in ('Stmt_Class', 'Stmt_Namespace', 'Stmt_Trait'):
                groups.append(el)
            else:
                tup = PHP.separate_namespaces(children(el))
                if tup[0] or tup[1]:
                    groups += tup[0]
                    nodes += tup[1]
                    body += tup[2]
                else:
                    body.append(el)
        return groups, nodes, body

    @staticmethod
    def make_nodes(tree, parent):
        """
        Given a tree element of all the lines in a function, create the node along
        with the calls and variables internal to it.
        Also make the nested subnodes

        :param tree ast:
        :param parent Group:
        :rtype: list[Node]
        """
        assert tree['nodeType'] in ('Stmt_Function', 'Stmt_ClassMethod', 'Expr_Closure')

        if tree['nodeType'] == 'Expr_Closure':
            token = '(Closure)'
        else:
            token = tree['name']['name']
        is_constructor = token == '__construct' and parent.group_type == GROUP_TYPE.CLASS

        tree_body = tree['stmts']
        subgroup_trees, subnode_trees, this_scope_body = PHP.separate_namespaces(tree_body)
        assert not subgroup_trees
        calls = make_calls(this_scope_body)
        variables = make_local_variables(this_scope_body, parent)

        if parent.group_type == GROUP_TYPE.CLASS and parent.parent.group_type == GROUP_TYPE.NAMESPACE:
            import_tokens = [djoin(parent.parent.token, parent.token, token)]
        if parent.group_type in (GROUP_TYPE.NAMESPACE, GROUP_TYPE.CLASS):
            import_tokens = [djoin(parent.token, token)]
        else:
            import_tokens = [token]

        node = Node(token, calls, variables, parent, import_tokens=import_tokens,
                    is_constructor=is_constructor, line_number=lineno(tree))

        subnodes = flatten([PHP.make_nodes(t, parent) for t in subnode_trees])
        return [node] + subnodes

    @staticmethod
    def make_root_node(lines, parent):
        """
        The "root_node" is an implict node of lines which are executed in the global
        scope on the file itself and not otherwise part of any function.

        :param lines list[ast]:
        :param parent Group:
        :rtype: Node
        """
        token = "(global)"
        line_number = lineno(lines[0]) if lines else 0
        calls = make_calls(lines)
        variables = make_local_variables(lines, parent)
        root_node = Node(token, calls, variables, parent,
                         line_number=line_number)
        return root_node

    @staticmethod
    def make_class_group(tree, parent):
        """
        Given an AST for the subgroup (a class), generate that subgroup.
        In this function, we will also need to generate all of the nodes internal
        to the group.

        Specific to PHP, this can also be a namespace or class.

        :param tree ast:
        :param parent Group:
        :rtype: Group
        """
        assert tree['nodeType'] in ('Stmt_Class', 'Stmt_Namespace', 'Stmt_Trait')
        subgroup_trees, node_trees, body_trees = PHP.separate_namespaces(tree['stmts'])

        token = get_name(tree['name'])
        display_type = tree['nodeType'][5:]

        inherits = get_inherits(tree)

        group_type = GROUP_TYPE.CLASS
        if display_type == 'Namespace':
            group_type = GROUP_TYPE.NAMESPACE

        import_tokens = [token]
        if display_type == 'Class' and parent.group_type == GROUP_TYPE.NAMESPACE:
            import_tokens = [djoin(parent.token, token)]

        class_group = Group(token, group_type, display_type, import_tokens=import_tokens,
                            parent=parent, inherits=inherits, line_number=lineno(tree))

        for subgroup_tree in subgroup_trees:
            class_group.add_subgroup(PHP.make_class_group(subgroup_tree, class_group))

        for node_tree in node_trees:
            for new_node in PHP.make_nodes(node_tree, parent=class_group):
                class_group.add_node(new_node)

        if group_type == GROUP_TYPE.NAMESPACE:
            class_group.add_node(PHP.make_root_node(body_trees, class_group))
            for node in class_group.nodes:
                node.variables += [Variable(n.token, n, line_number=n.line_number)
                                   for n in class_group.nodes]

        return class_group

    @staticmethod
    def file_import_tokens(filename):
        """
        Returns the token(s) we would use if importing this file from another.

        :param filename str:
        :rtype: list[str]
        """
        return []


================================================
FILE: code2flow/python.py
================================================
import ast
import logging
import os

from .model import (OWNER_CONST, GROUP_TYPE, Group, Node, Call, Variable,
                    BaseLanguage, djoin)


def get_call_from_func_element(func):
    """
    Given a python ast that represents a function call, clear and create our
    generic Call object. Some calls have no chance at resolution (e.g. array[2](param))
    so we return nothing instead.

    :param func ast:
    :rtype: Call|None
    """
    assert type(func) in (ast.Attribute, ast.Name, ast.Subscript, ast.Call)
    if type(func) == ast.Attribute:
        owner_token = []
        val = func.value
        while True:
            try:
                owner_token.append(getattr(val, 'attr', val.id))
            except AttributeError:
                pass
            val = getattr(val, 'value', None)
            if not val:
                break
        if owner_token:
            owner_token = djoin(*reversed(owner_token))
        else:
            owner_token = OWNER_CONST.UNKNOWN_VAR
        return Call(token=func.attr, line_number=func.lineno, owner_token=owner_token)
    if type(func) == ast.Name:
        return Call(token=func.id, line_number=func.lineno)
    if type(func) in (ast.Subscript, ast.Call):
        return None


def make_calls(lines):
    """
    Given a list of lines, find all calls in this list.

    :param lines list[ast]:
    :rtype: list[Call]
    """

    calls = []
    for tree in lines:
        for element in ast.walk(tree):
            if type(element) != ast.Call:
                continue
            call = get_call_from_func_element(element.func)
            if call:
                calls.append(call)
    return calls


def process_assign(element):
    """
    Given an element from the ast which is an assignment statement, return a
    Variable that points_to the type of object being assigned. For now, the
    points_to is a string but that is resolved later.

    :param element ast:
    :rtype: Variable
    """

    if type(element.value) != ast.Call:
        return []
    call = get_call_from_func_element(element.value.func)
    if not call:
        return []

    ret = []
    for target in element.targets:
        if type(target) != ast.Name:
            continue
        token = target.id
        ret.append(Variable(token, call, element.lineno))
    return ret


def process_import(element):
    """
    Given an element from the ast which is an import statement, return a
    Variable that points_to the module being imported. For now, the
    points_to is a string but that is resolved later.

    :param element ast:
    :rtype: Variable
    """
    ret = []

    for single_import in element.names:
        assert isinstance(single_import, ast.alias)
        token = single_import.asname or single_import.name
        rhs = single_import.name

        if hasattr(element, 'module') and element.module:
            rhs = djoin(element.module, rhs)
        ret.append(Variable(token, points_to=rhs, line_number=element.lineno))
    return ret


def make_local_variables(lines, parent):
    """
    Given an ast of all the lines in a function, generate a list of
    variables in that function. Variables are tokens and what they link to.
    In this case, what it links to is just a string. However, that is resolved
    later.

    :param lines list[ast]:
    :param parent Group:
    :rtype: list[Variable]
    """
    variables = []
    for tree in lines:
        for element in ast.walk(tree):
            if type(element) == ast.Assign:
                variables += process_assign(element)
            if type(element) in (ast.Import, ast.ImportFrom):
                variables += process_import(element)
    if parent.group_type == GROUP_TYPE.CLASS:
        variables.append(Variable('self', parent, lines[0].lineno))

    variables = list(filter(None, variables))
    return variables


def get_inherits(tree):
    """
    Get what superclasses this class inherits
    This handles exact names like 'MyClass' but skips things like 'cls' and 'mod.MyClass'
    Resolving those would be difficult
    :param tree ast:
    :rtype: list[str]
    """
    return [base.id for base in tree.bases if type(base) == ast.Name]


class Python(BaseLanguage):
    @staticmethod
    def assert_dependencies():
        pass

    @staticmethod
    def get_tree(filename, _):
        """
        Get the entire AST for this file

        :param filename str:
        :rtype: ast
        """
        try:
            with open(filename) as f:
                raw = f.read()
        except ValueError:
            with open(filename, encoding='UTF-8') as f:
                raw = f.read()
        return ast.parse(raw)

    @staticmethod
    def separate_namespaces(tree):
        """
        Given an AST, recursively separate that AST into lists of ASTs for the
        subgroups, nodes, and body. This is an intermediate step to allow for
        cleaner processing downstream

        :param tree ast:
        :returns: tuple of group, node, and body trees. These are processed
                  downstream into real Groups and Nodes.
        :rtype: (list[ast], list[ast], list[ast])
        """
        groups = []
        nodes = []
        body = []
        for el in tree.body:
            if type(el) in (ast.FunctionDef, ast.AsyncFunctionDef):
                nodes.append(el)
            elif type(el) == ast.ClassDef:
                groups.append(el)
            elif getattr(el, 'body', None):
                tup = Python.separate_namespaces(el)
                groups += tup[0]
                nodes += tup[1]
                body += tup[2]
            else:
                body.append(el)
        return groups, nodes, body

    @staticmethod
    def make_nodes(tree, parent):
        """
        Given an ast of all the lines in a function, create the node along with the
        calls and variables internal to it.

        :param tree ast:
        :param parent Group:
        :rtype: list[Node]
        """
        token = tree.name
        line_number = tree.lineno
        calls = make_calls(tree.body)
        variables = make_local_variables(tree.body, parent)
        is_constructor = False
        if parent.group_type == GROUP_TYPE.CLASS and token in ['__init__', '__new__']:
            is_constructor = True

        import_tokens = []
        if parent.group_type == GROUP_TYPE.FILE:
            import_tokens = [djoin(parent.token, token)]

        return [Node(token, calls, variables, parent, import_tokens=import_tokens,
                     line_number=line_number, is_constructor=is_constructor)]

    @staticmethod
    def make_root_node(lines, parent):
        """
        The "root_node" is an implict node of lines which are executed in the global
        scope on the file itself and not otherwise part of any function.

        :param lines list[ast]:
        :param parent Group:
        :rtype: Node
        """
        token = "(global)"
        line_number = 0
        calls = make_calls(lines)
        variables = make_local_variables(lines, parent)
        return Node(token, calls, variables, line_number=line_number, parent=parent)

    @staticmethod
    def make_class_group(tree, parent):
        """
        Given an AST for the subgroup (a class), generate that subgroup.
        In this function, we will also need to generate all of the nodes internal
        to the group.

        :param tree ast:
        :param parent Group:
        :rtype: Group
        """
        assert type(tree) == ast.ClassDef
        subgroup_trees, node_trees, body_trees = Python.separate_namespaces(tree)

        group_type = GROUP_TYPE.CLASS
        token = tree.name
        display_name = 'Class'
        line_number = tree.lineno

        import_tokens = [djoin(parent.token, token)]
        inherits = get_inherits(tree)

        class_group = Group(token, group_type, display_name, import_tokens=import_tokens,
                            inherits=inherits, line_number=line_number, parent=parent)

        for node_tree in node_trees:
            class_group.add_node(Python.make_nodes(node_tree, parent=class_group)[0])

        for subgroup_tree in subgroup_trees:
            logging.warning("Code2flow does not support nested classes. Skipping %r in %r.",
                            subgroup_tree.name, parent.token)
        return class_group

    @staticmethod
    def file_import_tokens(filename):
        """
        Returns the token(s) we would use if importing this file from another.

        :param filename str:
        :rtype: list[str]
        """
        return [os.path.split(filename)[-1].rsplit('.py', 1)[0]]


================================================
FILE: code2flow/ruby.py
================================================
import json
import subprocess

from .model import (Group, Node, Call, Variable, BaseLanguage,
                    OWNER_CONST, GROUP_TYPE, is_installed, flatten)


def resolve_owner(owner_el):
    """
    Resolve who owns the call, if anyone.
    So if the expression is i_ate.pie(). And i_ate is a Person, the callee is Person.
    This is returned as a string and eventually set to the owner_token in the call

    :param owner_el ast:
    :rtype: str|None
    """
    if not owner_el or not isinstance(owner_el, list):
        return None
    if owner_el[0] == 'begin':
        # skip complex ownership
        return OWNER_CONST.UNKNOWN_VAR
    if owner_el[0] == 'send':
        # sends are complex too
        return OWNER_CONST.UNKNOWN_VAR
    if owner_el[0] == 'lvar':
        # var.func()
        return owner_el[1]
    if owner_el[0] == 'ivar':
        # @var.func()
        return owner_el[1]
    if owner_el[0] == 'self':
        return 'self'
    if owner_el[0] == 'const':
        return owner_el[2]

    return OWNER_CONST.UNKNOWN_VAR


def get_call_from_send_el(func_el):
    """
    Given an ast that represents a send call, clear and create our
    generic Call object. Some calls have no chance at resolution (e.g. array[2](param))
    so we return nothing instead.

    :param func_el ast:
    :rtype: Call|None
    """
    owner_el = func_el[1]
    token = func_el[2]
    owner = resolve_owner(owner_el)
    if owner and token == 'new':
        # Taking out owner_token for constructors as a little hack to make it work
        return Call(token=owner)
    return Call(token=token,
                owner_token=owner)


def walk(tree_el):
    """
    Given an ast element (list), walk it in a dfs to get every el (list) out of it

    :param tree_el ast:
    :rtype: list[ast]
    """

    if not tree_el:
        return []
    ret = [tree_el]
    for el in tree_el:
        if isinstance(el, list):
            ret += walk(el)
    return ret


def make_calls(body_el):
    """
    Given a list of lines, find all calls in this list.

    :param body_el ast:
    :rtype: list[Call]
    """
    calls = []
    for el in walk(body_el):
        if el[0] == 'send':
            calls.append(get_call_from_send_el(el))
    return calls


def process_assign(assignment_el):
    """
    Given an assignment statement, return a
    Variable that points_to the type of object being assigned. The
    points_to is often a string but that is resolved later.

    :param assignment_el ast:
    :rtype: Variable
    """

    assert assignment_el[0] == 'lvasgn'
    varname = assignment_el[1]
    if assignment_el[2][0] == 'send':
        call = get_call_from_send_el(assignment_el[2])
        return Variable(varname, call)
    # else is something like `varname = num`
    return None


def make_local_variables(tree_el, parent):
    """
    Given an ast of all the lines in a function, generate a list of
    variables in that function. Variables are tokens and what they link to.
    In this case, what it links to is just a string. However, that is resolved
    later.

    Also return variables for the outer scope parent

    :param tree_el ast:
    :param parent Group:
    :rtype: list[Variable]
    """
    variables = []
    for el in tree_el:
        if el[0] == 'lvasgn':
            variables.append(process_assign(el))

    # Make a 'self' variable for use anywhere we need it that points to the class
    if isinstance(parent, Group) and parent.group_type == GROUP_TYPE.CLASS:
        variables.append(Variable('self', parent))

    variables = list(filter(None, variables))
    return variables


def as_lines(tree_el):
    """
    Ruby ast bodies are structured differently depending on circumstances.
    This ensures that they are structured as a list of statements

    :param tree_el ast:
    :rtype: list[tree_el]
    """
    if not tree_el:
        return []
    if isinstance(tree_el[0], list):
        return tree_el
    if tree_el[0] == 'begin':
        return tree_el
    return [tree_el]


def get_tree_body(tree_el):
    """
    Depending on the type of element, get the body of that element

    :param tree_el ast:
    :rtype: list[tree_el]
    """

    if tree_el[0] == 'module':
        body_struct = tree_el[2]
    elif tree_el[0] == 'defs':
        body_struct = tree_el[4]
    else:
        body_struct = tree_el[3]
    return as_lines(body_struct)


def get_inherits(tree, body_tree):
    """
    Get the various types of inheritances this class/module can have

    :param tree ast:
    :param body_tree list[ast]
    :rtype: list[str]
    """
    inherits = []

    # extends
    if tree[0] == 'class' and tree[2]:
        inherits.append(tree[2][2])

    # module automatically extends same-named modules
    if tree[0] == 'module':
        inherits.append(tree[1][2])

    # mixins
    for el in body_tree:
        if el[0] == 'send' and el[2] == 'include':
            inherits.append(el[3][2])

    return inherits


class Ruby(BaseLanguage):
    @staticmethod
    def assert_dependencies():
        """Assert that ruby-parse is installed"""
        assert is_installed('ruby-parse'), "The 'parser' gem is requred to " \
                                           "parse ruby files but was not found " \
                                           "on the path. Install it from gem " \
                                           "and try again."

    @staticmethod
    def get_tree(filename, lang_params):
        """
        Get the entire AST for this file

        :param filename str:
        :param lang_params LanguageParams:
        :rtype: ast
        """
        version_flag = "--" + lang_params.ruby_version
        cmd = ["ruby-parse", "--emit-json", version_flag, filename]
        output = subprocess.check_output(cmd, stderr=subprocess.PIPE)
        try:
            tree = json.loads(output)
        except json.decoder.JSONDecodeError:
            raise AssertionError(
                "Ruby-parse could not parse file %r. You may have a syntax error. "
                "For more detail, try running the command `ruby-parse %s`. " %
                (filename, filename)) from None
        assert isinstance(tree, list)

        if tree[0] not in ('module', 'begin'):
            # one-line files
            tree = [tree]
        return tree

    @staticmethod
    def separate_namespaces(tree):
        """
        Given a tree element, recursively separate that AST into lists of ASTs for the
        subgroups, nodes, and body. This is an intermediate step to allow for
        cleaner processing downstream

        :param tree ast:
        :returns: tuple of group, node, and body trees. These are processed
                  downstream into real Groups and Nodes.
        :rtype: (list[ast], list[ast], list[ast])
        """
        groups = []
        nodes = []
        body = []
        for el in as_lines(tree):
            if el[0] in ('def', 'defs'):
                nodes.append(el)
            elif el[0] in ('class', 'module'):
                groups.append(el)
            else:
                body.append(el)
        return groups, nodes, body

    @staticmethod
    def make_nodes(tree, parent):
        """
        Given a tree element of all the lines in a function, create the node along
        with the calls and variables internal to it.
        Also make the nested subnodes

        :param tree ast:
        :param parent Group:
        :rtype: list[Node]
        """
        if tree[0] == 'defs':
            token = tree[2]  # def self.func
        else:
            token = tree[1]  # def func

        is_constructor = token == 'initialize' and parent.group_type == GROUP_TYPE.CLASS

        tree_body = get_tree_body(tree)
        subgroup_trees, subnode_trees, this_scope_body = Ruby.separate_namespaces(tree_body)
        assert not subgroup_trees
        calls = make_calls(this_scope_body)
        variables = make_local_variables(this_scope_body, parent)
        node = Node(token, calls, variables,
                    parent=parent, is_constructor=is_constructor)

        # This is a little different from the other languages in that
        # the node is now on the parent
        subnodes = flatten([Ruby.make_nodes(t, parent) for t in subnode_trees])
        return [node] + subnodes

    @staticmethod
    def make_root_node(lines, parent):
        """
        The "root_node" is an implict node of lines which are executed in the global
        scope on the file itself and not otherwise part of any function.

        :param lines list[ast]:
        :param parent Group:
        :rtype: Node
        """
        token = "(global)"
        calls = make_calls(lines)
        variables = make_local_variables(lines, parent)
        root_node = Node(token, calls, variables, parent=parent)
        return root_node

    @staticmethod
    def make_class_group(tree, parent):
        """
        Given an AST for the subgroup (a class), generate that subgroup.
        In this function, we will also need to generate all of the nodes internal
        to the group.

        :param tree ast:
        :param parent Group:
        :rtype: Group
        """
        assert tree[0] in ('class', 'module')
        tree_body = get_tree_body(tree)
        subgroup_trees, node_trees, body_trees = Ruby.separate_namespaces(tree_body)

        group_type = GROUP_TYPE.CLASS
        if tree[0] == 'module':
            group_type = GROUP_TYPE.NAMESPACE
        display_type = tree[0].capitalize()
        assert tree[1][0] == 'const'
        token = tree[1][2]

        inherits = get_inherits(tree, body_trees)
        class_group = Group(token, group_type, display_type,
                            inherits=inherits, parent=parent)

        for subgroup_tree in subgroup_trees:
            class_group.add_subgroup(Ruby.make_class_group(subgroup_tree, class_group))

        for node_tree in node_trees:
            for new_node in Ruby.make_nodes(node_tree, parent=class_group):
                class_group.add_node(new_node)
        for node in class_group.nodes:
            node.variables += [Variable(n.token, n) for n in class_group.nodes]

        return class_group

    @staticmethod
    def file_import_tokens(filename):
        """
        Returns the token(s) we would use if importing this file from another.

        :param filename str:
        :rtype: list[str]
        """
        return []


================================================
FILE: make_expected.py
================================================
#!/usr/bin/env python3

import os
import pprint
import sys
import tempfile

from code2flow.engine import main
from tests.test_graphs import get_edges_set_from_file, get_nodes_set_from_file

DESCRIPTION = """
This file is a tool to generate test cases given a directory
"""

if __name__ == '__main__':
    output_filename = tempfile.NamedTemporaryFile(suffix='.gv').name
    args = sys.argv[1:] + ['--output', output_filename]
    main(args)
    output_file = open(output_filename, 'r')

    generated_edges = get_edges_set_from_file(output_file)
    generated_nodes = get_nodes_set_from_file(output_file)
    directory = os.path.split(sys.argv[1])[-1]

    ret = {
        'test_name': directory,
        'directory': directory,
        'kwargs': sys.argv[2:],
        'expected_edges': list(map(list, generated_edges)),
        'expected_nodes': list(generated_nodes),
    }

    ret = pprint.pformat(ret, sort_dicts=False)
    ret = " " + ret.replace("'", '"')[1:-1]
    print('\n'.join("           " + l for l in ret.split('\n')))


================================================
FILE: requirements_dev.txt
================================================
# Testing
pytest>=6.2.5,<7.0
pygraphviz>=1.7,<2.0
pytest-cov>=2.10.1,<3.0
pytest-mock>=3.6.1,<4.0
pytest-xdist>=2.5.0,<3.0

# Development
ipdb>=0.13.9,<1.0
icecream>=2.1.1,<3.0


================================================
FILE: setup.py
================================================
from setuptools import setup

from code2flow.engine import VERSION

url_base = 'https://github.com/scottrogowski/code2flow'
download_url = '%s/archive/code2flow-%s.tar.gz' % (url_base, VERSION)

setup(
    name='code2flow',
    version=VERSION,
    description='Visualize your source code as DOT flowcharts',
    long_description=open('README.md', encoding='utf-8').read(),
    long_description_content_type="text/markdown",
    entry_points={
        'console_scripts': ['code2flow=code2flow.engine:main'],
    },
    license='MIT',
    author='Scott Rogowski',
    author_email='scottmrogowski@gmail.com',
    url=url_base,
    download_url=download_url,
    packages=['code2flow'],
    python_requires='>=3.6',
    include_package_data=True,
    classifiers=[
        'Natural Language :: English',
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ]
)


================================================
FILE: tests/__init__.py
================================================


================================================
FILE: tests/test_code/js/ambiguous_names/ambiguous_names.js
================================================
class Abra {
    constructor() {
        this.abra_it();
        }
    magic() {
        console.log('magic 1');
    }

    abra_it() {

    }
}


class Cadabra {
    magic() {
        console.log('magic 2');
    }

    cadabra_it(a) {
        let b = a.abra_it()
        let c = "d"
    }
}

function main(cls) {
    obj = cls()
    obj.magic()
    obj.cadabra_it()
}

main()


================================================
FILE: tests/test_code/js/bad_parse/file_a_good.js
================================================
function a() {}
a()


================================================
FILE: tests/test_code/js/bad_parse/file_b_bad.js
================================================
function a() {
a)


================================================
FILE: tests/test_code/js/chained/chained.js
================================================
class Chain {
    constructor(val) {
        this.val = val;
    }

    add(b) {
        this.val += b
        return this;
    }

    sub(b) {
        this.val -= b;
        return this;
    }

    mul(b) {
        this.val *= b;
        return this;
    }
}

console.log((new Chain(5)).add(5).sub(2).mul(10));


================================================
FILE: tests/test_code/js/class_in_function/class_in_function.js
================================================
function rectangleClassFactory() {
    class Rectangle {
        constructor(height, width) {
            this.height = height;
            this.width = width;
        }
    }
    return Rectangle
}

rectangleClassFactory()


================================================
FILE: tests/test_code/js/classes/classes.js
================================================
function print_hi() {
    console.log("HI")
}

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
    this.i = 0;
    print_hi();
    this.calcArea()
  }
  calcArea() {
    this.incr();
    return this.height * this.width
  }
  incr() {
    this.i++;
  }
}

function do_calc() {
    const the_area = square.calcArea()
    calcit()
    const square = new Rectangle(10, 10);
}

const do_calc_wrapper = function() {
    console.log("BANANAS")
    do_calc();
}

const square = new Rectangle(10, 10);
square.calcArea()
do_calc_wrapper()


================================================
FILE: tests/test_code/js/complex_ownership/complex_ownership.js
================================================
class ABC {
    constructor() {
        this.b = 5;
    }
    doit() {
        let _this = this;
        return _this.b;
    }
    apply() {
        return 5;
    }
    ret_def() {
        return DEF
    }
}

class DEF {
    toABC() {
        calls = 5;
        return new ABC();
    }
}

class GHI {
    doit2(varname) {
        return varname.apply()
    }
    doit3() {
        console.log("");
    }
}

var empty_var;
var double_decl = [], empty_var;
calls = 5;
let abc = new DEF();
abc.toABC().doit(calls);

new GHI().doit2()
var inp = AbsentClass()
inp.a.b.c.apply(null, arguments);

var jsism = (function() {
    return "no other language does this crazy nonsense";
})()

var jsism_2 = (function() {
    return "no other language does this crazy nonsense";
}).anything()


var obj_calls = {
    'abc': ABC,
    'def': DEF,
    'ghi': GHI,
}
obj_calls['ghi'].doit3();

// This below shouldn't match anything because it's too complex of a constructor
var def = new abc.ret_def()


================================================
FILE: tests/test_code/js/exclude_modules/exclude_modules.js
================================================
//Node/CommonJS
const fs = require('fs')
const {readFile, chmod} = require('fs')


function readFileSync() {
    console.log("This is the local readFileSync");
}


function beta() {
    print("this still connects")
    readFileSync()
    b = Nothing()
    b.beta()
    chmod();
}


function alpha() {
    fs.readFileSync("exclude_modules.js");
    beta()
    match()
    alpha()
}


alpha()
module.exports = {alpha}


================================================
FILE: tests/test_code/js/exclude_modules_es6/exclude_modules_es6.js
================================================
//Node/CommonJS
const fs = import('fs');
import {readFile, chmod} from 'fs';


function readFileSync() {
    console.log("This is the local readFileSync");
}


function beta() {
    print("this still connects")
    readFileSync()
    b = Nothing()
    b.beta()
    chmod();
}


function alpha() {
    fs.readFileSync("exclude_modules.js");
    beta()
    match()
    alpha()
}


alpha()
module.exports = {alpha}


================================================
FILE: tests/test_code/js/globals/globals.js
================================================
var g = (function() {
    function a() {
        function c() {
            function d() {
                console.log('d');
            }
            d()
        }
        b();
        c();
    }

    function b() {
        console.log("c");
    }

    return {
        'a': a,
        'b': b
        }
})()


================================================
FILE: tests/test_code/js/inheritance/inheritance.js
================================================
// from https://ruby-doc.com/docs/ProgrammingRuby/html/tut_modules.html

function majorNum() {}

function pentaNum() {}

class MajorScales {
  majorNum() {
    this.numNotes = 7;
    return this.numNotes;
  }
}

class FakeMajorScales {
  majorNum() {
    console.log("Not this one")
  }
}

class ScaleDemo extends MajorScales {
  constructor() {
    console.log(this.majorNum())
  }
}

let sd = new ScaleDemo();
pentaNum()


================================================
FILE: tests/test_code/js/inheritance_attr/inheritance_attr.js
================================================

class ClsA {
    bark() {
        console.log("woof");
    }
}
class ClsB {
    meow() {
        console.log("meow");
    }
}

ClsA.B = ClsB;


class ClsC extends ClsA.B {}

let c = new ClsC();
c.meow();


================================================
FILE: tests/test_code/js/moment/moment.js
================================================
//! moment.js
//! version : 2.29.1
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com

;(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    global.moment = factory()
}(this, (function () { 'use strict';

    var hookCallback;

    function hooks() {
        return hookCallback.apply(null, arguments);
    }

    // This is done to register the method called with moment()
    // without creating circular dependencies.
    function setHookCallback(callback) {
        hookCallback = callback;
    }

    function isArray(input) {
        return (
            input instanceof Array ||
            Object.prototype.toString.call(input) === '[object Array]'
        );
    }

    function isObject(input) {
        // IE8 will treat undefined and null as object if it wasn't for
        // input != null
        return (
            input != null &&
            Object.prototype.toString.call(input) === '[object Object]'
        );
    }

    function hasOwnProp(a, b) {
        return Object.prototype.hasOwnProperty.call(a, b);
    }

    function isObjectEmpty(obj) {
        if (Object.getOwnPropertyNames) {
            return Object.getOwnPropertyNames(obj).length === 0;
        } else {
            var k;
            for (k in obj) {
                if (hasOwnProp(obj, k)) {
                    return false;
                }
            }
            return true;
        }
    }

    function isUndefined(input) {
        return input === void 0;
    }

    function isNumber(input) {
        return (
            typeof input === 'number' ||
            Object.prototype.toString.call(input) === '[object Number]'
        );
    }

    function isDate(input) {
        return (
            input instanceof Date ||
            Object.prototype.toString.call(input) === '[object Date]'
        );
    }

    function map(arr, fn) {
        var res = [],
            i;
        for (i = 0; i < arr.length; ++i) {
            res.push(fn(arr[i], i));
        }
        return res;
    }

    function extend(a, b) {
        for (var i in b) {
            if (hasOwnProp(b, i)) {
                a[i] = b[i];
            }
        }

        if (hasOwnProp(b, 'toString')) {
            a.toString = b.toString;
        }

        if (hasOwnProp(b, 'valueOf')) {
            a.valueOf = b.valueOf;
        }

        return a;
    }

    function createUTC(input, format, locale, strict) {
        return createLocalOrUTC(input, format, locale, strict, true).utc();
    }

    function defaultParsingFlags() {
        // We need to deep clone this object.
        return {
            empty: false,
            unusedTokens: [],
            unusedInput: [],
            overflow: -2,
            charsLeftOver: 0,
            nullInput: false,
            invalidEra: null,
            invalidMonth: null,
            invalidFormat: false,
            userInvalidated: false,
            iso: false,
            parsedDateParts: [],
            era: null,
            meridiem: null,
            rfc2822: false,
            weekdayMismatch: false,
        };
    }

    function getParsingFlags(m) {
        if (m._pf == null) {
            m._pf = defaultParsingFlags();
        }
        return m._pf;
    }

    var some;
    if (Array.prototype.some) {
        some = Array.prototype.some;
    } else {
        some = function (fun) {
            var t = Object(this),
                len = t.length >>> 0,
                i;

            for (i = 0; i < len; i++) {
                if (i in t && fun.call(this, t[i], i, t)) {
                    return true;
                }
            }

            return false;
        };
    }

    function isValid(m) {
        if (m._isValid == null) {
            var flags = getParsingFlags(m),
                parsedParts = some.call(flags.parsedDateParts, function (i) {
                    return i != null;
                }),
                isNowValid =
                    !isNaN(m._d.getTime()) &&
                    flags.overflow < 0 &&
                    !flags.empty &&
                    !flags.invalidEra &&
                    !flags.invalidMonth &&
                    !flags.invalidWeekday &&
                    !flags.weekdayMismatch &&
                    !flags.nullInput &&
                    !flags.invalidFormat &&
                    !flags.userInvalidated &&
                    (!flags.meridiem || (flags.meridiem && parsedParts));

            if (m._strict) {
                isNowValid =
                    isNowValid &&
                    flags.charsLeftOver === 0 &&
                    flags.unusedTokens.length === 0 &&
                    flags.bigHour === undefined;
            }

            if (Object.isFrozen == null || !Object.isFrozen(m)) {
                m._isValid = isNowValid;
            } else {
                return isNowValid;
            }
        }
        return m._isValid;
    }

    function createInvalid(flags) {
        var m = createUTC(NaN);
        if (flags != null) {
            extend(getParsingFlags(m), flags);
        } else {
            getParsingFlags(m).userInvalidated = true;
        }

        return m;
    }

    // Plugins that add properties should also add the key here (null value),
    // so we can properly clone ourselves.
    var momentProperties = (hooks.momentProperties = []),
        updateInProgress = false;

    function copyConfig(to, from) {
        var i, prop, val;

        if (!isUndefined(from._isAMomentObject)) {
            to._isAMomentObject = from._isAMomentObject;
        }
        if (!isUndefined(from._i)) {
            to._i = from._i;
        }
        if (!isUndefined(from._f)) {
            to._f = from._f;
        }
        if (!isUndefined(from._l)) {
            to._l = from._l;
        }
        if (!isUndefined(from._strict)) {
            to._strict = from._strict;
        }
        if (!isUndefined(from._tzm)) {
            to._tzm = from._tzm;
        }
        if (!isUndefined(from._isUTC)) {
            to._isUTC = from._isUTC;
        }
        if (!isUndefined(from._offset)) {
            to._offset = from._offset;
        }
        if (!isUndefined(from._pf)) {
            to._pf = getParsingFlags(from);
        }
        if (!isUndefined(from._locale)) {
            to._locale = from._locale;
        }

        if (momentProperties.length > 0) {
            for (i = 0; i < momentProperties.length; i++) {
                prop = momentProperties[i];
                val = from[prop];
                if (!isUndefined(val)) {
                    to[prop] = val;
                }
            }
        }

        return to;
    }

    // Moment prototype object
    function Moment(config) {
        copyConfig(this, config);
        this._d = new Date(config._d != null ? config._d.getTime() : NaN);
        if (!this.isValid()) {
            this._d = new Date(NaN);
        }
        // Prevent infinite loop in case updateOffset creates new moment
        // objects.
        if (updateInProgress === false) {
            updateInProgress = true;
            hooks.updateOffset(this);
            updateInProgress = false;
        }
    }

    function isMoment(obj) {
        return (
            obj instanceof Moment || (obj != null && obj._isAMomentObject != null)
        );
    }

    function warn(msg) {
        if (
            hooks.suppressDeprecationWarnings === false &&
            typeof console !== 'undefined' &&
            console.warn
        ) {
            console.warn('Deprecation warning: ' + msg);
        }
    }

    function deprecate(msg, fn) {
        var firstTime = true;

        return extend(function () {
            if (hooks.deprecationHandler != null) {
                hooks.deprecationHandler(null, msg);
            }
            if (firstTime) {
                var args = [],
                    arg,
                    i,
                    key;
                for (i = 0; i < arguments.length; i++) {
                    arg = '';
                    if (typeof arguments[i] === 'object') {
                        arg += '\n[' + i + '] ';
                        for (key in arguments[0]) {
                            if (hasOwnProp(arguments[0], key)) {
                                arg += key + ': ' + arguments[0][key] + ', ';
                            }
                        }
                        arg = arg.slice(0, -2); // Remove trailing comma and space
                    } else {
                        arg = arguments[i];
                    }
                    args.push(arg);
                }
                warn(
                    msg +
                        '\nArguments: ' +
                        Array.prototype.slice.call(args).join('') +
                        '\n' +
                        new Error().stack
                );
                firstTime = false;
            }
            return fn.apply(this, arguments);
        }, fn);
    }

    var deprecations = {};

    function deprecateSimple(name, msg) {
        if (hooks.deprecationHandler != null) {
            hooks.deprecationHandler(name, msg);
        }
        if (!deprecations[name]) {
            warn(msg);
            deprecations[name] = true;
        }
    }

    hooks.suppressDeprecationWarnings = false;
    hooks.deprecationHandler = null;

    function isFunction(input) {
        return (
            (typeof Function !== 'undefined' && input instanceof Function) ||
            Object.prototype.toString.call(input) === '[object Function]'
        );
    }

    function set(config) {
        var prop, i;
        for (i in config) {
            if (hasOwnProp(config, i)) {
                prop = config[i];
                if (isFunction(prop)) {
                    this[i] = prop;
                } else {
                    this['_' + i] = prop;
                }
            }
        }
        this._config = config;
        // Lenient ordinal parsing accepts just a number in addition to
        // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
        // TODO: Remove "ordinalParse" fallback in next major release.
        this._dayOfMonthOrdinalParseLenient = new RegExp(
            (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) +
                '|' +
                /\d{1,2}/.source
        );
    }

    function mergeConfigs(parentConfig, childConfig) {
        var res = extend({}, parentConfig),
            prop;
        for (prop in childConfig) {
            if (hasOwnProp(childConfig, prop)) {
                if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
                    res[prop] = {};
                    extend(res[prop], parentConfig[prop]);
                    extend(res[prop], childConfig[prop]);
                } else if (childConfig[prop] != null) {
                    res[prop] = childConfig[prop];
                } else {
                    delete res[prop];
                }
            }
        }
        for (prop in parentConfig) {
            if (
                hasOwnProp(parentConfig, prop) &&
                !hasOwnProp(childConfig, prop) &&
                isObject(parentConfig[prop])
            ) {
                // make sure changes to properties don't modify parent config
                res[prop] = extend({}, res[prop]);
            }
        }
        return res;
    }

    function Locale(config) {
        if (config != null) {
            this.set(config);
        }
    }

    var keys;

    if (Object.keys) {
        keys = Object.keys;
    } else {
        keys = function (obj) {
            var i,
                res = [];
            for (i in obj) {
                if (hasOwnProp(obj, i)) {
                    res.push(i);
                }
            }
            return res;
        };
    }

    var defaultCalendar = {
        sameDay: '[Today at] LT',
        nextDay: '[Tomorrow at] LT',
        nextWeek: 'dddd [at] LT',
        lastDay: '[Yesterday at] LT',
        lastWeek: '[Last] dddd [at] LT',
        sameElse: 'L',
    };

    function calendar(key, mom, now) {
        var output = this._calendar[key] || this._calendar['sameElse'];
        return isFunction(output) ? output.call(mom, now) : output;
    }

    function zeroFill(number, targetLength, forceSign) {
        var absNumber = '' + Math.abs(number),
            zerosToFill = targetLength - absNumber.length,
            sign = number >= 0;
        return (
            (sign ? (forceSign ? '+' : '') : '-') +
            Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) +
            absNumber
        );
    }

    var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,
        localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,
        formatFunctions = {},
        formatTokenFunctions = {};

    // token:    'M'
    // padded:   ['MM', 2]
    // ordinal:  'Mo'
    // callback: function () { this.month() + 1 }
    function addFormatToken(token, padded, ordinal, callback) {
        var func = callback;
        if (typeof callback === 'string') {
            func = function () {
                return this[callback]();
            };
        }
        if (token) {
            formatTokenFunctions[token] = func;
        }
        if (padded) {
            formatTokenFunctions[padded[0]] = function () {
                return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
            };
        }
        if (ordinal) {
            formatTokenFunctions[ordinal] = function () {
                return this.localeData().ordinal(
                    func.apply(this, arguments),
                    token
                );
            };
        }
    }

    function removeFormattingTokens(input) {
        if (input.match(/\[[\s\S]/)) {
            return input.replace(/^\[|\]$/g, '');
        }
        return input.replace(/\\/g, '');
    }

    function makeFormatFunction(format) {
        var array = format.match(formattingTokens),
            i,
            length;

        for (i = 0, length = array.length; i < length; i++) {
            if (formatTokenFunctions[array[i]]) {
                array[i] = formatTokenFunctions[array[i]];
            } else {
                array[i] = removeFormattingTokens(array[i]);
            }
        }

        return function (mom) {
            var output = '',
                i;
            for (i = 0; i < length; i++) {
                output += isFunction(array[i])
                    ? array[i].call(mom, format)
                    : array[i];
            }
            return output;
        };
    }

    // format date using native date object
    function formatMoment(m, format) {
        if (!m.isValid()) {
            return m.localeData().invalidDate();
        }

        format = expandFormat(format, m.localeData());
        formatFunctions[format] =
            formatFunctions[format] || makeFormatFunction(format);

        return formatFunctions[format](m);
    }

    function expandFormat(format, locale) {
        var i = 5;

        function replaceLongDateFormatTokens(input) {
            return locale.longDateFormat(input) || input;
        }

        localFormattingTokens.lastIndex = 0;
        while (i >= 0 && localFormattingTokens.test(format)) {
            format = format.replace(
                localFormattingTokens,
                replaceLongDateFormatTokens
            );
            localFormattingTokens.lastIndex = 0;
            i -= 1;
        }

        return format;
    }

    var defaultLongDateFormat = {
        LTS: 'h:mm:ss A',
        LT: 'h:mm A',
        L: 'MM/DD/YYYY',
        LL: 'MMMM D, YYYY',
        LLL: 'MMMM D, YYYY h:mm A',
        LLLL: 'dddd, MMMM D, YYYY h:mm A',
    };

    function longDateFormat(key) {
        var format = this._longDateFormat[key],
            formatUpper = this._longDateFormat[key.toUpperCase()];

        if (format || !formatUpper) {
            return format;
        }

        this._longDateFormat[key] = formatUpper
            .match(formattingTokens)
            .map(function (tok) {
                if (
                    tok === 'MMMM' ||
                    tok === 'MM' ||
                    tok === 'DD' ||
                    tok === 'dddd'
                ) {
                    return tok.slice(1);
                }
                return tok;
            })
            .join('');

        return this._longDateFormat[key];
    }

    var defaultInvalidDate = 'Invalid date';

    function invalidDate() {
        return this._invalidDate;
    }

    var defaultOrdinal = '%d',
        defaultDayOfMonthOrdinalParse = /\d{1,2}/;

    function ordinal(number) {
        return this._ordinal.replace('%d', number);
    }

    var defaultRelativeTime = {
        future: 'in %s',
        past: '%s ago',
        s: 'a few seconds',
        ss: '%d seconds',
        m: 'a minute',
        mm: '%d minutes',
        h: 'an hour',
        hh: '%d hours',
        d: 'a day',
        dd: '%d days',
        w: 'a week',
        ww: '%d weeks',
        M: 'a month',
        MM: '%d months',
        y: 'a year',
        yy: '%d years',
    };

    function relativeTime(number, withoutSuffix, string, isFuture) {
        var output = this._relativeTime[string];
        return isFunction(output)
            ? output(number, withoutSuffix, string, isFuture)
            : output.replace(/%d/i, number);
    }

    function pastFuture(diff, output) {
        var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
        return isFunction(format) ? format(output) : format.replace(/%s/i, output);
    }

    var aliases = {};

    function addUnitAlias(unit, shorthand) {
        var lowerCase = unit.toLowerCase();
        aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
    }

    function normalizeUnits(units) {
        return typeof units === 'string'
            ? aliases[units] || aliases[units.toLowerCase()]
            : undefined;
    }

    function normalizeObjectUnits(inputObject) {
        var normalizedInput = {},
            normalizedProp,
            prop;

        for (prop in inputObject) {
            if (hasOwnProp(inputObject, prop)) {
                normalizedProp = normalizeUnits(prop);
                if (normalizedProp) {
                    normalizedInput[normalizedProp] = inputObject[prop];
                }
            }
        }

        return normalizedInput;
    }

    var priorities = {};

    function addUnitPriority(unit, priority) {
        priorities[unit] = priority;
    }

    function getPrioritizedUnits(unitsObj) {
        var units = [],
            u;
        for (u in unitsObj) {
            if (hasOwnProp(unitsObj, u)) {
                units.push({ unit: u, priority: priorities[u] });
            }
        }
        units.sort(function (a, b) {
            return a.priority - b.priority;
        });
        return units;
    }

    function isLeapYear(year) {
        return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
    }

    function absFloor(number) {
        if (number < 0) {
            // -0 -> 0
            return Math.ceil(number) || 0;
        } else {
            return Math.floor(number);
        }
    }

    function toInt(argumentForCoercion) {
        var coercedNumber = +argumentForCoercion,
            value = 0;

        if (coercedNumber !== 0 && isFinite(coercedNumber)) {
            value = absFloor(coercedNumber);
        }

        return value;
    }

    function makeGetSet(unit, keepTime) {
        return function (value) {
            if (value != null) {
                set$1(this, unit, value);
                hooks.updateOffset(this, keepTime);
                return this;
            } else {
                return get(this, unit);
            }
        };
    }

    function get(mom, unit) {
        return mom.isValid()
            ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]()
            : NaN;
    }

    function set$1(mom, unit, value) {
        if (mom.isValid() && !isNaN(value)) {
            if (
                unit === 'FullYear' &&
                isLeapYear(mom.year()) &&
                mom.month() === 1 &&
                mom.date() === 29
            ) {
                value = toInt(value);
                mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](
                    value,
                    mom.month(),
                    daysInMonth(value, mom.month())
                );
            } else {
                mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
            }
        }
    }

    // MOMENTS

    function stringGet(units) {
        units = normalizeUnits(units);
        if (isFunction(this[units])) {
            return this[units]();
        }
        return this;
    }

    function stringSet(units, value) {
        if (typeof units === 'object') {
            units = normalizeObjectUnits(units);
            var prioritized = getPrioritizedUnits(units),
                i;
            for (i = 0; i < prioritized.length; i++) {
                this[prioritized[i].unit](units[prioritized[i].unit]);
            }
        } else {
            units = normalizeUnits(units);
            if (isFunction(this[units])) {
                return this[units](value);
            }
        }
        return this;
    }

    var match1 = /\d/, //       0 - 9
        match2 = /\d\d/, //      00 - 99
        match3 = /\d{3}/, //     000 - 999
        match4 = /\d{4}/, //    0000 - 9999
        match6 = /[+-]?\d{6}/, // -999999 - 999999
        match1to2 = /\d\d?/, //       0 - 99
        match3to4 = /\d\d\d\d?/, //     999 - 9999
        match5to6 = /\d\d\d\d\d\d?/, //   99999 - 999999
        match1to3 = /\d{1,3}/, //       0 - 999
        match1to4 = /\d{1,4}/, //       0 - 9999
        match1to6 = /[+-]?\d{1,6}/, // -999999 - 999999
        matchUnsigned = /\d+/, //       0 - inf
        matchSigned = /[+-]?\d+/, //    -inf - inf
        matchOffset = /Z|[+-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
        matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi, // +00 -00 +00:00 -00:00 +0000 -0000 or Z
        matchTimestamp = /[+-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
        // any word (or two) characters or numbers including two/three word month in arabic.
        // includes scottish gaelic two word and hyphenated months
        matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,
        regexes;

    regexes = {};

    function addRegexToken(token, regex, strictRegex) {
        regexes[token] = isFunction(regex)
            ? regex
            : function (isStrict, localeData) {
                  return isStrict && strictRegex ? strictRegex : regex;
              };
    }

    function getParseRegexForToken(token, config) {
        if (!hasOwnProp(regexes, token)) {
            return new RegExp(unescapeFormat(token));
        }

        return regexes[token](config._strict, config._locale);
    }

    // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
    function unescapeFormat(s) {
        return regexEscape(
            s
                .replace('\\', '')
                .replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (
                    matched,
                    p1,
                    p2,
                    p3,
                    p4
                ) {
                    return p1 || p2 || p3 || p4;
                })
        );
    }

    function regexEscape(s) {
        return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
    }

    var tokens = {};

    function addParseToken(token, callback) {
        var i,
            func = callback;
        if (typeof token === 'string') {
            token = [token];
        }
        if (isNumber(callback)) {
            func = function (input, array) {
                array[callback] = toInt(input);
            };
        }
        for (i = 0; i < token.length; i++) {
            tokens[token[i]] = func;
        }
    }

    function addWeekParseToken(token, callback) {
        addParseToken(token, function (input, array, config, token) {
            config._w = config._w || {};
            callback(input, config._w, config, token);
        });
    }

    function addTimeToArrayFromToken(token, input, config) {
        if (input != null && hasOwnProp(tokens, token)) {
            tokens[token](input, config._a, config, token);
        }
    }

    var YEAR = 0,
        MONTH = 1,
        DATE = 2,
        HOUR = 3,
        MINUTE = 4,
        SECOND = 5,
        MILLISECOND = 6,
        WEEK = 7,
        WEEKDAY = 8;

    function mod(n, x) {
        return ((n % x) + x) % x;
    }

    var indexOf;

    if (Array.prototype.indexOf) {
        indexOf = Array.prototype.indexOf;
    } else {
        indexOf = function (o) {
            // I know
            var i;
            for (i = 0; i < this.length; ++i) {
                if (this[i] === o) {
                    return i;
                }
            }
            return -1;
        };
    }

    function daysInMonth(year, month) {
        if (isNaN(year) || isNaN(month)) {
            return NaN;
        }
        var modMonth = mod(month, 12);
        year += (month - modMonth) / 12;
        return modMonth === 1
            ? isLeapYear(year)
                ? 29
                : 28
            : 31 - ((modMonth % 7) % 2);
    }

    // FORMATTING

    addFormatToken('M', ['MM', 2], 'Mo', function () {
        return this.month() + 1;
    });

    addFormatToken('MMM', 0, 0, function (format) {
        return this.localeData().monthsShort(this, format);
    });

    addFormatToken('MMMM', 0, 0, function (format) {
        return this.localeData().months(this, format);
    });

    // ALIASES

    addUnitAlias('month', 'M');

    // PRIORITY

    addUnitPriority('month', 8);

    // PARSING

    addRegexToken('M', match1to2);
    addRegexToken('MM', match1to2, match2);
    addRegexToken('MMM', function (isStrict, locale) {
        return locale.monthsShortRegex(isStrict);
    });
    addRegexToken('MMMM', function (isStrict, locale) {
        return locale.monthsRegex(isStrict);
    });

    addParseToken(['M', 'MM'], function (input, array) {
        array[MONTH] = toInt(input) - 1;
    });

    addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
        var month = config._locale.monthsParse(input, token, config._strict);
        // if we didn't find a month name, mark the date as invalid.
        if (month != null) {
            array[MONTH] = month;
        } else {
            getParsingFlags(config).invalidMonth = input;
        }
    });

    // LOCALES

    var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split(
            '_'
        ),
        defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split(
            '_'
        ),
        MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,
        defaultMonthsShortRegex = matchWord,
        defaultMonthsRegex = matchWord;

    function localeMonths(m, format) {
        if (!m) {
            return isArray(this._months)
                ? this._months
                : this._months['standalone'];
        }
        return isArray(this._months)
            ? this._months[m.month()]
            : this._months[
                  (this._months.isFormat || MONTHS_IN_FORMAT).test(format)
                      ? 'format'
                      : 'standalone'
              ][m.month()];
    }

    function localeMonthsShort(m, format) {
        if (!m) {
            return isArray(this._monthsShort)
                ? this._monthsShort
                : this._monthsShort['standalone'];
        }
        return isArray(this._monthsShort)
            ? this._monthsShort[m.month()]
            : this._monthsShort[
                  MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'
              ][m.month()];
    }

    function handleStrictParse(monthName, format, strict) {
        var i,
            ii,
            mom,
            llc = monthName.toLocaleLowerCase();
        if (!this._monthsParse) {
            // this is not used
            this._monthsParse = [];
            this._longMonthsParse = [];
            this._shortMonthsParse = [];
            for (i = 0; i < 12; ++i) {
                mom = createUTC([2000, i]);
                this._shortMonthsParse[i] = this.monthsShort(
                    mom,
                    ''
                ).toLocaleLowerCase();
                this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
            }
        }

        if (strict) {
            if (format === 'MMM') {
                ii = indexOf.call(this._shortMonthsParse, llc);
                return ii !== -1 ? ii : null;
            } else {
                ii = indexOf.call(this._longMonthsParse, llc);
                return ii !== -1 ? ii : null;
            }
        } else {
            if (format === 'MMM') {
                ii = indexOf.call(this._shortMonthsParse, llc);
                if (ii !== -1) {
                    return ii;
                }
                ii = indexOf.call(this._longMonthsParse, llc);
                return ii !== -1 ? ii : null;
            } else {
                ii = indexOf.call(this._longMonthsParse, llc);
                if (ii !== -1) {
                    return ii;
                }
                ii = indexOf.call(this._shortMonthsParse, llc);
                return ii !== -1 ? ii : null;
            }
        }
    }

    function localeMonthsParse(monthName, format, strict) {
        var i, mom, regex;

        if (this._monthsParseExact) {
            return handleStrictParse.call(this, monthName, format, strict);
        }

        if (!this._monthsParse) {
            this._monthsParse = [];
            this._longMonthsParse = [];
            this._shortMonthsParse = [];
        }

        // TODO: add sorting
        // Sorting makes sure if one month (or abbr) is a prefix of another
        // see sorting in computeMonthsParse
        for (i = 0; i < 12; i++) {
            // make the regex if we don't have it already
            mom = createUTC([2000, i]);
            if (strict && !this._longMonthsParse[i]) {
                this._longMonthsParse[i] = new RegExp(
                    '^' + this.months(mom, '').replace('.', '') + '$',
                    'i'
                );
                this._shortMonthsParse[i] = new RegExp(
                    '^' + this.monthsShort(mom, '').replace('.', '') + '$',
                    'i'
                );
            }
            if (!strict && !this._monthsParse[i]) {
                regex =
                    '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
                this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
            }
            // test the regex
            if (
                strict &&
                format === 'MMMM' &&
                this._longMonthsParse[i].test(monthName)
            ) {
                return i;
            } else if (
                strict &&
                format === 'MMM' &&
                this._shortMonthsParse[i].test(monthName)
            ) {
                return i;
            } else if (!strict && this._monthsParse[i].test(monthName)) {
                return i;
            }
        }
    }

    // MOMENTS

    function setMonth(mom, value) {
        var dayOfMonth;

        if (!mom.isValid()) {
            // No op
            return mom;
        }

        if (typeof value === 'string') {
            if (/^\d+$/.test(value)) {
                value = toInt(value);
            } else {
                value = mom.localeData().monthsParse(value);
                // TODO: Another silent failure?
                if (!isNumber(value)) {
                    return mom;
                }
            }
        }

        dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
        mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
        return mom;
    }

    function getSetMonth(value) {
        if (value != null) {
            setMonth(this, value);
            hooks.updateOffset(this, true);
            return this;
        } else {
            return get(this, 'Month');
        }
    }

    function getDaysInMonth() {
        return daysInMonth(this.year(), this.month());
    }

    function monthsShortRegex(isStrict) {
        if (this._monthsParseExact) {
            if (!hasOwnProp(this, '_monthsRegex')) {
                computeMonthsParse.call(this);
            }
            if (isStrict) {
                return this._monthsShortStrictRegex;
            } else {
                return this._monthsShortRegex;
            }
        } else {
            if (!hasOwnProp(this, '_monthsShortRegex')) {
                this._monthsShortRegex = defaultMonthsShortRegex;
            }
            return this._monthsShortStrictRegex && isStrict
                ? this._monthsShortStrictRegex
                : this._monthsShortRegex;
        }
    }

    function monthsRegex(isStrict) {
        if (this._monthsParseExact) {
            if (!hasOwnProp(this, '_monthsRegex')) {
                computeMonthsParse.call(this);
            }
            if (isStrict) {
                return this._monthsStrictRegex;
            } else {
                return this._monthsRegex;
            }
        } else {
            if (!hasOwnProp(this, '_monthsRegex')) {
                this._monthsRegex = defaultMonthsRegex;
            }
            return this._monthsStrictRegex && isStrict
                ? this._monthsStrictRegex
                : this._monthsRegex;
        }
    }

    function computeMonthsParse() {
        function cmpLenRev(a, b) {
            return b.length - a.length;
        }

        var shortPieces = [],
            longPieces = [],
            mixedPieces = [],
            i,
            mom;
        for (i = 0; i < 12; i++) {
            // make the regex if we don't have it already
            mom = createUTC([2000, i]);
            shortPieces.push(this.monthsShort(mom, ''));
            longPieces.push(this.months(mom, ''));
            mixedPieces.push(this.months(mom, ''));
            mixedPieces.push(this.monthsShort(mom, ''));
        }
        // Sorting makes sure if one month (or abbr) is a prefix of another it
        // will match the longer piece.
        shortPieces.sort(cmpLenRev);
        longPieces.sort(cmpLenRev);
        mixedPieces.sort(cmpLenRev);
        for (i = 0; i < 12; i++) {
            shortPieces[i] = regexEscape(shortPieces[i]);
            longPieces[i] = regexEscape(longPieces[i]);
        }
        for (i = 0; i < 24; i++) {
            mixedPieces[i] = regexEscape(mixedPieces[i]);
        }

        this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
        this._monthsShortRegex = this._monthsRegex;
        this._monthsStrictRegex = new RegExp(
            '^(' + longPieces.join('|') + ')',
            'i'
        );
        this._monthsShortStrictRegex = new RegExp(
            '^(' + shortPieces.join('|') + ')',
            'i'
        );
    }

    // FORMATTING

    addFormatToken('Y', 0, 0, function () {
        var y = this.year();
        return y <= 9999 ? zeroFill(y, 4) : '+' + y;
    });

    addFormatToken(0, ['YY', 2], 0, function () {
        return this.year() % 100;
    });

    addFormatToken(0, ['YYYY', 4], 0, 'year');
    addFormatToken(0, ['YYYYY', 5], 0, 'year');
    addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');

    // ALIASES

    addUnitAlias('year', 'y');

    // PRIORITIES

    addUnitPriority('year', 1);

    // PARSING

    addRegexToken('Y', matchSigned);
    addRegexToken('YY', match1to2, match2);
    addRegexToken('YYYY', match1to4, match4);
    addRegexToken('YYYYY', match1to6, match6);
    addRegexToken('YYYYYY', match1to6, match6);

    addParseToken(['YYYYY', 'YYYYYY'], YEAR);
    addParseToken('YYYY', function (input, array) {
        array[YEAR] =
            input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
    });
    addParseToken('YY', function (input, array) {
        array[YEAR] = hooks.parseTwoDigitYear(input);
    });
    addParseToken('Y', function (input, array) {
        array[YEAR] = parseInt(input, 10);
    });

    // HELPERS

    function daysInYear(year) {
        return isLeapYear(year) ? 366 : 365;
    }

    // HOOKS

    hooks.parseTwoDigitYear = function (input) {
        return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
    };

    // MOMENTS

    var getSetYear = makeGetSet('FullYear', true);

    function getIsLeapYear() {
        return isLeapYear(this.year());
    }

    function createDate(y, m, d, h, M, s, ms) {
        // can't just apply() to create a date:
        // https://stackoverflow.com/q/181348
        var date;
        // the date constructor remaps years 0-99 to 1900-1999
        if (y < 100 && y >= 0) {
            // preserve leap years using a full 400 year cycle, then reset
            date = new Date(y + 400, m, d, h, M, s, ms);
            if (isFinite(date.getFullYear())) {
                date.setFullYear(y);
            }
        } else {
            date = new Date(y, m, d, h, M, s, ms);
        }

        return date;
    }

    function createUTCDate(y) {
        var date, args;
        // the Date.UTC function remaps years 0-99 to 1900-1999
        if (y < 100 && y >= 0) {
            args = Array.prototype.slice.call(arguments);
            // preserve leap years using a full 400 year cycle, then reset
            args[0] = y + 400;
            date = new Date(Date.UTC.apply(null, args));
            if (isFinite(date.getUTCFullYear())) {
                date.setUTCFullYear(y);
            }
        } else {
            date = new Date(Date.UTC.apply(null, arguments));
        }

        return date;
    }

    // start-of-first-week - start-of-year
    function firstWeekOffset(year, dow, doy) {
        var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
            fwd = 7 + dow - doy,
            // first-week day local weekday -- which local weekday is fwd
            fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;

        return -fwdlw + fwd - 1;
    }

    // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
    function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
        var localWeekday = (7 + weekday - dow) % 7,
            weekOffset = firstWeekOffset(year, dow, doy),
            dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
            resYear,
            resDayOfYear;

        if (dayOfYear <= 0) {
            resYear = year - 1;
            resDayOfYear = daysInYear(resYear) + dayOfYear;
        } else if (dayOfYear > daysInYear(year)) {
            resYear = year + 1;
            resDayOfYear = dayOfYear - daysInYear(year);
        } else {
            resYear = year;
            resDayOfYear = dayOfYear;
        }

        return {
            year: resYear,
            dayOfYear: resDayOfYear,
        };
    }

    function weekOfYear(mom, dow, doy) {
        var weekOffset = firstWeekOffset(mom.year(), dow, doy),
            week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
            resWeek,
            resYear;

        if (week < 1) {
            resYear = mom.year() - 1;
            resWeek = week + weeksInYear(resYear, dow, doy);
        } else if (week > weeksInYear(mom.year(), dow, doy)) {
            resWeek = week - weeksInYear(mom.year(), dow, doy);
            resYear = mom.year() + 1;
        } else {
            resYear = mom.year();
            resWeek = week;
        }

        return {
            week: resWeek,
            year: resYear,
        };
    }

    function weeksInYear(year, dow, doy) {
        var weekOffset = firstWeekOffset(year, dow, doy),
            weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
        return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
    }

    // FORMATTING

    addFormatToken('w', ['ww', 2], 'wo', 'week');
    addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');

    // ALIASES

    addUnitAlias('week', 'w');
    addUnitAlias('isoWeek', 'W');

    // PRIORITIES

    addUnitPriority('week', 5);
    addUnitPriority('isoWeek', 5);

    // PARSING

    addRegexToken('w', match1to2);
    addRegexToken('ww', match1to2, match2);
    addRegexToken('W', match1to2);
    addRegexToken('WW', match1to2, match2);

    addWeekParseToken(['w', 'ww', 'W', 'WW'], function (
        input,
        week,
        config,
        token
    ) {
        week[token.substr(0, 1)] = toInt(input);
    });

    // HELPERS

    // LOCALES

    function localeWeek(mom) {
        return weekOfYear(mom, this._week.dow, this._week.doy).week;
    }

    var defaultLocaleWeek = {
        dow: 0, // Sunday is the first day of the week.
        doy: 6, // The week that contains Jan 6th is the first week of the year.
    };

    function localeFirstDayOfWeek() {
        return this._week.dow;
    }

    function localeFirstDayOfYear() {
        return this._week.doy;
    }

    // MOMENTS

    function getSetWeek(input) {
        var week = this.localeData().week(this);
        return input == null ? week : this.add((input - week) * 7, 'd');
    }

    function getSetISOWeek(input) {
        var week = weekOfYear(this, 1, 4).week;
        return input == null ? week : this.add((input - week) * 7, 'd');
    }

    // FORMATTING

    addFormatToken('d', 0, 'do', 'day');

    addFormatToken('dd', 0, 0, function (format) {
        return this.localeData().weekdaysMin(this, format);
    });

    addFormatToken('ddd', 0, 0, function (format) {
        return this.localeData().weekdaysShort(this, format);
    });

    addFormatToken('dddd', 0, 0, function (format) {
        return this.localeData().weekdays(this, format);
    });

    addFormatToken('e', 0, 0, 'weekday');
    addFormatToken('E', 0, 0, 'isoWeekday');

    // ALIASES

    addUnitAlias('day', 'd');
    addUnitAlias('weekday', 'e');
    addUnitAlias('isoWeekday', 'E');

    // PRIORITY
    addUnitPriority('day', 11);
    addUnitPriority('weekday', 11);
    addUnitPriority('isoWeekday', 11);

    // PARSING

    addRegexToken('d', match1to2);
    addRegexToken('e', match1to2);
    addRegexToken('E', match1to2);
    addRegexToken('dd', function (isStrict, locale) {
        return locale.weekdaysMinRegex(isStrict);
    });
    addRegexToken('ddd', function (isStrict, locale) {
        return locale.weekdaysShortRegex(isStrict);
    });
    addRegexToken('dddd', function (isStrict, locale) {
        return locale.weekdaysRegex(isStrict);
    });

    addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
        var weekday = config._locale.weekdaysParse(input, token, config._strict);
        // if we didn't get a weekday name, mark the date as invalid
        if (weekday != null) {
            week.d = weekday;
        } else {
            getParsingFlags(config).invalidWeekday = input;
        }
    });

    addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
        week[token] = toInt(input);
    });

    // HELPERS

    function parseWeekday(input, locale) {
        if (typeof input !== 'string') {
            return input;
        }

        if (!isNaN(input)) {
            return parseInt(input, 10);
        }

        input = locale.weekdaysParse(input);
        if (typeof input === 'number') {
            return input;
        }

        return null;
    }

    function parseIsoWeekday(input, locale) {
        if (typeof input === 'string') {
            return locale.weekdaysParse(input) % 7 || 7;
        }
        return isNaN(input) ? null : input;
    }

    // LOCALES
    function shiftWeekdays(ws, n) {
        return ws.slice(n, 7).concat(ws.slice(0, n));
    }

    var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split(
            '_'
        ),
        defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
        defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
        defaultWeekdaysRegex = matchWord,
        defaultWeekdaysShortRegex = matchWord,
        defaultWeekdaysMinRegex = matchWord;

    function localeWeekdays(m, format) {
        var weekdays = isArray(this._weekdays)
            ? this._weekdays
            : this._weekdays[
                  m && m !== true && this._weekdays.isFormat.test(format)
                      ? 'format'
                      : 'standalone'
              ];
        return m === true
            ? shiftWeekdays(weekdays, this._week.dow)
            : m
            ? weekdays[m.day()]
            : weekdays;
    }

    function localeWeekdaysShort(m) {
        return m === true
            ? shiftWeekdays(this._weekdaysShort, this._week.dow)
            : m
            ? this._weekdaysShort[m.day()]
            : this._weekdaysShort;
    }

    function localeWeekdaysMin(m) {
        return m === true
            ? shiftWeekdays(this._weekdaysMin, this._week.dow)
            : m
            ? this._weekdaysMin[m.day()]
            : this._weekdaysMin;
    }

    function handleStrictParse$1(weekdayName, format, strict) {
        var i,
            ii,
            mom,
            llc = weekdayName.toLocaleLowerCase();
        if (!this._weekdaysParse) {
            this._weekdaysParse = [];
            this._shortWeekdaysParse = [];
            this._minWeekdaysParse = [];

            for (i = 0; i < 7; ++i) {
                mom = createUTC([2000, 1]).day(i);
                this._minWeekdaysParse[i] = this.weekdaysMin(
                    mom,
                    ''
                ).toLocaleLowerCase();
                this._shortWeekdaysParse[i] = this.weekdaysShort(
                    mom,
                    ''
                ).toLocaleLowerCase();
                this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
            }
        }

        if (strict) {
            if (format === 'dddd') {
                ii = indexOf.call(this._weekdaysParse, llc);
                return ii !== -1 ? ii : null;
            } else if (format === 'ddd') {
                ii = indexOf.call(this._shortWeekdaysParse, llc);
                return ii !== -1 ? ii : null;
            } else {
                ii = indexOf.call(this._minWeekdaysParse, llc);
                return ii !== -1 ? ii : null;
            }
        } else {
            if (format === 'dddd') {
                ii = indexOf.call(this._weekdaysParse, llc);
                if (ii !== -1) {
                    return ii;
                }
                ii = indexOf.call(this._shortWeekdaysParse, llc);
                if (ii !== -1) {
                    return ii;
                }
                ii = indexOf.call(this._minWeekdaysParse, llc);
                return ii !== -1 ? ii : null;
            } else if (format === 'ddd') {
                ii = indexOf.call(this._shortWeekdaysParse, llc);
                if (ii !== -1) {
                    return ii;
                }
                ii = indexOf.call(this._weekdaysParse, llc);
                if (ii !== -1) {
                    return ii;
                }
                ii = indexOf.call(this._minWeekdaysParse, llc);
                return ii !== -1 ? ii : null;
            } else {
                ii = indexOf.call(this._minWeekdaysParse, llc);
                if (ii !== -1) {
                    return ii;
                }
                ii = indexOf.call(this._weekdaysParse, llc);
                if (ii !== -1) {
                    return ii;
                }
                ii = indexOf.call(this._shortWeekdaysParse, llc);
                return ii !== -1 ? ii : null;
            }
        }
    }

    function localeWeekdaysParse(weekdayName, format, strict) {
        var i, mom, regex;

        if (this._weekdaysParseExact) {
            return handleStrictParse$1.call(this, weekdayName, format, strict);
        }

        if (!this._weekdaysParse) {
            this._weekdaysParse = [];
            this._minWeekdaysParse = [];
            this._shortWeekdaysParse = [];
            this._fullWeekdaysParse = [];
        }

        for (i = 0; i < 7; i++) {
            // make the regex if we don't have it already

            mom = createUTC([2000, 1]).day(i);
            if (strict && !this._fullWeekdaysParse[i]) {
                this._fullWeekdaysParse[i] = new RegExp(
                    '^' + this.weekdays(mom, '').replace('.', '\\.?') + '$',
                    'i'
                );
                this._shortWeekdaysParse[i] = new RegExp(
                    '^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$',
                    'i'
                );
                this._minWeekdaysParse[i] = new RegExp(
                    '^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$',
                    'i'
                );
            }
            if (!this._weekdaysParse[i]) {
                regex =
                    '^' +
                    this.weekdays(mom, '') +
                    '|^' +
                    this.weekdaysShort(mom, '') +
                    '|^' +
                    this.weekdaysMin(mom, '');
                this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
            }
            // test the regex
            if (
                strict &&
                format === 'dddd' &&
                this._fullWeekdaysParse[i].test(weekdayName)
            ) {
                return i;
            } else if (
                strict &&
                format === 'ddd' &&
                this._shortWeekdaysParse[i].test(weekdayName)
            ) {
                return i;
            } else if (
                strict &&
                format === 'dd' &&
                this._minWeekdaysParse[i].test(weekdayName)
            ) {
                return i;
            } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
                return i;
            }
        }
    }

    // MOMENTS

    function getSetDayOfWeek(input) {
        if (!this.isValid()) {
            return input != null ? this : NaN;
        }
        var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
        if (input != null) {
            input = parseWeekday(input, this.localeData());
            return this.add(input - day, 'd');
        } else {
            return day;
        }
    }

    function getSetLocaleDayOfWeek(input) {
        if (!this.isValid()) {
            return input != null ? this : NaN;
        }
        var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
        return input == null ? weekday : this.add(input - weekday, 'd');
    }

    function getSetISODayOfWeek(input) {
        if (!this.isValid()) {
            return input != null ? this : NaN;
        }

        // behaves the same as moment#day except
        // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
        // as a setter, sunday should belong to the previous week.

        if (input != null) {
            var weekday = parseIsoWeekday(input, this.localeData());
            return this.day(this.day() % 7 ? weekday : weekday - 7);
        } else {
            return this.day() || 7;
        }
    }

    function weekdaysRegex(isStrict) {
        if (this._weekdaysParseExact) {
            if (!hasOwnProp(this, '_weekdaysRegex')) {
                computeWeekdaysParse.call(this);
            }
            if (isStrict) {
                return this._weekdaysStrictRegex;
            } else {
                return this._weekdaysRegex;
            }
        } else {
            if (!hasOwnProp(this, '_weekdaysRegex')) {
                this._weekdaysRegex = defaultWeekdaysRegex;
            }
            return this._weekdaysStrictRegex && isStrict
                ? this._weekdaysStrictRegex
                : this._weekdaysRegex;
        }
    }

    function weekdaysShortRegex(isStrict) {
        if (this._weekdaysParseExact) {
            if (!hasOwnProp(this, '_weekdaysRegex')) {
                computeWeekdaysParse.call(this);
            }
            if (isStrict) {
                return this._weekdaysShortStrictRegex;
            } else {
                return this._weekdaysShortRegex;
            }
        } else {
            if (!hasOwnProp(this, '_weekdaysShortRegex')) {
                this._weekdaysShortRegex = defaultWeekdaysShortRegex;
            }
            return this._weekdaysShortStrictRegex && isStrict
                ? this._weekdaysShortStrictRegex
                : this._weekdaysShortRegex;
        }
    }

    function weekdaysMinRegex(isStrict) {
        if (this._weekdaysParseExact) {
            if (!hasOwnProp(this, '_weekdaysRegex')) {
                computeWeekdaysParse.call(this);
            }
            if (isStrict) {
                return this._weekdaysMinStrictRegex;
            } else {
                return this._weekdaysMinRegex;
            }
        } else {
            if (!hasOwnProp(this, '_weekdaysMinRegex')) {
                this._weekdaysMinRegex = defaultWeekdaysMinRegex;
            }
            return this._weekdaysMinStrictRegex && isStrict
                ? this._weekdaysMinStrictRegex
                : this._weekdaysMinRegex;
        }
    }

    function computeWeekdaysParse() {
        function cmpLenRev(a, b) {
            return b.length - a.length;
        }

        var minPieces = [],
            shortPieces = [],
            longPieces = [],
            mixedPieces = [],
            i,
            mom,
            minp,
            shortp,
            longp;
        for (i = 0; i < 7; i++) {
            // make the regex if we don't have it already
            mom = createUTC([2000, 1]).day(i);
            minp = regexEscape(this.weekdaysMin(mom, ''));
            shortp = regexEscape(this.weekdaysShort(mom, ''));
            longp = regexEscape(this.weekdays(mom, ''));
            minPieces.push(minp);
            shortPieces.push(shortp);
            longPieces.push(longp);
            mixedPieces.push(minp);
            mixedPieces.push(shortp);
            mixedPieces.push(longp);
        }
        // Sorting makes sure if one weekday (or abbr) is a prefix of another it
        // will match the longer piece.
        minPieces.sort(cmpLenRev);
        shortPieces.sort(cmpLenRev);
        longPieces.sort(cmpLenRev);
        mixedPieces.sort(cmpLenRev);

        this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
        this._weekdaysShortRegex = this._weekdaysRegex;
        this._weekdaysMinRegex = this._weekdaysRegex;

        this._weekdaysStrictRegex = new RegExp(
            '^(' + longPieces.join('|') + ')',
            'i'
        );
        this._weekdaysShortStrictRegex = new RegExp(
            '^(' + shortPieces.join('|') + ')',
            'i'
        );
        this._weekdaysMinStrictRegex = new RegExp(
            '^(' + minPieces.join('|') + ')',
            'i'
        );
    }

    // FORMATTING

    function hFormat() {
        return this.hours() % 12 || 12;
    }

    function kFormat() {
        return this.hours() || 24;
    }

    addFormatToken('H', ['HH', 2], 0, 'hour');
    addFormatToken('h', ['hh', 2], 0, hFormat);
    addFormatToken('k', ['kk', 2], 0, kFormat);

    addFormatToken('hmm', 0, 0, function () {
        return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
    });

    addFormatToken('hmmss', 0, 0, function () {
        return (
            '' +
            hFormat.apply(this) +
            zeroFill(this.minutes(), 2) +
            zeroFill(this.seconds(), 2)
        );
    });

    addFormatToken('Hmm', 0, 0, function () {
        return '' + this.hours() + zeroFill(this.minutes(), 2);
    });

    addFormatToken('Hmmss', 0, 0, function () {
        return (
            '' +
            this.hours() +
            zeroFill(this.minutes(), 2) +
            zeroFill(this.seconds(), 2)
        );
    });

    function meridiem(token, lowercase) {
        addFormatToken(token, 0, 0, function () {
            return this.localeData().meridiem(
                this.hours(),
                this.minutes(),
                lowercase
            );
        });
    }

    meridiem('a', true);
    meridiem('A', false);

    // ALIASES

    addUnitAlias('hour', 'h');

    // PRIORITY
    addUnitPriority('hour', 13);

    // PARSING

    function matchMeridiem(isStrict, locale) {
        return locale._meridiemParse;
    }

    addRegexToken('a', matchMeridiem);
    addRegexToken('A', matchMeridiem);
    addRegexToken('H', match1to2);
    addRegexToken('h', match1to2);
    addRegexToken('k', match1to2);
    addRegexToken('HH', match1to2, match2);
    addRegexToken('hh', match1to2, match2);
    addRegexToken('kk', match1to2, match2);

    addRegexToken('hmm', match3to4);
    addRegexToken('hmmss', match5to6);
    addRegexToken('Hmm', match3to4);
    addRegexToken('Hmmss', match5to6);

    addParseToken(['H', 'HH'], HOUR);
    addParseToken(['k', 'kk'], function (input, array, config) {
        var kInput = toInt(input);
        array[HOUR] = kInput === 24 ? 0 : kInput;
    });
    addParseToken(['a', 'A'], function (input, array, config) {
        config._isPm = config._locale.isPM(input);
        config._meridiem = input;
    });
    addParseToken(['h', 'hh'], function (input, array, config) {
        array[HOUR] = toInt(input);
        getParsingFlags(config).bigHour = true;
    });
    addParseToken('hmm', function (input, array, config) {
        var pos = input.length - 2;
        array[HOUR] = toInt(input.substr(0, pos));
        array[MINUTE] = toInt(input.substr(pos));
        getParsingFlags(config).bigHour = true;
    });
    addParseToken('hmmss', function (input, array, config) {
        var pos1 = input.length - 4,
            pos2 = input.length - 2;
        array[HOUR] = toInt(input.substr(0, pos1));
        array[MINUTE] = toInt(input.substr(pos1, 2));
        array[SECOND] = toInt(input.substr(pos2));
        getParsingFlags(config).bigHour = true;
    });
    addParseToken('Hmm', function (input, array, config) {
        var pos = input.length - 2;
        array[HOUR] = toInt(input.substr(0, pos));
        array[MINUTE] = toInt(input.substr(pos));
    });
    addParseToken('Hmmss', function (input, array, config) {
        var pos1 = input.length - 4,
            pos2 = input.length - 2;
        array[HOUR] = toInt(input.substr(0, pos1));
        array[MINUTE] = toInt(input.substr(pos1, 2));
        array[SECOND] = toInt(input.substr(pos2));
    });

    // LOCALES

    function localeIsPM(input) {
        // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
        // Using charAt should be more compatible.
        return (input + '').toLowerCase().charAt(0) === 'p';
    }

    var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i,
        // Setting the hour should keep the time, because the user explicitly
        // specified which hour they want. So trying to maintain the same hour (in
        // a new timezone) makes sense. Adding/subtracting hours does not follow
        // this rule.
        getSetHour = makeGetSet('Hours', true);

    function localeMeridiem(hours, minutes, isLower) {
        if (hours > 11) {
            return isLower ? 'pm' : 'PM';
        } else {
            return isLower ? 'am' : 'AM';
        }
    }

    var baseConfig = {
        calendar: defaultCalendar,
        longDateFormat: defaultLongDateFormat,
        invalidDate: defaultInvalidDate,
        ordinal: defaultOrdinal,
        dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
        relativeTime: defaultRelativeTime,

        months: defaultLocaleMonths,
        monthsShort: defaultLocaleMonthsShort,

        week: defaultLocaleWeek,

        weekdays: defaultLocaleWeekdays,
        weekdaysMin: defaultLocaleWeekdaysMin,
        weekdaysShort: defaultLocaleWeekdaysShort,

        meridiemParse: defaultLocaleMeridiemParse,
    };

    // internal storage for locale config files
    var locales = {},
        localeFamilies = {},
        globalLocale;

    function commonPrefix(arr1, arr2) {
        var i,
            minl = Math.min(arr1.length, arr2.length);
        for (i = 0; i < minl; i += 1) {
            if (arr1[i] !== arr2[i]) {
                return i;
            }
        }
        return minl;
    }

    function normalizeLocale(key) {
        return key ? key.toLowerCase().replace('_', '-') : key;
    }

    // pick the locale from the array
    // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
    // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
    function chooseLocale(names) {
        var i = 0,
            j,
            next,
            locale,
            split;

        while (i < names.length) {
            split = normalizeLocale(names[i]).split('-');
            j = split.length;
            next = normalizeLocale(names[i + 1]);
            next = next ? next.split('-') : null;
            while (j > 0) {
                locale = loadLocale(split.slice(0, j).join('-'));
                if (locale) {
                    return locale;
                }
                if (
                    next &&
                    next.length >= j &&
                    commonPrefix(split, next) >= j - 1
                ) {
                    //the next array item is better than a shallower substring of this one
                    break;
                }
                j--;
            }
            i++;
        }
        return globalLocale;
    }

    function loadLocale(name) {
        var oldLocale = null,
            aliasedRequire;
        // TODO: Find a better way to register and load all the locales in Node
        if (
            locales[name] === undefined &&
            typeof module !== 'undefined' &&
            module &&
            module.exports
        ) {
            try {
                oldLocale = globalLocale._abbr;
                aliasedRequire = require;
                aliasedRequire('./locale/' + name);
                getSetGlobalLocale(oldLocale);
            } catch (e) {
                // mark as not found to avoid repeating expensive file require call causing high CPU
                // when trying to find en-US, en_US, en-us for every format call
                locales[name] = null; // null means not found
            }
        }
        return locales[name];
    }

    // This function will load locale and then set the global locale.  If
    // no arguments are passed in, it will simply return the current global
    // locale key.
    function getSetGlobalLocale(key, values) {
        var data;
        if (key) {
            if (isUndefined(values)) {
                data = getLocale(key);
            } else {
                data = defineLocale(key, values);
            }

            if (data) {
                // moment.duration._locale = moment._locale = data;
                globalLocale = data;
            } else {
                if (typeof console !== 'undefined' && console.warn) {
                    //warn user if arguments are passed but the locale could not be set
                    console.warn(
                        'Locale ' + key + ' not found. Did you forget to load it?'
                    );
                }
            }
        }

        return globalLocale._abbr;
    }

    function defineLocale(name, config) {
        if (config !== null) {
            var locale,
                parentConfig = baseConfig;
            config.abbr = name;
            if (locales[name] != null) {
                deprecateSimple(
                    'defineLocaleOverride',
                    'use moment.updateLocale(localeName, config) to change ' +
                        'an existing locale. moment.defineLocale(localeName, ' +
                        'config) should only be used for creating a new locale ' +
                        'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'
                );
                parentConfig = locales[name]._config;
            } else if (config.parentLocale != null) {
                if (locales[config.parentLocale] != null) {
                    parentConfig = locales[config.parentLocale]._config;
                } else {
                    locale = loadLocale(config.parentLocale);
                    if (locale != null) {
                        parentConfig = locale._config;
                    } else {
                        if (!localeFamilies[config.parentLocale]) {
                            localeFamilies[config.parentLocale] = [];
                        }
                        localeFamilies[config.parentLocale].push({
                            name: name,
                            config: config,
                        });
                        return null;
                    }
                }
            }
            locales[name] = new Locale(mergeConfigs(parentConfig, config));

            if (localeFamilies[name]) {
                localeFamilies[name].forEach(function (x) {
                    defineLocale(x.name, x.config);
                });
            }

            // backwards compat for now: also set the locale
            // make sure we set the locale AFTER all child locales have been
            // created, so we won't end up with the child locale set.
            getSetGlobalLocale(name);

            return locales[name];
        } else {
            // useful for testing
            delete locales[name];
            return null;
        }
    }

    function updateLocale(name, config) {
        if (config != null) {
            var locale,
                tmpLocale,
                parentConfig = baseConfig;

            if (locales[name] != null && locales[name].parentLocale != null) {
                // Update existing child locale in-place to avoid memory-leaks
                locales[name].set(mergeConfigs(locales[name]._config, config));
            } else {
                // MERGE
                tmpLocale = loadLocale(name);
                if (tmpLocale != null) {
                    parentConfig = tmpLocale._config;
                }
                config = mergeConfigs(parentConfig, config);
                if (tmpLocale == null) {
                    // updateLocale is called for creating a new locale
                    // Set abbr so it will have a name (getters return
                    // undefined otherwise).
                    config.abbr = name;
                }
                locale = new Locale(config);
                locale.parentLocale = locales[name];
                locales[name] = locale;
            }

            // backwards compat for now: also set the locale
            getSetGlobalLocale(name);
        } else {
            // pass null for config to unupdate, useful for tests
            if (locales[name] != null) {
                if (locales[name].parentLocale != null) {
                    locales[name] = locales[name].parentLocale;
                    if (name === getSetGlobalLocale()) {
                        getSetGlobalLocale(name);
                    }
                } else if (locales[name] != null) {
                    delete locales[name];
                }
            }
        }
        return locales[name];
    }

    // returns locale data
    function getLocale(key) {
        var locale;

        if (key && key._locale && key._locale._abbr) {
            key = key._locale._abbr;
        }

        if (!key) {
            return globalLocale;
        }

        if (!isArray(key)) {
            //short-circuit everything else
            locale = loadLocale(key);
            if (locale) {
                return locale;
            }
            key = [key];
        }

        return chooseLocale(key);
    }

    function listLocales() {
        return keys(locales);
    }

    function checkOverflow(m) {
        var overflow,
            a = m._a;

        if (a && getParsingFlags(m).overflow === -2) {
            overflow =
                a[MONTH] < 0 || a[MONTH] > 11
                    ? MONTH
                    : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH])
                    ? DATE
                    : a[HOUR] < 0 ||
                      a[HOUR] > 24 ||
                      (a[HOUR] === 24 &&
                          (a[MINUTE] !== 0 ||
                              a[SECOND] !== 0 ||
                              a[MILLISECOND] !== 0))
                    ? HOUR
                    : a[MINUTE] < 0 || a[MINUTE] > 59
                    ? MINUTE
                    : a[SECOND] < 0 || a[SECOND] > 59
                    ? SECOND
                    : a[MILLISECOND] < 0 || a[MILLISECOND] > 999
                    ? MILLISECOND
                    : -1;

            if (
                getParsingFlags(m)._overflowDayOfYear &&
                (overflow < YEAR || overflow > DATE)
            ) {
                overflow = DATE;
            }
            if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
                overflow = WEEK;
            }
            if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
                overflow = WEEKDAY;
            }

            getParsingFlags(m).overflow = overflow;
        }

        return m;
    }

    // iso 8601 regex
    // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
    var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
        basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
        tzRegex = /Z|[+-]\d\d(?::?\d\d)?/,
        isoDates = [
            ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
            ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
            ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
            ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
            ['YYYY-DDD', /\d{4}-\d{3}/],
            ['YYYY-MM', /\d{4}-\d\d/, false],
            ['YYYYYYMMDD', /[+-]\d{10}/],
            ['YYYYMMDD', /\d{8}/],
            ['GGGG[W]WWE', /\d{4}W\d{3}/],
            ['GGGG[W]WW', /\d{4}W\d{2}/, false],
            ['YYYYDDD', /\d{7}/],
            ['YYYYMM', /\d{6}/, false],
            ['YYYY', /\d{4}/, false],
        ],
        // iso time formats and regexes
        isoTimes = [
            ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
            ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
            ['HH:mm:ss', /\d\d:\d\d:\d\d/],
            ['HH:mm', /\d\d:\d\d/],
            ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
            ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
            ['HHmmss', /\d\d\d\d\d\d/],
            ['HHmm', /\d\d\d\d/],
            ['HH', /\d\d/],
        ],
        aspNetJsonRegex = /^\/?Date\((-?\d+)/i,
        // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
        rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,
        obsOffsets = {
            UT: 0,
            GMT: 0,
            EDT: -4 * 60,
            EST: -5 * 60,
            CDT: -5 * 60,
            CST: -6 * 60,
            MDT: -6 * 60,
            MST: -7 * 60,
            PDT: -7 * 60,
            PST: -8 * 60,
        };

    // date from iso format
    function configFromISO(config) {
        var i,
            l,
            string = config._i,
            match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
            allowTime,
            dateFormat,
            timeFormat,
            tzFormat;

        if (match) {
            getParsingFlags(config).iso = true;

            for (i = 0, l = isoDates.length; i < l; i++) {
                if (isoDates[i][1].exec(match[1])) {
                    dateFormat = isoDates[i][0];
                    allowTime = isoDates[i][2] !== false;
                    break;
                }
            }
            if (dateFormat == null) {
                config._isValid = false;
                return;
            }
            if (match[3]) {
                for (i = 0, l = isoTimes.length; i < l; i++) {
                    if (isoTimes[i][1].exec(match[3])) {
                        // match[2] should be 'T' or space
                        timeFormat = (match[2] || ' ') + isoTimes[i][0];
                        break;
                    }
                }
                if (timeFormat == null) {
                    config._isValid = false;
                    return;
                }
            }
            if (!allowTime && timeFormat != null) {
                config._isValid = false;
                return;
            }
            if (match[4]) {
                if (tzRegex.exec(match[4])) {
                    tzFormat = 'Z';
                } else {
                    config._isValid = false;
                    return;
                }
            }
            config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
            configFromStringAndFormat(config);
        } else {
            config._isValid = false;
        }
    }

    function extractFromRFC2822Strings(
        yearStr,
        monthStr,
        dayStr,
        hourStr,
        minuteStr,
        secondStr
    ) {
        var result = [
            untruncateYear(yearStr),
            defaultLocaleMonthsShort.indexOf(monthStr),
            parseInt(dayStr, 10),
            parseInt(hourStr, 10),
            parseInt(minuteStr, 10),
        ];

        if (secondStr) {
            result.push(parseInt(secondStr, 10));
        }

        return result;
    }

    function untruncateYear(yearStr) {
        var year = parseInt(yearStr, 10);
        if (year <= 49) {
            return 2000 + year;
        } else if (year <= 999) {
            return 1900 + year;
        }
        return year;
    }

    function preprocessRFC2822(s) {
        // Remove comments and folding whitespace and replace multiple-spaces with a single space
        return s
            .replace(/\([^)]*\)|[\n\t]/g, ' ')
            .replace(/(\s\s+)/g, ' ')
            .replace(/^\s\s*/, '')
            .replace(/\s\s*$/, '');
    }

    function checkWeekday(weekdayStr, parsedInput, config) {
        if (weekdayStr) {
            // TODO: Replace the vanilla JS Date object with an independent day-of-week check.
            var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),
                weekdayActual = new Date(
                    parsedInput[0],
                    parsedInput[1],
                    parsedInput[2]
                ).getDay();
            if (weekdayProvided !== weekdayActual) {
                getParsingFlags(config).weekdayMismatch = true;
                config._isValid = false;
                return false;
            }
        }
        return true;
    }

    function calculateOffset(obsOffset, militaryOffset, numOffset) {
        if (obsOffset) {
            return obsOffsets[obsOffset];
        } else if (militaryOffset) {
            // the only allowed military tz is Z
            return 0;
        } else {
            var hm = parseInt(numOffset, 10),
                m = hm % 100,
                h = (hm - m) / 100;
            return h * 60 + m;
        }
    }

    // date and time from ref 2822 format
    function configFromRFC2822(config) {
        var match = rfc2822.exec(preprocessRFC2822(config._i)),
            parsedArray;
        if (match) {
            p
Download .txt
gitextract_mss9r7uy/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── c2f
├── code2flow/
│   ├── __init__.py
│   ├── engine.py
│   ├── get_ast.js
│   ├── get_ast.php
│   ├── javascript.py
│   ├── model.py
│   ├── package.json
│   ├── php.py
│   ├── python.py
│   └── ruby.py
├── make_expected.py
├── requirements_dev.txt
├── setup.py
└── tests/
    ├── __init__.py
    ├── test_code/
    │   ├── js/
    │   │   ├── ambiguous_names/
    │   │   │   └── ambiguous_names.js
    │   │   ├── bad_parse/
    │   │   │   ├── file_a_good.js
    │   │   │   └── file_b_bad.js
    │   │   ├── chained/
    │   │   │   └── chained.js
    │   │   ├── class_in_function/
    │   │   │   └── class_in_function.js
    │   │   ├── classes/
    │   │   │   └── classes.js
    │   │   ├── complex_ownership/
    │   │   │   └── complex_ownership.js
    │   │   ├── exclude_modules/
    │   │   │   └── exclude_modules.js
    │   │   ├── exclude_modules_es6/
    │   │   │   └── exclude_modules_es6.js
    │   │   ├── globals/
    │   │   │   └── globals.js
    │   │   ├── inheritance/
    │   │   │   └── inheritance.js
    │   │   ├── inheritance_attr/
    │   │   │   └── inheritance_attr.js
    │   │   ├── moment/
    │   │   │   └── moment.js
    │   │   ├── scoping/
    │   │   │   └── scoping.js
    │   │   ├── simple_a_js/
    │   │   │   └── simple_a.js
    │   │   ├── simple_b_js/
    │   │   │   └── simple_b.js
    │   │   ├── ternary_new/
    │   │   │   └── ternary_new.js
    │   │   ├── two_file_imports/
    │   │   │   ├── imported.js
    │   │   │   └── importer.js
    │   │   ├── two_file_simple/
    │   │   │   ├── file_a.js
    │   │   │   ├── file_b.js
    │   │   │   └── shouldntberead
    │   │   └── weird_assignments/
    │   │       └── weird_assignments.js
    │   ├── mjs/
    │   │   └── two_file_imports_es6/
    │   │       ├── imported_es6.mjs
    │   │       └── importer_es6.mjs
    │   ├── php/
    │   │   ├── ambiguous_resolution/
    │   │   │   └── ambiguous_resolution.php
    │   │   ├── anon/
    │   │   │   └── anonymous_function.php
    │   │   ├── anon2/
    │   │   │   └── anonymous_function2.php
    │   │   ├── bad_php/
    │   │   │   ├── bad_php_a.php
    │   │   │   └── bad_php_b.php
    │   │   ├── branch/
    │   │   │   └── branch.php
    │   │   ├── chains/
    │   │   │   └── chains.php
    │   │   ├── factory/
    │   │   │   ├── currency.php
    │   │   │   └── factory.php
    │   │   ├── inheritance/
    │   │   │   └── inheritance.php
    │   │   ├── inheritance2/
    │   │   │   └── inheritance2.php
    │   │   ├── instance_methods/
    │   │   │   └── instance_methods.php
    │   │   ├── money/
    │   │   │   ├── Calculator/
    │   │   │   │   ├── BcMathCalculator.php
    │   │   │   │   └── GmpCalculator.php
    │   │   │   ├── Calculator.php
    │   │   │   ├── Converter.php
    │   │   │   ├── Currencies/
    │   │   │   │   ├── AggregateCurrencies.php
    │   │   │   │   ├── BitcoinCurrencies.php
    │   │   │   │   ├── CachedCurrencies.php
    │   │   │   │   ├── CurrencyList.php
    │   │   │   │   └── ISOCurrencies.php
    │   │   │   ├── Currencies.php
    │   │   │   ├── Currency.php
    │   │   │   ├── CurrencyPair.php
    │   │   │   ├── Exception/
    │   │   │   │   ├── FormatterException.php
    │   │   │   │   ├── InvalidArgumentException.php
    │   │   │   │   ├── ParserException.php
    │   │   │   │   ├── UnknownCurrencyException.php
    │   │   │   │   └── UnresolvableCurrencyPairException.php
    │   │   │   ├── Exception.php
    │   │   │   ├── Exchange/
    │   │   │   │   ├── ExchangerExchange.php
    │   │   │   │   ├── FixedExchange.php
    │   │   │   │   ├── IndirectExchange.php
    │   │   │   │   ├── IndirectExchangeQueuedItem.php
    │   │   │   │   ├── ReversedCurrenciesExchange.php
    │   │   │   │   └── SwapExchange.php
    │   │   │   ├── Exchange.php
    │   │   │   ├── Formatter/
    │   │   │   │   ├── AggregateMoneyFormatter.php
    │   │   │   │   ├── BitcoinMoneyFormatter.php
    │   │   │   │   ├── DecimalMoneyFormatter.php
    │   │   │   │   ├── IntlLocalizedDecimalFormatter.php
    │   │   │   │   └── IntlMoneyFormatter.php
    │   │   │   ├── Money.php
    │   │   │   ├── MoneyFactory.php
    │   │   │   ├── MoneyFormatter.php
    │   │   │   ├── MoneyParser.php
    │   │   │   ├── Number.php
    │   │   │   ├── PHPUnit/
    │   │   │   │   └── Comparator.php
    │   │   │   └── Parser/
    │   │   │       ├── AggregateMoneyParser.php
    │   │   │       ├── BitcoinMoneyParser.php
    │   │   │       ├── DecimalMoneyParser.php
    │   │   │       ├── IntlLocalizedDecimalParser.php
    │   │   │       └── IntlMoneyParser.php
    │   │   ├── namespace_a/
    │   │   │   └── namespace_a.php
    │   │   ├── namespace_b/
    │   │   │   ├── namespace_b1.php
    │   │   │   └── namespace_b2.php
    │   │   ├── namespace_c/
    │   │   │   ├── namespace_c1.php
    │   │   │   └── namespace_c2.php
    │   │   ├── nested/
    │   │   │   └── nested.php
    │   │   ├── nested_calls/
    │   │   │   └── nested_calls.php
    │   │   ├── publicprivateprotected/
    │   │   │   └── publicprivateprotected.php
    │   │   ├── resolve_correct_class/
    │   │   │   └── rcc.php
    │   │   ├── simple_a/
    │   │   │   └── simple_a.php
    │   │   ├── simple_b/
    │   │   │   └── simple_b.php
    │   │   ├── static/
    │   │   │   └── static.php
    │   │   ├── traits/
    │   │   │   └── traits.php
    │   │   ├── two_file_simple/
    │   │   │   ├── file_a.php
    │   │   │   └── file_b.php
    │   │   └── weird_assign/
    │   │       └── weird_assign.php
    │   ├── py/
    │   │   ├── ambiguous_resolution/
    │   │   │   └── ambiguous_resolution.py
    │   │   ├── async_basic/
    │   │   │   └── async_basic.py
    │   │   ├── chained/
    │   │   │   └── chained.py
    │   │   ├── exclude_modules/
    │   │   │   └── exclude_modules.py
    │   │   ├── exclude_modules_two_files/
    │   │   │   ├── exclude_modules_a.py
    │   │   │   └── exclude_modules_b.py
    │   │   ├── import_paths/
    │   │   │   ├── abra.py
    │   │   │   ├── cadabra.py
    │   │   │   └── import_paths.py
    │   │   ├── inherits/
    │   │   │   ├── inherits.py
    │   │   │   └── inherits_import.py
    │   │   ├── init/
    │   │   │   ├── init.py
    │   │   │   └── the_import.py
    │   │   ├── nested_calls/
    │   │   │   └── nested_calls.py
    │   │   ├── nested_class/
    │   │   │   └── nested_class.py
    │   │   ├── pytz/
    │   │   │   ├── __init__.py
    │   │   │   ├── exceptions.py
    │   │   │   ├── lazy.py
    │   │   │   ├── reference.py
    │   │   │   ├── tzfile.py
    │   │   │   └── tzinfo.py
    │   │   ├── resolve_correct_class/
    │   │   │   └── rcc.py
    │   │   ├── simple_a/
    │   │   │   └── simple_a.py
    │   │   ├── simple_b/
    │   │   │   └── simple_b.py
    │   │   ├── subset_find_exception/
    │   │   │   ├── two.py
    │   │   │   └── zero.py
    │   │   ├── two_file_simple/
    │   │   │   ├── file_a.py
    │   │   │   ├── file_b.py
    │   │   │   └── shouldntberead
    │   │   ├── weird_calls/
    │   │   │   └── weird_calls.py
    │   │   ├── weird_encoding/
    │   │   │   └── weird_encoding.py
    │   │   └── weird_imports/
    │   │       └── weird_imports.py
    │   └── rb/
    │       ├── ambiguous_resolution/
    │       │   └── ambiguous_resolution.rb
    │       ├── chains/
    │       │   └── chains.rb
    │       ├── doublecolon/
    │       │   └── doublecolon.rb
    │       ├── inheritance_2/
    │       │   └── inheritance_2.rb
    │       ├── instance_methods/
    │       │   └── instance_methods.rb
    │       ├── modules/
    │       │   └── modules.rb
    │       ├── nested/
    │       │   └── nested.rb
    │       ├── nested_classes/
    │       │   └── nested_classes.rb
    │       ├── onelinefile/
    │       │   └── onelinefile.rb
    │       ├── public_suffix/
    │       │   ├── public_suffix/
    │       │   │   ├── domain.rb
    │       │   │   ├── errors.rb
    │       │   │   ├── list.rb
    │       │   │   ├── rule.rb
    │       │   │   └── version.rb
    │       │   └── public_suffix.rb
    │       ├── resolve_correct_class/
    │       │   └── rcc.rb
    │       ├── simple_a/
    │       │   └── simple_a.rb
    │       ├── simple_b/
    │       │   └── simple_b.rb
    │       ├── split_modules/
    │       │   ├── split_modules_a.rb
    │       │   └── split_modules_b.rb
    │       ├── two_file_simple/
    │       │   ├── file_a.rb
    │       │   └── file_b.rb
    │       └── weird_chains/
    │           └── weird_chains.rb
    ├── test_graphs.py
    ├── test_interface.py
    └── testdata.py
Download .txt
SYMBOL INDEX (1209 symbols across 148 files)

FILE: code2flow/engine.py
  class LanguageParams (line 51) | class LanguageParams():
    method __init__ (line 55) | def __init__(self, source_type='script', ruby_version='27'):
  class SubsetParams (line 60) | class SubsetParams():
    method __init__ (line 64) | def __init__(self, target_function, upstream_depth, downstream_depth):
    method generate (line 70) | def generate(target_function, upstream_depth, downstream_depth):
  function _find_target_node (line 99) | def _find_target_node(subset_params, all_nodes):
  function _filter_nodes_for_subset (line 120) | def _filter_nodes_for_subset(subset_params, all_nodes, edges):
  function _filter_edges_for_subset (line 159) | def _filter_edges_for_subset(new_nodes, edges):
  function _filter_groups_for_subset (line 173) | def _filter_groups_for_subset(new_nodes, file_groups):
  function _filter_for_subset (line 195) | def _filter_for_subset(subset_params, all_nodes, edges, file_groups):
  function generate_json (line 211) | def generate_json(nodes, edges):
  function write_file (line 231) | def write_file(outfile, nodes, edges, groups, hide_legend=False,
  function determine_language (line 268) | def determine_language(individual_files):
  function get_sources_and_language (line 285) | def get_sources_and_language(raw_source_paths, language):
  function make_file_group (line 333) | def make_file_group(tree, filename, extension):
  function _find_link_for_call (line 366) | def _find_link_for_call(call, node_a, all_nodes):
  function _find_links (line 413) | def _find_links(node_a, all_nodes):
  function map_it (line 432) | def map_it(sources, extension, no_trimming, exclude_namespaces, exclude_...
  function _limit_namespaces (line 571) | def _limit_namespaces(file_groups, exclude_namespaces, include_only_name...
  function _limit_functions (line 613) | def _limit_functions(file_groups, exclude_functions, include_only_functi...
  function _generate_graphviz (line 639) | def _generate_graphviz(output_file, extension, final_img_filename):
  function _generate_final_img (line 658) | def _generate_final_img(output_file, extension, final_img_filename, num_...
  function code2flow (line 671) | def code2flow(raw_source_paths, output_file, language=None, hide_legend=...
  function main (line 771) | def main(sys_argv=None):

FILE: code2flow/javascript.py
  function lineno (line 10) | def lineno(el):
  function walk (line 24) | def walk(tree):
  function resolve_owner (line 46) | def resolve_owner(callee):
  function get_call_from_func_element (line 77) | def get_call_from_func_element(func):
  function make_calls (line 97) | def make_calls(body):
  function process_assign (line 116) | def process_assign(element):
  function make_local_variables (line 174) | def make_local_variables(tree, parent):
  function children (line 203) | def children(tree):
  function get_inherits (line 219) | def get_inherits(tree):
  function get_acorn_version (line 232) | def get_acorn_version():
  class Javascript (line 246) | class Javascript(BaseLanguage):
    method assert_dependencies (line 248) | def assert_dependencies():
    method get_tree (line 261) | def get_tree(filename, lang_params):
    method separate_namespaces (line 289) | def separate_namespaces(tree):
    method make_nodes (line 320) | def make_nodes(tree, parent):
    method make_root_node (line 362) | def make_root_node(lines, parent):
    method make_class_group (line 379) | def make_class_group(tree, parent):
    method file_import_tokens (line 408) | def file_import_tokens(filename):

FILE: code2flow/model.py
  class Namespace (line 12) | class Namespace(dict):
    method __init__ (line 17) | def __init__(self, *args, **kwargs):
    method __getattr__ (line 22) | def __getattr__(self, item):
  function is_installed (line 30) | def is_installed(executable_cmd):
  function djoin (line 45) | def djoin(*tup):
  function flatten (line 55) | def flatten(list_of_lists):
  function _resolve_str_variable (line 64) | def _resolve_str_variable(variable, file_groups):
  class BaseLanguage (line 86) | class BaseLanguage(abc.ABC):
    method assert_dependencies (line 98) | def assert_dependencies():
    method get_tree (line 105) | def get_tree(filename, lang_params):
    method separate_namespaces (line 113) | def separate_namespaces(tree):
    method make_nodes (line 121) | def make_nodes(tree, parent):
    method make_root_node (line 130) | def make_root_node(lines, parent):
    method make_class_group (line 139) | def make_class_group(tree, parent):
  class Variable (line 147) | class Variable():
    method __init__ (line 153) | def __init__(self, token, points_to, line_number=None):
    method __repr__ (line 165) | def __repr__(self):
    method to_string (line 168) | def to_string(self):
  class Call (line 178) | class Call():
    method __init__ (line 187) | def __init__(self, token, line_number=None, owner_token=None, definite...
    method __repr__ (line 193) | def __repr__(self):
    method to_string (line 196) | def to_string(self):
    method is_attr (line 206) | def is_attr(self):
    method matches_variable (line 213) | def matches_variable(self, variable):
  class Node (line 262) | class Node():
    method __init__ (line 263) | def __init__(self, token, calls, variables, parent, import_tokens=None,
    method __repr__ (line 279) | def __repr__(self):
    method __lt__ (line 282) | def __lt__(self, other):
    method name (line 285) | def name(self):
    method first_group (line 292) | def first_group(self):
    method file_group (line 302) | def file_group(self):
    method is_attr (line 312) | def is_attr(self):
    method token_with_ownership (line 321) | def token_with_ownership(self):
    method namespace_ownership (line 330) | def namespace_ownership(self):
    method label (line 342) | def label(self):
    method remove_from_parent (line 351) | def remove_from_parent(self):
    method get_variables (line 358) | def get_variables(self, line_number=None):
    method resolve_variables (line 378) | def resolve_variables(self, file_groups):
    method to_dot (line 404) | def to_dot(self):
    method to_dict (line 427) | def to_dict(self):
  function _wrap_as_variables (line 439) | def _wrap_as_variables(sequence):
  class Edge (line 450) | class Edge():
    method __init__ (line 451) | def __init__(self, node0, node1):
    method __repr__ (line 460) | def __repr__(self):
    method __lt__ (line 463) | def __lt__(self, other):
    method to_dot (line 468) | def to_dot(self):
    method to_dict (line 479) | def to_dict(self):
  class Group (line 490) | class Group():
    method __init__ (line 494) | def __init__(self, token, group_type, display_type, import_tokens=None,
    method __repr__ (line 510) | def __repr__(self):
    method __lt__ (line 513) | def __lt__(self, other):
    method label (line 516) | def label(self):
    method filename (line 523) | def filename(self):
    method add_subgroup (line 532) | def add_subgroup(self, sg):
    method add_node (line 539) | def add_node(self, node, is_root=False):
    method all_nodes (line 549) | def all_nodes(self):
    method get_constructor (line 559) | def get_constructor(self):
    method all_groups (line 571) | def all_groups(self):
    method get_variables (line 581) | def get_variables(self, line_number=None):
    method remove_from_parent (line 601) | def remove_from_parent(self):
    method all_parents (line 609) | def all_parents(self):
    method to_dot (line 618) | def to_dot(self):

FILE: code2flow/php.py
  function lineno (line 9) | def lineno(tree):
  function get_name (line 18) | def get_name(tree, from_='name'):
  function get_call_from_expr (line 37) | def get_call_from_expr(func_expr):
  function walk (line 81) | def walk(tree):
  function children (line 111) | def children(tree):
  function make_calls (line 131) | def make_calls(body_el):
  function process_assign (line 147) | def process_assign(assignment_el):
  function make_local_variables (line 167) | def make_local_variables(tree_el, parent):
  function get_inherits (line 195) | def get_inherits(tree):
  function run_ast_parser (line 214) | def run_ast_parser(filename):
  class PHP (line 229) | class PHP(BaseLanguage):
    method assert_dependencies (line 231) | def assert_dependencies():
    method get_tree (line 244) | def get_tree(filename, lang_params):
    method separate_namespaces (line 267) | def separate_namespaces(tree):
    method make_nodes (line 299) | def make_nodes(tree, parent):
    method make_root_node (line 337) | def make_root_node(lines, parent):
    method make_class_group (line 355) | def make_class_group(tree, parent):
    method file_import_tokens (line 402) | def file_import_tokens(filename):

FILE: code2flow/python.py
  function get_call_from_func_element (line 9) | def get_call_from_func_element(func):
  function make_calls (line 41) | def make_calls(lines):
  function process_assign (line 60) | def process_assign(element):
  function process_import (line 85) | def process_import(element):
  function make_local_variables (line 107) | def make_local_variables(lines, parent):
  function get_inherits (line 132) | def get_inherits(tree):
  class Python (line 143) | class Python(BaseLanguage):
    method assert_dependencies (line 145) | def assert_dependencies():
    method get_tree (line 149) | def get_tree(filename, _):
    method separate_namespaces (line 165) | def separate_namespaces(tree):
    method make_nodes (line 194) | def make_nodes(tree, parent):
    method make_root_node (line 219) | def make_root_node(lines, parent):
    method make_class_group (line 235) | def make_class_group(tree, parent):
    method file_import_tokens (line 268) | def file_import_tokens(filename):

FILE: code2flow/ruby.py
  function resolve_owner (line 8) | def resolve_owner(owner_el):
  function get_call_from_send_el (line 39) | def get_call_from_send_el(func_el):
  function walk (line 58) | def walk(tree_el):
  function make_calls (line 75) | def make_calls(body_el):
  function process_assign (line 89) | def process_assign(assignment_el):
  function make_local_variables (line 108) | def make_local_variables(tree_el, parent):
  function as_lines (line 134) | def as_lines(tree_el):
  function get_tree_body (line 151) | def get_tree_body(tree_el):
  function get_inherits (line 168) | def get_inherits(tree, body_tree):
  class Ruby (line 194) | class Ruby(BaseLanguage):
    method assert_dependencies (line 196) | def assert_dependencies():
    method get_tree (line 204) | def get_tree(filename, lang_params):
    method separate_namespaces (line 230) | def separate_namespaces(tree):
    method make_nodes (line 254) | def make_nodes(tree, parent):
    method make_root_node (line 285) | def make_root_node(lines, parent):
    method make_class_group (line 301) | def make_class_group(tree, parent):
    method file_import_tokens (line 338) | def file_import_tokens(filename):

FILE: tests/test_code/js/ambiguous_names/ambiguous_names.js
  class Abra (line 1) | class Abra {
    method constructor (line 2) | constructor() {
    method magic (line 5) | magic() {
    method abra_it (line 9) | abra_it() {
  class Cadabra (line 15) | class Cadabra {
    method magic (line 16) | magic() {
    method cadabra_it (line 20) | cadabra_it(a) {
  function main (line 26) | function main(cls) {

FILE: tests/test_code/js/bad_parse/file_a_good.js
  function a (line 1) | function a() {}

FILE: tests/test_code/js/chained/chained.js
  class Chain (line 1) | class Chain {
    method constructor (line 2) | constructor(val) {
    method add (line 6) | add(b) {
    method sub (line 11) | sub(b) {
    method mul (line 16) | mul(b) {

FILE: tests/test_code/js/class_in_function/class_in_function.js
  function rectangleClassFactory (line 1) | function rectangleClassFactory() {

FILE: tests/test_code/js/classes/classes.js
  function print_hi (line 1) | function print_hi() {
  class Rectangle (line 5) | class Rectangle {
    method constructor (line 6) | constructor(height, width) {
    method calcArea (line 13) | calcArea() {
    method incr (line 17) | incr() {
  function do_calc (line 22) | function do_calc() {

FILE: tests/test_code/js/complex_ownership/complex_ownership.js
  class ABC (line 1) | class ABC {
    method constructor (line 2) | constructor() {
    method doit (line 5) | doit() {
    method apply (line 9) | apply() {
    method ret_def (line 12) | ret_def() {
  class DEF (line 17) | class DEF {
    method toABC (line 18) | toABC() {
  class GHI (line 24) | class GHI {
    method doit2 (line 25) | doit2(varname) {
    method doit3 (line 28) | doit3() {

FILE: tests/test_code/js/exclude_modules/exclude_modules.js
  function readFileSync (line 6) | function readFileSync() {
  function beta (line 11) | function beta() {
  function alpha (line 20) | function alpha() {

FILE: tests/test_code/js/exclude_modules_es6/exclude_modules_es6.js
  function readFileSync (line 6) | function readFileSync() {
  function beta (line 11) | function beta() {
  function alpha (line 20) | function alpha() {

FILE: tests/test_code/js/globals/globals.js
  function a (line 2) | function a() {
  function b (line 13) | function b() {

FILE: tests/test_code/js/inheritance/inheritance.js
  function majorNum (line 3) | function majorNum() {}
  function pentaNum (line 5) | function pentaNum() {}
  class MajorScales (line 7) | class MajorScales {
    method majorNum (line 8) | majorNum() {
  class FakeMajorScales (line 14) | class FakeMajorScales {
    method majorNum (line 15) | majorNum() {
  class ScaleDemo (line 20) | class ScaleDemo extends MajorScales {
    method constructor (line 21) | constructor() {

FILE: tests/test_code/js/inheritance_attr/inheritance_attr.js
  class ClsA (line 2) | class ClsA {
    method bark (line 3) | bark() {
  class ClsB (line 7) | class ClsB {
    method meow (line 8) | meow() {
  class ClsC (line 16) | class ClsC extends ClsA.B {}

FILE: tests/test_code/js/moment/moment.js
  function hooks (line 15) | function hooks() {
  function setHookCallback (line 21) | function setHookCallback(callback) {
  function isArray (line 25) | function isArray(input) {
  function isObject (line 32) | function isObject(input) {
  function hasOwnProp (line 41) | function hasOwnProp(a, b) {
  function isObjectEmpty (line 45) | function isObjectEmpty(obj) {
  function isUndefined (line 59) | function isUndefined(input) {
  function isNumber (line 63) | function isNumber(input) {
  function isDate (line 70) | function isDate(input) {
  function map (line 77) | function map(arr, fn) {
  function extend (line 86) | function extend(a, b) {
  function createUTC (line 104) | function createUTC(input, format, locale, strict) {
  function defaultParsingFlags (line 108) | function defaultParsingFlags() {
  function getParsingFlags (line 130) | function getParsingFlags(m) {
  function isValid (line 156) | function isValid(m) {
  function createInvalid (line 192) | function createInvalid(flags) {
  function copyConfig (line 208) | function copyConfig(to, from) {
  function Moment (line 256) | function Moment(config) {
  function isMoment (line 271) | function isMoment(obj) {
  function warn (line 277) | function warn(msg) {
  function deprecate (line 287) | function deprecate(msg, fn) {
  function deprecateSimple (line 329) | function deprecateSimple(name, msg) {
  function isFunction (line 342) | function isFunction(input) {
  function set (line 349) | function set(config) {
  function mergeConfigs (line 372) | function mergeConfigs(parentConfig, childConfig) {
  function Locale (line 401) | function Locale(config) {
  function calendar (line 433) | function calendar(key, mom, now) {
  function zeroFill (line 438) | function zeroFill(number, targetLength, forceSign) {
  function addFormatToken (line 458) | function addFormatToken(token, padded, ordinal, callback) {
  function removeFormattingTokens (line 483) | function removeFormattingTokens(input) {
  function makeFormatFunction (line 490) | function makeFormatFunction(format) {
  function formatMoment (line 516) | function formatMoment(m, format) {
  function expandFormat (line 528) | function expandFormat(format, locale) {
  function longDateFormat (line 557) | function longDateFormat(key) {
  function invalidDate (line 585) | function invalidDate() {
  function ordinal (line 592) | function ordinal(number) {
  function relativeTime (line 615) | function relativeTime(number, withoutSuffix, string, isFuture) {
  function pastFuture (line 622) | function pastFuture(diff, output) {
  function addUnitAlias (line 629) | function addUnitAlias(unit, shorthand) {
  function normalizeUnits (line 634) | function normalizeUnits(units) {
  function normalizeObjectUnits (line 640) | function normalizeObjectUnits(inputObject) {
  function addUnitPriority (line 659) | function addUnitPriority(unit, priority) {
  function getPrioritizedUnits (line 663) | function getPrioritizedUnits(unitsObj) {
  function isLeapYear (line 677) | function isLeapYear(year) {
  function absFloor (line 681) | function absFloor(number) {
  function toInt (line 690) | function toInt(argumentForCoercion) {
  function makeGetSet (line 701) | function makeGetSet(unit, keepTime) {
  function get (line 713) | function get(mom, unit) {
  function set$1 (line 719) | function set$1(mom, unit, value) {
  function stringGet (line 741) | function stringGet(units) {
  function stringSet (line 749) | function stringSet(units, value) {
  function addRegexToken (line 789) | function addRegexToken(token, regex, strictRegex) {
  function getParseRegexForToken (line 797) | function getParseRegexForToken(token, config) {
  function unescapeFormat (line 806) | function unescapeFormat(s) {
  function regexEscape (line 822) | function regexEscape(s) {
  function addParseToken (line 828) | function addParseToken(token, callback) {
  function addWeekParseToken (line 844) | function addWeekParseToken(token, callback) {
  function addTimeToArrayFromToken (line 851) | function addTimeToArrayFromToken(token, input, config) {
  function mod (line 867) | function mod(n, x) {
  function daysInMonth (line 888) | function daysInMonth(year, month) {
  function localeMonths (line 960) | function localeMonths(m, format) {
  function localeMonthsShort (line 975) | function localeMonthsShort(m, format) {
  function handleStrictParse (line 988) | function handleStrictParse(monthName, format, strict) {
  function localeMonthsParse (line 1035) | function localeMonthsParse(monthName, format, strict) {
  function setMonth (line 1090) | function setMonth(mom, value) {
  function getSetMonth (line 1115) | function getSetMonth(value) {
  function getDaysInMonth (line 1125) | function getDaysInMonth() {
  function monthsShortRegex (line 1129) | function monthsShortRegex(isStrict) {
  function monthsRegex (line 1149) | function monthsRegex(isStrict) {
  function computeMonthsParse (line 1169) | function computeMonthsParse() {
  function daysInYear (line 1257) | function daysInYear(year) {
  function getIsLeapYear (line 1271) | function getIsLeapYear() {
  function createDate (line 1275) | function createDate(y, m, d, h, M, s, ms) {
  function createUTCDate (line 1293) | function createUTCDate(y) {
  function firstWeekOffset (line 1312) | function firstWeekOffset(year, dow, doy) {
  function dayOfYearFromWeeks (line 1322) | function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
  function weekOfYear (line 1346) | function weekOfYear(mom, dow, doy) {
  function weeksInYear (line 1369) | function weeksInYear(year, dow, doy) {
  function localeWeek (line 1410) | function localeWeek(mom) {
  function localeFirstDayOfWeek (line 1419) | function localeFirstDayOfWeek() {
  function localeFirstDayOfYear (line 1423) | function localeFirstDayOfYear() {
  function getSetWeek (line 1429) | function getSetWeek(input) {
  function getSetISOWeek (line 1434) | function getSetISOWeek(input) {
  function parseWeekday (line 1500) | function parseWeekday(input, locale) {
  function parseIsoWeekday (line 1517) | function parseIsoWeekday(input, locale) {
  function shiftWeekdays (line 1525) | function shiftWeekdays(ws, n) {
  function localeWeekdays (line 1538) | function localeWeekdays(m, format) {
  function localeWeekdaysShort (line 1553) | function localeWeekdaysShort(m) {
  function localeWeekdaysMin (line 1561) | function localeWeekdaysMin(m) {
  function handleStrictParse$1 (line 1569) | function handleStrictParse$1(weekdayName, format, strict) {
  function localeWeekdaysParse (line 1642) | function localeWeekdaysParse(weekdayName, format, strict) {
  function getSetDayOfWeek (line 1711) | function getSetDayOfWeek(input) {
  function getSetLocaleDayOfWeek (line 1724) | function getSetLocaleDayOfWeek(input) {
  function getSetISODayOfWeek (line 1732) | function getSetISODayOfWeek(input) {
  function weekdaysRegex (line 1749) | function weekdaysRegex(isStrict) {
  function weekdaysShortRegex (line 1769) | function weekdaysShortRegex(isStrict) {
  function weekdaysMinRegex (line 1789) | function weekdaysMinRegex(isStrict) {
  function computeWeekdaysParse (line 1809) | function computeWeekdaysParse() {
  function hFormat (line 1863) | function hFormat() {
  function kFormat (line 1867) | function kFormat() {
  function meridiem (line 1901) | function meridiem(token, lowercase) {
  function matchMeridiem (line 1923) | function matchMeridiem(isStrict, locale) {
  function localeIsPM (line 1983) | function localeIsPM(input) {
  function localeMeridiem (line 1996) | function localeMeridiem(hours, minutes, isLower) {
  function commonPrefix (line 2029) | function commonPrefix(arr1, arr2) {
  function normalizeLocale (line 2040) | function normalizeLocale(key) {
  function chooseLocale (line 2047) | function chooseLocale(names) {
  function loadLocale (line 2079) | function loadLocale(name) {
  function getSetGlobalLocale (line 2106) | function getSetGlobalLocale(key, values) {
  function defineLocale (line 2131) | function defineLocale(name, config) {
  function updateLocale (line 2185) | function updateLocale(name, config) {
  function getLocale (line 2231) | function getLocale(key) {
  function listLocales (line 2254) | function listLocales() {
  function checkOverflow (line 2258) | function checkOverflow(m) {
  function configFromISO (line 2351) | function configFromISO(config) {
  function extractFromRFC2822Strings (line 2407) | function extractFromRFC2822Strings(
  function untruncateYear (line 2430) | function untruncateYear(yearStr) {
  function preprocessRFC2822 (line 2440) | function preprocessRFC2822(s) {
  function checkWeekday (line 2449) | function checkWeekday(weekdayStr, parsedInput, config) {
  function calculateOffset (line 2467) | function calculateOffset(obsOffset, militaryOffset, numOffset) {
  function configFromRFC2822 (line 2482) | function configFromRFC2822(config) {
  function configFromString (line 2511) | function configFromString(config) {
  function defaults (line 2550) | function defaults(a, b, c) {
  function currentDateArray (line 2560) | function currentDateArray(config) {
  function configFromArray (line 2577) | function configFromArray(config) {
  function dayOfYearFromWeekInfo (line 2666) | function dayOfYearFromWeekInfo(config) {
  function configFromStringAndFormat (line 2734) | function configFromStringAndFormat(config) {
  function meridiemFixWrap (line 2823) | function meridiemFixWrap(locale, hour, meridiem) {
  function configFromStringAndArray (line 2849) | function configFromStringAndArray(config) {
  function configFromObject (line 2909) | function configFromObject(config) {
  function createFromConfig (line 2926) | function createFromConfig(config) {
  function prepareConfig (line 2937) | function prepareConfig(config) {
  function configFromInput (line 2970) | function configFromInput(config) {
  function createLocalOrUTC (line 2993) | function createLocalOrUTC(input, format, locale, strict, isUTC) {
  function createLocal (line 3024) | function createLocal(input, format, locale, strict) {
  function pickBy (line 3056) | function pickBy(fn, moments) {
  function min (line 3074) | function min() {
  function max (line 3080) | function max() {
  function isDurationValid (line 3102) | function isDurationValid(m) {
  function isValid$1 (line 3132) | function isValid$1() {
  function createInvalid$1 (line 3136) | function createInvalid$1() {
  function Duration (line 3140) | function Duration(duration) {
  function isDuration (line 3175) | function isDuration(obj) {
  function absRound (line 3179) | function absRound(number) {
  function compareArrays (line 3188) | function compareArrays(array1, array2, dontConvert) {
  function offset (line 3206) | function offset(token, separator) {
  function offsetFromString (line 3242) | function offsetFromString(matcher, string) {
  function cloneWithOffset (line 3260) | function cloneWithOffset(input, model) {
  function getDateOffset (line 3277) | function getDateOffset(m) {
  function getSetOffset (line 3301) | function getSetOffset(input, keepLocalTime, keepMinutes) {
  function getSetZone (line 3344) | function getSetZone(input, keepLocalTime) {
  function setOffsetToUTC (line 3358) | function setOffsetToUTC(keepLocalTime) {
  function setOffsetToLocal (line 3362) | function setOffsetToLocal(keepLocalTime) {
  function setOffsetToParsedOffset (line 3374) | function setOffsetToParsedOffset() {
  function hasAlignedHourOffset (line 3388) | function hasAlignedHourOffset(input) {
  function isDaylightSavingTime (line 3397) | function isDaylightSavingTime() {
  function isDaylightSavingTimeShifted (line 3404) | function isDaylightSavingTimeShifted() {
  function isLocal (line 3426) | function isLocal() {
  function isUtcOffset (line 3430) | function isUtcOffset() {
  function isUtc (line 3434) | function isUtc() {
  function createDuration (line 3445) | function createDuration(input, key) {
  function parseIso (line 3520) | function parseIso(inp, sign) {
  function positiveMomentsDifference (line 3529) | function positiveMomentsDifference(base, other) {
  function momentsDifference (line 3543) | function momentsDifference(base, other) {
  function createAdder (line 3562) | function createAdder(direction, name) {
  function addSubtract (line 3587) | function addSubtract(mom, duration, isAdding, updateOffset) {
  function isString (line 3616) | function isString(input) {
  function isMomentInput (line 3621) | function isMomentInput(input) {
  function isMomentInputObject (line 3634) | function isMomentInputObject(input) {
  function isNumberOrStringArray (line 3674) | function isNumberOrStringArray(input) {
  function isCalendarSpec (line 3686) | function isCalendarSpec(input) {
  function getCalendarFormat (line 3708) | function getCalendarFormat(myMoment, now) {
  function calendar$1 (line 3725) | function calendar$1(time, formats) {
  function clone (line 3755) | function clone() {
  function isAfter (line 3759) | function isAfter(input, units) {
  function isBefore (line 3772) | function isBefore(input, units) {
  function isBetween (line 3785) | function isBetween(from, to, units, inclusivity) {
  function isSame (line 3802) | function isSame(input, units) {
  function isSameOrAfter (line 3820) | function isSameOrAfter(input, units) {
  function isSameOrBefore (line 3824) | function isSameOrBefore(input, units) {
  function diff (line 3828) | function diff(input, units, asFloat) {
  function monthDiff (line 3877) | function monthDiff(a, b) {
  function toString (line 3907) | function toString() {
  function toISOString (line 3911) | function toISOString(keepOffset) {
  function inspect (line 3947) | function inspect() {
  function format (line 3969) | function format(inputString) {
  function from (line 3979) | function from(time, withoutSuffix) {
  function fromNow (line 3992) | function fromNow(withoutSuffix) {
  function to (line 3996) | function to(time, withoutSuffix) {
  function toNow (line 4009) | function toNow(withoutSuffix) {
  function locale (line 4016) | function locale(key) {
  function localeData (line 4041) | function localeData() {
  function mod$1 (line 4051) | function mod$1(dividend, divisor) {
  function localStartOfDate (line 4055) | function localStartOfDate(y, m, d) {
  function utcStartOfDate (line 4065) | function utcStartOfDate(y, m, d) {
  function startOf (line 4075) | function startOf(units) {
  function endOf (line 4138) | function endOf(units) {
  function valueOf (line 4207) | function valueOf() {
  function unix (line 4211) | function unix() {
  function toDate (line 4215) | function toDate() {
  function toArray (line 4219) | function toArray() {
  function toObject (line 4232) | function toObject() {
  function toJSON (line 4245) | function toJSON() {
  function isValid$2 (line 4250) | function isValid$2() {
  function parsingFlags (line 4254) | function parsingFlags() {
  function invalidAt (line 4258) | function invalidAt() {
  function creationData (line 4262) | function creationData() {
  function localeEras (line 4323) | function localeEras(m, format) {
  function localeErasParse (line 4351) | function localeErasParse(eraName, format, strict) {
  function localeErasConvertYear (line 4393) | function localeErasConvertYear(era, year) {
  function getEraName (line 4402) | function getEraName() {
  function getEraNarrow (line 4422) | function getEraNarrow() {
  function getEraAbbr (line 4442) | function getEraAbbr() {
  function getEraYear (line 4462) | function getEraYear() {
  function erasNameRegex (line 4488) | function erasNameRegex(isStrict) {
  function erasAbbrRegex (line 4495) | function erasAbbrRegex(isStrict) {
  function erasNarrowRegex (line 4502) | function erasNarrowRegex(isStrict) {
  function matchEraAbbr (line 4509) | function matchEraAbbr(isStrict, locale) {
  function matchEraName (line 4513) | function matchEraName(isStrict, locale) {
  function matchEraNarrow (line 4517) | function matchEraNarrow(isStrict, locale) {
  function matchEraYearOrdinal (line 4521) | function matchEraYearOrdinal(isStrict, locale) {
  function computeErasParse (line 4525) | function computeErasParse() {
  function addWeekYearFormatToken (line 4563) | function addWeekYearFormatToken(token, getter) {
  function getSetWeekYear (line 4608) | function getSetWeekYear(input) {
  function getSetISOWeekYear (line 4619) | function getSetISOWeekYear(input) {
  function getISOWeeksInYear (line 4630) | function getISOWeeksInYear() {
  function getISOWeeksInISOWeekYear (line 4634) | function getISOWeeksInISOWeekYear() {
  function getWeeksInYear (line 4638) | function getWeeksInYear() {
  function getWeeksInWeekYear (line 4643) | function getWeeksInWeekYear() {
  function getSetWeekYearHelper (line 4648) | function getSetWeekYearHelper(input, week, weekday, dow, doy) {
  function setWeekAll (line 4661) | function setWeekAll(weekYear, week, weekday, dow, doy) {
  function getSetQuarter (line 4692) | function getSetQuarter(input) {
  function getSetDayOfYear (line 4752) | function getSetDayOfYear(input) {
  function parseMs (line 4853) | function parseMs(input, array) {
  function getZoneAbbr (line 4870) | function getZoneAbbr() {
  function getZoneName (line 4874) | function getZoneName() {
  function createUnix (line 4982) | function createUnix(input) {
  function createInZone (line 4986) | function createInZone() {
  function preParsePostFormat (line 4990) | function preParsePostFormat(string) {
  function get$1 (line 5033) | function get$1(format, index, field, setter) {
  function listMonthsImpl (line 5039) | function listMonthsImpl(format, index, field) {
  function listWeekdaysImpl (line 5067) | function listWeekdaysImpl(localeSorted, format, index, field) {
  function listMonths (line 5103) | function listMonths(format, index) {
  function listMonthsShort (line 5107) | function listMonthsShort(format, index) {
  function listWeekdays (line 5111) | function listWeekdays(localeSorted, format, index) {
  function listWeekdaysShort (line 5115) | function listWeekdaysShort(localeSorted, format, index) {
  function listWeekdaysMin (line 5119) | function listWeekdaysMin(localeSorted, format, index) {
  function abs (line 5172) | function abs() {
  function addSubtract$1 (line 5189) | function addSubtract$1(duration, input, value, direction) {
  function add$1 (line 5200) | function add$1(input, value) {
  function subtract$1 (line 5205) | function subtract$1(input, value) {
  function absCeil (line 5209) | function absCeil(number) {
  function bubble (line 5217) | function bubble() {
  function daysToMonths (line 5272) | function daysToMonths(days) {
  function monthsToDays (line 5278) | function monthsToDays(months) {
  function as (line 5283) | function as(units) {
  function valueOf$1 (line 5328) | function valueOf$1() {
  function makeAs (line 5340) | function makeAs(alias) {
  function clone$1 (line 5356) | function clone$1() {
  function get$2 (line 5360) | function get$2(units) {
  function makeGetter (line 5365) | function makeGetter(name) {
  function weeks (line 5379) | function weeks() {
  function substituteTimeAgo (line 5395) | function substituteTimeAgo(string, number, withoutSuffix, isFuture, loca...
  function relativeTime$1 (line 5399) | function relativeTime$1(posNegDuration, withoutSuffix, thresholds, local...
  function getSetRelativeTimeRounding (line 5436) | function getSetRelativeTimeRounding(roundingFunction) {
  function getSetRelativeTimeThreshold (line 5448) | function getSetRelativeTimeThreshold(threshold, limit) {
  function humanize (line 5462) | function humanize(argWithSuffix, argThresholds) {
  function sign (line 5498) | function sign(x) {
  function toISOString$1 (line 5502) | function toISOString$1() {

FILE: tests/test_code/js/scoping/scoping.js
  function scope_confusion (line 1) | function scope_confusion() {
  class MyClass (line 5) | class MyClass {
    method scope_confusion (line 6) | scope_confusion() {
    method a (line 10) | a() {
    method b (line 14) | b() {

FILE: tests/test_code/js/simple_a_js/simple_a.js
  function func_b (line 1) | function func_b() {}
  function func_a (line 3) | function func_a() {

FILE: tests/test_code/js/simple_b_js/simple_b.js
  function a (line 3) | function a() {
  function b (line 8) | function b() {
  class C (line 13) | class C {
    method d (line 14) | d(param) {

FILE: tests/test_code/js/ternary_new/ternary_new.js
  class Abra (line 1) | class Abra {
    method init (line 2) | init() {
  class Cadabra (line 7) | class Cadabra {
    method init (line 8) | init() {
  function abra_fact (line 13) | function abra_fact() {
  function cadabra_fact (line 17) | function cadabra_fact() {
  class ClassMap (line 22) | class ClassMap {
    method fact (line 23) | fact(which) {

FILE: tests/test_code/js/two_file_imports/imported.js
  class myClass (line 1) | class myClass {
    method constructor (line 2) | constructor() {
    method doit (line 6) | get doit() {
    method doit2 (line 10) | doit2() {
  function inner (line 15) | function inner() {
  function bye (line 19) | function bye() {

FILE: tests/test_code/js/two_file_imports/importer.js
  function outer (line 3) | function outer() {

FILE: tests/test_code/js/two_file_simple/file_a.js
  function a (line 1) | function a() {

FILE: tests/test_code/js/two_file_simple/file_b.js
  function b (line 1) | function b() {
  function c (line 6) | function c() {}

FILE: tests/test_code/js/weird_assignments/weird_assignments.js
  function get_ab (line 1) | function get_ab() {

FILE: tests/test_code/mjs/two_file_imports_es6/imported_es6.mjs
  class myClass (line 1) | class myClass {
    method constructor (line 2) | constructor() {
    method doit (line 6) | doit() {
    method doit2 (line 10) | doit2() {
  function inner (line 15) | function inner() {

FILE: tests/test_code/mjs/two_file_imports_es6/importer_es6.mjs
  function outer (line 3) | function outer() {

FILE: tests/test_code/php/ambiguous_resolution/ambiguous_resolution.php
  class Abra (line 3) | class Abra {
    method magic (line 4) | function magic() {
    method abra_it (line 7) | function abra_it() {
  class Cadabra (line 13) | class Cadabra {
    method magic (line 14) | function magic() {
    method cadabra_it (line 18) | function cadabra_it($a) {
  function main (line 26) | function main($cls) {

FILE: tests/test_code/php/anon/anonymous_function.php
  function a (line 17) | function a() {}

FILE: tests/test_code/php/anon2/anonymous_function2.php
  function func_a (line 4) | function func_a($name) {

FILE: tests/test_code/php/branch/branch.php
  function a (line 2) | function a() {

FILE: tests/test_code/php/chains/chains.php
  function a (line 2) | function a() {
  function b (line 8) | function b() {
  class Cls (line 14) | class Cls {
    method __construct (line 15) | function __construct() {
    method a (line 21) | function a() {
    method b (line 26) | function b() {
  function c (line 33) | function c() {

FILE: tests/test_code/php/factory/currency.php
  class Currency (line 14) | final class Currency implements JsonSerializable
    method __construct (line 24) | public function __construct(string $code)
    method getCode (line 35) | public function getCode(): string
    method equals (line 43) | public function equals(Currency $other): bool
    method __toString (line 48) | public function __toString(): string
    method jsonSerialize (line 58) | public function jsonSerialize()
  type Currencies (line 68) | interface Currencies extends IteratorAggregate
    method contains (line 73) | public function contains(Currency $currency): bool;
    method subunitFor (line 80) | public function subunitFor(Currency $currency): int;
    method getIterator (line 85) | public function getIterator(): Traversable;

FILE: tests/test_code/php/inheritance/inheritance.php
  class FakeFruit (line 3) | class FakeFruit {
    method __construct (line 6) | public function __construct($name, $color) {
    method __construct (line 10) | public function __construct() {
    method intro (line 14) | public function intro() {
  class Fruit (line 20) | class Fruit {
    method __construct (line 23) | public function __construct($name, $color) {
    method intro (line 27) | public function intro() {
    method getColor (line 30) | public function getColor() {
  class PhantomFruit (line 35) | class PhantomFruit {
    method __construct (line 38) | public function __construct($name, $color) {
    method __construct (line 42) | public function __construct() {
    method intro (line 46) | public function intro() {
  class Strawberry (line 52) | class Strawberry extends Fruit {
    method message (line 53) | public function message() {

FILE: tests/test_code/php/inheritance2/inheritance2.php
  class Car (line 5) | abstract class Car {
    method __construct (line 7) | public function __construct($name) {
    method intro (line 10) | abstract public function intro() : string;
  type Noisy (line 14) | interface Noisy {
    method makeSound (line 15) | public function makeSound();
  class Audi (line 19) | class Audi extends Car implements Noisy {
    method intro (line 20) | public function intro() : string {
    method makeSound (line 24) | public function makeSound(): string {
  class Volvo (line 29) | class Volvo extends Car implements Noisy {
    method intro (line 30) | public function intro() : string {
    method makeSound (line 33) | public function makeSound(): string {
  class Citroen (line 38) | class Citroen extends Car implements Noisy {
    method intro (line 39) | public function intro() : string {

FILE: tests/test_code/php/instance_methods/instance_methods.php
  function main (line 3) | function main() {
  class Abra (line 7) | class Abra {
    method main (line 8) | function main() {
    method main2 (line 17) | function main2() {

FILE: tests/test_code/php/money/Calculator.php
  type Calculator (line 15) | interface Calculator
    method compare (line 29) | public static function compare(string $a, string $b): int;
    method add (line 41) | public static function add(string $amount, string $addend): string;
    method subtract (line 53) | public static function subtract(string $amount, string $subtrahend): s...
    method multiply (line 65) | public static function multiply(string $amount, string $multiplier): s...
    method divide (line 79) | public static function divide(string $amount, string $divisor): string;
    method ceil (line 90) | public static function ceil(string $number): string;
    method floor (line 101) | public static function floor(string $number): string;
    method absolute (line 112) | public static function absolute(string $number): string;
    method round (line 124) | public static function round(string $number, int $roundingMode): string;
    method share (line 137) | public static function share(string $amount, string $ratio, string $to...
    method mod (line 151) | public static function mod(string $amount, string $divisor): string;

FILE: tests/test_code/php/money/Calculator/BcMathCalculator.php
  class BcMathCalculator (line 21) | final class BcMathCalculator implements Calculator
    method compare (line 26) | public static function compare(string $a, string $b): int
    method add (line 32) | public static function add(string $amount, string $addend): string
    method subtract (line 38) | public static function subtract(string $amount, string $subtrahend): s...
    method multiply (line 44) | public static function multiply(string $amount, string $multiplier): s...
    method divide (line 50) | public static function divide(string $amount, string $divisor): string
    method ceil (line 60) | public static function ceil(string $number): string
    method floor (line 76) | public static function floor(string $number): string
    method absolute (line 96) | public static function absolute(string $number): string
    method round (line 108) | public static function round(string $number, int $roundingMode): string
    method roundDigit (line 192) | private static function roundDigit(Number $number): string
    method share (line 206) | public static function share(string $amount, string $ratio, string $to...
    method mod (line 212) | public static function mod(string $amount, string $divisor): string

FILE: tests/test_code/php/money/Calculator/GmpCalculator.php
  class GmpCalculator (line 39) | final class GmpCalculator implements Calculator
    method compare (line 44) | public static function compare(string $a, string $b): int
    method add (line 65) | public static function add(string $amount, string $addend): string
    method subtract (line 71) | public static function subtract(string $amount, string $subtrahend): s...
    method multiply (line 77) | public static function multiply(string $amount, string $multiplier): s...
    method divide (line 126) | public static function divide(string $amount, string $divisor): string
    method ceil (line 177) | public static function ceil(string $number): string
    method floor (line 193) | public static function floor(string $number): string
    method absolute (line 213) | public static function absolute(string $number): string
    method round (line 225) | public static function round(string $number, int $roundingMode): string
    method roundDigit (line 306) | private static function roundDigit(Number $number): string
    method share (line 319) | public static function share(string $amount, string $ratio, string $to...
    method mod (line 325) | public static function mod(string $amount, string $divisor): string

FILE: tests/test_code/php/money/Converter.php
  class Converter (line 14) | final class Converter
    method __construct (line 20) | public function __construct(Currencies $currencies, Exchange $exchange)
    method convert (line 26) | public function convert(Money $money, Currency $counterCurrency, int $...
    method convertAndReturnWithCurrencyPair (line 39) | public function convertAndReturnWithCurrencyPair(Money $money, Currenc...
    method convertAgainstCurrencyPair (line 49) | public function convertAgainstCurrencyPair(Money $money, CurrencyPair ...

FILE: tests/test_code/php/money/Currencies.php
  type Currencies (line 14) | interface Currencies extends IteratorAggregate
    method contains (line 19) | public function contains(Currency $currency): bool;
    method subunitFor (line 26) | public function subunitFor(Currency $currency): int;
    method getIterator (line 31) | public function getIterator(): Traversable;

FILE: tests/test_code/php/money/Currencies/AggregateCurrencies.php
  class AggregateCurrencies (line 16) | final class AggregateCurrencies implements Currencies
    method __construct (line 24) | public function __construct(array $currencies)
    method contains (line 29) | public function contains(Currency $currency): bool
    method subunitFor (line 40) | public function subunitFor(Currency $currency): int
    method getIterator (line 52) | public function getIterator(): Traversable

FILE: tests/test_code/php/money/Currencies/BitcoinCurrencies.php
  class BitcoinCurrencies (line 13) | final class BitcoinCurrencies implements Currencies
    method contains (line 19) | public function contains(Currency $currency): bool
    method subunitFor (line 24) | public function subunitFor(Currency $currency): int
    method getIterator (line 34) | public function getIterator(): Traversable

FILE: tests/test_code/php/money/Currencies/CachedCurrencies.php
  class CachedCurrencies (line 20) | final class CachedCurrencies implements Currencies
    method __construct (line 26) | public function __construct(Currencies $currencies, CacheItemPoolInter...
    method contains (line 32) | public function contains(Currency $currency): bool
    method subunitFor (line 49) | public function subunitFor(Currency $currency): int
    method getIterator (line 67) | public function getIterator(): Traversable

FILE: tests/test_code/php/money/Currencies/CurrencyList.php
  class CurrencyList (line 19) | final class CurrencyList implements Currencies
    method __construct (line 29) | public function __construct(array $currencies)
    method contains (line 34) | public function contains(Currency $currency): bool
    method subunitFor (line 39) | public function subunitFor(Currency $currency): int
    method getIterator (line 49) | public function getIterator(): Traversable

FILE: tests/test_code/php/money/Currencies/ISOCurrencies.php
  class ISOCurrencies (line 21) | final class ISOCurrencies implements Currencies
    method contains (line 35) | public function contains(Currency $currency): bool
    method subunitFor (line 40) | public function subunitFor(Currency $currency): int
    method numericCodeFor (line 54) | public function numericCodeFor(Currency $currency): int
    method getIterator (line 66) | public function getIterator(): Traversable
    method getCurrencies (line 88) | private function getCurrencies(): array
    method loadCurrencies (line 108) | private function loadCurrencies(): array

FILE: tests/test_code/php/money/Currency.php
  class Currency (line 18) | final class Currency implements JsonSerializable
    method __construct (line 28) | public function __construct(string $code)
    method getCode (line 39) | public function getCode(): string
    method equals (line 47) | public function equals(Currency $other): bool
    method __toString (line 52) | public function __toString(): string
    method jsonSerialize (line 62) | public function jsonSerialize()

FILE: tests/test_code/php/money/CurrencyPair.php
  class CurrencyPair (line 20) | final class CurrencyPair implements JsonSerializable
    method __construct (line 38) | public function __construct(Currency $baseCurrency, Currency $counterC...
    method createFromIso (line 52) | public static function createFromIso(string $iso): CurrencyPair
    method getCounterCurrency (line 74) | public function getCounterCurrency(): Currency
    method getBaseCurrency (line 82) | public function getBaseCurrency(): Currency
    method getConversionRatio (line 92) | public function getConversionRatio(): string
    method equals (line 100) | public function equals(CurrencyPair $other): bool
    method jsonSerialize (line 112) | public function jsonSerialize()

FILE: tests/test_code/php/money/Exception.php
  type Exception (line 10) | interface Exception

FILE: tests/test_code/php/money/Exception/FormatterException.php
  class FormatterException (line 13) | final class FormatterException extends RuntimeException implements Excep...

FILE: tests/test_code/php/money/Exception/InvalidArgumentException.php
  class InvalidArgumentException (line 10) | final class InvalidArgumentException extends CoreInvalidArgumentExceptio...
    method divisionByZero (line 13) | public static function divisionByZero(): self
    method moduloByZero (line 19) | public static function moduloByZero(): self

FILE: tests/test_code/php/money/Exception/ParserException.php
  class ParserException (line 13) | final class ParserException extends RuntimeException implements Exception

FILE: tests/test_code/php/money/Exception/UnknownCurrencyException.php
  class UnknownCurrencyException (line 13) | final class UnknownCurrencyException extends DomainException implements ...

FILE: tests/test_code/php/money/Exception/UnresolvableCurrencyPairException.php
  class UnresolvableCurrencyPairException (line 16) | final class UnresolvableCurrencyPairException extends InvalidArgumentExc...
    method createFromCurrencies (line 21) | public static function createFromCurrencies(Currency $baseCurrency, Cu...

FILE: tests/test_code/php/money/Exchange.php
  type Exchange (line 12) | interface Exchange
    method quote (line 19) | public function quote(Currency $baseCurrency, Currency $counterCurrenc...

FILE: tests/test_code/php/money/Exchange/ExchangerExchange.php
  class ExchangerExchange (line 23) | final class ExchangerExchange implements Exchange
    method __construct (line 27) | public function __construct(ExchangeRateProvider $exchanger)
    method quote (line 32) | public function quote(Currency $baseCurrency, Currency $counterCurrenc...

FILE: tests/test_code/php/money/Exchange/FixedExchange.php
  class FixedExchange (line 15) | final class FixedExchange implements Exchange
    method __construct (line 21) | public function __construct(array $list)
    method quote (line 26) | public function quote(Currency $baseCurrency, Currency $counterCurrenc...

FILE: tests/test_code/php/money/Exchange/IndirectExchange.php
  class IndirectExchange (line 21) | final class IndirectExchange implements Exchange
    method __construct (line 27) | public function __construct(Exchange $exchange, Currencies $currencies)
    method quote (line 33) | public function quote(Currency $baseCurrency, Currency $counterCurrenc...
    method getConversions (line 62) | private function getConversions(Currency $baseCurrency, Currency $coun...
    method reconstructConversionChain (line 117) | private function reconstructConversionChain(array $currencies, Indirec...

FILE: tests/test_code/php/money/Exchange/IndirectExchangeQueuedItem.php
  class IndirectExchangeQueuedItem (line 10) | final class IndirectExchangeQueuedItem
    method __construct (line 16) | public function __construct(Currency $currency)

FILE: tests/test_code/php/money/Exchange/ReversedCurrenciesExchange.php
  class ReversedCurrenciesExchange (line 18) | final class ReversedCurrenciesExchange implements Exchange
    method __construct (line 22) | public function __construct(Exchange $exchange)
    method quote (line 27) | public function quote(Currency $baseCurrency, Currency $counterCurrenc...

FILE: tests/test_code/php/money/Exchange/SwapExchange.php
  class SwapExchange (line 17) | final class SwapExchange implements Exchange
    method __construct (line 21) | public function __construct(Swap $swap)
    method quote (line 26) | public function quote(Currency $baseCurrency, Currency $counterCurrenc...

FILE: tests/test_code/php/money/Formatter/AggregateMoneyFormatter.php
  class AggregateMoneyFormatter (line 14) | final class AggregateMoneyFormatter implements MoneyFormatter
    method __construct (line 26) | public function __construct(array $formatters)
    method format (line 31) | public function format(Money $money): string

FILE: tests/test_code/php/money/Formatter/BitcoinMoneyFormatter.php
  class BitcoinMoneyFormatter (line 22) | final class BitcoinMoneyFormatter implements MoneyFormatter
    method __construct (line 28) | public function __construct(int $fractionDigits, Currencies $currencies)
    method format (line 34) | public function format(Money $money): string

FILE: tests/test_code/php/money/Formatter/DecimalMoneyFormatter.php
  class DecimalMoneyFormatter (line 19) | final class DecimalMoneyFormatter implements MoneyFormatter
    method __construct (line 23) | public function __construct(Currencies $currencies)
    method format (line 28) | public function format(Money $money): string

FILE: tests/test_code/php/money/Formatter/IntlLocalizedDecimalFormatter.php
  class IntlLocalizedDecimalFormatter (line 21) | final class IntlLocalizedDecimalFormatter implements MoneyFormatter
    method __construct (line 27) | public function __construct(NumberFormatter $formatter, Currencies $cu...
    method format (line 33) | public function format(Money $money): string

FILE: tests/test_code/php/money/Formatter/IntlMoneyFormatter.php
  class IntlMoneyFormatter (line 20) | final class IntlMoneyFormatter implements MoneyFormatter
    method __construct (line 26) | public function __construct(NumberFormatter $formatter, Currencies $cu...
    method format (line 32) | public function format(Money $money): string

FILE: tests/test_code/php/money/Money.php
  class Money (line 35) | final class Money implements JsonSerializable
    method __construct (line 76) | public function __construct(int|string $amount, Currency $currency)
    method isSameCurrency (line 97) | public function isSameCurrency(Money ...$others): bool
    method equals (line 112) | public function equals(Money $other): bool
    method compare (line 134) | public function compare(Money $other): int
    method greaterThan (line 147) | public function greaterThan(Money $other): bool
    method greaterThanOrEqual (line 152) | public function greaterThanOrEqual(Money $other): bool
    method lessThan (line 160) | public function lessThan(Money $other): bool
    method lessThanOrEqual (line 165) | public function lessThanOrEqual(Money $other): bool
    method getAmount (line 175) | public function getAmount(): string
    method getCurrency (line 183) | public function getCurrency(): Currency
    method add (line 194) | public function add(Money ...$addends): Money
    method subtract (line 218) | public function subtract(Money ...$subtrahends): Money
    method multiply (line 241) | public function multiply(int|string $multiplier, int $roundingMode = s...
    method divide (line 259) | public function divide(int|string $divisor, int $roundingMode = self::...
    method mod (line 275) | public function mod(Money $divisor): Money
    method allocate (line 299) | public function allocate(array $ratios): array
    method allocateTo (line 350) | public function allocateTo(int $n): array
    method ratioOf (line 358) | public function ratioOf(Money $money): string
    method round (line 373) | private function round(string $amount, int $roundingMode): string
    method roundToUnit (line 392) | public function roundToUnit(int $unit, int $roundingMode = self::ROUND...
    method absolute (line 411) | public function absolute(): Money
    method negative (line 419) | public function negative(): Money
    method isZero (line 428) | public function isZero(): bool
    method isPositive (line 436) | public function isPositive(): bool
    method isNegative (line 444) | public function isNegative(): bool
    method jsonSerialize (line 454) | public function jsonSerialize()
    method min (line 468) | public static function min(self $first, self ...$collection): Money
    method max (line 489) | public static function max(self $first, self ...$collection): Money
    method sum (line 505) | public static function sum(self $first, self ...$collection): Money
    method avg (line 511) | public static function avg(self $first, self ...$collection): Money
    method registerCalculator (line 517) | public static function registerCalculator(string $calculator): void
    method getCalculator (line 523) | public static function getCalculator(): string

FILE: tests/test_code/php/money/MoneyFactory.php
  type MoneyFactory (line 193) | trait MoneyFactory
    method __callStatic (line 210) | public static function __callStatic(string $method, array $arguments):...

FILE: tests/test_code/php/money/MoneyFormatter.php
  type MoneyFormatter (line 10) | interface MoneyFormatter
    method format (line 19) | public function format(Money $money): string;

FILE: tests/test_code/php/money/MoneyParser.php
  type MoneyParser (line 10) | interface MoneyParser
    method parse (line 17) | public function parse(string $money, Currency|null $fallbackCurrency =...

FILE: tests/test_code/php/money/Number.php
  class Number (line 29) | final class Number
    method __construct (line 38) | public function __construct(string $integerPart, string $fractionalPar...
    method fromString (line 49) | public static function fromString(string $number): self
    method fromFloat (line 60) | public static function fromFloat(float $number): self
    method fromNumber (line 66) | public static function fromNumber(int|string $number): self
    method isDecimal (line 75) | public function isDecimal(): bool
    method isInteger (line 80) | public function isInteger(): bool
    method isHalf (line 85) | public function isHalf(): bool
    method isCurrentEven (line 90) | public function isCurrentEven(): bool
    method isCloserToNext (line 97) | public function isCloserToNext(): bool
    method __toString (line 107) | public function __toString(): string
    method isNegative (line 117) | public function isNegative(): bool
    method getIntegerPart (line 123) | public function getIntegerPart(): string
    method getFractionalPart (line 129) | public function getFractionalPart(): string
    method getIntegerRoundingMultiplier (line 135) | public function getIntegerRoundingMultiplier(): string
    method base10 (line 144) | public function base10(int $number): self
    method parseIntegerPart (line 189) | private static function parseIntegerPart(string $number): string
    method parseFractionalPart (line 230) | private static function parseFractionalPart(string $number): string
    method roundMoneyValue (line 260) | public static function roundMoneyValue(string $moneyValue, int $target...

FILE: tests/test_code/php/money/PHPUnit/Comparator.php
  class Comparator (line 28) | final class Comparator extends \SebastianBergmann\Comparator\Comparator
    method __construct (line 32) | public function __construct()
    method accepts (line 46) | public function accepts($expected, $actual)
    method assertEquals (line 52) | public function assertEquals(

FILE: tests/test_code/php/money/Parser/AggregateMoneyParser.php
  class AggregateMoneyParser (line 17) | final class AggregateMoneyParser implements MoneyParser
    method __construct (line 29) | public function __construct(array $parsers)
    method parse (line 34) | public function parse(string $money, Currency|null $fallbackCurrency =...

FILE: tests/test_code/php/money/Parser/BitcoinMoneyParser.php
  class BitcoinMoneyParser (line 24) | final class BitcoinMoneyParser implements MoneyParser
    method __construct (line 28) | public function __construct(int $fractionDigits)
    method parse (line 33) | public function parse(string $money, Currency|null $fallbackCurrency =...

FILE: tests/test_code/php/money/Parser/DecimalMoneyParser.php
  class DecimalMoneyParser (line 25) | final class DecimalMoneyParser implements MoneyParser
    method __construct (line 31) | public function __construct(Currencies $currencies)
    method parse (line 36) | public function parse(string $money, Currency|null $fallbackCurrency =...

FILE: tests/test_code/php/money/Parser/IntlLocalizedDecimalParser.php
  class IntlLocalizedDecimalParser (line 25) | final class IntlLocalizedDecimalParser implements MoneyParser
    method __construct (line 31) | public function __construct(NumberFormatter $formatter, Currencies $cu...
    method parse (line 37) | public function parse(string $money, Currency|null $fallbackCurrency =...

FILE: tests/test_code/php/money/Parser/IntlMoneyParser.php
  class IntlMoneyParser (line 26) | final class IntlMoneyParser implements MoneyParser
    method __construct (line 32) | public function __construct(NumberFormatter $formatter, Currencies $cu...
    method parse (line 38) | public function parse(string $money, Currency|null $fallbackCurrency =...

FILE: tests/test_code/php/namespace_a/namespace_a.php
  function namespaced_func (line 4) | function namespaced_func() {
  class Namespaced_cls (line 8) | class Namespaced_cls {
    method __construct (line 9) | function __construct() {
    method instance_method (line 12) | function instance_method() {

FILE: tests/test_code/php/namespace_b/namespace_b2.php
  class Animal (line 35) | class Animal {
    method breathes (line 36) | static function breathes() {echo 'air';}
    method meows (line 37) | static function meows() {echo 'meoow2';}
  class Dog (line 42) | class Dog {
    method says (line 43) | static function says() {echo 'ruff';}
  class Cat (line 49) | class Cat extends animate\Animal {
    method says (line 50) | static function says() {echo 'meoow';}
    method meows (line 51) | static function meows() {echo 'meoow2';}

FILE: tests/test_code/php/namespace_c/namespace_c2.php
  function speak (line 5) | function speak() {
  class Cat (line 9) | class Cat {
    method meow (line 10) | static function meow() {
    method speak (line 13) | static function speak() {
  class Dog (line 18) | class Dog {
    method meow (line 19) | static function meow() {
    method speak (line 22) | static function speak() {

FILE: tests/test_code/php/nested/nested.php
  function outer (line 5) | function outer() {

FILE: tests/test_code/php/nested_calls/nested_calls.php
  function x_ (line 3) | function x_() {
  function y_ (line 6) | function y_() {}
  function z_ (line 7) | function z_() {}
  function func (line 9) | function func() {}
  function func2 (line 10) | function func2() {}
  class Cls (line 13) | class Cls {
    method a (line 14) | static function a($ret) {
    method b (line 17) | static function b($ret) {
    method c (line 20) | static function c($ret) {
    method func (line 24) | function func() {
    method func2 (line 28) | function func2() {

FILE: tests/test_code/php/publicprivateprotected/publicprivateprotected.php
  class Fruit (line 3) | class Fruit {
    method set_name (line 8) | function set_name($n) {  // a public function (default)
    method set_color (line 11) | protected function set_color($n) { // a protected function
    method set_weight (line 14) | private function set_weight($n) { // a private function
  function set_color_weight (line 19) | function set_color_weight($fruit) {

FILE: tests/test_code/php/resolve_correct_class/rcc.php
  function func_1 (line 2) | function func_1() {
  class Alpha (line 6) | class Alpha {
    method func_1 (line 7) | function func_1() {
    method func_2 (line 14) | function func_2() {
  class Beta (line 19) | class Beta {
    method func_1 (line 20) | function func_1() {
    method func_2 (line 26) | function func_2() {

FILE: tests/test_code/php/simple_a/simple_a.php
  function func_b (line 3) | function func_b() {
  function func_a (line 7) | function func_a() {

FILE: tests/test_code/php/simple_b/simple_b.php
  function a (line 5) | function a($param) {
  function b (line 10) | function b() {
  class C (line 15) | class C {
    method d (line 16) | function d($param) {

FILE: tests/test_code/php/static/static.php
  function say_name (line 3) | function say_name() {}
  class Greeting (line 5) | class Greeting {
    method welcome (line 6) | public static function welcome() {
    method say_name (line 10) | function say_name() {
    method __construct (line 14) | function __construct($name) {
  function welcome (line 20) | function welcome() {}

FILE: tests/test_code/php/traits/traits.php
  type message1 (line 2) | trait message1 {
    method msg1 (line 3) | public function msg1() {
  type message2 (line 8) | trait message2 {
    method msg2 (line 9) | public function msg2() {
  class Welcome (line 14) | class Welcome {
    method __construct (line 17) | function __construct() {
  class Welcome2 (line 22) | class Welcome2 {
  function welcome1 (line 26) | function welcome1() {
  function welcome2 (line 32) | function welcome2() {

FILE: tests/test_code/php/two_file_simple/file_a.php
  function a (line 3) | function a() {

FILE: tests/test_code/php/two_file_simple/file_b.php
  function b (line 2) | function b() {
  function c (line 7) | function c() {}

FILE: tests/test_code/php/weird_assign/weird_assign.php
  function a (line 3) | function a() {}
  function b (line 4) | function b() {}
  function c (line 6) | function c($x, $y) {

FILE: tests/test_code/py/ambiguous_resolution/ambiguous_resolution.py
  class Abra (line 1) | class Abra():
    method magic (line 2) | def magic():
    method abra_it (line 5) | def abra_it():
  class Cadabra (line 9) | class Cadabra():
    method magic (line 10) | def magic():
    method cadabra_it (line 13) | def cadabra_it(a=None):
  function main (line 17) | def main(cls):

FILE: tests/test_code/py/async_basic/async_basic.py
  class A (line 3) | class A:
    method __init__ (line 4) | def __init__(self):
    method test (line 6) | async def test(self):
  function main (line 9) | async def main():

FILE: tests/test_code/py/chained/chained.py
  class Chain (line 1) | class Chain():
    method __init__ (line 2) | def __init__(self, val):
    method add (line 5) | def add(self, b):
    method sub (line 9) | def sub(self, b):
    method mul (line 13) | def mul(self, b):

FILE: tests/test_code/py/exclude_modules/exclude_modules.py
  function search (line 5) | def search():
  function beta (line 9) | def beta():
  function alpha (line 16) | def alpha():

FILE: tests/test_code/py/exclude_modules_two_files/exclude_modules_a.py
  function a (line 5) | def a():
  function b (line 9) | def b():

FILE: tests/test_code/py/exclude_modules_two_files/exclude_modules_b.py
  function match (line 1) | def match():

FILE: tests/test_code/py/import_paths/abra.py
  function abra2 (line 1) | def abra2():

FILE: tests/test_code/py/import_paths/cadabra.py
  function cadabra2 (line 1) | def cadabra2():

FILE: tests/test_code/py/import_paths/import_paths.py
  function main (line 5) | def main():
  function main2 (line 10) | def main2():

FILE: tests/test_code/py/inherits/inherits.py
  function majorNum (line 6) | def majorNum():
  function pentaNum (line 10) | def pentaNum():
  class FakePentatonicScales (line 14) | class FakePentatonicScales():
    method pentaNum (line 15) | def pentaNum(self):
  class ScaleDemo (line 21) | class ScaleDemo(MajorScales, PentatonicScales):
    method __init__ (line 22) | def __init__(self):
    method nothing (line 27) | def nothing(self):
  class ScaleDemoLimited (line 31) | class ScaleDemoLimited(MajorScales):
    method __init__ (line 32) | def __init__(self):

FILE: tests/test_code/py/inherits/inherits_import.py
  class MajorScales (line 3) | class MajorScales():
    method majorNum (line 4) | def majorNum(self):
  class PentatonicScales (line 10) | class PentatonicScales():
    method pentaNum (line 11) | def pentaNum(self):

FILE: tests/test_code/py/init/init.py
  class Abra (line 4) | class Abra():
    method __init__ (line 5) | def __init__(self):
    method cadabra (line 8) | def cadabra(self):
  function b (line 12) | def b():

FILE: tests/test_code/py/init/the_import.py
  class ProvincialClass (line 1) | class ProvincialClass():
    method __init__ (line 2) | def __init__(self):
  class HiddenClass (line 6) | class HiddenClass():
    method __init__ (line 7) | def __init__(self):
  function imported_func (line 11) | def imported_func():

FILE: tests/test_code/py/nested_calls/nested_calls.py
  function trace (line 3) | def trace(fn: Callable) -> Callable:
  function do_something (line 9) | def do_something(msg):

FILE: tests/test_code/py/nested_class/nested_class.py
  class Outer (line 1) | class Outer():
    class Inner (line 2) | class Inner():
      method inner_func (line 3) | def inner_func():
    method outer_func (line 6) | def outer_func(a):
    method __init__ (line 10) | def __init__(self):

FILE: tests/test_code/py/pytz/__init__.py
  function ascii (line 47) | def ascii(s):
  function ascii (line 64) | def ascii(s):
  function open_resource (line 78) | def open_resource(name):
  function resource_exists (line 111) | def resource_exists(name):
  function timezone (line 130) | def timezone(zone):
  function _unmunge_zone (line 193) | def _unmunge_zone(zone):
  function _case_insensitive_zone_lookup (line 201) | def _case_insensitive_zone_lookup(zone):
  class UTC (line 213) | class UTC(BaseTzInfo):
    method fromutc (line 225) | def fromutc(self, dt):
    method utcoffset (line 230) | def utcoffset(self, dt):
    method tzname (line 233) | def tzname(self, dt):
    method dst (line 236) | def dst(self, dt):
    method __reduce__ (line 239) | def __reduce__(self):
    method localize (line 242) | def localize(self, dt, is_dst=False):
    method normalize (line 248) | def normalize(self, dt, is_dst=False):
    method __repr__ (line 256) | def __repr__(self):
    method __str__ (line 259) | def __str__(self):
  function _UTC (line 266) | def _UTC():
  function _p (line 301) | def _p(*args):
  class _CountryTimezoneDict (line 313) | class _CountryTimezoneDict(LazyDict):
    method __call__ (line 345) | def __call__(self, iso3166_code):
    method _fill (line 349) | def _fill(self):
  class _CountryNameDict (line 372) | class _CountryNameDict(LazyDict):
    method _fill (line 378) | def _fill(self):
  class _FixedOffset (line 398) | class _FixedOffset(datetime.tzinfo):
    method __init__ (line 402) | def __init__(self, minutes):
    method utcoffset (line 408) | def utcoffset(self, dt):
    method __reduce__ (line 411) | def __reduce__(self):
    method dst (line 414) | def dst(self, dt):
    method tzname (line 417) | def tzname(self, dt):
    method __repr__ (line 420) | def __repr__(self):
    method localize (line 423) | def localize(self, dt, is_dst=False):
    method normalize (line 429) | def normalize(self, dt, is_dst=False):
  function FixedOffset (line 438) | def FixedOffset(offset, _tzinfos={}):
  function _test (line 508) | def _test():

FILE: tests/test_code/py/pytz/exceptions.py
  class Error (line 11) | class Error(Exception):
  class UnknownTimeZoneError (line 15) | class UnknownTimeZoneError(KeyError, Error):
  class InvalidTimeError (line 38) | class InvalidTimeError(Error):
  class AmbiguousTimeError (line 42) | class AmbiguousTimeError(InvalidTimeError):
  class NonExistentTimeError (line 53) | class NonExistentTimeError(InvalidTimeError):

FILE: tests/test_code/py/pytz/lazy.py
  class LazyDict (line 16) | class LazyDict(DictMixin):
    method __getitem__ (line 20) | def __getitem__(self, key):
    method __contains__ (line 30) | def __contains__(self, key):
    method __iter__ (line 40) | def __iter__(self):
    method __len__ (line 50) | def __len__(self):
    method keys (line 60) | def keys(self):
  class LazyList (line 71) | class LazyList(list):
    method __new__ (line 84) | def __new__(cls, fill_iter=None):
  class LazySet (line 121) | class LazySet(set):
    method __new__ (line 139) | def __new__(cls, fill_iter=None):

FILE: tests/test_code/py/pytz/reference.py
  class FixedOffset (line 25) | class FixedOffset(tzinfo):
    method __init__ (line 28) | def __init__(self, offset, name):
    method utcoffset (line 32) | def utcoffset(self, dt):
    method tzname (line 35) | def tzname(self, dt):
    method dst (line 38) | def dst(self, dt):
  class LocalTimezone (line 54) | class LocalTimezone(tzinfo):
    method utcoffset (line 56) | def utcoffset(self, dt):
    method dst (line 62) | def dst(self, dt):
    method tzname (line 68) | def tzname(self, dt):
    method _isdst (line 71) | def _isdst(self, dt):
  function first_sunday_on_or_after (line 82) | def first_sunday_on_or_after(dt):
  class USTimeZone (line 97) | class USTimeZone(tzinfo):
    method __init__ (line 99) | def __init__(self, hours, reprname, stdname, dstname):
    method __repr__ (line 105) | def __repr__(self):
    method tzname (line 108) | def tzname(self, dt):
    method utcoffset (line 114) | def utcoffset(self, dt):
    method dst (line 117) | def dst(self, dt):

FILE: tests/test_code/py/pytz/tzfile.py
  function _byte_string (line 12) | def _byte_string(s):
  function _std_string (line 19) | def _std_string(s):
  function build_tzinfo (line 24) | def build_tzinfo(zone, fp):

FILE: tests/test_code/py/pytz/tzinfo.py
  function memorized_timedelta (line 18) | def memorized_timedelta(seconds):
  function memorized_datetime (line 31) | def memorized_datetime(seconds):
  function memorized_ttinfo (line 45) | def memorized_ttinfo(*args):
  function _to_seconds (line 61) | def _to_seconds(td):
  class BaseTzInfo (line 66) | class BaseTzInfo(tzinfo):
    method __str__ (line 72) | def __str__(self):
  class StaticTzInfo (line 76) | class StaticTzInfo(BaseTzInfo):
    method fromutc (line 82) | def fromutc(self, dt):
    method utcoffset (line 88) | def utcoffset(self, dt, is_dst=None):
    method dst (line 96) | def dst(self, dt, is_dst=None):
    method tzname (line 104) | def tzname(self, dt, is_dst=None):
    method localize (line 112) | def localize(self, dt, is_dst=False):
    method normalize (line 118) | def normalize(self, dt, is_dst=False):
    method __repr__ (line 147) | def __repr__(self):
    method __reduce__ (line 150) | def __reduce__(self):
  class DstTzInfo (line 156) | class DstTzInfo(BaseTzInfo):
    method __init__ (line 179) | def __init__(self, _inf=None, _tzinfos=None):
    method fromutc (line 193) | def fromutc(self, dt):
    method normalize (line 203) | def normalize(self, dt):
    method localize (line 258) | def localize(self, dt, is_dst=False):
    method utcoffset (line 396) | def utcoffset(self, dt, is_dst=None):
    method dst (line 427) | def dst(self, dt, is_dst=None):
    method tzname (line 466) | def tzname(self, dt, is_dst=None):
    method __repr__ (line 504) | def __repr__(self):
    method __reduce__ (line 518) | def __reduce__(self):
  function unpickler (line 529) | def unpickler(zone, utcoffset=None, dstoffset=None, tzname=None):

FILE: tests/test_code/py/resolve_correct_class/rcc.py
  function func_1 (line 1) | def func_1():
  class Alpha (line 5) | class Alpha():
    method func_1 (line 6) | def func_1(self):
    method func_2 (line 13) | def func_2(self):
  class Beta (line 17) | class Beta():
    method func_1 (line 18) | def func_1(self):
    method func_2 (line 23) | def func_2(self):

FILE: tests/test_code/py/simple_a/simple_a.py
  function func_b (line 1) | def func_b():
  function func_a (line 4) | def func_a():

FILE: tests/test_code/py/simple_b/simple_b.py
  function a (line 6) | def a():
  function b (line 11) | def b():
  class c (line 15) | class c():
    method d (line 16) | def d(a="String"):

FILE: tests/test_code/py/subset_find_exception/two.py
  function private (line 1) | def private():
  class Abra (line 4) | class Abra:
    method func (line 5) | def func():
  class Cadabra (line 8) | class Cadabra:
    method func (line 9) | def func():

FILE: tests/test_code/py/subset_find_exception/zero.py
  function private (line 1) | def private():
  class Abra (line 4) | class Abra:
    method other (line 5) | def other():

FILE: tests/test_code/py/two_file_simple/file_a.py
  function a (line 4) | def a():

FILE: tests/test_code/py/two_file_simple/file_b.py
  function b (line 1) | def b():
  function c (line 5) | def c():

FILE: tests/test_code/py/weird_calls/weird_calls.py
  function print_it (line 1) | def print_it(string):
  function func_a (line 5) | def func_a():
  function func_b (line 9) | def func_b():
  function func_c (line 13) | def func_c():
  function factory (line 27) | def factory():

FILE: tests/test_code/py/weird_encoding/weird_encoding.py
  function a (line 1) | def a():

FILE: tests/test_code/py/weird_imports/weird_imports.py
  function main (line 5) | def main():

FILE: tests/test_code/rb/ambiguous_resolution/ambiguous_resolution.rb
  class Abra (line 1) | class Abra
    method magic (line 2) | def magic()
    method abra_it (line 5) | def abra_it()
  class Cadabra (line 9) | class Cadabra
    method magic (line 10) | def magic()
    method cadabra_it (line 13) | def cadabra_it(a: nil)
  function main (line 18) | def main(cls)

FILE: tests/test_code/rb/chains/chains.rb
  function a (line 1) | def a
  function b (line 6) | def b
  class Cls (line 11) | class Cls
    method initialize (line 12) | def initialize
    method a (line 16) | def a
    method b (line 21) | def b
  function c (line 27) | def c

FILE: tests/test_code/rb/doublecolon/doublecolon.rb
  class Class1 (line 9) | class Class1
    method func_a (line 10) | def self.func_a
    method func_b (line 15) | def self.func_b
  class Class2 (line 22) | class Class2
    method func_a (line 23) | def self.func_a
    method func_b (line 28) | def self.func_b

FILE: tests/test_code/rb/inheritance_2/inheritance_2.rb
  function speak (line 3) | def speak
  class Animal (line 7) | class Animal
    method speak (line 8) | def speak
  class GoodDog (line 13) | class GoodDog < Animal
  class Cat (line 16) | class Cat < Animal
    method meow (line 17) | def meow

FILE: tests/test_code/rb/instance_methods/instance_methods.rb
  function main (line 1) | def main
  class Abra (line 5) | class Abra
    method main (line 6) | def main
    method main2 (line 15) | def main2

FILE: tests/test_code/rb/modules/modules.rb
  function majorNum (line 3) | def majorNum
  function pentaNum (line 6) | def pentaNum
  type MajorScales (line 9) | module MajorScales
    function majorNum (line 10) | def majorNum
  type PentatonicScales (line 16) | module PentatonicScales
    function pentaNum (line 17) | def pentaNum
  class ScaleDemo (line 23) | class ScaleDemo
    method initialize (line 26) | def initialize
  class ScaleDemoLimited (line 32) | class ScaleDemoLimited
    method initialize (line 34) | def initialize

FILE: tests/test_code/rb/nested/nested.rb
  type Mod (line 1) | module Mod
    function func_1 (line 2) | def func_1
    function func_2 (line 6) | def func_2
    class Nested (line 10) | class Nested
      method initialize (line 11) | def initialize
      method func_1 (line 15) | def func_1
      method func_2 (line 19) | def func_2
  function func_1 (line 27) | def func_1
  function func_2 (line 32) | def func_2

FILE: tests/test_code/rb/nested_classes/nested_classes.rb
  class Mod (line 1) | class Mod
    method func_1 (line 2) | def func_1
    method func_2 (line 6) | def func_2
    class Nested (line 10) | class Nested
      method initialize (line 11) | def initialize
      method func_1 (line 15) | def func_1
      method func_2 (line 19) | def func_2
  function func_1 (line 27) | def func_1
  function func_2 (line 32) | def func_2

FILE: tests/test_code/rb/public_suffix/public_suffix.rb
  type PublicSuffix (line 23) | module PublicSuffix
    function parse (line 69) | def self.parse(name, list: List.default, default_rule: list.default_ru...
    function valid? (line 125) | def self.valid?(name, list: List.default, default_rule: list.default_r...
    function domain (line 142) | def self.domain(name, **options)
    function decompose (line 151) | def self.decompose(name, rule)
    function normalize (line 166) | def self.normalize(name)

FILE: tests/test_code/rb/public_suffix/public_suffix/domain.rb
  type PublicSuffix (line 9) | module PublicSuffix
    class Domain (line 12) | class Domain
      method name_to_labels (line 28) | def self.name_to_labels(name)
      method initialize (line 65) | def initialize(*args)
      method to_s (line 73) | def to_s
      method to_a (line 89) | def to_a
      method name (line 105) | def name
      method domain (line 137) | def domain
      method subdomain (line 169) | def subdomain
      method domain? (line 198) | def domain?
      method subdomain? (line 229) | def subdomain?

FILE: tests/test_code/rb/public_suffix/public_suffix/errors.rb
  type PublicSuffix (line 9) | module PublicSuffix
    class Error (line 11) | class Error < StandardError
    class DomainInvalid (line 25) | class DomainInvalid < Error
    class DomainNotAllowed (line 38) | class DomainNotAllowed < DomainInvalid

FILE: tests/test_code/rb/public_suffix/public_suffix/list.rb
  type PublicSuffix (line 9) | module PublicSuffix
    class List (line 40) | class List
      method default (line 50) | def self.default(**options)
      method default= (line 58) | def self.default=(value)
      method parse (line 69) | def self.parse(input, private_domains: true)
      method initialize (line 106) | def initialize
      method == (line 120) | def ==(other)
      method each (line 128) | def each(&block)
      method add (line 141) | def add(rule)
      method size (line 150) | def size
      method empty? (line 157) | def empty?
      method clear (line 164) | def clear
      method find (line 174) | def find(name, default: default_rule, **options)
      method select (line 199) | def select(name, ignore_private: false)
      method default_rule (line 226) | def default_rule
      method entry_to_rule (line 238) | def entry_to_rule(entry, value)
      method rule_to_entry (line 242) | def rule_to_entry(rule)

FILE: tests/test_code/rb/public_suffix/public_suffix/rule.rb
  type PublicSuffix (line 9) | module PublicSuffix
    type Rule (line 22) | module Rule
      class Base (line 102) | class Base
        method build (line 118) | def self.build(content, private: false)
        method initialize (line 126) | def initialize(value:, length: nil, private: false)
        method == (line 138) | def ==(other)
        method match? (line 164) | def match?(name)
        method parts (line 174) | def parts
        method decompose (line 181) | def decompose(*)
      class Normal (line 188) | class Normal < Base
        method rule (line 193) | def rule
        method decompose (line 201) | def decompose(domain)
        method parts (line 211) | def parts
      class Wildcard (line 218) | class Wildcard < Base
        method build (line 224) | def self.build(content, private: false)
        method initialize (line 232) | def initialize(value:, length: nil, private: false)
        method rule (line 240) | def rule
        method decompose (line 248) | def decompose(domain)
        method parts (line 258) | def parts
      class Exception (line 265) | class Exception < Base
        method build (line 271) | def self.build(content, private: false)
        method rule (line 278) | def rule
        method decompose (line 286) | def decompose(domain)
        method parts (line 301) | def parts
      function factory (line 326) | def self.factory(content, private: false)
      function default (line 344) | def self.default

FILE: tests/test_code/rb/public_suffix/public_suffix/version.rb
  type PublicSuffix (line 10) | module PublicSuffix

FILE: tests/test_code/rb/resolve_correct_class/rcc.rb
  function func_1 (line 1) | def func_1
  class Alpha (line 5) | class Alpha
    method func_1 (line 6) | def func_1()
    method func_2 (line 14) | def func_2()
  class Beta (line 19) | class Beta
    method func_1 (line 20) | def func_1()
    method func_2 (line 26) | def func_2()

FILE: tests/test_code/rb/simple_a/simple_a.rb
  function func_b (line 1) | def func_b(var)
  function func_a (line 5) | def func_a()

FILE: tests/test_code/rb/simple_b/simple_b.rb
  function a (line 6) | def a()
  function b (line 11) | def b()
  class Cls (line 15) | class Cls
    method initialize (line 16) | def initialize(val)
    method d (line 19) | def d(a="String")

FILE: tests/test_code/rb/split_modules/split_modules_a.rb
  function say_hi (line 3) | def say_hi
  function say_bye (line 6) | def say_bye
  type Split (line 9) | module Split
    function say_hi (line 10) | def self.say_hi

FILE: tests/test_code/rb/split_modules/split_modules_b.rb
  type Split (line 1) | module Split
    class MyClass (line 3) | class MyClass
      method initialize (line 4) | def initialize
      method doit (line 8) | def doit
    function say_bye (line 14) | def self.say_bye

FILE: tests/test_code/rb/two_file_simple/file_a.rb
  function abra (line 3) | def abra()

FILE: tests/test_code/rb/two_file_simple/file_b.rb
  function babra (line 1) | def babra()
  function cadabra (line 6) | def cadabra()

FILE: tests/test_code/rb/weird_chains/weird_chains.rb
  class DivByTwo (line 1) | class DivByTwo
    method initialize (line 2) | def initialize(val: 0)
    method + (line 6) | def +(val)
    method - (line 11) | def -(val)
    method * (line 16) | def *(val)
    method result (line 21) | def result()

FILE: tests/test_graphs.py
  function _edge (line 29) | def _edge(tup):
  function assert_eq (line 33) | def assert_eq(seq_a, seq_b):
  function test_all (line 60) | def test_all(test_tup):
  function get_nodes_set_from_file (line 83) | def get_nodes_set_from_file(dot_file):
  function get_edges_set_from_file (line 97) | def get_edges_set_from_file(dot_file):

FILE: tests/test_interface.py
  function test_generate_image (line 26) | def test_generate_image():
  function test_not_installed (line 40) | def test_not_installed():
  function test_invalid_extension (line 51) | def test_invalid_extension():
  function test_no_files (line 57) | def test_no_files():
  function test_graphviz_error (line 63) | def test_graphviz_error(caplog):
  function test_no_files_2 (line 70) | def test_no_files_2():
  function test_json (line 87) | def test_json():
  function test_weird_encoding (line 106) | def test_weird_encoding():
  function test_repr (line 124) | def test_repr():
  function test_bad_acorn (line 141) | def test_bad_acorn(mocker, caplog):
  function test_bad_ruby_parse (line 148) | def test_bad_ruby_parse(mocker):
  function test_bad_php_parse_a (line 155) | def test_bad_php_parse_a():
  function test_bad_php_parse_b (line 161) | def test_bad_php_parse_b():
  function test_no_source_type (line 167) | def test_no_source_type():
  function test_cli_no_args (line 174) | def test_cli_no_args(capsys):
  function test_cli_verbose_quiet (line 180) | def test_cli_verbose_quiet(capsys):
  function test_cli_log_default (line 185) | def test_cli_log_default(mocker):
  function test_cli_log_verbose (line 192) | def test_cli_log_verbose(mocker):
  function test_cli_log_quiet (line 199) | def test_cli_log_quiet(mocker):
  function test_subset_cli (line 205) | def test_subset_cli(mocker):
Condensed preview — 174 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (717K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 22,
    "preview": "github: scottrogowski\n"
  },
  {
    "path": ".gitignore",
    "chars": 105,
    "preview": ".DS_Store\n__pycache__\nout.*\n.coverage*\nhtmlcov\nbuild/\n*.egg-info\ndist\ncomposer.json\ncomposer.lock\nvendor\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1104,
    "preview": "# Code2flow CHANGELOG\n\n## [2.5.1] - 2023-01-08\n- Minor fix for installing code2flow in windows environments\n- Minor READ"
  },
  {
    "path": "LICENSE",
    "chars": 1054,
    "preview": "Copyright 2021 Scott Rogowski\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this soft"
  },
  {
    "path": "MANIFEST.in",
    "chars": 89,
    "preview": "include LICENSE, CHANGELOG.md\ninclude code2flow/get_ast.js\ninclude code2flow/get_ast.php\n"
  },
  {
    "path": "Makefile",
    "chars": 202,
    "preview": "build:\n\trm -rf dist\n\tpython3 setup.py sdist\n\ntest:\n\tpytest -n=4 --cov-report=html --cov-report=term --cov=code2flow -x\n\n"
  },
  {
    "path": "README.md",
    "chars": 8214,
    "preview": "![code2flow logo](https://raw.githubusercontent.com/scottrogowski/code2flow/master/assets/code2flowlogo.png)\n\n![Version "
  },
  {
    "path": "c2f",
    "chars": 120,
    "preview": "#!/usr/bin/env python3\n\nimport sys\nfrom code2flow.engine import main\n\nif __name__ == \"__main__\":\n    main(sys.argv[1:])\n"
  },
  {
    "path": "code2flow/__init__.py",
    "chars": 80,
    "preview": "from .engine import code2flow, VERSION\n\ncode2flow = code2flow\nVERSION = VERSION\n"
  },
  {
    "path": "code2flow/engine.py",
    "chars": 33239,
    "preview": "import argparse\nimport collections\nimport json\nimport logging\nimport os\nimport subprocess\nimport sys\nimport time\n\nfrom ."
  },
  {
    "path": "code2flow/get_ast.js",
    "chars": 326,
    "preview": "const fs = require('fs');\nconst {Parser} = require(\"acorn\")\n\nconst sourceType = process.argv[2]\nconst src = fs.readFileS"
  },
  {
    "path": "code2flow/get_ast.php",
    "chars": 427,
    "preview": "<?php\nrequire_once __DIR__ . '/vendor/autoload.php';\n\nuse PhpParser\\Error;\nuse PhpParser\\NodeDumper;\nuse PhpParser\\Parse"
  },
  {
    "path": "code2flow/javascript.py",
    "chars": 14486,
    "preview": "import logging\nimport os\nimport json\nimport subprocess\n\nfrom .model import (Group, Node, Call, Variable, BaseLanguage,\n "
  },
  {
    "path": "code2flow/model.py",
    "chars": 19944,
    "preview": "import abc\nimport os\n\n\nTRUNK_COLOR = '#966F33'\nLEAF_COLOR = '#6db33f'\nEDGE_COLORS = [\"#000000\", \"#E69F00\", \"#56B4E9\", \"#"
  },
  {
    "path": "code2flow/package.json",
    "chars": 50,
    "preview": "{\n  \"dependencies\": {\n    \"acorn\": \"^8.6.0\"\n  }\n}\n"
  },
  {
    "path": "code2flow/php.py",
    "chars": 13531,
    "preview": "import json\nimport os\nimport subprocess\n\nfrom .model import (Group, Node, Call, Variable, BaseLanguage,\n                "
  },
  {
    "path": "code2flow/python.py",
    "chars": 8644,
    "preview": "import ast\nimport logging\nimport os\n\nfrom .model import (OWNER_CONST, GROUP_TYPE, Group, Node, Call, Variable,\n         "
  },
  {
    "path": "code2flow/ruby.py",
    "chars": 10405,
    "preview": "import json\nimport subprocess\n\nfrom .model import (Group, Node, Call, Variable, BaseLanguage,\n                    OWNER_"
  },
  {
    "path": "make_expected.py",
    "chars": 1034,
    "preview": "#!/usr/bin/env python3\n\nimport os\nimport pprint\nimport sys\nimport tempfile\n\nfrom code2flow.engine import main\nfrom tests"
  },
  {
    "path": "requirements_dev.txt",
    "chars": 177,
    "preview": "# Testing\npytest>=6.2.5,<7.0\npygraphviz>=1.7,<2.0\npytest-cov>=2.10.1,<3.0\npytest-mock>=3.6.1,<4.0\npytest-xdist>=2.5.0,<3"
  },
  {
    "path": "setup.py",
    "chars": 953,
    "preview": "from setuptools import setup\n\nfrom code2flow.engine import VERSION\n\nurl_base = 'https://github.com/scottrogowski/code2fl"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/test_code/js/ambiguous_names/ambiguous_names.js",
    "chars": 377,
    "preview": "class Abra {\n    constructor() {\n        this.abra_it();\n        }\n    magic() {\n        console.log('magic 1');\n    }\n\n"
  },
  {
    "path": "tests/test_code/js/bad_parse/file_a_good.js",
    "chars": 20,
    "preview": "function a() {}\na()\n"
  },
  {
    "path": "tests/test_code/js/bad_parse/file_b_bad.js",
    "chars": 18,
    "preview": "function a() {\na)\n"
  },
  {
    "path": "tests/test_code/js/chained/chained.js",
    "chars": 312,
    "preview": "class Chain {\n    constructor(val) {\n        this.val = val;\n    }\n\n    add(b) {\n        this.val += b\n        return th"
  },
  {
    "path": "tests/test_code/js/class_in_function/class_in_function.js",
    "chars": 224,
    "preview": "function rectangleClassFactory() {\n    class Rectangle {\n        constructor(height, width) {\n            this.height = "
  },
  {
    "path": "tests/test_code/js/classes/classes.js",
    "chars": 580,
    "preview": "function print_hi() {\n    console.log(\"HI\")\n}\n\nclass Rectangle {\n  constructor(height, width) {\n    this.height = height"
  },
  {
    "path": "tests/test_code/js/complex_ownership/complex_ownership.js",
    "chars": 984,
    "preview": "class ABC {\n    constructor() {\n        this.b = 5;\n    }\n    doit() {\n        let _this = this;\n        return _this.b;"
  },
  {
    "path": "tests/test_code/js/exclude_modules/exclude_modules.js",
    "chars": 416,
    "preview": "//Node/CommonJS\nconst fs = require('fs')\nconst {readFile, chmod} = require('fs')\n\n\nfunction readFileSync() {\n    console"
  },
  {
    "path": "tests/test_code/js/exclude_modules_es6/exclude_modules_es6.js",
    "chars": 412,
    "preview": "//Node/CommonJS\nconst fs = import('fs');\nimport {readFile, chmod} from 'fs';\n\n\nfunction readFileSync() {\n    console.log"
  },
  {
    "path": "tests/test_code/js/globals/globals.js",
    "chars": 309,
    "preview": "var g = (function() {\n    function a() {\n        function c() {\n            function d() {\n                console.log('"
  },
  {
    "path": "tests/test_code/js/inheritance/inheritance.js",
    "chars": 423,
    "preview": "// from https://ruby-doc.com/docs/ProgrammingRuby/html/tut_modules.html\n\nfunction majorNum() {}\n\nfunction pentaNum() {}\n"
  },
  {
    "path": "tests/test_code/js/inheritance_attr/inheritance_attr.js",
    "chars": 205,
    "preview": "\nclass ClsA {\n    bark() {\n        console.log(\"woof\");\n    }\n}\nclass ClsB {\n    meow() {\n        console.log(\"meow\");\n "
  },
  {
    "path": "tests/test_code/js/moment/moment.js",
    "chars": 173902,
    "preview": "//! moment.js\n//! version : 2.29.1\n//! authors : Tim Wood, Iskren Chernev, Moment.js contributors\n//! license : MIT\n//! "
  },
  {
    "path": "tests/test_code/js/scoping/scoping.js",
    "chars": 294,
    "preview": "function scope_confusion() {\n    console.log('scope_confusion');\n}\n\nclass MyClass {\n    scope_confusion() {\n        cons"
  },
  {
    "path": "tests/test_code/js/simple_a_js/simple_a.js",
    "chars": 58,
    "preview": "function func_b() {}\n\nfunction func_a() {\n    func_b();\n}\n"
  },
  {
    "path": "tests/test_code/js/simple_b_js/simple_b.js",
    "chars": 149,
    "preview": "\n\nfunction a() {\n    b();\n}\n\n\nfunction b() {\n    a(\"STRC #\");\n}\n\n\nclass C {\n    d(param) {\n        a(\"AnotherSTR\");\n    "
  },
  {
    "path": "tests/test_code/js/ternary_new/ternary_new.js",
    "chars": 509,
    "preview": "class Abra {\n    init() {\n        return this.init()\n    }\n}\n\nclass Cadabra {\n    init() {\n        return this.init()\n  "
  },
  {
    "path": "tests/test_code/js/two_file_imports/imported.js",
    "chars": 279,
    "preview": "class myClass {\n    constructor() {\n        this.doit()\n    }\n\n    get doit() {\n        this.doit2()\n    }\n\n    doit2() "
  },
  {
    "path": "tests/test_code/js/two_file_imports/importer.js",
    "chars": 134,
    "preview": "const {myClass, inner, hi} = require(\"imported\");\n\nfunction outer() {\n    let cls = new myClass();\n    inner();\n    hi()"
  },
  {
    "path": "tests/test_code/js/two_file_simple/file_a.js",
    "chars": 32,
    "preview": "function a() {\n    b()\n}\n\n\na();\n"
  },
  {
    "path": "tests/test_code/js/two_file_simple/file_b.js",
    "chars": 59,
    "preview": "function b() {\n    console.log(\"here\")\n}\n\n\nfunction c() {}\n"
  },
  {
    "path": "tests/test_code/js/two_file_simple/shouldntberead",
    "chars": 26,
    "preview": "function d() {\n    e();\n}\n"
  },
  {
    "path": "tests/test_code/js/weird_assignments/weird_assignments.js",
    "chars": 70,
    "preview": "function get_ab() {\n    return {a: 1, b: 2};\n}\n\nlet {a, b} = get_ab()\n"
  },
  {
    "path": "tests/test_code/mjs/two_file_imports_es6/imported_es6.mjs",
    "chars": 235,
    "preview": "class myClass {\n    constructor() {\n        this.doit();\n    }\n\n    doit() {\n        this.doit2();\n    }\n\n    doit2() {\n"
  },
  {
    "path": "tests/test_code/mjs/two_file_imports_es6/importer_es6.mjs",
    "chars": 125,
    "preview": "import {myClass, inner} from \"./imported_es6.mjs\";\n\nfunction outer() {\n    let cls = new myClass();\n    inner();\n}\n\noute"
  },
  {
    "path": "tests/test_code/php/ambiguous_resolution/ambiguous_resolution.php",
    "chars": 537,
    "preview": "<?php\n\nclass Abra {\n    function magic() {\n        echo \"Abra.magic() cant resolve\";\n    }\n    function abra_it() {\n    "
  },
  {
    "path": "tests/test_code/php/anon/anonymous_function.php",
    "chars": 223,
    "preview": "\n<?php\n\n\n$greet = (static function($name): void\n{\n    printf(\"Hello %s\\r\\n\", $name);\n})('World');\n\n$saybye = (static fun"
  },
  {
    "path": "tests/test_code/php/anon2/anonymous_function2.php",
    "chars": 150,
    "preview": "\n<?php\n\nfunction func_a($name) {\n    echo \"Hello \" + $name;\n}\n\n$greet = (static function($name): void\n{\n    func_a($name"
  },
  {
    "path": "tests/test_code/php/bad_php/bad_php_a.php",
    "chars": 17,
    "preview": "<?php\na()\ndsl\n?>\n"
  },
  {
    "path": "tests/test_code/php/bad_php/bad_php_b.php",
    "chars": 40,
    "preview": "<html>\n<body>Hello World</body>\n</html>\n"
  },
  {
    "path": "tests/test_code/php/branch/branch.php",
    "chars": 52,
    "preview": "<?php\nfunction a() {\n\n}\n\nif (true) {\n    a();\n}\n\n?>\n"
  },
  {
    "path": "tests/test_code/php/chains/chains.php",
    "chars": 400,
    "preview": "<?php\nfunction a() {\n    echo \"A\";\n    b();\n}\n\n\nfunction b() {\n    echo \"B\";\n    a();\n}\n\n\nclass Cls {\n    function __con"
  },
  {
    "path": "tests/test_code/php/factory/currency.php",
    "chars": 1680,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\n/**\n * Currency Value Object.\n *\n * Holds Currency specific data.\n *\n"
  },
  {
    "path": "tests/test_code/php/factory/factory.php",
    "chars": 1158,
    "preview": "<?php\n\nrequire 'currency.php';\n\nuse Money\\Currencies;\nuse Money\\Currency;\n\n(static function (): void {\n    $buffer = <<<"
  },
  {
    "path": "tests/test_code/php/inheritance/inheritance.php",
    "chars": 1305,
    "preview": " <?php\n// from https://www.w3schools.com/php/php_oop_inheritance.asp\nclass FakeFruit {\n  public $name;\n  public $color;\n"
  },
  {
    "path": "tests/test_code/php/inheritance2/inheritance2.php",
    "chars": 1135,
    "preview": "<?php\n// from https://www.w3schools.com/php/php_oop_classes_abstract.asp\n\n// Parent class\nabstract class Car {\n  public "
  },
  {
    "path": "tests/test_code/php/instance_methods/instance_methods.php",
    "chars": 563,
    "preview": "<?php\n\nfunction main() {\n    echo \"main; This is called from abra.main\";\n}\n\nclass Abra {\n    function main() {\n        e"
  },
  {
    "path": "tests/test_code/php/money/Calculator/BcMathCalculator.php",
    "chars": 5804,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Calculator;\n\nuse InvalidArgumentException as CoreInvalidArgumentExcepti"
  },
  {
    "path": "tests/test_code/php/money/Calculator/GmpCalculator.php",
    "chars": 10290,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Calculator;\n\nuse InvalidArgumentException as CoreInvalidArgumentExcepti"
  },
  {
    "path": "tests/test_code/php/money/Calculator.php",
    "chars": 3749,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse Money\\Exception\\InvalidArgumentException;\n\n/**\n * Money calculati"
  },
  {
    "path": "tests/test_code/php/money/Converter.php",
    "chars": 2421,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse InvalidArgumentException;\n\nuse function sprintf;\n\n/**\n * Provides"
  },
  {
    "path": "tests/test_code/php/money/Currencies/AggregateCurrencies.php",
    "chars": 1480,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Currencies;\n\nuse AppendIterator;\nuse Money\\Currencies;\nuse Money\\Curren"
  },
  {
    "path": "tests/test_code/php/money/Currencies/BitcoinCurrencies.php",
    "chars": 868,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Currencies;\n\nuse ArrayIterator;\nuse Money\\Currencies;\nuse Money\\Currenc"
  },
  {
    "path": "tests/test_code/php/money/Currencies/CachedCurrencies.php",
    "chars": 2244,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Currencies;\n\nuse ArrayIterator;\nuse Cache\\TagInterop\\TaggableCacheItemI"
  },
  {
    "path": "tests/test_code/php/money/Currencies/CurrencyList.php",
    "chars": 1401,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Currencies;\n\nuse ArrayIterator;\nuse Money\\Currencies;\nuse Money\\Currenc"
  },
  {
    "path": "tests/test_code/php/money/Currencies/ISOCurrencies.php",
    "chars": 3208,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Currencies;\n\nuse ArrayIterator;\nuse Money\\Currencies;\nuse Money\\Currenc"
  },
  {
    "path": "tests/test_code/php/money/Currencies.php",
    "chars": 727,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse IteratorAggregate;\nuse Money\\Exception\\UnknownCurrencyException;\n"
  },
  {
    "path": "tests/test_code/php/money/Currency.php",
    "chars": 1138,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse JsonSerializable;\n\nuse function strtoupper;\n\n/**\n * Currency Valu"
  },
  {
    "path": "tests/test_code/php/money/CurrencyPair.php",
    "chars": 3134,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse InvalidArgumentException;\nuse JsonSerializable;\n\nuse function ass"
  },
  {
    "path": "tests/test_code/php/money/Exception/FormatterException.php",
    "chars": 259,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exception;\n\nuse Money\\Exception;\nuse RuntimeException;\n\n/**\n * Thrown w"
  },
  {
    "path": "tests/test_code/php/money/Exception/InvalidArgumentException.php",
    "chars": 555,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exception;\n\nuse InvalidArgumentException as CoreInvalidArgumentExceptio"
  },
  {
    "path": "tests/test_code/php/money/Exception/ParserException.php",
    "chars": 251,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exception;\n\nuse Money\\Exception;\nuse RuntimeException;\n\n/**\n * Thrown w"
  },
  {
    "path": "tests/test_code/php/money/Exception/UnknownCurrencyException.php",
    "chars": 262,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exception;\n\nuse DomainException;\nuse Money\\Exception;\n\n/**\n * Thrown wh"
  },
  {
    "path": "tests/test_code/php/money/Exception/UnresolvableCurrencyPairException.php",
    "chars": 786,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exception;\n\nuse InvalidArgumentException;\nuse Money\\Currency;\nuse Money"
  },
  {
    "path": "tests/test_code/php/money/Exception.php",
    "chars": 146,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\n/**\n * Common interface for all exceptions thrown by this library.\n *"
  },
  {
    "path": "tests/test_code/php/money/Exchange/ExchangerExchange.php",
    "chars": 1428,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exchange;\n\nuse Exchanger\\Contract\\ExchangeRateProvider;\nuse Exchanger\\C"
  },
  {
    "path": "tests/test_code/php/money/Exchange/FixedExchange.php",
    "chars": 1107,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exchange;\n\nuse Money\\Currency;\nuse Money\\CurrencyPair;\nuse Money\\Except"
  },
  {
    "path": "tests/test_code/php/money/Exchange/IndirectExchange.php",
    "chars": 4264,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exchange;\n\nuse Money\\Currencies;\nuse Money\\Currency;\nuse Money\\Currency"
  },
  {
    "path": "tests/test_code/php/money/Exchange/IndirectExchangeQueuedItem.php",
    "chars": 395,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exchange;\n\nuse Money\\Currency;\n\n/** @internal for sole consumption by {"
  },
  {
    "path": "tests/test_code/php/money/Exchange/ReversedCurrenciesExchange.php",
    "chars": 1307,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exchange;\n\nuse Money\\Currency;\nuse Money\\CurrencyPair;\nuse Money\\Except"
  },
  {
    "path": "tests/test_code/php/money/Exchange/SwapExchange.php",
    "chars": 985,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Exchange;\n\nuse Exchanger\\Exception\\Exception as ExchangerException;\nuse"
  },
  {
    "path": "tests/test_code/php/money/Exchange.php",
    "chars": 574,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse Money\\Exception\\UnresolvableCurrencyPairException;\n\n/**\n * Provid"
  },
  {
    "path": "tests/test_code/php/money/Formatter/AggregateMoneyFormatter.php",
    "chars": 1226,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Formatter;\n\nuse Money\\Exception\\FormatterException;\nuse Money\\Money;\nus"
  },
  {
    "path": "tests/test_code/php/money/Formatter/BitcoinMoneyFormatter.php",
    "chars": 2344,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Formatter;\n\nuse Money\\Currencies;\nuse Money\\Currencies\\BitcoinCurrencie"
  },
  {
    "path": "tests/test_code/php/money/Formatter/DecimalMoneyFormatter.php",
    "chars": 1389,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Formatter;\n\nuse Money\\Currencies;\nuse Money\\Money;\nuse Money\\MoneyForma"
  },
  {
    "path": "tests/test_code/php/money/Formatter/IntlLocalizedDecimalFormatter.php",
    "chars": 1651,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Formatter;\n\nuse Money\\Currencies;\nuse Money\\Money;\nuse Money\\MoneyForma"
  },
  {
    "path": "tests/test_code/php/money/Formatter/IntlMoneyFormatter.php",
    "chars": 1633,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Formatter;\n\nuse Money\\Currencies;\nuse Money\\Money;\nuse Money\\MoneyForma"
  },
  {
    "path": "tests/test_code/php/money/Money.php",
    "chars": 14736,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse InvalidArgumentException;\nuse JsonSerializable;\nuse Money\\Calcula"
  },
  {
    "path": "tests/test_code/php/money/MoneyFactory.php",
    "chars": 10768,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse InvalidArgumentException;\n\n/**\n * This is a generated file. Do no"
  },
  {
    "path": "tests/test_code/php/money/MoneyFormatter.php",
    "chars": 309,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\n/**\n * Formats Money objects.\n */\ninterface MoneyFormatter\n{\n    /**\n"
  },
  {
    "path": "tests/test_code/php/money/MoneyParser.php",
    "chars": 343,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\n/**\n * Parses a string into a Money object.\n */\ninterface MoneyParser"
  },
  {
    "path": "tests/test_code/php/money/Number.php",
    "chars": 9302,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money;\n\nuse InvalidArgumentException;\n\nuse function abs;\nuse function explode"
  },
  {
    "path": "tests/test_code/php/money/PHPUnit/Comparator.php",
    "chars": 1979,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\PHPUnit;\n\nuse Money\\Currencies\\AggregateCurrencies;\nuse Money\\Currencie"
  },
  {
    "path": "tests/test_code/php/money/Parser/AggregateMoneyParser.php",
    "chars": 1008,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Parser;\n\nuse Money\\Currency;\nuse Money\\Exception;\nuse Money\\Money;\nuse "
  },
  {
    "path": "tests/test_code/php/money/Parser/BitcoinMoneyParser.php",
    "chars": 1835,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Parser;\n\nuse Money\\Currencies\\BitcoinCurrencies;\nuse Money\\Currency;\nus"
  },
  {
    "path": "tests/test_code/php/money/Parser/DecimalMoneyParser.php",
    "chars": 2514,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Parser;\n\nuse Money\\Currencies;\nuse Money\\Currency;\nuse Money\\Exception\\"
  },
  {
    "path": "tests/test_code/php/money/Parser/IntlLocalizedDecimalParser.php",
    "chars": 2413,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Parser;\n\nuse Money\\Currencies;\nuse Money\\Currency;\nuse Money\\Exception\\"
  },
  {
    "path": "tests/test_code/php/money/Parser/IntlMoneyParser.php",
    "chars": 2440,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Money\\Parser;\n\nuse Money\\Currencies;\nuse Money\\Currency;\nuse Money\\Exception\\"
  },
  {
    "path": "tests/test_code/php/namespace_a/namespace_a.php",
    "chars": 314,
    "preview": "<?php\nnamespace NS;\n\nfunction namespaced_func() {\n    echo \"namespaced_func\";\n}\n\nclass Namespaced_cls {\n    function __c"
  },
  {
    "path": "tests/test_code/php/namespace_b/namespace_b1.php",
    "chars": 285,
    "preview": "<?php\n\nrequire_once(\"namespace_b2.php\");\nuse foo;\nuse foo as feline;\n\n// namespaced_func();\n// $a = new Namespaced_cls()"
  },
  {
    "path": "tests/test_code/php/namespace_b/namespace_b2.php",
    "chars": 1064,
    "preview": "<?php\n// namespace NSA {\n\n//     function namespaced_func() {\n//         echo \"namespaced_func\";\n//     }\n\n//     class "
  },
  {
    "path": "tests/test_code/php/namespace_c/namespace_c1.php",
    "chars": 140,
    "preview": "<?php\nrequire_once(\"namespace_c2.php\");\n\nuse function Outer\\Inner\\speak as talk;\nuse Outer\\Inner\\Cat as Kitty;\n\ntalk();\n"
  },
  {
    "path": "tests/test_code/php/namespace_c/namespace_c2.php",
    "chars": 356,
    "preview": "<?php\n\nnamespace Outer\\Inner;\n\nfunction speak() {\n    echo \"global speak\";\n}\n\nclass Cat {\n    static function meow() {\n "
  },
  {
    "path": "tests/test_code/php/nested/nested.php",
    "chars": 197,
    "preview": "<?php\n\n// Nested functions in PHP are global so should be placed on the same scope\n\nfunction outer() {\n    echo \"outer\";"
  },
  {
    "path": "tests/test_code/php/nested_calls/nested_calls.php",
    "chars": 518,
    "preview": "<?php\n\nfunction x_() {\n    (new Cls()).func2();\n}\nfunction y_() {}\nfunction z_() {}\n\nfunction func() {}\nfunction func2()"
  },
  {
    "path": "tests/test_code/php/publicprivateprotected/publicprivateprotected.php",
    "chars": 571,
    "preview": "<?php\n// From https://www.w3schools.com/php/php_oop_access_modifiers.asp\nclass Fruit {\n  public $name;\n  public $color;\n"
  },
  {
    "path": "tests/test_code/php/resolve_correct_class/rcc.php",
    "chars": 462,
    "preview": "<?php\nfunction func_1() {\n    echo \"this should never get called\";\n}\n\nclass Alpha {\n    function func_1() {\n        echo"
  },
  {
    "path": "tests/test_code/php/simple_a/simple_a.php",
    "chars": 105,
    "preview": "<?php\n\nfunction func_b() {\n    echo \"hello world\";\n}\n\nfunction func_a() {\n    func_b();\n}\n\nfunc_a();\n\n?>\n"
  },
  {
    "path": "tests/test_code/php/simple_b/simple_b.php",
    "chars": 223,
    "preview": "<?php\n\nrequire __DIR__.'/../vendor/autoload.php';\n\nfunction a($param) {\n    b($param);\n}\n\n\nfunction b() {\n    a(\"STRC #\""
  },
  {
    "path": "tests/test_code/php/static/static.php",
    "chars": 388,
    "preview": " <?php\n\nfunction say_name() {}\n\nclass Greeting {\n  public static function welcome() {\n    echo \"Hello World!\";\n  }\n\n  fu"
  },
  {
    "path": "tests/test_code/php/traits/traits.php",
    "chars": 480,
    "preview": "<?php\ntrait message1 {\n  public function msg1() {\n    echo \"OOP is fun! \";\n  }\n}\n\ntrait message2 {\n  public function msg"
  },
  {
    "path": "tests/test_code/php/two_file_simple/file_a.php",
    "chars": 70,
    "preview": "<?php\ninclude_once('file_b.php');\nfunction a() {\n    b();\n}\n\n\na();\n?>\n"
  },
  {
    "path": "tests/test_code/php/two_file_simple/file_b.php",
    "chars": 61,
    "preview": "<?php\nfunction b() {\n    echo \"here\";\n}\n\n\nfunction c() {}\n?>\n"
  },
  {
    "path": "tests/test_code/php/weird_assign/weird_assign.php",
    "chars": 123,
    "preview": "<?php\n\nfunction a() {}\nfunction b() {}\n\nfunction c($x, $y) {\n    [$integer, $remainder] = a(b($x), b((string) $y));\n\n}\n\n"
  },
  {
    "path": "tests/test_code/py/ambiguous_resolution/ambiguous_resolution.py",
    "chars": 254,
    "preview": "class Abra():\n    def magic():\n        pass\n\n    def abra_it():\n        pass\n\n\nclass Cadabra():\n    def magic():\n       "
  },
  {
    "path": "tests/test_code/py/async_basic/async_basic.py",
    "chars": 188,
    "preview": "import asyncio\n\nclass A:\n    def __init__(self):\n        pass\n    async def test(self):\n        print(\"hello world\")\n\nas"
  },
  {
    "path": "tests/test_code/py/chained/chained.py",
    "chars": 302,
    "preview": "class Chain():\n    def __init__(self, val):\n        self.val = val\n\n    def add(self, b):\n        self.val += b\n        "
  },
  {
    "path": "tests/test_code/py/exclude_modules/exclude_modules.py",
    "chars": 316,
    "preview": "import re\nfrom re import match\n\n\ndef search():\n    print(\"This is the wrong search\")\n\n\ndef beta():\n    print(\"this still"
  },
  {
    "path": "tests/test_code/py/exclude_modules_two_files/exclude_modules_a.py",
    "chars": 105,
    "preview": "import re\nfrom exclude_modules_b import match\n\n\ndef a():\n    re.match()\n\n\ndef b():\n    match()\n\n\na()\nb()\n"
  },
  {
    "path": "tests/test_code/py/exclude_modules_two_files/exclude_modules_b.py",
    "chars": 32,
    "preview": "def match():\n    print(\"match\")\n"
  },
  {
    "path": "tests/test_code/py/import_paths/abra.py",
    "chars": 19,
    "preview": "def abra2():\n\tpass\n"
  },
  {
    "path": "tests/test_code/py/import_paths/cadabra.py",
    "chars": 22,
    "preview": "def cadabra2():\n\tpass\n"
  },
  {
    "path": "tests/test_code/py/import_paths/import_paths.py",
    "chars": 153,
    "preview": "import abra\nfrom . import cadabra\nfrom abra import abra2 as abra3\n\ndef main():\n\tabra3()\n\tcadabra.cadabra2()\n\tmain2()\n\nde"
  },
  {
    "path": "tests/test_code/py/inherits/inherits.py",
    "chars": 688,
    "preview": "# from https://ruby-doc.com/docs/ProgrammingRuby/html/tut_modules.html\n\nfrom inherits_import import MajorScales, Pentato"
  },
  {
    "path": "tests/test_code/py/inherits/inherits_import.py",
    "chars": 356,
    "preview": "# from https://ruby-doc.com/docs/ProgrammingRuby/html/tut_modules.html\n\nclass MajorScales():\n    def majorNum(self):\n   "
  },
  {
    "path": "tests/test_code/py/init/init.py",
    "chars": 269,
    "preview": "from the_import import ProvincialClass as pc, imported_func\n\n\nclass Abra():\n    def __init__(self):\n        self.cadabra"
  },
  {
    "path": "tests/test_code/py/init/the_import.py",
    "chars": 181,
    "preview": "class ProvincialClass():\n    def __init__(self):\n        print(\"here\")\n\n\nclass HiddenClass():\n    def __init__(self):\n  "
  },
  {
    "path": "tests/test_code/py/nested_calls/nested_calls.py",
    "chars": 295,
    "preview": "from typing import Callable\n\ndef trace(fn: Callable) -> Callable:\n    def wrapper(*args, **kwargs):\n        print('trace"
  },
  {
    "path": "tests/test_code/py/nested_class/nested_class.py",
    "chars": 298,
    "preview": "class Outer():\n    class Inner():\n        def inner_func():\n            Outer().outer_func()\n\n    def outer_func(a):\n   "
  },
  {
    "path": "tests/test_code/py/pytz/__init__.py",
    "chars": 14486,
    "preview": "'''\ndatetime.tzinfo timezone definitions generated from the\nOlson timezone database:\n\n    ftp://elsie.nci.nih.gov/pub/tz"
  },
  {
    "path": "tests/test_code/py/pytz/exceptions.py",
    "chars": 1571,
    "preview": "'''\nCustom exceptions raised by pytz.\n'''\n\n__all__ = [\n    'UnknownTimeZoneError', 'InvalidTimeError', 'AmbiguousTimeErr"
  },
  {
    "path": "tests/test_code/py/pytz/lazy.py",
    "chars": 5404,
    "preview": "from threading import RLock\ntry:\n    from collections.abc import Mapping as DictMixin\nexcept ImportError:  # Python < 3."
  },
  {
    "path": "tests/test_code/py/pytz/reference.py",
    "chars": 3778,
    "preview": "'''\nReference tzinfo implementations from the Python docs.\nUsed for testing against as they are only correct for the yea"
  },
  {
    "path": "tests/test_code/py/pytz/tzfile.py",
    "chars": 4723,
    "preview": "'''\n$Id: tzfile.py,v 1.8 2004/06/03 00:15:24 zenzen Exp $\n'''\n\nfrom datetime import datetime\nfrom struct import unpack, "
  },
  {
    "path": "tests/test_code/py/pytz/tzinfo.py",
    "chars": 19273,
    "preview": "'''Base classes and helpers for building zone specific tzinfo classes'''\n\nfrom datetime import datetime, timedelta, tzin"
  },
  {
    "path": "tests/test_code/py/resolve_correct_class/rcc.py",
    "chars": 400,
    "preview": "def func_1():\n    print(\"global func_1\")\n\n\nclass Alpha():\n    def func_1(self):\n        print(\"alpha func_1\")\n        se"
  },
  {
    "path": "tests/test_code/py/simple_a/simple_a.py",
    "chars": 51,
    "preview": "def func_b():\n    pass\n\ndef func_a():\n    func_b()\n"
  },
  {
    "path": "tests/test_code/py/simple_b/simple_b.py",
    "chars": 146,
    "preview": "\"\"\"\nComments\n\"\"\"\n\n\ndef a():\n    b()\n\n\n# comments\ndef b():\n    a(\"\"\"STRC #\"\"\")\n\n\nclass c():\n    def d(a=\"String\"):\n      "
  },
  {
    "path": "tests/test_code/py/subset_find_exception/two.py",
    "chars": 100,
    "preview": "def private():\n\tpass\n\nclass Abra:\n\tdef func():\n\t\tprivate()\n\nclass Cadabra:\n\tdef func():\n\t\tprivate()\n"
  },
  {
    "path": "tests/test_code/py/subset_find_exception/zero.py",
    "chars": 61,
    "preview": "def private():\n\tpass\n\nclass Abra:\n\tdef other():\n\t\tprivate()\n\n"
  },
  {
    "path": "tests/test_code/py/two_file_simple/file_a.py",
    "chars": 77,
    "preview": "from file_b import b\n\n\ndef a():\n    b()\n\n\nif __name__ == '__main__':\n    a()\n"
  },
  {
    "path": "tests/test_code/py/two_file_simple/file_b.py",
    "chars": 47,
    "preview": "def b():\n    print(\"here\")\n\n\ndef c():\n    pass\n"
  },
  {
    "path": "tests/test_code/py/two_file_simple/shouldntberead",
    "chars": 17,
    "preview": "def d():\n    e()\n"
  },
  {
    "path": "tests/test_code/py/weird_calls/weird_calls.py",
    "chars": 331,
    "preview": "def print_it(string):\n    print(string)\n\n\ndef func_a():\n    print_it(\"func_a\")\n\n\ndef func_b():\n    print_it(\"func_a\")\n\n\n"
  },
  {
    "path": "tests/test_code/py/weird_encoding/weird_encoding.py",
    "chars": 32,
    "preview": "def a():\n    print(\"😅😂😘\")\n\n\na()\n"
  },
  {
    "path": "tests/test_code/py/weird_imports/weird_imports.py",
    "chars": 179,
    "preview": "from re import (search, match)\nimport re.match as mitch\n\n\ndef main():\n    a = b = search(\"abc\", \"def\")\n    c = mitch(\"ab"
  },
  {
    "path": "tests/test_code/rb/ambiguous_resolution/ambiguous_resolution.rb",
    "chars": 256,
    "preview": "class Abra\n    def magic()\n    end\n\n    def abra_it()\n    end\nend\n\nclass Cadabra\n    def magic()\n    end\n\n    def cadabr"
  },
  {
    "path": "tests/test_code/rb/chains/chains.rb",
    "chars": 280,
    "preview": "def a\n    puts \"A\"\n    b\nend\n\ndef b\n    puts \"B\"\n    a\nend\n\nclass Cls\n    def initialize\n        puts \"init\"\n    end\n\n  "
  },
  {
    "path": "tests/test_code/rb/doublecolon/doublecolon.rb",
    "chars": 470,
    "preview": "# def func_a\n#     puts \"global func_a\"\n# end\n\n# def func_b\n#     puts \"global func_b\"\n# end\n\nclass Class1\n    def self."
  },
  {
    "path": "tests/test_code/rb/inheritance_2/inheritance_2.rb",
    "chars": 295,
    "preview": "# from https://launchschool.com/books/oo_ruby/read/inheritance\n\ndef speak\n  puts \"WOOF\"\nend\n\nclass Animal\n  def speak\n  "
  },
  {
    "path": "tests/test_code/rb/instance_methods/instance_methods.rb",
    "chars": 365,
    "preview": "def main\n    puts(\"This is not called\")\nend\n\nclass Abra\n    def main\n        def nested\n            puts(\"This will refe"
  },
  {
    "path": "tests/test_code/rb/modules/modules.rb",
    "chars": 618,
    "preview": "# from https://ruby-doc.com/docs/ProgrammingRuby/html/tut_modules.html\n\ndef majorNum\nend\n\ndef pentaNum\nend\n\nmodule Major"
  },
  {
    "path": "tests/test_code/rb/nested/nested.rb",
    "chars": 581,
    "preview": "module Mod\n    def func_1\n        puts(\"func_1 outer\")\n    end\n\n    def func_2\n        puts(\"func_2 outer\")\n    end\n\n   "
  },
  {
    "path": "tests/test_code/rb/nested_classes/nested_classes.rb",
    "chars": 607,
    "preview": "class Mod\n    def func_1\n        puts(\"func_1 outer\")\n    end\n\n    def func_2\n        puts(\"func_2 outer\")\n    end\n\n    "
  },
  {
    "path": "tests/test_code/rb/onelinefile/onelinefile.rb",
    "chars": 31,
    "preview": "adder = lambda { |a, b| a + b}\n"
  },
  {
    "path": "tests/test_code/rb/public_suffix/public_suffix/domain.rb",
    "chars": 6758,
    "preview": "# frozen_string_literal: true\n\n# = Public Suffix\n#\n# Domain name parser based on the Public Suffix List.\n#\n# Copyright ("
  },
  {
    "path": "tests/test_code/rb/public_suffix/public_suffix/errors.rb",
    "chars": 874,
    "preview": "# frozen_string_literal: true\n\n# = Public Suffix\n#\n# Domain name parser based on the Public Suffix List.\n#\n# Copyright ("
  },
  {
    "path": "tests/test_code/rb/public_suffix/public_suffix/list.rb",
    "chars": 6720,
    "preview": "# frozen_string_literal: true\n\n# = Public Suffix\n#\n# Domain name parser based on the Public Suffix List.\n#\n# Copyright ("
  },
  {
    "path": "tests/test_code/rb/public_suffix/public_suffix/rule.rb",
    "chars": 10473,
    "preview": "# frozen_string_literal: true\n\n# = Public Suffix\n#\n# Domain name parser based on the Public Suffix List.\n#\n# Copyright ("
  },
  {
    "path": "tests/test_code/rb/public_suffix/public_suffix/version.rb",
    "chars": 249,
    "preview": "# frozen_string_literal: true\n\n#\n# = Public Suffix\n#\n# Domain name parser based on the Public Suffix List.\n#\n# Copyright"
  },
  {
    "path": "tests/test_code/rb/public_suffix/public_suffix.rb",
    "chars": 6391,
    "preview": "# frozen_string_literal: true\n\n# = Public Suffix\n#\n# Domain name parser based on the Public Suffix List.\n#\n# Copyright ("
  },
  {
    "path": "tests/test_code/rb/resolve_correct_class/rcc.rb",
    "chars": 426,
    "preview": "def func_1\n    puts \"this should never get called\"\nend\n\nclass Alpha\n    def func_1()\n        puts \"alpha func_1\"\n       "
  },
  {
    "path": "tests/test_code/rb/simple_a/simple_a.rb",
    "chars": 84,
    "preview": "def func_b(var)\n    puts \"hello world\"\nend\n\ndef func_a()\n    func_b()\nend\n\nfunc_a()\n"
  },
  {
    "path": "tests/test_code/rb/simple_b/simple_b.rb",
    "chars": 224,
    "preview": "\"\"\"\nComments\n\"\"\"\n\n\ndef a()\n    b()\nend\n\n# comments\ndef b()\n    a(\"\"\"STRC #\"\"\")\nend\n\nclass Cls\n    def initialize(val)\n  "
  },
  {
    "path": "tests/test_code/rb/split_modules/split_modules_a.rb",
    "chars": 189,
    "preview": "require_relative(\"split_modules_b\")\n\ndef say_hi\nend\n\ndef say_bye\nend\n\nmodule Split\n    def self.say_hi\n        puts \"hi\""
  },
  {
    "path": "tests/test_code/rb/split_modules/split_modules_b.rb",
    "chars": 238,
    "preview": "module Split\n\n    class MyClass\n        def initialize\n            puts \"initialize\"\n        end\n\n        def doit\n     "
  },
  {
    "path": "tests/test_code/rb/two_file_simple/file_a.rb",
    "chars": 82,
    "preview": "require_relative 'file_b.rb'\n\ndef abra()\n    babra()\nend\n\n\nif true\n    abra()\nend\n"
  },
  {
    "path": "tests/test_code/rb/two_file_simple/file_b.rb",
    "chars": 56,
    "preview": "def babra()\n    print(\"here\")\nend\n\n\ndef cadabra()\nend\n\n\n"
  },
  {
    "path": "tests/test_code/rb/weird_chains/weird_chains.rb",
    "chars": 377,
    "preview": "class DivByTwo\n    def initialize(val: 0)\n        @val = val\n    end\n\n    def +(val)\n        @val += (val / 2)\n        s"
  },
  {
    "path": "tests/test_graphs.py",
    "chars": 3367,
    "preview": "import io\nimport os\nimport sys\n\nimport pygraphviz\nimport pytest\n\nsys.path.append(os.getcwd().split('/tests')[0])\n\nfrom c"
  },
  {
    "path": "tests/test_interface.py",
    "chars": 7788,
    "preview": "import json\nimport locale\nimport logging\nimport os\nimport shutil\nimport sys\n\nimport pytest\n\nsys.path.append(os.getcwd()."
  },
  {
    "path": "tests/testdata.py",
    "chars": 160144,
    "preview": "testdata = {\n    \"py\": [\n        {\n            \"test_name\": \"simple_a\",\n            \"directory\": \"simple_a\",\n           "
  }
]

About this extraction

This page contains the full source code of the scottrogowski/code2flow GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 174 files (658.0 KB), approximately 157.2k tokens, and a symbol index with 1209 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!