# solc-ast
[![Pypi Status](https://img.shields.io/pypi/v/py-solc-ast.svg)](https://pypi.org/project/py-solc-ast/) [![Build Status](https://img.shields.io/github/workflow/status/iamdefinitelyahuman/py-solc-ast/main%20workflow/master)](https://github.com/iamdefinitelyahuman/py-solc-ast/actions) [![Coverage Status](https://img.shields.io/codecov/c/github/iamdefinitelyahuman/py-solc-ast)](https://codecov.io/gh/iamdefinitelyahuman/py-solc-ast)
A tool for exploring the Solidity abstrax syntrax tree as generated by the [solc](https://github.com/ethereum/solidity) compiler.
## Installation
You can install the latest release via `pip`:
```bash
$ pip install py-solc-ast
```
Or clone the repo and use `setuptools`:
```bash
$ python setup.py install
```
## Usage
First, use [py-solc-x](https://github.com/iamdefinitelyahuman/py-solc-x) to compile your contracts to the [standard JSON output format](https://solidity.readthedocs.io/en/latest/using-the-compiler.html#output-description).
```python
>>> import json
>>> import solcx
>>> input_json = json.load(open('input.json'))
>>> output_json = solcx.compile_standard(input_json)
```
Next, import `solcast` and initialize using `from_standard_output_json` or `from_standard_output`. This returns a list of `SourceUnit` objects, which each represent the base AST node in a Solidity source file.
```python
>>> import solcast
>>> nodes = solcast.from_standard_output(output_json)
>>> nodes
[<SourceUnit iterable 'contracts/Token.sol'>, <SourceUnit iterable 'contracts/SafeMath.sol'>]
```
You can also generate a single `SourceUnit` directly from that source's AST:
```python
>>> import solcast
>>> node = solcast.from_ast(output_json["sources"]["contracts/Token.sol"]["ast"])
>>> node
<SourceUnit iterable 'contracts/Token.sol'>
```
### Interacting with Nodes
Each node has the following attributes:
```python
>>> node
<FunctionDefinition iterable 'mul'>
>>> node.depth # Number of nodes between this node and the SourceUnit
2
>>> node.offset # Absolute source offsets as a (start, stop) tuple
(1693, 2151)
>>> node.contract_id # Contract ID as given by the standard compiler JSON
2
>>> node.fields # List of fields for this node
['baseNodeType', 'documentation', 'id', 'implemented', 'kind', 'modifiers', 'name', 'nodeType', 'nodes', 'parameters', 'returnParameters', 'scope', 'src', 'stateMutability', 'superFunction', 'visibility']
```
Fields mostly follow the expected [AST grammar](https://solidity.readthedocs.io/en/latest/miscellaneous.html#language-grammar). One notable difference: `Block` nodes are omitted and the body of each `Block` is available within it's parent as the attribute `nodes`. Nodes containing a body are iterable and can be accessed with list-like syntax. Additionally, any child node with a `name` field is accessible using dict-like syntax.
The following additional fields are also available:
* Most nodes have a `baseNodeType` field as defined in [grammar.py](solcast/grammar.py)
* `ContractDefinition` nodes have `dependencies` and `libraries` fields that point to related `ContractDefition` nodes
Some Examples:
```python
>>> source_node
<SourceUnit iterable 'contracts/math/SafeMath.sol'>
>>> source_node.keys()
['absolutePath', 'children', 'contract_id', 'depth', 'exportedSymbols', 'id', 'is_child_of', 'is_parent_of', 'keys', 'nodeType', 'nodes', 'offset', 'parent', 'parents', 'src']
>>> source_node.nodes
[<PragmaDirective object>, <ContractDefinition iterable 'SafeMath'>]
>>> source_node[1]
<ContractDefinition iterable 'SafeMath'>
>>> source_node['SafeMath']
<ContractDefinition iterable 'SafeMath'>
>>> source_node['SafeMath'].keys()
['baseContracts', 'children', 'contractDependencies', 'contractKind', 'contract_id', 'dependencies', 'depth', 'documentation', 'fullyImplemented', 'id', 'is_child_of', 'is_parent_of', 'keys', 'libraries', 'linearizedBaseContracts', 'name', 'nodeType', 'nodes', 'offset', 'parent', 'parents', 'scope', 'src']
>>> source_node['SafeMath'].nodes
[<FunctionDefinition iterable 'add'>, <FunctionDefinition iterable 'sub'>, <FunctionDefinition iterable 'mul'>, <FunctionDefinition iterable 'div'>, <FunctionDefinition iterable 'mod'>]
>>> source_node['SafeMath']['mul']
<FunctionDefinition iterable 'mul'>
>>> source_node['SafeMath']['mul']
[<IfStatement object>, <VariableDeclarationStatement object>, <FunctionCall object>, <Return object>]
```
### Exploring the Tree
The `Node.children()` method is used to search and filter through child nodes of a given node. It takes any of the following keyword arguments:
* `depth`: Number of levels of children to traverse. `0` returns only this node.
* `include_self`: Includes this node in the results.
* `include_parents`: Includes nodes that match in the results, when they also have child nodes that match.
* `include_children`: If True, as soon as a match is found it's children will not be included in the search.
* `required_offset`: Only match nodes with a source offset that contains this offset.
* `offset_limits`: Only match nodes when their source offset is contained inside this source offset.
* `filters`: Dictionary of `{'attribute': "value"}` that children must match. Can also be given as a list of dicts, children that match any of the dicts will be returned.
* `exclude_filter`: Dictionary of `{'attribute': "value"}` that children cannot match.
```python
>>> node = s['Token']['transfer']
>>> node.children(
include_children=False,
filters={'nodeType': "FunctionCall", "expression.name": "require"}
)
[<FunctionCall>]
```
`Node.parent()` and `Node.parents()` are used to travel back up the tree. They take the following arguments:
* `depth`: Depth limit. If given as a negative value, it will be subtracted from this object's depth.
* `filters`: Dictionary of `{'attribute': "value"}` that parents must match.
`Node.parent()` returns one result, `Node.parents()` returns a list of matches.
```python
>>> node.parents()
[<ContractDefinition iterable 'Token'>, <SourceUnit iterable object 'contracts/Token.sol'>]
```
## Tests
To run the test suite:
```bash
$ tox
```
## Development
Comments, questions, criticisms and pull requests are welcomed! Feel free to open an issue if you encounter a problem or would like to suggest a new feature.
## License
This project is licensed under the [MIT license](LICENSE).
Raw data
{
"_id": null,
"home_page": "https://github.com/iamdefinitelyahuman/py-solc-ast",
"name": "py-solc-ast",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.6, <4",
"maintainer_email": "",
"keywords": "ethereum,solidity,solc,ast",
"author": "Ben Hauser",
"author_email": "ben@hauser.id",
"download_url": "https://files.pythonhosted.org/packages/6b/49/4e463051d0936525866a516fc49b703e537e7f1e66234f8eb5df317be9c3/py-solc-ast-1.2.9.tar.gz",
"platform": "",
"description": "# solc-ast\n\n[![Pypi Status](https://img.shields.io/pypi/v/py-solc-ast.svg)](https://pypi.org/project/py-solc-ast/) [![Build Status](https://img.shields.io/github/workflow/status/iamdefinitelyahuman/py-solc-ast/main%20workflow/master)](https://github.com/iamdefinitelyahuman/py-solc-ast/actions) [![Coverage Status](https://img.shields.io/codecov/c/github/iamdefinitelyahuman/py-solc-ast)](https://codecov.io/gh/iamdefinitelyahuman/py-solc-ast)\n\nA tool for exploring the Solidity abstrax syntrax tree as generated by the [solc](https://github.com/ethereum/solidity) compiler.\n\n## Installation\n\nYou can install the latest release via `pip`:\n\n```bash\n$ pip install py-solc-ast\n```\n\nOr clone the repo and use `setuptools`:\n\n```bash\n$ python setup.py install\n```\n\n## Usage\n\nFirst, use [py-solc-x](https://github.com/iamdefinitelyahuman/py-solc-x) to compile your contracts to the [standard JSON output format](https://solidity.readthedocs.io/en/latest/using-the-compiler.html#output-description).\n\n```python\n>>> import json\n>>> import solcx\n>>> input_json = json.load(open('input.json'))\n>>> output_json = solcx.compile_standard(input_json)\n```\n\nNext, import `solcast` and initialize using `from_standard_output_json` or `from_standard_output`. This returns a list of `SourceUnit` objects, which each represent the base AST node in a Solidity source file.\n\n```python\n>>> import solcast\n>>> nodes = solcast.from_standard_output(output_json)\n>>> nodes\n[<SourceUnit iterable 'contracts/Token.sol'>, <SourceUnit iterable 'contracts/SafeMath.sol'>]\n```\n\nYou can also generate a single `SourceUnit` directly from that source's AST:\n\n```python\n>>> import solcast\n>>> node = solcast.from_ast(output_json[\"sources\"][\"contracts/Token.sol\"][\"ast\"])\n>>> node\n<SourceUnit iterable 'contracts/Token.sol'>\n```\n\n### Interacting with Nodes\n\nEach node has the following attributes:\n\n```python\n>>> node\n<FunctionDefinition iterable 'mul'>\n\n>>> node.depth # Number of nodes between this node and the SourceUnit\n2\n\n>>> node.offset # Absolute source offsets as a (start, stop) tuple\n(1693, 2151)\n\n>>> node.contract_id # Contract ID as given by the standard compiler JSON\n2\n\n>>> node.fields # List of fields for this node\n['baseNodeType', 'documentation', 'id', 'implemented', 'kind', 'modifiers', 'name', 'nodeType', 'nodes', 'parameters', 'returnParameters', 'scope', 'src', 'stateMutability', 'superFunction', 'visibility']\n\n```\n\nFields mostly follow the expected [AST grammar](https://solidity.readthedocs.io/en/latest/miscellaneous.html#language-grammar). One notable difference: `Block` nodes are omitted and the body of each `Block` is available within it's parent as the attribute `nodes`. Nodes containing a body are iterable and can be accessed with list-like syntax. Additionally, any child node with a `name` field is accessible using dict-like syntax.\n\nThe following additional fields are also available:\n\n* Most nodes have a `baseNodeType` field as defined in [grammar.py](solcast/grammar.py)\n* `ContractDefinition` nodes have `dependencies` and `libraries` fields that point to related `ContractDefition` nodes\n\nSome Examples:\n\n```python\n>>> source_node\n<SourceUnit iterable 'contracts/math/SafeMath.sol'>\n\n>>> source_node.keys()\n['absolutePath', 'children', 'contract_id', 'depth', 'exportedSymbols', 'id', 'is_child_of', 'is_parent_of', 'keys', 'nodeType', 'nodes', 'offset', 'parent', 'parents', 'src']\n\n>>> source_node.nodes\n[<PragmaDirective object>, <ContractDefinition iterable 'SafeMath'>]\n\n>>> source_node[1]\n<ContractDefinition iterable 'SafeMath'>\n\n>>> source_node['SafeMath']\n<ContractDefinition iterable 'SafeMath'>\n\n>>> source_node['SafeMath'].keys()\n['baseContracts', 'children', 'contractDependencies', 'contractKind', 'contract_id', 'dependencies', 'depth', 'documentation', 'fullyImplemented', 'id', 'is_child_of', 'is_parent_of', 'keys', 'libraries', 'linearizedBaseContracts', 'name', 'nodeType', 'nodes', 'offset', 'parent', 'parents', 'scope', 'src']\n\n>>> source_node['SafeMath'].nodes\n[<FunctionDefinition iterable 'add'>, <FunctionDefinition iterable 'sub'>, <FunctionDefinition iterable 'mul'>, <FunctionDefinition iterable 'div'>, <FunctionDefinition iterable 'mod'>]\n\n>>> source_node['SafeMath']['mul']\n<FunctionDefinition iterable 'mul'>\n\n>>> source_node['SafeMath']['mul']\n[<IfStatement object>, <VariableDeclarationStatement object>, <FunctionCall object>, <Return object>]\n```\n\n### Exploring the Tree\n\nThe `Node.children()` method is used to search and filter through child nodes of a given node. It takes any of the following keyword arguments:\n\n* `depth`: Number of levels of children to traverse. `0` returns only this node.\n* `include_self`: Includes this node in the results.\n* `include_parents`: Includes nodes that match in the results, when they also have child nodes that match.\n* `include_children`: If True, as soon as a match is found it's children will not be included in the search.\n* `required_offset`: Only match nodes with a source offset that contains this offset.\n* `offset_limits`: Only match nodes when their source offset is contained inside this source offset.\n* `filters`: Dictionary of `{'attribute': \"value\"}` that children must match. Can also be given as a list of dicts, children that match any of the dicts will be returned.\n* `exclude_filter`: Dictionary of `{'attribute': \"value\"}` that children cannot match.\n\n```python\n>>> node = s['Token']['transfer']\n>>> node.children(\n include_children=False,\n filters={'nodeType': \"FunctionCall\", \"expression.name\": \"require\"}\n)\n[<FunctionCall>]\n```\n\n`Node.parent()` and `Node.parents()` are used to travel back up the tree. They take the following arguments:\n\n* `depth`: Depth limit. If given as a negative value, it will be subtracted from this object's depth.\n* `filters`: Dictionary of `{'attribute': \"value\"}` that parents must match.\n\n`Node.parent()` returns one result, `Node.parents()` returns a list of matches.\n\n```python\n>>> node.parents()\n[<ContractDefinition iterable 'Token'>, <SourceUnit iterable object 'contracts/Token.sol'>]\n```\n\n## Tests\n\nTo run the test suite:\n\n```bash\n$ tox\n```\n\n## Development\n\nComments, questions, criticisms and pull requests are welcomed! Feel free to open an issue if you encounter a problem or would like to suggest a new feature.\n\n## License\n\nThis project is licensed under the [MIT license](LICENSE).\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A tool for exploring the abstract syntax tree generated by solc.",
"version": "1.2.9",
"split_keywords": [
"ethereum",
"solidity",
"solc",
"ast"
],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "2f80c23c4a9b634948c42df059c62b4b",
"sha256": "f636217ef77bbe0f9c87a71af2f6cc9577f6301aa2ffb9af119f4c8fa8522b2d"
},
"downloads": -1,
"filename": "py_solc_ast-1.2.9-py3-none-any.whl",
"has_sig": false,
"md5_digest": "2f80c23c4a9b634948c42df059c62b4b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6, <4",
"size": 10028,
"upload_time": "2021-07-26T19:22:24",
"upload_time_iso_8601": "2021-07-26T19:22:24.394783Z",
"url": "https://files.pythonhosted.org/packages/1c/9f/b16a81f4507c680b92fffbc992dd7c032f0e232fc9e2a03817536c39c9dc/py_solc_ast-1.2.9-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "4fe28cda65bbf61542b8b00620780ee3",
"sha256": "5a5c3bb1998de32eed4b793ebbf2f14f1fd5c681cf8b62af6b8f9f76b805164d"
},
"downloads": -1,
"filename": "py-solc-ast-1.2.9.tar.gz",
"has_sig": false,
"md5_digest": "4fe28cda65bbf61542b8b00620780ee3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6, <4",
"size": 9269,
"upload_time": "2021-07-26T19:22:26",
"upload_time_iso_8601": "2021-07-26T19:22:26.691549Z",
"url": "https://files.pythonhosted.org/packages/6b/49/4e463051d0936525866a516fc49b703e537e7f1e66234f8eb5df317be9c3/py-solc-ast-1.2.9.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2021-07-26 19:22:26",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "iamdefinitelyahuman",
"github_project": "py-solc-ast",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "py-solc-ast"
}