diff --git a/main/search/search_index.json b/main/search/search_index.json index cfe0a5f6f1..a60d921c84 100644 --- a/main/search/search_index.json +++ b/main/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"slang","text":""},{"location":"#solidity-compiler-tooling-by-nomicfoundation","title":"Solidity compiler tooling by @NomicFoundation","text":"
A modular set of compiler APIs empowering the next generation of Solidity code analysis and developer tooling. Written in Rust and distributed in multiple languages.
This repository maintains the source code and release process for these projects:
\u2757 This project is still in alpha, and is under active development. If you are planning on using it, please reach out to us on Telegram so we can help you get started.
To make the developer experience as seamless and consistent as possible, we recommend using the VS Code devcontainer included in this repository. It is a light image that has the minimum required tools to build this project. If you are not familiar with containerized development, I recommend taking a look at the official VS Code guide. Using a devcontainer allows us to quickly setup/teardown the environment, and install/setup different dependencies for different projects, without polluting the local environment. In the future, it will enable us to include Windows and Mac OS specific images for cross-platform testing.
If you would like to still develop outside a container, this should still be possible, as the CI will guarantee that your changes are safe. We intend to keep the images to a bare minimum, and install most dependencies through scripts you can run locally. However, using a common development container means sharing and standardizing useful settings and extensions for the editor (VS Code), the terminal (zsh), and any other tools.
In the future, if we decide to enable code spaces, we can have a 1-click button to create and warm up a powerful dev machine to use in seconds, and running completely remote in a browser tab. It will make it trivial to switch between different versions and branches, or even use and debug multiple ones at the same time from different tabs.
"},{"location":"internals/development/#hermit","title":"Hermit","text":"To install language-specific binaries and packages, we use Hermit, which installs all tools only when it is first needed/invoked, so you can quickly setup and build different projects quickly. It also takes care of updating your $PATH
as you cd
in and out of different projects, to make sure you are using the right tools every time. Follow this guide to install it locally to your machine, or simply build any included project, and it will bootstrap itself if it is missing.
To ensure consistency, and a good experience for first-time developers, all build/test/run/debug commands should be written, versioned, and documented inside the infra_cli
crate. This means that any dev instructions are well documented, versioned, and verified/executed with every build. It also means that we can minimize any manual setup or teardown steps during development, and just rely on that cli.
You can access all such commands (from the hermit environment) by just running the infra
script, which just refers to $REPO_ROOT/scripts/bin/infra
. If this is your first time contributing, we recommend starting with infra --help
to familiarize yourself with its capabilities.
We manage versioning through changesets. Each pull request can describe what user facing changes it is introducing, and include this information as a \"changeset\" markdown file along with source changes. These changeset files are analyzed and used to create another pull request to bump the repository version and update dependencies. Once the version bump is merged, artifacts are built and released to all registries.
"},{"location":"internals/development/#managing-dependencies","title":"Managing Dependencies","text":"Our $REPO_ROOT/.github/dependabot.yml
config runs automatic updates to our dependencies on a weekly basis. This handles github-actions
, npm
, cargo
, and pip
packages. However, two kinds of dependencies still need to be updated manually for now:
$RUST_STABLE_VERSION
and $RUST_NIGHTLY_VERSION
defined in hermit.hcl
and updated via rustup install
.$REPO_ROOT/bin/XXX.pkg
, and updated via hermit install
.This repository is split into multiple projects at the root folder. Each project has its own dependencies and tools used to build, test, and ship different parts of the repository. For example, a Rust environment for the compiler, a Python environment for documentation, and a NodeJS environment for linters. This allows us to implement different binaries, APIs, and internal tools/scripts, and package/version them together, while having minimal inter-dependencies.
All dependencies should have exact full versions, and we can rely on tooling to automatically upgrade it over time. It allows us to have perfectly reproducible builds for every commit, a critical aspect for compilers, and developer tools in general.
"},{"location":"internals/repository-structure/#directory-structure","title":"Directory Structure","text":"Currently, the repository has the following projects:
.changeset/
: pending user visible changes not released yet..devcontainer/
: self-contained Docker image to develop, build, test, and publish.crates/
:infra/
: contains the CLI used for development, and utilities to build/test/run/debug all other projects.codegen/
: language analysis APs that convert input crates to output crates below.solidity/
:inputs/
: Solidity language definition.outputs/
: different packages and artifacts produced from it.documentation/
: mkdocs site to render project documentation.This document describes the new language definition model (AKA DSL v2), and the features/possibilities it enables for both syntax and semantic analysis. Each section describes a part of the definition model, and how it can affect the scanner, parser, CST, and AST.
This is a collection of different discussions we had over the last few weeks, and can (and should) be broken down into smaller work items if needed. It should be possible to map the current definition to the old one, so that we do incremental progress, instead of rewriting everything at once.
"},{"location":"internals/design-docs/language-definition-v2/#cst","title":"CST","text":"We currently produce an untyped tree of nodes. It holds all parts of the input (byte for byte), even whitespace, comments, and unrecognized (skipped) parts. We can reconstruct the original input back from the CST, just by iterating on nodes in order. For memory/performance reasons, we don't hold positions/location information in the tree, but they are calculated during iterating/visiting the tree.
The CST is useful for many use cases:
Here is an example of the node type, similar to what we have now:
pub enum Node {\n Terminal { node: Rc<TerminalNode> },\n Nonterminal { node: Rc<NonterminalNode> },\n}\n\npub struct TerminalNode {\n pub kind: TerminalKind,\n pub text: String,\n}\n\npub struct NonterminalNode {\n pub kind: NonterminalKind,\n pub text_length: TextIndex,\n pub children: Vec<Node>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#ast","title":"AST","text":"We intend to also produce a strongly typed tree (structs and enums). Having strong types provides safety/correctness guarantees for users. It also allows us to generate visitor and rewriter APIs automatically.
Each AST node should provide an API to get the underlying CST node, where users can iterate over the actual terminals as they appear in input, and get their position in the source code. However, this is a one-way operation. CST nodes don't hold references to their AST nodes.
Note: some compilers drop syntactic elements that don't carry semantic information from their AST (like semicolons, or commas). However, we don't make that distinction, as we intend to implement further analysis in the form of micro-passes, that each can rewrite and pick parts of the tree that are relevant to them. So our initial tree (AST) should be complete.
"},{"location":"internals/design-docs/language-definition-v2/#versioning","title":"Versioning","text":"The biggest benefit of the new language definition is that it allows scanners and parsers to attempt parsing input belonging to any language version, and report errors afterwards if the input is not valid for the selected version. This is a huge benefit over existing parsers, where they will either parse an inaccurate superset of all versions, or they parse a specific version, and produce unhelpful errors like Unrecognized 'abstract' keyword
when the current language version doesn't support it.
Not only we will be able to recover from such errors and continue parsing, producing an accurate/complete tree at the end, but we will also be able to produce much better errors like: The 'abstract' keyword is not supported in the current version X. Please upgrade to version Y instead to be able to use it
.
Tokens consist of one or more TokenDefinition
. Each definition is separate/unique, but produces the same TerminalKind
. This is useful for tokens like DecimalLiteral
and HexLiteral
who can have multiple forms, but each form is enabled or disabled in certain versions of the language.
All definitions have a unique Scanner
, and they can be combined in the same trie/FSM to produce a single token at each position in the input. Afterwards, the scanner can compare the definition's enabled
property with the current language version, adding an error if they don't match, but continue parsing anyway.
Keywords also contribute a TerminalKind
, and consist of one or more KeywordDefinition
. But they have additional semantics:
First, instead of defining a regular Scanner
, it defines a KeywordValue
that produces a finite set of possibilities. Most only produce one value (like abstract
or contract
), but some can produce multiple, like bytesN
or fixedMxN
, that can have different values for M
and N
. This is important for us to build hash sets and quickly check for membership.
Second, because keywords can also overlap with identifiers, each keyword has an identifier
property that refers to which identifier token they can match. Instead of being part of same trie/FSM as tokens, whenever we want to scan a keyword, we try to scan its identifier instead. Afterwards, we check if its contents match one of the values of the keyword.
Third, they have two additional reserved
property. We should use these when we scan identifiers, to make sure that the resulting identifier doesn't match a reserved keyword, and if so, we should report an error, but continue parsing.
Unique to Solidity, keywords can be reserved
in versions before or after the versions they are enabled
in. They can also be not reserved
in versions before or after the versions they stop being enabled
in. So we have to have these additional checks, to be able to catch cases like when a certain input can both be a keyword and an identifier, or neither.
We should also be able to generate a public API is_keyword(TerminalKind)
for users to conveniently detect them if needed.
Trivia items are similar tokens, contributing their own TerminalKind
. They are referred to from the language's top-level leading_trivia
and trailing_trivia
properties. Before and after each token, the scanner should try to scan these tokens, collecting them in a flat list.
Previously, we used to create many LeadingTrivia
and TrailingTrivia
nodes that hold whitespace/comments. Not only this is wasteful memory-wise, it is also unnatural/unexpected to wrap whitespace in nonterminal nodes. Instead, I propose treating them like any other token, and storing them as siblings to the tokens they belong to (in-order). Not only this is simpler, it is also more efficient, and is natural to how input is consumed and produced.
Fragments are not visible to users, and don't contribute a TerminalKind
. They are just a utility used to refactor common parts of the grammar, and avoid duplication. During processing the language definition, they are inlined wherever they are referenced.
Structs represent a flat list (sequence) of typed fields. They are the simplest nonterminal, and generate a struct
AST type. Their fields match 1-1 with the item fields. The struct name contributes a NonterminalKind
.
Each field can be either Required(T)
or Optional(T)
. Required fields are always present and parsed. Optional fields can be omitted if they don't exist, and are represented with Rust's Option<T>
type (or TypeScript T | undefined
). However, optional fields have an additional enabled
property. After parsing optional fields, we should compare them with the current language version, and produce errors if they don't match, but continue parsing normally.
The type of each field can be a Nonterminal(T)
or Terminal(Set<T>)
. A nonterminal field refers to another item, and holds its type. A terminal field refers to one or more terminal items (all valid in this position), and is of type TerminalNode
.
Additionally, the struct also stores the CST node that holds its contents:
DefinitionStruct(\n name = ParametersDeclaration,\n fields = (\n open_paren = Required(Terminal([OpenParen])),\n parameters = Required(Nonterminal(Parameters)),\n close_paren = Required(Terminal([CloseParen]))\n )\n)\n
AST Typepub struct ParametersDeclaration {\n pub open_paren: Rc<TerminalNode>,\n pub parameters: Rc<Parameters>,\n pub close_paren: Rc<TerminalNode>,\n\n pub cst: Rc<NonterminalNode>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#enum-items","title":"Enum Items","text":"Enums represent an ordered choice operator of multiple variants (possibilities). The enum name itself does NOT contribute a NonterminalKind
, since it will always result in one of its variants (each with a unique TerminalKind
or a NonterminalKind
. They only exist in the AST, and don't affect the CST at all.
We attempt to parse each variant (in-order), and choose the first one that succeeds. However, each variant can have an additional enabled
property. We should always try to parse the variants that are valid in the current version first, and if not, still parse the rest, but produce an error afterwards. The fields of each variant are parsed similar to a struct fields (example above).
Enum(\n name = FunctionBody,\n variants = [\n EnumVariant(name = Block, reference = Block),\n EnumVariant(name = Semicolon, reference = Semicolon)\n ]\n)\n
AST Typepub enum FunctionBody {\n Block {\n block: Rc<Block>,\n\n cst: Rc<NonterminalNode>,\n },\n Semicolon {\n semicolon: Rc<TerminalNode>,\n\n cst: Rc<NonterminalNode>,\n },\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#repeated-items","title":"Repeated Items","text":"Repeated items represent a list of items of the same kind. The item name contributes a NonterminalKind
. The AST type is a wrapper around a Vec<T>
, with any utilities we need to add for convenience.
It has an allow_empty
boolean property, which allows parsing zero items. If it is false
, we should still allow parsing zero items, but produce an error afterwards.
Repeated(\n name = FunctionAttributes,\n repeated = FunctionAttribute,\n allow_empty = true\n)\n
AST Typepub struct FunctionAttributes {\n pub items: Vec<Rc<FunctionAttribute>>\n\n pub cst: Rc<NonterminalNode>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#separated-items","title":"Separated Items","text":"Separated items represent a list of items of the same kind, separated by a delimiter. The item name contributes a NonterminalKind
. The AST type is a wrapper around two Vec<T>
for items and their delimiters, with any utilities we need to add for convenience. For example, we should add APIs to create iterators for only the separated items, the separators, or both (in-order).
It has an allow_empty
boolean property, which allows parsing zero items. If it is false
, we should still allow parsing zero items, but produce an error afterwards. We should also allow parsing a trailing separator at the end, but still produce an error afterwards.
Separated(\n name = EventParameters,\n separated = EventParameter,\n separator = Comma,\n allow_empty = true\n)\n
AST Typepub struct EventParameters {\n pub items: Vec<Rc<EventParameter>>\n pub separators: Vec<Rc<TerminalNode>>\n\n pub cst: Rc<NonterminalNode>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#precedence-items","title":"Precedence Items","text":"This is perhaps the most complex nonterminal. It still uses the same PRATT algorithm from the previous implementation (no changes there), but adapted for the new AST types. It has two lists:
First, a list of precedence_expressions
, with each expression having a list of operators. Each operator has its own versioning (enabled
property), a list of fields, and a model (prefix/postfix/binary).
The operators from all expressions are flattened and combined in the parent PRATT parser. That grouping is only used to indicate that some operators can produce the same PrecedenceExpression
name. However, we should exclude operators that don't match the current language version. This is useful for things like ExponentiationExpression
where it has two operators with different associativity, but defined in enabled/disabled in different versions.
Second, a list of primary_expressions
, with their own versioning (enabled
property) as well. We should try to parse them as an operator (similar to EnumItem
), and produce an error if the version doesn't match afterwards.
It is important to note that the item name doesn't contribute a NonterminalKind
, but each PrecedenceExpression
under it contributes one.
Precedence(\n name = Expression,\n precedence_expressions = [\n PrecedenceExpression(\n name = AdditionExpression,\n operators = [PrecedenceOperator(\n model = BinaryLeftAssociative,\n fields = (operator = Required(Terminal([Plus])))\n )]\n ),\n PrecedenceExpression(\n name = FunctionCallExpression,\n operators = [PrecedenceOperator(\n model = Postfix,\n fields = (\n open_paren = Required(Terminal([OpenParen])),\n arguments = Required(Nonterminal(Arguments)),\n close_paren = Required(Terminal([CloseParen]))\n )\n )]\n ),\n PrecedenceExpression(\n name = NegationExpression,\n operators = [PrecedenceOperator(\n model = Prefix,\n fields = (operator = Required(Terminal([Not])))\n )]\n )\n )],\n primary_expressions = [\n PrimaryExpression(expression = Identifier),\n PrimaryExpression(expression = NumberLiteral),\n PrimaryExpression(expression = StringLiteral)\n ]\n)\n
AST Typepub enum Expression {\n AdditionExpression { expression: Rc<AdditionExpression> },\n FunctionCallExpression { expression: Rc<FunctionCallExpression> },\n NegationExpression { expression: Rc<NegationExpression> },\n\n Identifier { expression: Rc<TerminalNode> },\n NumberLiteral { expression: Rc<TerminalNode> },\n StringLiteral { expression: Rc<TerminalNode> },\n}\n\npub struct AdditionExpression {\n // 'left_operand' auto-generated (before) because it is a binary expression, and same type as parent\n pub left_operand: Rc<Expression>,\n // operator 'fields' are flattened into the expression node here\n pub operator: Rc<TerminalNode>,\n // 'right_operand' auto-generated (after) because it is a binary expression, and same type as parent\n pub right_operand: Rc<Expression>,\n\n pub cst: Rc<NonterminalNode>,\n}\n\npub struct FunctionCallExpression {\n // 'operand' auto-generated (before) because it is a postfix expression, and same type as parent\n pub operand: Rc<Expression>,\n // operator 'fields' are flattened into the expression node here\n pub open_paren: Rc<TerminalNode>,\n pub arguments: Rc<Arguments>,\n pub close_paren: Rc<TerminalNode>,\n\n pub cst: Rc<NonterminalNode>,\n}\n\npub struct NegationExpression {\n // operator 'fields' are flattened into the expression node here\n pub operator: Rc<TerminalNode>,\n // 'operand' auto-generated (after) because it is a prefix expression, and same type as parent\n pub operand: Rc<Expression>,\n\n pub cst: Rc<NonterminalNode>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#error-recovery","title":"Error Recovery","text":"For the CST, I think the current algorithms work well, and we should be able to keep them. Unrecognized (skipped) input is grouped into one token, and we can just add it as-is to the cst
node under its AST node.
During AST construction, we will simply check for TerminalKind::UNRECOGNIZED
nodes, and skip construction if there are any.
Based on the above, I propose the following changes to the current public API:
TokenKind
to TerminalKind
, since it will also refer to trivia.RuleKind
to NonterminalKind
, since \"rule\" is ambiguous.TerminalKind::SKIPPED
to UNRECOGNIZED
for clarity.LexicalContext
and fn scan(TokenKind)
from the public API, as it is a short-term workaround, and will be replaced later when we have language embedding.ProductionKind
completely, since it is no longer needed. We only need to expose fn parse(NonterminalKind)
.EndOFFileTrivia
no longer exists, ParseResult
should collect any remaining trivia at the end of the input, and include it in the ParseResult
returned, for any kind of node, not just SourceUnit
.The current CST visitors/cursors should still work as-is, since the CST tree will be unchanged. However, the new AST types allow us in the future to produce typed visitors and traits with named functions for every node type, similar to a lot of other AST processing libraries. I want to at least produce an immutable Visitor
and a mutable Rewriter
.
This specification compiles information from 83 publicly released versions of Solidity:
0.4.11
0.4.12
0.4.13
0.4.14
0.4.15
0.4.16
0.4.17
0.4.18
0.4.19
0.4.20
0.4.21
0.4.22
0.4.23
0.4.24
0.4.25
0.4.26
0.5.0
0.5.1
0.5.2
0.5.3
0.5.4
0.5.5
0.5.6
0.5.7
0.5.8
0.5.9
0.5.10
0.5.11
0.5.12
0.5.13
0.5.14
0.5.15
0.5.16
0.5.17
0.6.0
0.6.1
0.6.2
0.6.3
0.6.4
0.6.5
0.6.6
0.6.7
0.6.8
0.6.9
0.6.10
0.6.11
0.6.12
0.7.0
0.7.1
0.7.2
0.7.3
0.7.4
0.7.5
0.7.6
0.8.0
0.8.1
0.8.2
0.8.3
0.8.4
0.8.5
0.8.6
0.8.7
0.8.8
0.8.9
0.8.10
0.8.11
0.8.12
0.8.13
0.8.14
0.8.15
0.8.16
0.8.17
0.8.18
0.8.19
0.8.20
0.8.21
0.8.22
0.8.23
0.8.24
0.8.25
0.8.26
0.8.27
0.8.28
Among which, 34 versions have breaking changes:
0.4.11
0.4.12
0.4.14
0.4.16
0.4.21
0.4.22
0.4.25
0.5.0
0.5.3
0.5.5
0.5.8
0.5.10
0.5.12
0.5.14
0.6.0
0.6.2
0.6.5
0.6.7
0.6.8
0.6.11
0.7.0
0.7.1
0.7.4
0.8.0
0.8.4
0.8.7
0.8.8
0.8.13
0.8.18
0.8.19
0.8.22
0.8.24
0.8.25
0.8.27
This comment line declares that the source code is licensed under the GPL version 3.0. Machine-readable license specifiers are important in a setting where publishing the source code is the default. The comment is recognized by the compiler anywhere in the file at the file level, but it is recommended to put it at the top of the file.
// SPDX-License-Identifier: GPL-3.0\n
When omitted, the compiler produces a warning to add one. The compiler does not validate that the license is part of the list allowed by SPDX, but it does include the supplied string in the metadata.
If you do not want to specify a license or if the source code is not open-source, please use the special value UNLICENSED
. Note that UNLICENSED
(no usage allowed, not present in SPDX license list) is different from UNLICENSE
(grants all rights to everyone).
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/02-source-unit/","title":"1.2. Source Unit","text":""},{"location":"solidity-specification/01-file-structure/02-source-unit/#12-source-unit","title":"1.2. Source Unit","text":""},{"location":"solidity-specification/01-file-structure/02-source-unit/#syntax","title":"Syntax","text":"SourceUnit = (* members: *) SourceUnitMembers;
SourceUnitMembers = (* item: *) SourceUnitMember*;
SourceUnitMember = (* variant: *) PragmaDirective | (* variant: *) ImportDirective | (* variant: *) ContractDefinition | (* variant: *) InterfaceDefinition | (* variant: *) LibraryDefinition | (* variant: *) StructDefinition (* Introduced in 0.6.0 *) | (* variant: *) EnumDefinition (* Introduced in 0.6.0 *) | (* variant: *) FunctionDefinition (* Introduced in 0.7.1 *) | (* variant: *) ErrorDefinition (* Introduced in 0.8.4 *) | (* variant: *) UserDefinedValueTypeDefinition (* Introduced in 0.8.8 *) | (* variant: *) UsingDirective (* Introduced in 0.8.13 *) | (* variant: *) EventDefinition (* Introduced in 0.8.22 *) | (* variant: *) ConstantDefinition; (* Introduced in 0.7.4 *)"},{"location":"solidity-specification/01-file-structure/02-source-unit/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/","title":"1.3. Pragma Directives","text":""},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#13-pragma-directives","title":"1.3. Pragma Directives","text":""},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#syntax","title":"Syntax","text":"PragmaDirective = (* pragma_keyword: *) PRAGMA_KEYWORD (* pragma: *) Pragma (* semicolon: *) SEMICOLON;
Pragma = (* variant: *) AbicoderPragma | (* variant: *) ExperimentalPragma | (* variant: *) VersionPragma;
AbicoderPragma = (* abicoder_keyword: *) ABICODER_KEYWORD (* version: *) IDENTIFIER;
ExperimentalPragma = (* experimental_keyword: *) EXPERIMENTAL_KEYWORD (* feature: *) ExperimentalFeature;
ExperimentalFeature = (* variant: *) IDENTIFIER | (* variant: *) StringLiteral;
VersionPragma = (* solidity_keyword: *) SOLIDITY_KEYWORD (* sets: *) VersionExpressionSets;
VersionExpressionSets = (* item: *) VersionExpressionSet ((* separator: *) BAR_BAR (* item: *) VersionExpressionSet)*;
VersionExpressionSet = (* item: *) VersionExpression+;
VersionExpression = (* variant: *) VersionRange | (* variant: *) VersionTerm;
VersionRange = (* start: *) VersionLiteral (* minus: *) MINUS (* end: *) VersionLiteral;
VersionTerm = (* operator: *) VersionOperator? (* literal: *) VersionLiteral;
VersionOperator = (* variant: *) CARET | (* variant: *) TILDE | (* variant: *) EQUAL | (* variant: *) LESS_THAN | (* variant: *) GREATER_THAN | (* variant: *) LESS_THAN_EQUAL | (* variant: *) GREATER_THAN_EQUAL;
VersionLiteral = (* variant: *) SimpleVersionLiteral | (* variant: *) SINGLE_QUOTED_VERSION_LITERAL | (* variant: *) DOUBLE_QUOTED_VERSION_LITERAL;
SimpleVersionLiteral = (* item: *) VERSION_SPECIFIER ((* separator: *) PERIOD (* item: *) VERSION_SPECIFIER)*;
VERSION_SPECIFIER = \u00abVERSION_SPECIFIER_FRAGMENT\u00bb;
SINGLE_QUOTED_VERSION_LITERAL = \"'\" \u00abVERSION_SPECIFIER_FRAGMENT\u00bb (\".\" \u00abVERSION_SPECIFIER_FRAGMENT\u00bb)* \"'\";
DOUBLE_QUOTED_VERSION_LITERAL = '\"' \u00abVERSION_SPECIFIER_FRAGMENT\u00bb (\".\" \u00abVERSION_SPECIFIER_FRAGMENT\u00bb)* '\"';
\u00abVERSION_SPECIFIER_FRAGMENT\u00bb = (\"0\"\u2026\"9\" | \"x\" | \"X\" | \"*\")+;
(* Never reserved *)ABICODER_KEYWORD = \"abicoder\";
(* Never reserved *)EXPERIMENTAL_KEYWORD = \"experimental\";
(* Never reserved *)SOLIDITY_KEYWORD = \"solidity\";"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#version-pragma","title":"Version Pragma","text":"
This line declares which Solidity language version it was written for. This is to ensure that the contract is not compilable with a new (breaking) compiler version, where it could behave differently. An error is produced if the running compiler version does not match these requirements.
Note that multiple version pragmas are supported, and the compiler will verify each pragma separately.
For example, this line specifies that the source code is written for Solidity version 0.4.16
, or a newer version of the language up to, but not including version 0.9.0
:
pragma solidity >=0.4.16 <0.9.0;\n
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#abi-coder-pragma","title":"ABI Coder Pragma","text":"Used to instruct the compiler to choose a specific ABI encoder/decoder. The new ABI coder (v2) is able to encode and decode arbitrarily nested arrays and structs. It might produce less optimal code and has not received as much testing as the old encoder.
pragma abicoder v1;\n// OR\npragma abicoder v2;\n
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#experimental-pragma","title":"Experimental Pragma","text":"It can be used to enable features of the compiler or language that are not yet enabled by default. Compilers should produce an error on unrecognized pragmas (or earlier versions before they were released), and a warning before the stable version. After the stable version, this should not have an effect.
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#abiencoderv2","title":"ABIEncoderV2","text":"Please see the abicoder
pragma defined above.
pragma experimental ABIEncoderV2;\n
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#smtchecker","title":"SMTChecker","text":"If you use SMTChecker
, then you get additional safety warnings which are obtained by querying an SMT solver. The component does not yet support all features of the Solidity language and likely outputs many warnings. In case it reports unsupported features, the analysis may not be fully sound.
pragma experimental SMTChecker;\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/04-import-directives/","title":"1.4. Import Directives","text":""},{"location":"solidity-specification/01-file-structure/04-import-directives/#14-import-directives","title":"1.4. Import Directives","text":""},{"location":"solidity-specification/01-file-structure/04-import-directives/#syntax","title":"Syntax","text":"ImportDirective = (* import_keyword: *) IMPORT_KEYWORD (* clause: *) ImportClause (* semicolon: *) SEMICOLON;
ImportClause = (* variant: *) PathImport | (* variant: *) NamedImport | (* variant: *) ImportDeconstruction;
PathImport = (* path: *) StringLiteral (* alias: *) ImportAlias?;
NamedImport = (* asterisk: *) ASTERISK (* alias: *) ImportAlias (* from_keyword: *) FROM_KEYWORD (* path: *) StringLiteral;
ImportDeconstruction = (* open_brace: *) OPEN_BRACE (* symbols: *) ImportDeconstructionSymbols (* close_brace: *) CLOSE_BRACE (* from_keyword: *) FROM_KEYWORD (* path: *) StringLiteral;
ImportDeconstructionSymbols = (* item: *) ImportDeconstructionSymbol ((* separator: *) COMMA (* item: *) ImportDeconstructionSymbol)*;
ImportDeconstructionSymbol = (* name: *) IDENTIFIER (* alias: *) ImportAlias?;
ImportAlias = (* as_keyword: *) AS_KEYWORD (* identifier: *) IDENTIFIER;"},{"location":"solidity-specification/01-file-structure/04-import-directives/#importing-files","title":"Importing Files","text":"
At a file level, you can use import statements of the following form:
import \"filename\";\n
This statement imports all global symbols from filename
(and symbols imported there) into the current global scope. This form is not recommended for use, because it unpredictably pollutes the namespace. If you add new top-level items inside filename
, they automatically appear in all files that import like this from \u201cfilename\u201d. It is better to import specific symbols explicitly, which results in all global symbols being available under the myFile
symbol:
import * as myFile from \"filename\";\n// OR\nimport \"filename\" as myFile;\n
"},{"location":"solidity-specification/01-file-structure/04-import-directives/#importing-specific-symbols","title":"Importing Specific Symbols","text":"You can import only the symbols you use from a specific file, using the syntax:
import {symbol1, symbol2} from \"filename\";\n
Which will create symbol1
and symbol1
to use in your code. If there is a naming collision, you can rename symbols while importing. For example, the code below creates new global symbols alias
and symbol2
which reference symbol1
and symbol2
from inside filename
, respectively:
import {symbol1 as alias, symbol2} from \"filename\";\n
"},{"location":"solidity-specification/01-file-structure/04-import-directives/#virtual-file-system","title":"Virtual File System","text":"In order to be able to support reproducible builds on all platforms, the Solidity compiler has to abstract away the details of the filesystem where source files are stored. For this reason import paths do not refer directly to files in the host filesystem. Instead the compiler maintains an internal database (virtual filesystem or VFS for short) where each source unit is assigned a unique source unit name which is an opaque and unstructured identifier. The import path specified in an import statement is translated into a source unit name and used to find the corresponding source unit in this database.
solc
binary CLI, you can pass disk file system paths to be used.solc
JSON API, you can pass the explicit file contents to be parsed.Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/05-using-directives/","title":"1.5. Using Directives","text":""},{"location":"solidity-specification/01-file-structure/05-using-directives/#15-using-directives","title":"1.5. Using Directives","text":""},{"location":"solidity-specification/01-file-structure/05-using-directives/#syntax","title":"Syntax","text":"UsingDirective = (* using_keyword: *) USING_KEYWORD (* clause: *) UsingClause (* for_keyword: *) FOR_KEYWORD (* target: *) UsingTarget (* global_keyword: *) GLOBAL_KEYWORD? (* Introduced in 0.8.13 *) (* semicolon: *) SEMICOLON;
UsingClause = (* variant: *) IdentifierPath | (* variant: *) UsingDeconstruction; (* Introduced in 0.8.13 *)
(* Introduced in 0.8.13 *)UsingDeconstruction = (* open_brace: *) OPEN_BRACE (* symbols: *) UsingDeconstructionSymbols (* close_brace: *) CLOSE_BRACE;
(* Introduced in 0.8.13 *)UsingDeconstructionSymbols = (* item: *) UsingDeconstructionSymbol ((* separator: *) COMMA (* item: *) UsingDeconstructionSymbol)*;
(* Introduced in 0.8.13 *)UsingDeconstructionSymbol = (* name: *) IdentifierPath (* alias: *) UsingAlias?; (* Introduced in 0.8.19 *)
(* Introduced in 0.8.19 *)UsingAlias = (* as_keyword: *) AS_KEYWORD (* operator: *) UsingOperator;
(* Introduced in 0.8.19 *)UsingOperator = (* variant: *) AMPERSAND | (* variant: *) ASTERISK | (* variant: *) BANG_EQUAL | (* variant: *) BAR | (* variant: *) CARET | (* variant: *) EQUAL_EQUAL | (* variant: *) GREATER_THAN | (* variant: *) GREATER_THAN_EQUAL | (* variant: *) LESS_THAN | (* variant: *) LESS_THAN_EQUAL | (* variant: *) MINUS | (* variant: *) PERCENT | (* variant: *) PLUS | (* variant: *) SLASH | (* variant: *) TILDE;
UsingTarget = (* variant: *) TypeName | (* variant: *) ASTERISK;"},{"location":"solidity-specification/01-file-structure/05-using-directives/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/06-trivia/","title":"1.6. Trivia","text":""},{"location":"solidity-specification/01-file-structure/06-trivia/#16-trivia","title":"1.6. Trivia","text":""},{"location":"solidity-specification/01-file-structure/06-trivia/#syntax","title":"Syntax","text":"WHITESPACE = (\" \" | \"\\t\")+;
END_OF_LINE = \"\\n\" | (\"\\r\" \"\\n\"?);
SINGLE_LINE_COMMENT = \"//\" (?!\"/\") (!(\"\\r\" | \"\\n\"))*;
MULTI_LINE_COMMENT = \"/*\" (?!\"*\" !\"/\") (!\"*\" | (\"*\" (?!\"/\")))* \"*/\";
SINGLE_LINE_NAT_SPEC_COMMENT = \"///\" (!(\"\\r\" | \"\\n\"))*;
MULTI_LINE_NAT_SPEC_COMMENT = \"/**\" (?!\"/\") (!\"*\" | (\"*\" (?!\"/\")))* \"*/\";"},{"location":"solidity-specification/01-file-structure/06-trivia/#single-line-comments","title":"Single Line Comments","text":"
A single-line comment is terminated by any unicode line terminator (LF
, VF
, FF
, CR
, NEL
, LS
or PS
) in UTF-8 encoding. The terminator is still part of the source code after the comment, so if it is not an ASCII symbol (these are NEL
, LS
and PS
), it will lead to a parser error.
// This is a single-line comment.\n
"},{"location":"solidity-specification/01-file-structure/06-trivia/#multi-line-comments","title":"Multi-line Comments","text":"Comments starting with /*
and ending with */
are allowed to range multiple lines:
/*\nThis is a\nmulti-line comment.\n*/\n
"},{"location":"solidity-specification/01-file-structure/06-trivia/#natspec-comments","title":"NatSpec Comments","text":"Additionally, there is another type of comment called a NatSpec comment. They are written with a triple slash ///
or a double asterisk block /**...*/
and they should be used directly above function declarations or statements. It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI).
/// @author My Team Name\n/// @title A simple contract example\ncontract MyContract {}\n
Please see the NatSpec Format section for further information.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/","title":"1.7. Nat Spec Format","text":""},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#17-nat-spec-format","title":"1.7. Nat Spec Format","text":""},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#what-is-natspec","title":"What is NatSpec?","text":"Solidity contracts can use a special form of comments to provide rich documentation for functions, return variables and more. This special form is named the Ethereum Natural Language Specification Format (NatSpec). It was inspired by Doxygen, and while it uses Doxygen-style comments and tags, there is no intention to keep strict compatibility with Doxygen.
It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI). It is used in:
Documentation can be inserted above each contract
, interface
, library
, function
, event
and state variable
.
They can either exist in a single line format, starting with ///
:
/// @title An example contract\ncontract MyContract {}\n
And also in multi-line format, starting with /**
and ending with */
:
/**\n * @title An example contract\n */\ncontract MyContract {}\n
"},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#tags","title":"Tags","text":"Tags categorize different comments according to their purpose. The table below shows the different tags supported. Please note that they are optional, and without one, the entire comment will be interpreted as it had a @notice
tag.
@title
A title that should describe the contract/interface contract
, library
, interface
@author
The name of the author contract
, library
, interface
@notice
Explain to an end user what this does contract
, library
, interface
, function
, event
, state variable
@dev
Explain to a developer any extra details contract
, library
, interface
, function
, event
, state variable
@param
Documents a parameter just like in Doxygen (must be followed by parameter name) function
, event
@return
Documents the return variables of a contract's function function
, state variable
@inheritdoc
Copies all missing tags from the base function (must be followed by the contract name) function
, state variable
@custom:FOO
Custom tag, semantics is application-defined can be used everywhere"},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#function-return-types","title":"Function Return Types","text":"If your function returns multiple values, like (int quotient, int remainder)
then use multiple @return
statements in the same format as the @param
statements.
Custom tags start with @custom:
and must be followed by one or more lowercase letters or hyphens. It cannot start with a hyphen however. They can be used everywhere and are part of the developer documentation. For example, @custom:foo
or @custom:foo-bar
. A good use case is analysis and verification tools.
The Solidity compiler will pass through NatSpec documentation from your Solidity source code to the JSON output as described in this guide. The consumer of this JSON output may present this to the end-user directly or it may apply some pre-processing.
Specifying these dynamic expressions is outside the scope of the Solidity documentation. However, you can find one useful example in the RadSpec Project, where it evaluates references to function inputs to its values. For example, this line:
/// @notice This function will multiply `a` by 7\n
Can be evaluated as the following, where the value of a
is 10
:
This function will multiply 10 by 7\n
"},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#inheritance","title":"Inheritance","text":"Functions without NatSpec will automatically inherit the documentation of their base function. Exceptions to this are:
@inheritdoc
tag which specifies which contract should be used to inherit.Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/08-keywords/","title":"1.8. Keywords","text":""},{"location":"solidity-specification/01-file-structure/08-keywords/#18-keywords","title":"1.8. Keywords","text":""},{"location":"solidity-specification/01-file-structure/08-keywords/#syntax","title":"Syntax","text":"(* Introduced in 0.6.0 *)ABSTRACT_KEYWORD = \"abstract\";
(* Never reserved *)ADDRESS_KEYWORD = \"address\";
AFTER_KEYWORD = \"after\";
(* Reserved in 0.5.0 *)ALIAS_KEYWORD = \"alias\";
ANONYMOUS_KEYWORD = \"anonymous\";
(* Reserved in 0.5.0 *)APPLY_KEYWORD = \"apply\";
AS_KEYWORD = \"as\";
ASSEMBLY_KEYWORD = \"assembly\";
(* Reserved in 0.5.0 *)AUTO_KEYWORD = \"auto\";
BOOL_KEYWORD = \"bool\";
BREAK_KEYWORD = \"break\";
(* Deprecated in 0.8.0 *)BYTE_KEYWORD = \"byte\";
BYTES_KEYWORD = \"bytes\" (\"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"8\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"16\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"24\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"32\")?;
(* Introduced in 0.5.0 *)(* Reserved in 0.5.0 *)CALL_DATA_KEYWORD = \"calldata\";
CASE_KEYWORD = \"case\";
(* Introduced in 0.6.0 *)CATCH_KEYWORD = \"catch\";
CONSTANT_KEYWORD = \"constant\";
(* Introduced in 0.4.22 *)(* Reserved in 0.5.0 *)CONSTRUCTOR_KEYWORD = \"constructor\";
CONTINUE_KEYWORD = \"continue\";
CONTRACT_KEYWORD = \"contract\";
(* Reserved in 0.5.0 *)COPY_OF_KEYWORD = \"copyof\";
DAYS_KEYWORD = \"days\";
DEFAULT_KEYWORD = \"default\";
(* Reserved in 0.5.0 *)DEFINE_KEYWORD = \"define\";
DELETE_KEYWORD = \"delete\";
DO_KEYWORD = \"do\";
ELSE_KEYWORD = \"else\";
(* Introduced in 0.4.21 *)(* Reserved in 0.5.0 *)EMIT_KEYWORD = \"emit\";
ENUM_KEYWORD = \"enum\";
(* Introduced in 0.8.4 *)(* Never reserved *)ERROR_KEYWORD = \"error\";
ETHER_KEYWORD = \"ether\";
EVENT_KEYWORD = \"event\";
EXTERNAL_KEYWORD = \"external\";
(* Reserved in 0.6.0 *)FALLBACK_KEYWORD = \"fallback\";
FALSE_KEYWORD = \"false\";
FINAL_KEYWORD = \"final\";
(* Deprecated in 0.7.0 *)(* Reserved until 0.7.0 *)FINNEY_KEYWORD = \"finney\";
FIXED_KEYWORD = \"fixed\";FIXED_KEYWORD = \"fixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\") \"x\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\");FIXED_KEYWORD = \"fixed\" (\"184x8\" | \"184x16\" | \"184x24\" | \"184x32\" | \"184x40\" | \"184x48\" | \"184x56\" | \"184x64\" | \"184x72\" | \"192x8\" | \"192x16\" | \"192x24\" | \"192x32\" | \"192x40\" | \"192x48\" | \"192x56\" | \"192x64\" | \"200x8\" | \"200x16\" | \"200x24\" | \"200x32\" | \"200x40\" | \"200x48\" | \"200x56\" | \"208x8\" | \"208x16\" | \"208x24\" | \"208x32\" | \"208x40\" | \"208x48\" | \"216x8\" | \"216x16\" | \"216x24\" | \"216x32\" | \"216x40\" | \"224x8\" | \"224x16\" | \"224x24\" | \"224x32\" | \"232x8\" | \"232x16\" | \"232x24\" | \"240x8\" | \"240x16\" | \"248x8\");(* Reserved in 0.4.14 *)FIXED_KEYWORD = \"fixed\" (\"184x80\" | \"192x72\" | \"192x80\" | \"200x64\" | \"200x72\" | \"200x80\" | \"208x56\" | \"208x64\" | \"208x72\" | \"208x80\" | \"216x48\" | \"216x56\" | \"216x64\" | \"216x72\" | \"216x80\" | \"224x40\" | \"224x48\" | \"224x56\" | \"224x64\" | \"224x72\" | \"224x80\" | \"232x32\" | \"232x40\" | \"232x48\" | \"232x56\" | \"232x64\" | \"232x72\" | \"232x80\" | \"240x24\" | \"240x32\" | \"240x40\" | \"240x48\" | \"240x56\" | \"240x64\" | \"240x72\" | \"240x80\" | \"248x16\" | \"248x24\" | \"248x32\" | \"248x40\" | \"248x48\" | \"248x56\" | \"248x64\" | \"248x72\" | \"248x80\" | \"256x8\" | \"256x16\" | \"256x24\" | \"256x32\" | \"256x40\" | \"256x48\" | \"256x56\" | \"256x64\" | \"256x72\" | \"256x80\");(* Reserved in 0.4.14 *)FIXED_KEYWORD = \"fixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\") \"x\" (\"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"33\" | \"34\" | \"35\" | \"36\" | \"37\" | \"38\" | \"39\" | \"41\" | \"42\" | \"43\" | \"44\" | \"45\" | \"46\" | \"47\" | \"49\" | \"50\" | \"51\" | \"52\" | \"53\" | \"54\" | \"55\" | \"57\" | \"58\" | \"59\" | \"60\" | \"61\" | \"62\" | \"63\" | \"65\" | \"66\" | \"67\" | \"68\" | \"69\" | \"70\" | \"71\" | \"73\" | \"74\" | \"75\" | \"76\" | \"77\" | \"78\" | \"79\");
FOR_KEYWORD = \"for\";
(* Never reserved *)FROM_KEYWORD = \"from\";
FUNCTION_KEYWORD = \"function\";
(* Introduced in 0.8.13 *)(* Never reserved *)GLOBAL_KEYWORD = \"global\";
(* Introduced in 0.6.11 *)(* Reserved in 0.7.0 *)GWEI_KEYWORD = \"gwei\";
HEX_KEYWORD = \"hex\";
HOURS_KEYWORD = \"hours\";
IF_KEYWORD = \"if\";
(* Introduced in 0.6.5 *)(* Reserved in 0.5.0 *)IMMUTABLE_KEYWORD = \"immutable\";
(* Reserved in 0.5.0 *)IMPLEMENTS_KEYWORD = \"implements\";
IMPORT_KEYWORD = \"import\";
INDEXED_KEYWORD = \"indexed\";
IN_KEYWORD = \"in\";
INLINE_KEYWORD = \"inline\";
INTERFACE_KEYWORD = \"interface\";
INTERNAL_KEYWORD = \"internal\";
INT_KEYWORD = \"int\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\")?;
IS_KEYWORD = \"is\";
LET_KEYWORD = \"let\";
LIBRARY_KEYWORD = \"library\";
(* Reserved in 0.5.0 *)MACRO_KEYWORD = \"macro\";
MAPPING_KEYWORD = \"mapping\";
MATCH_KEYWORD = \"match\";
MEMORY_KEYWORD = \"memory\";
MINUTES_KEYWORD = \"minutes\";
MODIFIER_KEYWORD = \"modifier\";
(* Reserved in 0.5.0 *)MUTABLE_KEYWORD = \"mutable\";
NEW_KEYWORD = \"new\";
NULL_KEYWORD = \"null\";
OF_KEYWORD = \"of\";
(* Introduced in 0.6.0 *)(* Reserved in 0.5.0 *)OVERRIDE_KEYWORD = \"override\";
(* Reserved in 0.5.0 *)PARTIAL_KEYWORD = \"partial\";
PAYABLE_KEYWORD = \"payable\";
PRAGMA_KEYWORD = \"pragma\";
PRIVATE_KEYWORD = \"private\";
(* Reserved in 0.5.0 *)PROMISE_KEYWORD = \"promise\";
PUBLIC_KEYWORD = \"public\";
(* Introduced in 0.4.16 *)PURE_KEYWORD = \"pure\";
(* Reserved in 0.6.0 *)RECEIVE_KEYWORD = \"receive\";
(* Reserved in 0.5.0 *)REFERENCE_KEYWORD = \"reference\";
RELOCATABLE_KEYWORD = \"relocatable\";
RETURN_KEYWORD = \"return\";
RETURNS_KEYWORD = \"returns\";
(* Introduced in 0.8.4 *)(* Never reserved *)REVERT_KEYWORD = \"revert\";
(* Reserved in 0.5.0 *)SEALED_KEYWORD = \"sealed\";
SECONDS_KEYWORD = \"seconds\";
(* Reserved in 0.5.0 *)SIZE_OF_KEYWORD = \"sizeof\";
STATIC_KEYWORD = \"static\";
STORAGE_KEYWORD = \"storage\";
STRING_KEYWORD = \"string\";
STRUCT_KEYWORD = \"struct\";
SUPER_KEYWORD = \"super\";
(* Reserved in 0.5.0 *)SUPPORTS_KEYWORD = \"supports\";
SWITCH_KEYWORD = \"switch\";
(* Deprecated in 0.7.0 *)(* Reserved until 0.7.0 *)SZABO_KEYWORD = \"szabo\";
THIS_KEYWORD = \"this\";
(* Deprecated in 0.5.0 *)THROW_KEYWORD = \"throw\";
(* Introduced in 0.8.27 *)(* Never reserved *)TRANSIENT_KEYWORD = \"transient\";
TRUE_KEYWORD = \"true\";
(* Introduced in 0.6.0 *)TRY_KEYWORD = \"try\";
(* Reserved in 0.5.0 *)TYPE_DEF_KEYWORD = \"typedef\";
(* Introduced in 0.5.3 *)TYPE_KEYWORD = \"type\";
TYPE_OF_KEYWORD = \"typeof\";
UFIXED_KEYWORD = \"ufixed\";UFIXED_KEYWORD = \"ufixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\") \"x\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\");UFIXED_KEYWORD = \"ufixed\" (\"184x8\" | \"184x16\" | \"184x24\" | \"184x32\" | \"184x40\" | \"184x48\" | \"184x56\" | \"184x64\" | \"184x72\" | \"192x8\" | \"192x16\" | \"192x24\" | \"192x32\" | \"192x40\" | \"192x48\" | \"192x56\" | \"192x64\" | \"200x8\" | \"200x16\" | \"200x24\" | \"200x32\" | \"200x40\" | \"200x48\" | \"200x56\" | \"208x8\" | \"208x16\" | \"208x24\" | \"208x32\" | \"208x40\" | \"208x48\" | \"216x8\" | \"216x16\" | \"216x24\" | \"216x32\" | \"216x40\" | \"224x8\" | \"224x16\" | \"224x24\" | \"224x32\" | \"232x8\" | \"232x16\" | \"232x24\" | \"240x8\" | \"240x16\" | \"248x8\");(* Reserved in 0.4.14 *)UFIXED_KEYWORD = \"ufixed\" (\"184x80\" | \"192x72\" | \"192x80\" | \"200x64\" | \"200x72\" | \"200x80\" | \"208x56\" | \"208x64\" | \"208x72\" | \"208x80\" | \"216x48\" | \"216x56\" | \"216x64\" | \"216x72\" | \"216x80\" | \"224x40\" | \"224x48\" | \"224x56\" | \"224x64\" | \"224x72\" | \"224x80\" | \"232x32\" | \"232x40\" | \"232x48\" | \"232x56\" | \"232x64\" | \"232x72\" | \"232x80\" | \"240x24\" | \"240x32\" | \"240x40\" | \"240x48\" | \"240x56\" | \"240x64\" | \"240x72\" | \"240x80\" | \"248x16\" | \"248x24\" | \"248x32\" | \"248x40\" | \"248x48\" | \"248x56\" | \"248x64\" | \"248x72\" | \"248x80\" | \"256x8\" | \"256x16\" | \"256x24\" | \"256x32\" | \"256x40\" | \"256x48\" | \"256x56\" | \"256x64\" | \"256x72\" | \"256x80\");(* Reserved in 0.4.14 *)UFIXED_KEYWORD = \"ufixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\") \"x\" (\"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"33\" | \"34\" | \"35\" | \"36\" | \"37\" | \"38\" | \"39\" | \"41\" | \"42\" | \"43\" | \"44\" | \"45\" | \"46\" | \"47\" | \"49\" | \"50\" | \"51\" | \"52\" | \"53\" | \"54\" | \"55\" | \"57\" | \"58\" | \"59\" | \"60\" | \"61\" | \"62\" | \"63\" | \"65\" | \"66\" | \"67\" | \"68\" | \"69\" | \"70\" | \"71\" | \"73\" | \"74\" | \"75\" | \"76\" | \"77\" | \"78\" | \"79\");
UINT_KEYWORD = \"uint\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\")?;
(* Introduced in 0.8.0 *)(* Reserved in 0.5.0 *)UNCHECKED_KEYWORD = \"unchecked\";
USING_KEYWORD = \"using\";
(* Deprecated in 0.5.0 *)VAR_KEYWORD = \"var\";
(* Introduced in 0.4.16 *)VIEW_KEYWORD = \"view\";
(* Introduced in 0.6.0 *)(* Reserved in 0.6.0 *)VIRTUAL_KEYWORD = \"virtual\";
WEEKS_KEYWORD = \"weeks\";
WEI_KEYWORD = \"wei\";
WHILE_KEYWORD = \"while\";
(* Deprecated in 0.5.0 *)YEARS_KEYWORD = \"years\";"},{"location":"solidity-specification/01-file-structure/08-keywords/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/09-punctuation/","title":"1.9. Punctuation","text":""},{"location":"solidity-specification/01-file-structure/09-punctuation/#19-punctuation","title":"1.9. Punctuation","text":""},{"location":"solidity-specification/01-file-structure/09-punctuation/#syntax","title":"Syntax","text":"OPEN_PAREN = \"(\";
CLOSE_PAREN = \")\";
OPEN_BRACKET = \"[\";
CLOSE_BRACKET = \"]\";
OPEN_BRACE = \"{\";
CLOSE_BRACE = \"}\";
COMMA = \",\";
PERIOD = \".\";
QUESTION_MARK = \"?\";
SEMICOLON = \";\";
COLON = \":\";
COLON_EQUAL = \":=\";
EQUAL = \"=\";
(* Deprecated in 0.5.0 *)EQUAL_COLON = \"=:\";
EQUAL_EQUAL = \"==\";
EQUAL_GREATER_THAN = \"=>\";
ASTERISK = \"*\";
ASTERISK_EQUAL = \"*=\";
ASTERISK_ASTERISK = \"**\";
BAR = \"|\";
BAR_EQUAL = \"|=\";
BAR_BAR = \"||\";
AMPERSAND = \"&\";
AMPERSAND_EQUAL = \"&=\";
AMPERSAND_AMPERSAND = \"&&\";
LESS_THAN = \"<\";
LESS_THAN_EQUAL = \"<=\";
LESS_THAN_LESS_THAN = \"<<\";
LESS_THAN_LESS_THAN_EQUAL = \"<<=\";
GREATER_THAN = \">\";
GREATER_THAN_EQUAL = \">=\";
GREATER_THAN_GREATER_THAN = \">>\";
GREATER_THAN_GREATER_THAN_EQUAL = \">>=\";
GREATER_THAN_GREATER_THAN_GREATER_THAN = \">>>\";
GREATER_THAN_GREATER_THAN_GREATER_THAN_EQUAL = \">>>=\";
PLUS = \"+\";
PLUS_EQUAL = \"+=\";
PLUS_PLUS = \"++\";
MINUS = \"-\";
MINUS_EQUAL = \"-=\";
MINUS_MINUS = \"--\";
MINUS_GREATER_THAN = \"->\";
SLASH = \"/\" (?!\"*\" | \"/\" | \"=\");
SLASH_EQUAL = \"/=\";
PERCENT = \"%\";
PERCENT_EQUAL = \"%=\";
BANG = \"!\";
BANG_EQUAL = \"!=\";
CARET = \"^\";
CARET_EQUAL = \"^=\";
TILDE = \"~\";"},{"location":"solidity-specification/01-file-structure/09-punctuation/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/","title":"2. Definitions","text":""},{"location":"solidity-specification/02-definitions/#2-definitions","title":"2. Definitions","text":"ContractDefinition = (* abstract_keyword: *) ABSTRACT_KEYWORD? (* Introduced in 0.6.0 *) (* contract_keyword: *) CONTRACT_KEYWORD (* name: *) IDENTIFIER (* inheritance: *) InheritanceSpecifier? (* open_brace: *) OPEN_BRACE (* members: *) ContractMembers (* close_brace: *) CLOSE_BRACE;
InheritanceSpecifier = (* is_keyword: *) IS_KEYWORD (* types: *) InheritanceTypes;
InheritanceTypes = (* item: *) InheritanceType ((* separator: *) COMMA (* item: *) InheritanceType)*;
InheritanceType = (* type_name: *) IdentifierPath (* arguments: *) ArgumentsDeclaration?;
ContractMembers = (* item: *) ContractMember*;
ContractMember = (* variant: *) UsingDirective | (* variant: *) FunctionDefinition | (* variant: *) ConstructorDefinition (* Introduced in 0.4.22 *) | (* variant: *) ReceiveFunctionDefinition (* Introduced in 0.6.0 *) | (* variant: *) FallbackFunctionDefinition (* Introduced in 0.6.0 *) | (* variant: *) UnnamedFunctionDefinition (* Deprecated in 0.6.0 *) | (* variant: *) ModifierDefinition | (* variant: *) StructDefinition | (* variant: *) EnumDefinition | (* variant: *) EventDefinition | (* variant: *) ErrorDefinition (* Introduced in 0.8.4 *) | (* variant: *) UserDefinedValueTypeDefinition (* Introduced in 0.8.8 *) | (* variant: *) StateVariableDefinition;"},{"location":"solidity-specification/02-definitions/01-contracts/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/02-interfaces/","title":"2.2. Interfaces","text":""},{"location":"solidity-specification/02-definitions/02-interfaces/#22-interfaces","title":"2.2. Interfaces","text":""},{"location":"solidity-specification/02-definitions/02-interfaces/#syntax","title":"Syntax","text":"InterfaceDefinition = (* interface_keyword: *) INTERFACE_KEYWORD (* name: *) IDENTIFIER (* inheritance: *) InheritanceSpecifier? (* open_brace: *) OPEN_BRACE (* members: *) InterfaceMembers (* close_brace: *) CLOSE_BRACE;
InterfaceMembers = (* item: *) ContractMember*;"},{"location":"solidity-specification/02-definitions/02-interfaces/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/03-libraries/","title":"2.3. Libraries","text":""},{"location":"solidity-specification/02-definitions/03-libraries/#23-libraries","title":"2.3. Libraries","text":""},{"location":"solidity-specification/02-definitions/03-libraries/#syntax","title":"Syntax","text":"LibraryDefinition = (* library_keyword: *) LIBRARY_KEYWORD (* name: *) IDENTIFIER (* open_brace: *) OPEN_BRACE (* members: *) LibraryMembers (* close_brace: *) CLOSE_BRACE;
LibraryMembers = (* item: *) ContractMember*;"},{"location":"solidity-specification/02-definitions/03-libraries/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/04-structs/","title":"2.4. Structs","text":""},{"location":"solidity-specification/02-definitions/04-structs/#24-structs","title":"2.4. Structs","text":""},{"location":"solidity-specification/02-definitions/04-structs/#syntax","title":"Syntax","text":"StructDefinition = (* struct_keyword: *) STRUCT_KEYWORD (* name: *) IDENTIFIER (* open_brace: *) OPEN_BRACE (* members: *) StructMembers (* close_brace: *) CLOSE_BRACE;
StructMembers = (* item: *) StructMember*;
StructMember = (* type_name: *) TypeName (* name: *) IDENTIFIER (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/02-definitions/04-structs/#struct-types","title":"Struct Types","text":"
Structs are custom defined types that can group several variables. They can be defined inside or outside contracts.
struct Voter {\n address delegate;\n uint vote;\n}\n
You can also create new objects of this struct using the following syntax:
contract MyContract {\n function create() public {\n Voter memory v = Voter({\n delegate: msg.sender,\n vote: 1\n });\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/05-enums/","title":"2.5. Enums","text":""},{"location":"solidity-specification/02-definitions/05-enums/#25-enums","title":"2.5. Enums","text":""},{"location":"solidity-specification/02-definitions/05-enums/#syntax","title":"Syntax","text":"EnumDefinition = (* enum_keyword: *) ENUM_KEYWORD (* name: *) IDENTIFIER (* open_brace: *) OPEN_BRACE (* members: *) EnumMembers (* close_brace: *) CLOSE_BRACE;
EnumMembers = ((* item: *) IDENTIFIER ((* separator: *) COMMA (* item: *) IDENTIFIER)*)?;"},{"location":"solidity-specification/02-definitions/05-enums/#enum-types","title":"Enum Types","text":"
Enums can be used to create custom types with a finite set of constant values. Enums can be declared on the file level, outside of contract or library definitions.
enum ActionChoices {\n One,\n Two\n}\n\ncontract MyContract {\n function choose() public pure returns (ActionChoices) {\n return ActionChoices.Two;\n }\n}\n
Enums require at least one member, and its default value when declared is the first member. Enums cannot have more than 256 members.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/06-constants/","title":"2.6. Constants","text":""},{"location":"solidity-specification/02-definitions/06-constants/#26-constants","title":"2.6. Constants","text":""},{"location":"solidity-specification/02-definitions/06-constants/#syntax","title":"Syntax","text":"(* Introduced in 0.7.4 *)ConstantDefinition = (* type_name: *) TypeName (* constant_keyword: *) CONSTANT_KEYWORD (* name: *) IDENTIFIER (* equal: *) EQUAL (* value: *) Expression (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/02-definitions/06-constants/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/07-state-variables/","title":"2.7. State Variables","text":""},{"location":"solidity-specification/02-definitions/07-state-variables/#27-state-variables","title":"2.7. State Variables","text":""},{"location":"solidity-specification/02-definitions/07-state-variables/#syntax","title":"Syntax","text":"StateVariableDefinition = (* type_name: *) TypeName (* attributes: *) StateVariableAttributes (* name: *) IDENTIFIER (* value: *) StateVariableDefinitionValue? (* semicolon: *) SEMICOLON;
StateVariableDefinitionValue = (* equal: *) EQUAL (* value: *) Expression;
StateVariableAttributes = (* item: *) StateVariableAttribute*;
StateVariableAttribute = (* variant: *) OverrideSpecifier (* Introduced in 0.6.0 *) | (* variant: *) CONSTANT_KEYWORD | (* variant: *) INTERNAL_KEYWORD | (* variant: *) PRIVATE_KEYWORD | (* variant: *) PUBLIC_KEYWORD | (* variant: *) IMMUTABLE_KEYWORD (* Introduced in 0.6.5 *) | (* variant: *) TRANSIENT_KEYWORD; (* Introduced in 0.8.27 *)"},{"location":"solidity-specification/02-definitions/07-state-variables/#state-variables","title":"State Variables","text":"
State variables are variables whose values are permanently stored in contract storage.
contract MyContract {\n uint myStateVariable;\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/08-functions/","title":"2.8. Functions","text":""},{"location":"solidity-specification/02-definitions/08-functions/#28-functions","title":"2.8. Functions","text":""},{"location":"solidity-specification/02-definitions/08-functions/#syntax","title":"Syntax","text":"FunctionDefinition = (* function_keyword: *) FUNCTION_KEYWORD (* name: *) FunctionName (* parameters: *) ParametersDeclaration (* attributes: *) FunctionAttributes (* returns: *) ReturnsDeclaration? (* body: *) FunctionBody;
FunctionName = (* variant: *) IDENTIFIER | (* variant: *) FALLBACK_KEYWORD | (* variant: *) RECEIVE_KEYWORD;
ParametersDeclaration = (* open_paren: *) OPEN_PAREN (* parameters: *) Parameters (* close_paren: *) CLOSE_PAREN;
Parameters = ((* item: *) Parameter ((* separator: *) COMMA (* item: *) Parameter)*)?;
Parameter = (* type_name: *) TypeName (* storage_location: *) StorageLocation? (* name: *) IDENTIFIER?;
FunctionAttributes = (* item: *) FunctionAttribute*;
FunctionAttribute = (* variant: *) ModifierInvocation | (* variant: *) OverrideSpecifier (* Introduced in 0.6.0 *) | (* variant: *) CONSTANT_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) INTERNAL_KEYWORD | (* variant: *) PAYABLE_KEYWORD | (* variant: *) PRIVATE_KEYWORD | (* variant: *) PUBLIC_KEYWORD | (* variant: *) PURE_KEYWORD (* Introduced in 0.4.16 *) | (* variant: *) VIEW_KEYWORD (* Introduced in 0.4.16 *) | (* variant: *) VIRTUAL_KEYWORD; (* Introduced in 0.6.0 *)
(* Introduced in 0.6.0 *)OverrideSpecifier = (* override_keyword: *) OVERRIDE_KEYWORD (* overridden: *) OverridePathsDeclaration?;
(* Introduced in 0.6.0 *)OverridePathsDeclaration = (* open_paren: *) OPEN_PAREN (* paths: *) OverridePaths (* close_paren: *) CLOSE_PAREN;
(* Introduced in 0.6.0 *)OverridePaths = (* item: *) IdentifierPath ((* separator: *) COMMA (* item: *) IdentifierPath)*;
ReturnsDeclaration = (* returns_keyword: *) RETURNS_KEYWORD (* variables: *) ParametersDeclaration;
FunctionBody = (* variant: *) Block | (* variant: *) SEMICOLON;
(* Introduced in 0.4.22 *)ConstructorDefinition = (* constructor_keyword: *) CONSTRUCTOR_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) ConstructorAttributes (* body: *) Block;
(* Introduced in 0.4.22 *)ConstructorAttributes = (* item: *) ConstructorAttribute*;
(* Introduced in 0.4.22 *)ConstructorAttribute = (* variant: *) ModifierInvocation | (* variant: *) INTERNAL_KEYWORD | (* variant: *) OVERRIDE_KEYWORD (* Introduced in 0.6.0 and deprecated in 0.6.7. *) | (* variant: *) PAYABLE_KEYWORD | (* variant: *) PUBLIC_KEYWORD | (* variant: *) VIRTUAL_KEYWORD; (* Introduced in 0.6.0 and deprecated in 0.6.7. *)
(* Deprecated in 0.6.0 *)UnnamedFunctionDefinition = (* function_keyword: *) FUNCTION_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) UnnamedFunctionAttributes (* body: *) FunctionBody;
(* Deprecated in 0.6.0 *)UnnamedFunctionAttributes = (* item: *) UnnamedFunctionAttribute*;
(* Deprecated in 0.6.0 *)UnnamedFunctionAttribute = (* variant: *) ModifierInvocation | (* variant: *) CONSTANT_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) INTERNAL_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) PAYABLE_KEYWORD | (* variant: *) PRIVATE_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) PUBLIC_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) PURE_KEYWORD (* Introduced in 0.4.16 and deprecated in 0.6.0. *) | (* variant: *) VIEW_KEYWORD; (* Introduced in 0.4.16 and deprecated in 0.6.0. *)
(* Introduced in 0.6.0 *)FallbackFunctionDefinition = (* fallback_keyword: *) FALLBACK_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) FallbackFunctionAttributes (* returns: *) ReturnsDeclaration? (* body: *) FunctionBody;
(* Introduced in 0.6.0 *)FallbackFunctionAttributes = (* item: *) FallbackFunctionAttribute*;
(* Introduced in 0.6.0 *)FallbackFunctionAttribute = (* variant: *) ModifierInvocation | (* variant: *) OverrideSpecifier | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) PAYABLE_KEYWORD | (* variant: *) PURE_KEYWORD | (* variant: *) VIEW_KEYWORD | (* variant: *) VIRTUAL_KEYWORD;
(* Introduced in 0.6.0 *)ReceiveFunctionDefinition = (* receive_keyword: *) RECEIVE_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) ReceiveFunctionAttributes (* body: *) FunctionBody;
(* Introduced in 0.6.0 *)ReceiveFunctionAttributes = (* item: *) ReceiveFunctionAttribute*;
(* Introduced in 0.6.0 *)ReceiveFunctionAttribute = (* variant: *) ModifierInvocation | (* variant: *) OverrideSpecifier | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) PAYABLE_KEYWORD | (* variant: *) VIRTUAL_KEYWORD;"},{"location":"solidity-specification/02-definitions/08-functions/#function-definitions","title":"Function Definitions","text":"
Functions are the executable units of code. Functions are usually defined inside a contract, but they can also be defined outside of contracts.
contract MyContract {\n function contractFunction() public {\n // Inside the contract\n }\n}\n\nfunction helperFunction() {\n // Outside the contract\n}\n
Functions can be overloaded, where multiple functions with the same name, but with different parameters, can co-exist.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/09-modifiers/","title":"2.9. Modifiers","text":""},{"location":"solidity-specification/02-definitions/09-modifiers/#29-modifiers","title":"2.9. Modifiers","text":""},{"location":"solidity-specification/02-definitions/09-modifiers/#syntax","title":"Syntax","text":"ModifierDefinition = (* modifier_keyword: *) MODIFIER_KEYWORD (* name: *) IDENTIFIER (* parameters: *) ParametersDeclaration? (* attributes: *) ModifierAttributes (* body: *) FunctionBody;
ModifierAttributes = (* item: *) ModifierAttribute*;
ModifierAttribute = (* variant: *) OverrideSpecifier (* Introduced in 0.6.0 *) | (* variant: *) VIRTUAL_KEYWORD; (* Introduced in 0.6.0 *)
ModifierInvocation = (* name: *) IdentifierPath (* arguments: *) ArgumentsDeclaration?;"},{"location":"solidity-specification/02-definitions/09-modifiers/#function-modifiers","title":"Function Modifiers","text":"
Function modifiers can be used to amend the semantics of functions in a declarative way:
contract MyContract {\n modifier onlySeller() {\n require(msg.sender == seller, \"Only seller can call this.\");\n _; // Function body will be inserted here\n }\n\n function myFunction() public view onlySeller {\n // Code here will be executed after `onlySeller` is executed.\n }\n}\n
Unlike functions, modifiers cannot be overloaded.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/10-events/","title":"2.10. Events","text":""},{"location":"solidity-specification/02-definitions/10-events/#210-events","title":"2.10. Events","text":""},{"location":"solidity-specification/02-definitions/10-events/#syntax","title":"Syntax","text":"EventDefinition = (* event_keyword: *) EVENT_KEYWORD (* name: *) IDENTIFIER (* parameters: *) EventParametersDeclaration (* anonymous_keyword: *) ANONYMOUS_KEYWORD? (* semicolon: *) SEMICOLON;
EventParametersDeclaration = (* open_paren: *) OPEN_PAREN (* parameters: *) EventParameters (* close_paren: *) CLOSE_PAREN;
EventParameters = ((* item: *) EventParameter ((* separator: *) COMMA (* item: *) EventParameter)*)?;
EventParameter = (* type_name: *) TypeName (* indexed_keyword: *) INDEXED_KEYWORD? (* name: *) IDENTIFIER?;"},{"location":"solidity-specification/02-definitions/10-events/#event-definitions","title":"Event Definitions","text":"
Events are convenient interfaces with the EVM logging facilities. They have to be defined inside a contract:
contract MyContract {\n // Defining an event\n event BidPlacedEvent(address bidder, uint amount);\n\n function bid() public payable {\n // Triggering an event\n emit BidPlacedEvent(msg.sender, msg.value);\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/11-user-defined-value-types/","title":"2.11. User Defined Value Types","text":""},{"location":"solidity-specification/02-definitions/11-user-defined-value-types/#211-user-defined-value-types","title":"2.11. User Defined Value Types","text":""},{"location":"solidity-specification/02-definitions/11-user-defined-value-types/#syntax","title":"Syntax","text":"(* Introduced in 0.8.8 *)UserDefinedValueTypeDefinition = (* type_keyword: *) TYPE_KEYWORD (* name: *) IDENTIFIER (* is_keyword: *) IS_KEYWORD (* value_type: *) ElementaryType (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/02-definitions/11-user-defined-value-types/#user-defined-value-types","title":"User Defined Value Types","text":"
A user defined value type allows creating a zero cost abstraction over an elementary value type. This is similar to a type alias. A user defined value type is defined using type C is V
, where C
is the name of the newly introduced type and V
has to be a built-in value type (the underlying type).
type MyInteger is uint256;\n\nlibrary MyLibrary {\n function add(MyInteger a, MyInteger b) internal pure returns (MyInteger) {\n return MyInteger.wrap(MyInteger.unwrap(a) + MyInteger.unwrap(b));\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/12-errors/","title":"2.12. Errors","text":""},{"location":"solidity-specification/02-definitions/12-errors/#212-errors","title":"2.12. Errors","text":""},{"location":"solidity-specification/02-definitions/12-errors/#syntax","title":"Syntax","text":"(* Introduced in 0.8.4 *)ErrorDefinition = (* error_keyword: *) ERROR_KEYWORD (* name: *) IDENTIFIER (* members: *) ErrorParametersDeclaration (* semicolon: *) SEMICOLON;
(* Introduced in 0.8.4 *)ErrorParametersDeclaration = (* open_paren: *) OPEN_PAREN (* parameters: *) ErrorParameters (* close_paren: *) CLOSE_PAREN;
(* Introduced in 0.8.4 *)ErrorParameters = ((* item: *) ErrorParameter ((* separator: *) COMMA (* item: *) ErrorParameter)*)?;
(* Introduced in 0.8.4 *)ErrorParameter = (* type_name: *) TypeName (* name: *) IDENTIFIER?;"},{"location":"solidity-specification/02-definitions/12-errors/#error-definitions","title":"Error Definitions","text":"
Errors allow you to define descriptive names and data for failure situations. Errors can be used in revert statements. In comparison to string descriptions, errors are much cheaper and allow you to encode additional data. You can use NatSpec to describe the error to the user. They can also be defined inside or outside contracts:
contract Token {\n error NotEnoughFunds(uint requested, uint available);\n\n function transfer(address to, uint amount) public {\n uint balance = balances[msg.sender];\n if (balance < amount)\n revert NotEnoughFunds(amount, balance);\n\n // Continue with the transfer...\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/03-types/","title":"3. Types","text":""},{"location":"solidity-specification/03-types/#3-types","title":"3. Types","text":"TypeName = (* variant: *) ArrayTypeName | (* variant: *) FunctionType | (* variant: *) MappingType | (* variant: *) ElementaryType | (* variant: *) IdentifierPath;
(* Postfix unary operator *)ArrayTypeName = (* operand: *) TypeName (* open_bracket: *) OPEN_BRACKET (* index: *) Expression? (* close_bracket: *) CLOSE_BRACKET;
FunctionType = (* function_keyword: *) FUNCTION_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) FunctionTypeAttributes (* returns: *) ReturnsDeclaration?;
FunctionTypeAttributes = (* item: *) FunctionTypeAttribute*;
FunctionTypeAttribute = (* variant: *) INTERNAL_KEYWORD | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) PRIVATE_KEYWORD | (* variant: *) PUBLIC_KEYWORD | (* variant: *) CONSTANT_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) PURE_KEYWORD (* Introduced in 0.4.16 *) | (* variant: *) VIEW_KEYWORD (* Introduced in 0.4.16 *) | (* variant: *) PAYABLE_KEYWORD;
MappingType = (* mapping_keyword: *) MAPPING_KEYWORD (* open_paren: *) OPEN_PAREN (* key_type: *) MappingKey (* equal_greater_than: *) EQUAL_GREATER_THAN (* value_type: *) MappingValue (* close_paren: *) CLOSE_PAREN;
MappingKey = (* key_type: *) MappingKeyType (* name: *) IDENTIFIER?; (* Introduced in 0.8.18 *)
MappingKeyType = (* variant: *) ElementaryType | (* variant: *) IdentifierPath;
MappingValue = (* type_name: *) TypeName (* name: *) IDENTIFIER?; (* Introduced in 0.8.18 *)"},{"location":"solidity-specification/03-types/01-advanced-types/#function-types","title":"Function Types","text":"
Function types are the types of functions. Variables of function type can be assigned from functions and function parameters of function type can be used to pass functions to and return functions from function calls. They come in two flavors, internal
and external
.
Function types are notated as follows:
function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]\n
In contrast to the parameter types, the return types cannot be empty. If the function type should not return anything, the whole returns (<return types>)
part has to be omitted.
By default, function types are internal, so the internal
keyword can be omitted. Note that this only applies to function types. Visibility has to be specified explicitly for functions defined in contracts, they do not have a default.
contract Oracle {\n Request[] private requests;\n\n function query(bytes memory data, function(uint) external callback) public {\n requests.push(Request(data, callback));\n }\n\n function reply(uint requestID, uint response) public {\n requests[requestID].callback(response);\n }\n}\n
"},{"location":"solidity-specification/03-types/01-advanced-types/#mapping-types","title":"Mapping Types","text":"Mapping types use the syntax mapping(_KeyType => _ValueType)
and variables of mapping type are declared using the syntax mapping(_KeyType => _ValueType) _VariableName
.
contract MappingExample {\n mapping(address => uint) public balances;\n\n function update(uint newBalance) public {\n balances[msg.sender] = newBalance;\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/03-types/02-elementary-types/","title":"3.2. Elementary Types","text":""},{"location":"solidity-specification/03-types/02-elementary-types/#32-elementary-types","title":"3.2. Elementary Types","text":""},{"location":"solidity-specification/03-types/02-elementary-types/#syntax","title":"Syntax","text":"ElementaryType = (* variant: *) BOOL_KEYWORD | (* variant: *) BYTE_KEYWORD (* Deprecated in 0.8.0 *) | (* variant: *) STRING_KEYWORD | (* variant: *) AddressType | (* variant: *) BYTES_KEYWORD | (* variant: *) INT_KEYWORD | (* variant: *) UINT_KEYWORD | (* variant: *) FIXED_KEYWORD | (* variant: *) UFIXED_KEYWORD;
AddressType = (* address_keyword: *) ADDRESS_KEYWORD (* payable_keyword: *) PAYABLE_KEYWORD?;"},{"location":"solidity-specification/03-types/02-elementary-types/#address-types","title":"Address Types","text":"
The address type comes in two flavours, which are largely identical:
address
: Holds a 20 byte value (size of an Ethereum address).address payable
: Same as address
, but with the additional members transfer
and send
.Hexadecimal literals that pass the address checksum test, for example 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF
are of address
type. Hexadecimal literals that are between 39 and 41 digits long and do not pass the checksum test produce an error. You can prepend (for int
types) or append (for bytesNN
types) zeros to remove the error.
The value types bytes1
, bytes2
, bytes3
, \u2026, bytes32
hold a sequence of bytes from one to up to 32.
The bytes
type is similar to bytes1[]
, but it is packed tightly in calldata and memory.
Variables of type string
are equal to bytes
but do not allow length or index access. If you want to access the byte-representation of a string s
, use bytes(s)
. Keep in mind that you are accessing the low-level bytes of the UTF-8 representation, and not the individual characters.
Memory arrays with dynamic length can be created using the new
keyword:
contract MyContract {\n function myFunction(uint length) public pure {\n bytes memory b = new bytes(length);\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/04-statements/","title":"4. Statements","text":""},{"location":"solidity-specification/04-statements/#4-statements","title":"4. Statements","text":"Block = (* open_brace: *) OPEN_BRACE (* statements: *) Statements (* close_brace: *) CLOSE_BRACE;
Statements = (* item: *) Statement*;
Statement = (* variant: *) IfStatement | (* variant: *) ForStatement | (* variant: *) WhileStatement | (* variant: *) DoWhileStatement | (* variant: *) ContinueStatement | (* variant: *) BreakStatement | (* variant: *) ReturnStatement | (* variant: *) ThrowStatement (* Deprecated in 0.5.0 *) | (* variant: *) EmitStatement (* Introduced in 0.4.21 *) | (* variant: *) TryStatement (* Introduced in 0.6.0 *) | (* variant: *) RevertStatement (* Introduced in 0.8.4 *) | (* variant: *) AssemblyStatement | (* variant: *) Block | (* variant: *) UncheckedBlock (* Introduced in 0.8.0 *) | (* variant: *) TupleDeconstructionStatement | (* variant: *) VariableDeclarationStatement | (* variant: *) ExpressionStatement;
(* Introduced in 0.8.0 *)UncheckedBlock = (* unchecked_keyword: *) UNCHECKED_KEYWORD (* block: *) Block;
ExpressionStatement = (* expression: *) Expression (* semicolon: *) SEMICOLON;
AssemblyStatement = (* assembly_keyword: *) ASSEMBLY_KEYWORD (* label: *) StringLiteral? (* flags: *) AssemblyFlagsDeclaration? (* body: *) YulBlock;
AssemblyFlagsDeclaration = (* open_paren: *) OPEN_PAREN (* flags: *) AssemblyFlags (* close_paren: *) CLOSE_PAREN;
AssemblyFlags = (* item: *) StringLiteral ((* separator: *) COMMA (* item: *) StringLiteral)*;"},{"location":"solidity-specification/04-statements/01-blocks/#unchecked-blocks","title":"Unchecked Blocks","text":"
Starting with v0.8.0
, by default, all arithmetic operations are checked for underflow or overflow, which means that if the result of an operation falls outside the value range of the type, the call is reverted through a failing assertion. This can be disabled using the unchecked
block, resulting in wrapping arithmetic:
unchecked {\n i++;\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/04-statements/02-declaration-statements/","title":"4.2. Declaration Statements","text":""},{"location":"solidity-specification/04-statements/02-declaration-statements/#42-declaration-statements","title":"4.2. Declaration Statements","text":""},{"location":"solidity-specification/04-statements/02-declaration-statements/#syntax","title":"Syntax","text":"TupleDeconstructionStatement = (* var_keyword: *) VAR_KEYWORD? (* Deprecated in 0.5.0 *) (* open_paren: *) OPEN_PAREN (* elements: *) TupleDeconstructionElements (* close_paren: *) CLOSE_PAREN (* equal: *) EQUAL (* expression: *) Expression (* semicolon: *) SEMICOLON;
TupleDeconstructionElements = (* item: *) TupleDeconstructionElement ((* separator: *) COMMA (* item: *) TupleDeconstructionElement)*;
TupleDeconstructionElement = (* member: *) TupleMember?;
TupleMember = (* variant: *) TypedTupleMember | (* variant: *) UntypedTupleMember;
TypedTupleMember = (* type_name: *) TypeName (* storage_location: *) StorageLocation? (* name: *) IDENTIFIER;
UntypedTupleMember = (* storage_location: *) StorageLocation? (* name: *) IDENTIFIER;
VariableDeclarationStatement = (* variable_type: *) VariableDeclarationType (* storage_location: *) StorageLocation? (* name: *) IDENTIFIER (* value: *) VariableDeclarationValue? (* semicolon: *) SEMICOLON;
VariableDeclarationType = (* variant: *) TypeName | (* variant: *) VAR_KEYWORD; (* Deprecated in 0.5.0 *)
VariableDeclarationValue = (* equal: *) EQUAL (* expression: *) Expression;
StorageLocation = (* variant: *) MEMORY_KEYWORD | (* variant: *) STORAGE_KEYWORD | (* variant: *) CALL_DATA_KEYWORD; (* Introduced in 0.5.0 *)"},{"location":"solidity-specification/04-statements/02-declaration-statements/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/04-statements/03-control-statements/","title":"4.3. Control Statements","text":""},{"location":"solidity-specification/04-statements/03-control-statements/#43-control-statements","title":"4.3. Control Statements","text":""},{"location":"solidity-specification/04-statements/03-control-statements/#syntax","title":"Syntax","text":"IfStatement = (* if_keyword: *) IF_KEYWORD (* open_paren: *) OPEN_PAREN (* condition: *) Expression (* close_paren: *) CLOSE_PAREN (* body: *) Statement (* else_branch: *) ElseBranch?;
ElseBranch = (* else_keyword: *) ELSE_KEYWORD (* body: *) Statement;
ForStatement = (* for_keyword: *) FOR_KEYWORD (* open_paren: *) OPEN_PAREN (* initialization: *) ForStatementInitialization (* condition: *) ForStatementCondition (* iterator: *) Expression? (* close_paren: *) CLOSE_PAREN (* body: *) Statement;
ForStatementInitialization = (* variant: *) TupleDeconstructionStatement | (* variant: *) VariableDeclarationStatement | (* variant: *) ExpressionStatement | (* variant: *) SEMICOLON;
ForStatementCondition = (* variant: *) ExpressionStatement | (* variant: *) SEMICOLON;
WhileStatement = (* while_keyword: *) WHILE_KEYWORD (* open_paren: *) OPEN_PAREN (* condition: *) Expression (* close_paren: *) CLOSE_PAREN (* body: *) Statement;
DoWhileStatement = (* do_keyword: *) DO_KEYWORD (* body: *) Statement (* while_keyword: *) WHILE_KEYWORD (* open_paren: *) OPEN_PAREN (* condition: *) Expression (* close_paren: *) CLOSE_PAREN (* semicolon: *) SEMICOLON;
ContinueStatement = (* continue_keyword: *) CONTINUE_KEYWORD (* semicolon: *) SEMICOLON;
BreakStatement = (* break_keyword: *) BREAK_KEYWORD (* semicolon: *) SEMICOLON;
ReturnStatement = (* return_keyword: *) RETURN_KEYWORD (* expression: *) Expression? (* semicolon: *) SEMICOLON;
(* Introduced in 0.4.21 *)EmitStatement = (* emit_keyword: *) EMIT_KEYWORD (* event: *) IdentifierPath (* arguments: *) ArgumentsDeclaration (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/04-statements/03-control-statements/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/04-statements/04-error-handling/","title":"4.4. Error Handling","text":""},{"location":"solidity-specification/04-statements/04-error-handling/#44-error-handling","title":"4.4. Error Handling","text":""},{"location":"solidity-specification/04-statements/04-error-handling/#syntax","title":"Syntax","text":"(* Introduced in 0.6.0 *)TryStatement = (* try_keyword: *) TRY_KEYWORD (* expression: *) Expression (* returns: *) ReturnsDeclaration? (* body: *) Block (* catch_clauses: *) CatchClauses;
(* Introduced in 0.6.0 *)CatchClauses = (* item: *) CatchClause+;
(* Introduced in 0.6.0 *)CatchClause = (* catch_keyword: *) CATCH_KEYWORD (* error: *) CatchClauseError? (* body: *) Block;
(* Introduced in 0.6.0 *)CatchClauseError = (* name: *) IDENTIFIER? (* parameters: *) ParametersDeclaration;
(* Introduced in 0.8.4 *)RevertStatement = (* revert_keyword: *) REVERT_KEYWORD (* error: *) IdentifierPath? (* arguments: *) ArgumentsDeclaration (* semicolon: *) SEMICOLON;
(* Deprecated in 0.5.0 *)ThrowStatement = (* throw_keyword: *) THROW_KEYWORD (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/04-statements/04-error-handling/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/","title":"5. Expressions","text":""},{"location":"solidity-specification/05-expressions/#5-expressions","title":"5. Expressions","text":"Expression = (* variant: *) AssignmentExpression | (* variant: *) ConditionalExpression | (* variant: *) OrExpression | (* variant: *) AndExpression | (* variant: *) EqualityExpression | (* variant: *) ComparisonExpression | (* variant: *) BitwiseOrExpression | (* variant: *) BitwiseXorExpression | (* variant: *) BitwiseAndExpression | (* variant: *) ShiftExpression | (* variant: *) AdditiveExpression | (* variant: *) MultiplicativeExpression | (* variant: *) ExponentiationExpression | (* variant: *) PostfixExpression | (* variant: *) PrefixExpression | (* variant: *) FunctionCallExpression | (* variant: *) CallOptionsExpression | (* variant: *) MemberAccessExpression | (* variant: *) IndexAccessExpression | (* variant: *) NewExpression | (* variant: *) TupleExpression | (* variant: *) TypeExpression (* Introduced in 0.5.3 *) | (* variant: *) ArrayExpression | (* variant: *) HexNumberExpression | (* variant: *) DecimalNumberExpression | (* variant: *) StringExpression | (* variant: *) ElementaryType | (* variant: *) PAYABLE_KEYWORD (* Introduced in 0.6.0 *) | (* variant: *) THIS_KEYWORD | (* variant: *) SUPER_KEYWORD | (* variant: *) TRUE_KEYWORD | (* variant: *) FALSE_KEYWORD | (* variant: *) IDENTIFIER;
(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) BAR_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) PLUS_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) MINUS_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) CARET_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) SLASH_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) PERCENT_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) ASTERISK_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) AMPERSAND_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) LESS_THAN_LESS_THAN_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_GREATER_THAN_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_GREATER_THAN_GREATER_THAN_EQUAL (* right_operand: *) Expression;
(* Postfix unary operator *)ConditionalExpression = (* operand: *) Expression (* question_mark: *) QUESTION_MARK (* true_expression: *) Expression (* colon: *) COLON (* false_expression: *) Expression;
(* Left-associative binary operator *)OrExpression = (* left_operand: *) Expression (* operator: *) BAR_BAR (* right_operand: *) Expression;
(* Left-associative binary operator *)AndExpression = (* left_operand: *) Expression (* operator: *) AMPERSAND_AMPERSAND (* right_operand: *) Expression;
(* Left-associative binary operator *)EqualityExpression = (* left_operand: *) Expression (* operator: *) EQUAL_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)EqualityExpression = (* left_operand: *) Expression (* operator: *) BANG_EQUAL (* right_operand: *) Expression;
(* Left-associative binary operator *)ComparisonExpression = (* left_operand: *) Expression (* operator: *) LESS_THAN (* right_operand: *) Expression;(* Left-associative binary operator *)ComparisonExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN (* right_operand: *) Expression;(* Left-associative binary operator *)ComparisonExpression = (* left_operand: *) Expression (* operator: *) LESS_THAN_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)ComparisonExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_EQUAL (* right_operand: *) Expression;
(* Left-associative binary operator *)BitwiseOrExpression = (* left_operand: *) Expression (* operator: *) BAR (* right_operand: *) Expression;
(* Left-associative binary operator *)BitwiseXorExpression = (* left_operand: *) Expression (* operator: *) CARET (* right_operand: *) Expression;
(* Left-associative binary operator *)BitwiseAndExpression = (* left_operand: *) Expression (* operator: *) AMPERSAND (* right_operand: *) Expression;
(* Left-associative binary operator *)ShiftExpression = (* left_operand: *) Expression (* operator: *) LESS_THAN_LESS_THAN (* right_operand: *) Expression;(* Left-associative binary operator *)ShiftExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_GREATER_THAN (* right_operand: *) Expression;(* Left-associative binary operator *)ShiftExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_GREATER_THAN_GREATER_THAN (* right_operand: *) Expression;
(* Left-associative binary operator *)AdditiveExpression = (* left_operand: *) Expression (* operator: *) PLUS (* right_operand: *) Expression;(* Left-associative binary operator *)AdditiveExpression = (* left_operand: *) Expression (* operator: *) MINUS (* right_operand: *) Expression;
(* Left-associative binary operator *)MultiplicativeExpression = (* left_operand: *) Expression (* operator: *) ASTERISK (* right_operand: *) Expression;(* Left-associative binary operator *)MultiplicativeExpression = (* left_operand: *) Expression (* operator: *) SLASH (* right_operand: *) Expression;(* Left-associative binary operator *)MultiplicativeExpression = (* left_operand: *) Expression (* operator: *) PERCENT (* right_operand: *) Expression;
(* Left-associative binary operator *)(* Deprecated in 0.8.0 *)ExponentiationExpression = (* left_operand: *) Expression (* operator: *) ASTERISK_ASTERISK (* right_operand: *) Expression;(* Right-associative binary operator *)(* Introduced in 0.8.0 *)ExponentiationExpression = (* left_operand: *) Expression (* operator: *) ASTERISK_ASTERISK (* right_operand: *) Expression;
(* Postfix unary operator *)PostfixExpression = (* operand: *) Expression (* operator: *) PLUS_PLUS;(* Postfix unary operator *)PostfixExpression = (* operand: *) Expression (* operator: *) MINUS_MINUS;
(* Prefix unary operator *)PrefixExpression = (* operator: *) PLUS_PLUS (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) MINUS_MINUS (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) TILDE (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) BANG (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) MINUS (* operand: *) Expression;(* Prefix unary operator *)(* Deprecated in 0.5.0 *)PrefixExpression = (* operator: *) PLUS (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) DELETE_KEYWORD (* operand: *) Expression;
(* Postfix unary operator *)FunctionCallExpression = (* operand: *) Expression (* arguments: *) ArgumentsDeclaration;
(* Postfix unary operator *)(* Introduced in 0.6.2 *)CallOptionsExpression = (* operand: *) Expression (* open_brace: *) OPEN_BRACE (* options: *) CallOptions (* close_brace: *) CLOSE_BRACE;
(* Postfix unary operator *)MemberAccessExpression = (* operand: *) Expression (* period: *) PERIOD (* member: *) IDENTIFIER;
(* Postfix unary operator *)IndexAccessExpression = (* operand: *) Expression (* open_bracket: *) OPEN_BRACKET (* start: *) Expression? (* end: *) IndexAccessEnd? (* close_bracket: *) CLOSE_BRACKET;
IndexAccessEnd = (* colon: *) COLON (* end: *) Expression?;"},{"location":"solidity-specification/05-expressions/01-base-expressions/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/02-function-calls/","title":"5.2. Function Calls","text":""},{"location":"solidity-specification/05-expressions/02-function-calls/#52-function-calls","title":"5.2. Function Calls","text":""},{"location":"solidity-specification/05-expressions/02-function-calls/#syntax","title":"Syntax","text":"ArgumentsDeclaration = (* variant: *) PositionalArgumentsDeclaration | (* variant: *) NamedArgumentsDeclaration;
PositionalArgumentsDeclaration = (* open_paren: *) OPEN_PAREN (* arguments: *) PositionalArguments (* close_paren: *) CLOSE_PAREN;
PositionalArguments = ((* item: *) Expression ((* separator: *) COMMA (* item: *) Expression)*)?;
NamedArgumentsDeclaration = (* open_paren: *) OPEN_PAREN (* arguments: *) NamedArgumentGroup? (* close_paren: *) CLOSE_PAREN;
NamedArgumentGroup = (* open_brace: *) OPEN_BRACE (* arguments: *) NamedArguments (* close_brace: *) CLOSE_BRACE;
NamedArguments = ((* item: *) NamedArgument ((* separator: *) COMMA (* item: *) NamedArgument)*)?;
(* Introduced in 0.6.2 *)CallOptions = (* item: *) NamedArgument ((* separator: *) COMMA (* item: *) NamedArgument)*;
NamedArgument = (* name: *) IDENTIFIER (* colon: *) COLON (* value: *) Expression;"},{"location":"solidity-specification/05-expressions/02-function-calls/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/03-primary-expressions/","title":"5.3. Primary Expressions","text":""},{"location":"solidity-specification/05-expressions/03-primary-expressions/#53-primary-expressions","title":"5.3. Primary Expressions","text":""},{"location":"solidity-specification/05-expressions/03-primary-expressions/#syntax","title":"Syntax","text":"(* Introduced in 0.5.3 *)TypeExpression = (* type_keyword: *) TYPE_KEYWORD (* open_paren: *) OPEN_PAREN (* type_name: *) TypeName (* close_paren: *) CLOSE_PAREN;
NewExpression = (* new_keyword: *) NEW_KEYWORD (* type_name: *) TypeName;
TupleExpression = (* open_paren: *) OPEN_PAREN (* items: *) TupleValues (* close_paren: *) CLOSE_PAREN;
TupleValues = (* item: *) TupleValue ((* separator: *) COMMA (* item: *) TupleValue)*;
TupleValue = (* expression: *) Expression?;
ArrayExpression = (* open_bracket: *) OPEN_BRACKET (* items: *) ArrayValues (* close_bracket: *) CLOSE_BRACKET;
ArrayValues = (* item: *) Expression ((* separator: *) COMMA (* item: *) Expression)*;"},{"location":"solidity-specification/05-expressions/03-primary-expressions/#array-literals","title":"Array Literals","text":"
An array literal is a comma-separated list of one or more expressions, enclosed in square brackets ([...]
). For example [1, a, f(3)]
. It is always a statically-sized memory array whose length is the number of expressions.
contract MyContract {\n function someFunction() public pure {\n otherFunction([uint(1), 2, 3]);\n }\n}\n
"},{"location":"solidity-specification/05-expressions/03-primary-expressions/#array-slices","title":"Array Slices","text":"Array slices are a view on a contiguous portion of an array. They are written as x[start:end]
, where start
and end
are expressions resulting in a uint256 type (or implicitly convertible to it). The first element of the slice is x[start]
and the last element is x[end - 1]
.
Both start
and end
are optional: start
defaults to 0
and end
defaults to the length of the array.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/04-numbers/","title":"5.4. Numbers","text":""},{"location":"solidity-specification/05-expressions/04-numbers/#54-numbers","title":"5.4. Numbers","text":""},{"location":"solidity-specification/05-expressions/04-numbers/#syntax","title":"Syntax","text":"HexNumberExpression = (* literal: *) HEX_LITERAL (* unit: *) NumberUnit?; (* Deprecated in 0.5.0 *)
DecimalNumberExpression = (* literal: *) DECIMAL_LITERAL (* unit: *) NumberUnit?;
HEX_LITERAL = \"0x\" \u00abHEX_CHARACTER\u00bb+ (\"_\" \u00abHEX_CHARACTER\u00bb+)* (?!\u00abIDENTIFIER_START\u00bb);(* Deprecated in 0.5.0 *)HEX_LITERAL = \"0X\" \u00abHEX_CHARACTER\u00bb+ (\"_\" \u00abHEX_CHARACTER\u00bb+)* (?!\u00abIDENTIFIER_START\u00bb);
DECIMAL_LITERAL = \".\" \u00abDECIMAL_DIGITS\u00bb \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);DECIMAL_LITERAL = \u00abDECIMAL_DIGITS\u00bb (?!\".\") \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);(* Deprecated in 0.5.0 *)DECIMAL_LITERAL = \u00abDECIMAL_DIGITS\u00bb \".\" (?!\u00abDECIMAL_DIGITS\u00bb) \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);(* Deprecated in 0.5.0 *)DECIMAL_LITERAL = \u00abDECIMAL_DIGITS\u00bb \".\" \u00abDECIMAL_DIGITS\u00bb \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);(* Introduced in 0.5.0 *)DECIMAL_LITERAL = \u00abDECIMAL_DIGITS\u00bb (\".\" \u00abDECIMAL_DIGITS\u00bb)? \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);
\u00abDECIMAL_DIGITS\u00bb = \"0\"\u2026\"9\"+ (\"_\" \"0\"\u2026\"9\"+)*;
\u00abDECIMAL_EXPONENT\u00bb = (\"e\" | \"E\") \"-\"? \u00abDECIMAL_DIGITS\u00bb;
NumberUnit = (* variant: *) WEI_KEYWORD | (* variant: *) GWEI_KEYWORD (* Introduced in 0.6.11 *) | (* variant: *) SZABO_KEYWORD (* Deprecated in 0.7.0 *) | (* variant: *) FINNEY_KEYWORD (* Deprecated in 0.7.0 *) | (* variant: *) ETHER_KEYWORD | (* variant: *) SECONDS_KEYWORD | (* variant: *) MINUTES_KEYWORD | (* variant: *) HOURS_KEYWORD | (* variant: *) DAYS_KEYWORD | (* variant: *) WEEKS_KEYWORD | (* variant: *) YEARS_KEYWORD; (* Deprecated in 0.5.0 *)"},{"location":"solidity-specification/05-expressions/04-numbers/#integers","title":"Integers","text":"
Signed (int8
..int256
) and unsigned (uint8
..uint256
) integers of various sizes, from 8 to 256 bits, moving up in steps of 8 bits. uint
and int
are aliases for uint256
and int256
, respectively.
Integers in Solidity are restricted to a certain range. For example, with uint32
, this is 0
up to 2**32 - 1
.
Integer literals are formed from a sequence of digits in the range 0-9
. They are interpreted as decimals. For example, 69
means sixty nine.
Octal literals do not exist in Solidity and leading zeros are invalid.
"},{"location":"solidity-specification/05-expressions/04-numbers/#decimals","title":"Decimals","text":"Decimal fractional literals are formed by a .
with at least one number on one side. Examples include 1.
, .1
and 1.3
.
Signed fixed
and unsigned fixed ufixed
point number of various sizes. Keywords ufixedMxN
and fixedMxN
, where M
represents the number of bits taken by the type and N
represents how many decimal points are available. M
must be divisible by 8 and goes from 8 to 256 bits. N
must be between 0 and 80, inclusive. ufixed
and fixed
are aliases for ufixed128x18
and fixed128x18
, respectively.
Fixed point numbers are not fully supported by Solidity yet. They can be declared, but cannot be assigned to or from.
"},{"location":"solidity-specification/05-expressions/04-numbers/#scientific-notation","title":"Scientific Notation","text":"Scientific notation in the form of 2e10
is also supported, where the mantissa can be fractional but the exponent has to be an integer. The literal MeE
is equivalent to M * 10**E
. Examples include 2e10
, -2e10
, 2e-10
, 2.5e1
.
Underscores can be used to separate the digits of a numeric literal to aid readability. For example, decimal 123_000
, hexadecimal 0x2eff_abcde
, scientific decimal notation 1_2e345_678
are all valid. Underscores are only allowed between two digits and only one consecutive underscore is allowed. There is no additional semantic meaning added to a number literal containing underscores, the underscores are ignored.
A literal number can take a suffix of wei
, gwei
or ether
to specify a sub-denomination of Ether, where Ether numbers without a postfix are assumed to be Wei.
assert(1 wei == 1);\nassert(1 gwei == 1e9);\nassert(1 szabo == 1e12);\nassert(1 finney == 1e15);\nassert(1 ether == 1e18);\n
"},{"location":"solidity-specification/05-expressions/04-numbers/#time-units","title":"Time Units","text":"Suffixes that can be used to specify units of time where seconds are the base unit and units are considered naively in the following way:
assert(1 == 1 seconds);\nassert(1 minutes == 60 seconds);\nassert(1 hours == 60 minutes);\nassert(1 days == 24 hours);\nassert(1 weeks == 7 days);\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/05-strings/","title":"5.5. Strings","text":""},{"location":"solidity-specification/05-expressions/05-strings/#55-strings","title":"5.5. Strings","text":""},{"location":"solidity-specification/05-expressions/05-strings/#syntax","title":"Syntax","text":"StringExpression = (* variant: *) StringLiteral (* Deprecated in 0.5.14 *) | (* variant: *) StringLiterals (* Introduced in 0.5.14 *) | (* variant: *) HexStringLiteral (* Deprecated in 0.5.14 *) | (* variant: *) HexStringLiterals (* Introduced in 0.5.14 *) | (* variant: *) UnicodeStringLiterals; (* Introduced in 0.7.0 *)
(* Introduced in 0.5.14 *)StringLiterals = (* item: *) StringLiteral+;
StringLiteral = (* variant: *) SINGLE_QUOTED_STRING_LITERAL | (* variant: *) DOUBLE_QUOTED_STRING_LITERAL;
(* Deprecated in 0.4.25 *)SINGLE_QUOTED_STRING_LITERAL = \"'\" (\u00abESCAPE_SEQUENCE_ARBITRARY\u00bb | !(\"'\" | \"\\\\\" | \"\\r\" | \"\\n\"))* \"'\";(* Introduced in 0.4.25 and deprecated in 0.7.0. *)SINGLE_QUOTED_STRING_LITERAL = \"'\" (\u00abESCAPE_SEQUENCE\u00bb | !(\"'\" | \"\\\\\" | \"\\r\" | \"\\n\"))* \"'\";SINGLE_QUOTED_STRING_LITERAL = \"'\" (\u00abESCAPE_SEQUENCE\u00bb | \" \"\u2026\"&\" | \"(\"\u2026\"[\" | \"]\"\u2026\"~\")* \"'\";
(* Deprecated in 0.4.25 *)DOUBLE_QUOTED_STRING_LITERAL = '\"' (\u00abESCAPE_SEQUENCE_ARBITRARY\u00bb | !('\"' | \"\\\\\" | \"\\r\" | \"\\n\"))* '\"';(* Introduced in 0.4.25 and deprecated in 0.7.0. *)DOUBLE_QUOTED_STRING_LITERAL = '\"' (\u00abESCAPE_SEQUENCE\u00bb | !('\"' | \"\\\\\" | \"\\r\" | \"\\n\"))* '\"';DOUBLE_QUOTED_STRING_LITERAL = '\"' (\u00abESCAPE_SEQUENCE\u00bb | \" \"\u2026\"!\" | \"#\"\u2026\"[\" | \"]\"\u2026\"~\")* '\"';
(* Introduced in 0.5.14 *)HexStringLiterals = (* item: *) HexStringLiteral+;
HexStringLiteral = (* variant: *) SINGLE_QUOTED_HEX_STRING_LITERAL | (* variant: *) DOUBLE_QUOTED_HEX_STRING_LITERAL;
SINGLE_QUOTED_HEX_STRING_LITERAL = \"hex'\" \u00abHEX_STRING_CONTENTS\u00bb? \"'\";
DOUBLE_QUOTED_HEX_STRING_LITERAL = 'hex\"' \u00abHEX_STRING_CONTENTS\u00bb? '\"';
\u00abHEX_STRING_CONTENTS\u00bb = \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb (\"_\"? \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb)*;
\u00abHEX_CHARACTER\u00bb = \"0\"\u2026\"9\" | \"a\"\u2026\"f\" | \"A\"\u2026\"F\";
(* Introduced in 0.7.0 *)UnicodeStringLiterals = (* item: *) UnicodeStringLiteral+;
(* Introduced in 0.7.0 *)UnicodeStringLiteral = (* variant: *) SINGLE_QUOTED_UNICODE_STRING_LITERAL | (* variant: *) DOUBLE_QUOTED_UNICODE_STRING_LITERAL;
(* Introduced in 0.7.0 *)SINGLE_QUOTED_UNICODE_STRING_LITERAL = \"unicode'\" (\u00abESCAPE_SEQUENCE\u00bb | !(\"'\" | \"\\\\\" | \"\\r\" | \"\\n\"))* \"'\";
(* Introduced in 0.7.0 *)DOUBLE_QUOTED_UNICODE_STRING_LITERAL = 'unicode\"' (\u00abESCAPE_SEQUENCE\u00bb | !('\"' | \"\\\\\" | \"\\r\" | \"\\n\"))* '\"';
\u00abESCAPE_SEQUENCE\u00bb = \"\\\\\" (\u00abASCII_ESCAPE\u00bb | \u00abHEX_BYTE_ESCAPE\u00bb | \u00abUNICODE_ESCAPE\u00bb);
(* Deprecated in 0.4.25 *)\u00abESCAPE_SEQUENCE_ARBITRARY\u00bb = \"\\\\\" (!(\"x\" | \"u\") | \u00abHEX_BYTE_ESCAPE\u00bb | \u00abUNICODE_ESCAPE\u00bb);
\u00abASCII_ESCAPE\u00bb = \"n\" | \"r\" | \"t\" | \"'\" | '\"' | \"\\\\\" | \"\\r\\n\" | \"\\r\" | \"\\n\";
\u00abHEX_BYTE_ESCAPE\u00bb = \"x\" \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb;
\u00abUNICODE_ESCAPE\u00bb = \"u\" \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb;"},{"location":"solidity-specification/05-expressions/05-strings/#string-literals","title":"String Literals","text":"
String literals are written with either double or single-quotes (\"foo\"
or 'bar'
), and they can also be split into multiple consecutive parts (\"foo\" \"bar\"
is equivalent to \"foobar\"
) which can be helpful when dealing with long strings. They do not imply trailing zeroes as in C; \"foo\"
represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to bytes1
, ..., bytes32
if they fit.
String literals can only contain printable ASCII characters, which means the characters between 0x20
and 0x7E
inclusively.
While regular string literals can only contain ASCII, unicode literals (prefixed with the keyword unicode
) can contain any valid UTF-8 sequence. They also support the very same escape sequences as regular string literals.
string memory a = unicode\"Hello \ud83d\ude03\";\n
"},{"location":"solidity-specification/05-expressions/05-strings/#hexadecimal-literals","title":"Hexadecimal Literals","text":"Hexadecimal literals are prefixed with the keyword hex
and are enclosed in double or single-quotes (hex\"001122FF\"
, hex'0011_22_FF'
). Their content must be hexadecimal digits which can optionally use a single underscore as separator between byte boundaries. The value of the literal will be the binary representation of the hexadecimal sequence.
Hexadecimal literals behave like string literals and have the same convertibility restrictions. Additionally, multiple hexadecimal literals separated by whitespace are concatenated into a single literal: hex\"00112233\" hex\"44556677\"
is equivalent to hex\"0011223344556677\"
String literals also support the following escape characters:
\\<newline>
(escapes an actual newline)\\\\
(backslash)\\'
(single quote)\\\"
(double quote)\\n
(newline)break\\r
(carriage return)\\t
(tab)\\xNN
(hex escape, takes a hex value and inserts the appropriate byte)\\uNNNN
(unicode escape, takes a Unicode code point and inserts an UTF-8 sequence)Any Unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to terminate the string literal. Newline only terminates the string literal if it is not preceded by a \\
.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/06-identifiers/","title":"5.6. Identifiers","text":""},{"location":"solidity-specification/05-expressions/06-identifiers/#56-identifiers","title":"5.6. Identifiers","text":""},{"location":"solidity-specification/05-expressions/06-identifiers/#syntax","title":"Syntax","text":"IdentifierPath = (* item: *) IDENTIFIER ((* separator: *) PERIOD (* item: *) IDENTIFIER)*;
IDENTIFIER = \u00abIDENTIFIER_START\u00bb \u00abIDENTIFIER_PART\u00bb*;
\u00abIDENTIFIER_START\u00bb = \"_\" | \"$\" | \"a\"\u2026\"z\" | \"A\"\u2026\"Z\";
\u00abIDENTIFIER_PART\u00bb = \u00abIDENTIFIER_START\u00bb | \"0\"\u2026\"9\";"},{"location":"solidity-specification/05-expressions/06-identifiers/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/06-yul/","title":"6. Yul","text":""},{"location":"solidity-specification/06-yul/#6-yul","title":"6. Yul","text":"YulBlock = (* open_brace: *) OPEN_BRACE (* statements: *) YulStatements (* close_brace: *) CLOSE_BRACE;
YulStatements = (* item: *) YulStatement*;
YulStatement = (* variant: *) YulBlock | (* variant: *) YulFunctionDefinition | (* variant: *) YulStackAssignmentStatement (* Deprecated in 0.5.0 *) | (* variant: *) YulIfStatement | (* variant: *) YulForStatement | (* variant: *) YulSwitchStatement | (* variant: *) YulLeaveStatement (* Introduced in 0.6.0 *) | (* variant: *) YulBreakStatement | (* variant: *) YulContinueStatement | (* variant: *) YulVariableAssignmentStatement | (* variant: *) YulLabel (* Deprecated in 0.5.0 *) | (* variant: *) YulVariableDeclarationStatement | (* variant: *) YulExpression;
YulFunctionDefinition = (* function_keyword: *) YUL_FUNCTION_KEYWORD (* name: *) YUL_IDENTIFIER (* parameters: *) YulParametersDeclaration (* returns: *) YulReturnsDeclaration? (* body: *) YulBlock;
YulParametersDeclaration = (* open_paren: *) OPEN_PAREN (* parameters: *) YulParameters (* close_paren: *) CLOSE_PAREN;
YulParameters = ((* item: *) YUL_IDENTIFIER ((* separator: *) COMMA (* item: *) YUL_IDENTIFIER)*)?;
YulReturnsDeclaration = (* minus_greater_than: *) MINUS_GREATER_THAN (* variables: *) YulVariableNames;
YulVariableNames = (* item: *) YUL_IDENTIFIER ((* separator: *) COMMA (* item: *) YUL_IDENTIFIER)*;
YulVariableDeclarationStatement = (* let_keyword: *) YUL_LET_KEYWORD (* variables: *) YulVariableNames (* value: *) YulVariableDeclarationValue?;
YulVariableDeclarationValue = (* assignment: *) YulAssignmentOperator (* expression: *) YulExpression;
YulVariableAssignmentStatement = (* variables: *) YulPaths (* assignment: *) YulAssignmentOperator (* expression: *) YulExpression;
YulAssignmentOperator = (* variant: *) COLON_EQUAL | (* variant: *) YulColonAndEqual; (* Deprecated in 0.5.5 *)
(* Deprecated in 0.5.5 *)YulColonAndEqual = (* colon: *) COLON (* equal: *) EQUAL;
(* Deprecated in 0.5.0 *)YulStackAssignmentStatement = (* assignment: *) YulStackAssignmentOperator (* variable: *) YUL_IDENTIFIER;
(* Deprecated in 0.5.0 *)YulStackAssignmentOperator = (* variant: *) EQUAL_COLON | (* variant: *) YulEqualAndColon;
(* Deprecated in 0.5.0 *)YulEqualAndColon = (* equal: *) EQUAL (* colon: *) COLON;
YulIfStatement = (* if_keyword: *) YUL_IF_KEYWORD (* condition: *) YulExpression (* body: *) YulBlock;
YulForStatement = (* for_keyword: *) YUL_FOR_KEYWORD (* initialization: *) YulBlock (* condition: *) YulExpression (* iterator: *) YulBlock (* body: *) YulBlock;
YulSwitchStatement = (* switch_keyword: *) YUL_SWITCH_KEYWORD (* expression: *) YulExpression (* cases: *) YulSwitchCases;
YulSwitchCases = (* item: *) YulSwitchCase+;
YulSwitchCase = (* variant: *) YulDefaultCase | (* variant: *) YulValueCase;
YulDefaultCase = (* default_keyword: *) YUL_DEFAULT_KEYWORD (* body: *) YulBlock;
YulValueCase = (* case_keyword: *) YUL_CASE_KEYWORD (* value: *) YulLiteral (* body: *) YulBlock;
(* Introduced in 0.6.0 *)YulLeaveStatement = (* leave_keyword: *) YUL_LEAVE_KEYWORD;
YulBreakStatement = (* break_keyword: *) YUL_BREAK_KEYWORD;
YulContinueStatement = (* continue_keyword: *) YUL_CONTINUE_KEYWORD;
(* Deprecated in 0.5.0 *)YulLabel = (* label: *) YUL_IDENTIFIER (* colon: *) COLON;"},{"location":"solidity-specification/06-yul/01-yul-statements/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/06-yul/02-yul-expressions/","title":"6.2. Yul Expressions","text":""},{"location":"solidity-specification/06-yul/02-yul-expressions/#62-yul-expressions","title":"6.2. Yul Expressions","text":""},{"location":"solidity-specification/06-yul/02-yul-expressions/#syntax","title":"Syntax","text":"YulExpression = (* variant: *) YulFunctionCallExpression | (* variant: *) YulLiteral | (* variant: *) YulBuiltInFunction | (* variant: *) YulPath;
(* Postfix unary operator *)YulFunctionCallExpression = (* operand: *) YulExpression (* open_paren: *) OPEN_PAREN (* arguments: *) YulArguments (* close_paren: *) CLOSE_PAREN;
YulArguments = ((* item: *) YulExpression ((* separator: *) COMMA (* item: *) YulExpression)*)?;
YulPaths = (* item: *) YulPath ((* separator: *) COMMA (* item: *) YulPath)*;
YulPath = (* item: *) YUL_IDENTIFIER ((* separator: *) PERIOD (* item: *) YUL_IDENTIFIER)*;
(* Introduced in 0.5.8 and deprecated in 0.7.0. *)YUL_IDENTIFIER = \u00abIDENTIFIER_START\u00bb (\u00abIDENTIFIER_PART\u00bb | \".\")*;YUL_IDENTIFIER = \u00abIDENTIFIER_START\u00bb \u00abIDENTIFIER_PART\u00bb*;
YulBuiltInFunction = (* variant: *) YUL_ADD_KEYWORD | (* variant: *) YUL_ADD_MOD_KEYWORD | (* variant: *) YUL_ADDRESS_KEYWORD | (* variant: *) YUL_AND_KEYWORD | (* variant: *) YUL_BALANCE_KEYWORD | (* variant: *) YUL_BLOCK_HASH_KEYWORD | (* variant: *) YUL_BYTE_KEYWORD | (* variant: *) YUL_CALL_CODE_KEYWORD | (* variant: *) YUL_CALL_DATA_COPY_KEYWORD | (* variant: *) YUL_CALL_DATA_LOAD_KEYWORD | (* variant: *) YUL_CALL_DATA_SIZE_KEYWORD | (* variant: *) YUL_CALLER_KEYWORD | (* variant: *) YUL_CALL_KEYWORD | (* variant: *) YUL_CALL_VALUE_KEYWORD | (* variant: *) YUL_COIN_BASE_KEYWORD | (* variant: *) YUL_CREATE_KEYWORD | (* variant: *) YUL_DELEGATE_CALL_KEYWORD | (* variant: *) YUL_DIV_KEYWORD | (* variant: *) YUL_EQ_KEYWORD | (* variant: *) YUL_EXP_KEYWORD | (* variant: *) YUL_EXT_CODE_COPY_KEYWORD | (* variant: *) YUL_EXT_CODE_SIZE_KEYWORD | (* variant: *) YUL_GAS_KEYWORD | (* variant: *) YUL_GAS_LIMIT_KEYWORD | (* variant: *) YUL_GAS_PRICE_KEYWORD | (* variant: *) YUL_GT_KEYWORD | (* variant: *) YUL_INVALID_KEYWORD | (* variant: *) YUL_IS_ZERO_KEYWORD | (* variant: *) YUL_JUMP_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) YUL_JUMPI_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) YUL_LOG_0_KEYWORD | (* variant: *) YUL_LOG_1_KEYWORD | (* variant: *) YUL_LOG_2_KEYWORD | (* variant: *) YUL_LOG_3_KEYWORD | (* variant: *) YUL_LOG_4_KEYWORD | (* variant: *) YUL_LT_KEYWORD | (* variant: *) YUL_M_LOAD_KEYWORD | (* variant: *) YUL_MOD_KEYWORD | (* variant: *) YUL_M_SIZE_KEYWORD | (* variant: *) YUL_M_STORE_8_KEYWORD | (* variant: *) YUL_M_STORE_KEYWORD | (* variant: *) YUL_MUL_KEYWORD | (* variant: *) YUL_MUL_MOD_KEYWORD | (* variant: *) YUL_NOT_KEYWORD | (* variant: *) YUL_NUMBER_KEYWORD | (* variant: *) YUL_ORIGIN_KEYWORD | (* variant: *) YUL_OR_KEYWORD | (* variant: *) YUL_POP_KEYWORD | (* variant: *) YUL_RETURN_KEYWORD | (* variant: *) YUL_REVERT_KEYWORD | (* variant: *) YUL_S_DIV_KEYWORD | (* variant: *) YUL_SELF_DESTRUCT_KEYWORD | (* variant: *) YUL_SGT_KEYWORD | (* variant: *) YUL_SIGN_EXTEND_KEYWORD | (* variant: *) YUL_S_LOAD_KEYWORD | (* variant: *) YUL_SLT_KEYWORD | (* variant: *) YUL_S_MOD_KEYWORD | (* variant: *) YUL_S_STORE_KEYWORD | (* variant: *) YUL_STOP_KEYWORD | (* variant: *) YUL_SUB_KEYWORD | (* variant: *) YUL_TIMESTAMP_KEYWORD | (* variant: *) YUL_XOR_KEYWORD | (* variant: *) YUL_KECCAK_256_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_SHA_3_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) YUL_SUICIDE_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) YUL_RETURN_DATA_COPY_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_RETURN_DATA_SIZE_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_STATIC_CALL_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_CREATE_2_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_EXT_CODE_HASH_KEYWORD (* Introduced in 0.5.0 *) | (* variant: *) YUL_SAR_KEYWORD | (* variant: *) YUL_SHL_KEYWORD | (* variant: *) YUL_SHR_KEYWORD | (* variant: *) YUL_CHAIN_ID_KEYWORD | (* variant: *) YUL_SELF_BALANCE_KEYWORD | (* variant: *) YUL_BASE_FEE_KEYWORD (* Introduced in 0.8.7 *) | (* variant: *) YUL_DIFFICULTY_KEYWORD (* Deprecated in 0.8.18 *) | (* variant: *) YUL_PREV_RANDAO_KEYWORD (* Introduced in 0.8.18 *) | (* variant: *) YUL_BLOB_BASE_FEE_KEYWORD (* Introduced in 0.8.24 *) | (* variant: *) YUL_BLOB_HASH_KEYWORD (* Introduced in 0.8.24 *) | (* variant: *) YUL_T_LOAD_KEYWORD (* Introduced in 0.8.24 *) | (* variant: *) YUL_T_STORE_KEYWORD (* Introduced in 0.8.24 *) | (* variant: *) YUL_M_COPY_KEYWORD; (* Introduced in 0.8.24 *)
YulLiteral = (* variant: *) YUL_TRUE_KEYWORD | (* variant: *) YUL_FALSE_KEYWORD | (* variant: *) YUL_DECIMAL_LITERAL | (* variant: *) YUL_HEX_LITERAL | (* variant: *) HexStringLiteral | (* variant: *) StringLiteral;
YUL_DECIMAL_LITERAL = (\"0\" | (\"1\"\u2026\"9\" \"0\"\u2026\"9\"*)) (?!\u00abIDENTIFIER_START\u00bb);
YUL_HEX_LITERAL = \"0x\" \u00abHEX_CHARACTER\u00bb+ (?!\u00abIDENTIFIER_START\u00bb);"},{"location":"solidity-specification/06-yul/02-yul-expressions/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/06-yul/03-yul-keywords/","title":"6.3. Yul Keywords","text":""},{"location":"solidity-specification/06-yul/03-yul-keywords/#63-yul-keywords","title":"6.3. Yul Keywords","text":""},{"location":"solidity-specification/06-yul/03-yul-keywords/#syntax","title":"Syntax","text":"(* Reserved until 0.7.1 *)YUL_ABSTRACT_KEYWORD = \"abstract\";
YUL_ADD_KEYWORD = \"add\";
YUL_ADD_MOD_KEYWORD = \"addmod\";
(* Never reserved *)YUL_ADDRESS_KEYWORD = \"address\";
(* Reserved until 0.7.1 *)YUL_AFTER_KEYWORD = \"after\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_ALIAS_KEYWORD = \"alias\";
YUL_AND_KEYWORD = \"and\";
(* Reserved until 0.7.1 *)YUL_ANONYMOUS_KEYWORD = \"anonymous\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_APPLY_KEYWORD = \"apply\";
(* Reserved until 0.7.1 *)YUL_AS_KEYWORD = \"as\";
(* Reserved until 0.7.1 *)YUL_ASSEMBLY_KEYWORD = \"assembly\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_AUTO_KEYWORD = \"auto\";
YUL_BALANCE_KEYWORD = \"balance\";
(* Introduced in 0.8.7 *)(* Reserved in 0.8.7 *)YUL_BASE_FEE_KEYWORD = \"basefee\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_BLOB_BASE_FEE_KEYWORD = \"blobbasefee\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_BLOB_HASH_KEYWORD = \"blobhash\";
YUL_BLOCK_HASH_KEYWORD = \"blockhash\";
(* Reserved until 0.5.10 *)YUL_BOOL_KEYWORD = \"bool\";
YUL_BREAK_KEYWORD = \"break\";
YUL_BYTE_KEYWORD = \"byte\";
(* Reserved until 0.7.1 *)YUL_BYTES_KEYWORD = \"bytes\" (\"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"8\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"16\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"24\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"32\")?;
YUL_CALL_CODE_KEYWORD = \"callcode\";
YUL_CALL_DATA_COPY_KEYWORD = \"calldatacopy\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_CALL_DATA_KEYWORD = \"calldata\";
YUL_CALL_DATA_LOAD_KEYWORD = \"calldataload\";
YUL_CALL_DATA_SIZE_KEYWORD = \"calldatasize\";
YUL_CALLER_KEYWORD = \"caller\";
YUL_CALL_KEYWORD = \"call\";
YUL_CALL_VALUE_KEYWORD = \"callvalue\";
YUL_CASE_KEYWORD = \"case\";
(* Reserved until 0.7.1 *)YUL_CATCH_KEYWORD = \"catch\";
(* Reserved in 0.5.12 *)YUL_CHAIN_ID_KEYWORD = \"chainid\";
YUL_COIN_BASE_KEYWORD = \"coinbase\";
(* Reserved until 0.7.1 *)YUL_CONSTANT_KEYWORD = \"constant\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_CONSTRUCTOR_KEYWORD = \"constructor\";
YUL_CONTINUE_KEYWORD = \"continue\";
(* Reserved until 0.7.1 *)YUL_CONTRACT_KEYWORD = \"contract\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_COPY_OF_KEYWORD = \"copyof\";
YUL_CREATE_KEYWORD = \"create\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_CREATE_2_KEYWORD = \"create2\";
(* Reserved until 0.7.1 *)YUL_DAYS_KEYWORD = \"days\";
YUL_DEFAULT_KEYWORD = \"default\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_DEFINE_KEYWORD = \"define\";
YUL_DELEGATE_CALL_KEYWORD = \"delegatecall\";
(* Reserved until 0.7.1 *)YUL_DELETE_KEYWORD = \"delete\";
(* Deprecated in 0.8.18 *)YUL_DIFFICULTY_KEYWORD = \"difficulty\";
YUL_DIV_KEYWORD = \"div\";
(* Reserved until 0.7.1 *)YUL_DO_KEYWORD = \"do\";
(* Reserved until 0.7.1 *)YUL_ELSE_KEYWORD = \"else\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_EMIT_KEYWORD = \"emit\";
(* Reserved until 0.7.1 *)YUL_ENUM_KEYWORD = \"enum\";
YUL_EQ_KEYWORD = \"eq\";
(* Reserved until 0.7.1 *)YUL_ETHER_KEYWORD = \"ether\";
(* Reserved until 0.7.1 *)YUL_EVENT_KEYWORD = \"event\";
YUL_EXP_KEYWORD = \"exp\";
YUL_EXT_CODE_COPY_KEYWORD = \"extcodecopy\";
(* Introduced in 0.5.0 *)(* Reserved in 0.5.0 *)YUL_EXT_CODE_HASH_KEYWORD = \"extcodehash\";
YUL_EXT_CODE_SIZE_KEYWORD = \"extcodesize\";
(* Reserved until 0.7.1 *)YUL_EXTERNAL_KEYWORD = \"external\";
(* Reserved from 0.6.0 until 0.7.1 *)YUL_FALLBACK_KEYWORD = \"fallback\";
YUL_FALSE_KEYWORD = \"false\";
(* Reserved until 0.7.1 *)YUL_FINAL_KEYWORD = \"final\";
(* Reserved until 0.7.0 *)YUL_FINNEY_KEYWORD = \"finney\";
(* Reserved until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\";(* Reserved until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\") \"x\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\");(* Reserved until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\" (\"184x8\" | \"184x16\" | \"184x24\" | \"184x32\" | \"184x40\" | \"184x48\" | \"184x56\" | \"184x64\" | \"184x72\" | \"192x8\" | \"192x16\" | \"192x24\" | \"192x32\" | \"192x40\" | \"192x48\" | \"192x56\" | \"192x64\" | \"200x8\" | \"200x16\" | \"200x24\" | \"200x32\" | \"200x40\" | \"200x48\" | \"200x56\" | \"208x8\" | \"208x16\" | \"208x24\" | \"208x32\" | \"208x40\" | \"208x48\" | \"216x8\" | \"216x16\" | \"216x24\" | \"216x32\" | \"216x40\" | \"224x8\" | \"224x16\" | \"224x24\" | \"224x32\" | \"232x8\" | \"232x16\" | \"232x24\" | \"240x8\" | \"240x16\" | \"248x8\");(* Reserved from 0.4.14 until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\" (\"184x80\" | \"192x72\" | \"192x80\" | \"200x64\" | \"200x72\" | \"200x80\" | \"208x56\" | \"208x64\" | \"208x72\" | \"208x80\" | \"216x48\" | \"216x56\" | \"216x64\" | \"216x72\" | \"216x80\" | \"224x40\" | \"224x48\" | \"224x56\" | \"224x64\" | \"224x72\" | \"224x80\" | \"232x32\" | \"232x40\" | \"232x48\" | \"232x56\" | \"232x64\" | \"232x72\" | \"232x80\" | \"240x24\" | \"240x32\" | \"240x40\" | \"240x48\" | \"240x56\" | \"240x64\" | \"240x72\" | \"240x80\" | \"248x16\" | \"248x24\" | \"248x32\" | \"248x40\" | \"248x48\" | \"248x56\" | \"248x64\" | \"248x72\" | \"248x80\" | \"256x8\" | \"256x16\" | \"256x24\" | \"256x32\" | \"256x40\" | \"256x48\" | \"256x56\" | \"256x64\" | \"256x72\" | \"256x80\");(* Reserved from 0.4.14 until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\") \"x\" (\"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"33\" | \"34\" | \"35\" | \"36\" | \"37\" | \"38\" | \"39\" | \"41\" | \"42\" | \"43\" | \"44\" | \"45\" | \"46\" | \"47\" | \"49\" | \"50\" | \"51\" | \"52\" | \"53\" | \"54\" | \"55\" | \"57\" | \"58\" | \"59\" | \"60\" | \"61\" | \"62\" | \"63\" | \"65\" | \"66\" | \"67\" | \"68\" | \"69\" | \"70\" | \"71\" | \"73\" | \"74\" | \"75\" | \"76\" | \"77\" | \"78\" | \"79\");
YUL_FOR_KEYWORD = \"for\";
YUL_FUNCTION_KEYWORD = \"function\";
YUL_GAS_KEYWORD = \"gas\";
YUL_GAS_LIMIT_KEYWORD = \"gaslimit\";
YUL_GAS_PRICE_KEYWORD = \"gasprice\";
YUL_GT_KEYWORD = \"gt\";
(* Reserved from 0.7.0 until 0.7.1 *)YUL_GWEI_KEYWORD = \"gwei\";
YUL_HEX_KEYWORD = \"hex\";
(* Reserved until 0.7.1 *)YUL_HOURS_KEYWORD = \"hours\";
YUL_IF_KEYWORD = \"if\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_IMMUTABLE_KEYWORD = \"immutable\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_IMPLEMENTS_KEYWORD = \"implements\";
(* Reserved until 0.7.1 *)YUL_IMPORT_KEYWORD = \"import\";
(* Reserved until 0.7.1 *)YUL_INDEXED_KEYWORD = \"indexed\";
(* Reserved until 0.6.8 *)YUL_IN_KEYWORD = \"in\";
(* Reserved until 0.7.1 *)YUL_INLINE_KEYWORD = \"inline\";
(* Reserved until 0.7.1 *)YUL_INTERFACE_KEYWORD = \"interface\";
(* Reserved until 0.7.1 *)YUL_INTERNAL_KEYWORD = \"internal\";
(* Reserved until 0.7.1 *)YUL_INT_KEYWORD = \"int\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\")?;
YUL_INVALID_KEYWORD = \"invalid\";
(* Reserved until 0.7.1 *)YUL_IS_KEYWORD = \"is\";
YUL_IS_ZERO_KEYWORD = \"iszero\";
(* Deprecated in 0.5.0 *)YUL_JUMP_KEYWORD = \"jump\";
(* Deprecated in 0.5.0 *)YUL_JUMPI_KEYWORD = \"jumpi\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_KECCAK_256_KEYWORD = \"keccak256\";
(* Introduced in 0.6.0 *)(* Reserved in 0.7.1 *)YUL_LEAVE_KEYWORD = \"leave\";
YUL_LET_KEYWORD = \"let\";
(* Reserved until 0.7.1 *)YUL_LIBRARY_KEYWORD = \"library\";
YUL_LOG_0_KEYWORD = \"log0\";
YUL_LOG_1_KEYWORD = \"log1\";
YUL_LOG_2_KEYWORD = \"log2\";
YUL_LOG_3_KEYWORD = \"log3\";
YUL_LOG_4_KEYWORD = \"log4\";
YUL_LT_KEYWORD = \"lt\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_MACRO_KEYWORD = \"macro\";
(* Reserved until 0.7.1 *)YUL_MAPPING_KEYWORD = \"mapping\";
(* Reserved until 0.7.1 *)YUL_MATCH_KEYWORD = \"match\";
(* Reserved until 0.7.1 *)YUL_MEMORY_KEYWORD = \"memory\";
(* Reserved until 0.7.1 *)YUL_MINUTES_KEYWORD = \"minutes\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_M_COPY_KEYWORD = \"mcopy\";
YUL_M_LOAD_KEYWORD = \"mload\";
YUL_MOD_KEYWORD = \"mod\";
(* Reserved until 0.7.1 *)YUL_MODIFIER_KEYWORD = \"modifier\";
YUL_M_SIZE_KEYWORD = \"msize\";
YUL_M_STORE_KEYWORD = \"mstore\";
YUL_M_STORE_8_KEYWORD = \"mstore8\";
YUL_MUL_KEYWORD = \"mul\";
YUL_MUL_MOD_KEYWORD = \"mulmod\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_MUTABLE_KEYWORD = \"mutable\";
(* Reserved until 0.7.1 *)YUL_NEW_KEYWORD = \"new\";
YUL_NOT_KEYWORD = \"not\";
(* Reserved until 0.7.1 *)YUL_NULL_KEYWORD = \"null\";
YUL_NUMBER_KEYWORD = \"number\";
(* Reserved until 0.7.1 *)YUL_OF_KEYWORD = \"of\";
YUL_OR_KEYWORD = \"or\";
YUL_ORIGIN_KEYWORD = \"origin\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_OVERRIDE_KEYWORD = \"override\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_PARTIAL_KEYWORD = \"partial\";
(* Reserved until 0.7.1 *)YUL_PAYABLE_KEYWORD = \"payable\";
YUL_POP_KEYWORD = \"pop\";
(* Reserved until 0.7.1 *)YUL_PRAGMA_KEYWORD = \"pragma\";
(* Introduced in 0.8.18 *)(* Reserved in 0.8.18 *)YUL_PREV_RANDAO_KEYWORD = \"prevrandao\";
(* Reserved until 0.7.1 *)YUL_PRIVATE_KEYWORD = \"private\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_PROMISE_KEYWORD = \"promise\";
(* Reserved until 0.7.1 *)YUL_PUBLIC_KEYWORD = \"public\";
(* Reserved until 0.7.1 *)YUL_PURE_KEYWORD = \"pure\";
(* Reserved from 0.6.0 until 0.7.1 *)YUL_RECEIVE_KEYWORD = \"receive\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_REFERENCE_KEYWORD = \"reference\";
(* Reserved until 0.7.1 *)YUL_RELOCATABLE_KEYWORD = \"relocatable\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_RETURN_DATA_COPY_KEYWORD = \"returndatacopy\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_RETURN_DATA_SIZE_KEYWORD = \"returndatasize\";
YUL_RETURN_KEYWORD = \"return\";
(* Reserved until 0.7.1 *)YUL_RETURNS_KEYWORD = \"returns\";
YUL_REVERT_KEYWORD = \"revert\";
(* Reserved in 0.4.21 *)YUL_SAR_KEYWORD = \"sar\";
YUL_S_DIV_KEYWORD = \"sdiv\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_SEALED_KEYWORD = \"sealed\";
(* Reserved until 0.7.1 *)YUL_SECONDS_KEYWORD = \"seconds\";
(* Reserved in 0.5.12 *)YUL_SELF_BALANCE_KEYWORD = \"selfbalance\";
YUL_SELF_DESTRUCT_KEYWORD = \"selfdestruct\";
YUL_SGT_KEYWORD = \"sgt\";
(* Deprecated in 0.5.0 *)(* Reserved until 0.5.0 *)YUL_SHA_3_KEYWORD = \"sha3\";
(* Reserved in 0.4.21 *)YUL_SHL_KEYWORD = \"shl\";
(* Reserved in 0.4.21 *)YUL_SHR_KEYWORD = \"shr\";
YUL_SIGN_EXTEND_KEYWORD = \"signextend\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_SIZE_OF_KEYWORD = \"sizeof\";
YUL_S_LOAD_KEYWORD = \"sload\";
YUL_SLT_KEYWORD = \"slt\";
YUL_S_MOD_KEYWORD = \"smod\";
YUL_S_STORE_KEYWORD = \"sstore\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_STATIC_CALL_KEYWORD = \"staticcall\";
(* Reserved until 0.7.1 *)YUL_STATIC_KEYWORD = \"static\";
YUL_STOP_KEYWORD = \"stop\";
(* Reserved until 0.7.1 *)YUL_STORAGE_KEYWORD = \"storage\";
(* Reserved until 0.7.1 *)YUL_STRING_KEYWORD = \"string\";
(* Reserved until 0.7.1 *)YUL_STRUCT_KEYWORD = \"struct\";
YUL_SUB_KEYWORD = \"sub\";
(* Deprecated in 0.5.0 *)(* Reserved until 0.5.0 *)YUL_SUICIDE_KEYWORD = \"suicide\";
YUL_SUPER_KEYWORD = \"super\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_SUPPORTS_KEYWORD = \"supports\";
YUL_SWITCH_KEYWORD = \"switch\";
(* Reserved until 0.7.0 *)YUL_SZABO_KEYWORD = \"szabo\";
YUL_TIMESTAMP_KEYWORD = \"timestamp\";
YUL_THIS_KEYWORD = \"this\";
(* Reserved until 0.7.1 *)YUL_THROW_KEYWORD = \"throw\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_T_LOAD_KEYWORD = \"tload\";
YUL_TRUE_KEYWORD = \"true\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_T_STORE_KEYWORD = \"tstore\";
(* Reserved until 0.7.1 *)YUL_TRY_KEYWORD = \"try\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_TYPE_DEF_KEYWORD = \"typedef\";
(* Reserved until 0.7.1 *)YUL_TYPE_KEYWORD = \"type\";
(* Reserved until 0.7.1 *)YUL_TYPE_OF_KEYWORD = \"typeof\";
(* Reserved until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\";(* Reserved until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\") \"x\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\");(* Reserved until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\" (\"184x8\" | \"184x16\" | \"184x24\" | \"184x32\" | \"184x40\" | \"184x48\" | \"184x56\" | \"184x64\" | \"184x72\" | \"192x8\" | \"192x16\" | \"192x24\" | \"192x32\" | \"192x40\" | \"192x48\" | \"192x56\" | \"192x64\" | \"200x8\" | \"200x16\" | \"200x24\" | \"200x32\" | \"200x40\" | \"200x48\" | \"200x56\" | \"208x8\" | \"208x16\" | \"208x24\" | \"208x32\" | \"208x40\" | \"208x48\" | \"216x8\" | \"216x16\" | \"216x24\" | \"216x32\" | \"216x40\" | \"224x8\" | \"224x16\" | \"224x24\" | \"224x32\" | \"232x8\" | \"232x16\" | \"232x24\" | \"240x8\" | \"240x16\" | \"248x8\");(* Reserved from 0.4.14 until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\" (\"184x80\" | \"192x72\" | \"192x80\" | \"200x64\" | \"200x72\" | \"200x80\" | \"208x56\" | \"208x64\" | \"208x72\" | \"208x80\" | \"216x48\" | \"216x56\" | \"216x64\" | \"216x72\" | \"216x80\" | \"224x40\" | \"224x48\" | \"224x56\" | \"224x64\" | \"224x72\" | \"224x80\" | \"232x32\" | \"232x40\" | \"232x48\" | \"232x56\" | \"232x64\" | \"232x72\" | \"232x80\" | \"240x24\" | \"240x32\" | \"240x40\" | \"240x48\" | \"240x56\" | \"240x64\" | \"240x72\" | \"240x80\" | \"248x16\" | \"248x24\" | \"248x32\" | \"248x40\" | \"248x48\" | \"248x56\" | \"248x64\" | \"248x72\" | \"248x80\" | \"256x8\" | \"256x16\" | \"256x24\" | \"256x32\" | \"256x40\" | \"256x48\" | \"256x56\" | \"256x64\" | \"256x72\" | \"256x80\");(* Reserved from 0.4.14 until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\") \"x\" (\"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"33\" | \"34\" | \"35\" | \"36\" | \"37\" | \"38\" | \"39\" | \"41\" | \"42\" | \"43\" | \"44\" | \"45\" | \"46\" | \"47\" | \"49\" | \"50\" | \"51\" | \"52\" | \"53\" | \"54\" | \"55\" | \"57\" | \"58\" | \"59\" | \"60\" | \"61\" | \"62\" | \"63\" | \"65\" | \"66\" | \"67\" | \"68\" | \"69\" | \"70\" | \"71\" | \"73\" | \"74\" | \"75\" | \"76\" | \"77\" | \"78\" | \"79\");
(* Reserved until 0.7.1 *)YUL_UINT_KEYWORD = \"uint\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\")?;
(* Reserved from 0.5.0 until 0.7.1 *)YUL_UNCHECKED_KEYWORD = \"unchecked\";
(* Reserved until 0.7.1 *)YUL_USING_KEYWORD = \"using\";
(* Reserved until 0.6.5 *)YUL_VAR_KEYWORD = \"var\";
(* Reserved until 0.7.1 *)YUL_VIEW_KEYWORD = \"view\";
(* Reserved from 0.6.0 until 0.7.1 *)YUL_VIRTUAL_KEYWORD = \"virtual\";
(* Reserved until 0.7.1 *)YUL_WEEKS_KEYWORD = \"weeks\";
(* Reserved until 0.7.1 *)YUL_WEI_KEYWORD = \"wei\";
(* Reserved until 0.7.1 *)YUL_WHILE_KEYWORD = \"while\";
(* Reserved until 0.7.1 *)YUL_YEARS_KEYWORD = \"years\";
YUL_XOR_KEYWORD = \"xor\";"},{"location":"solidity-specification/06-yul/03-yul-keywords/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"user-guide/","title":"User Guide","text":"At its core, Slang is a collection of APIs that are meant to analyze the source code, starting with the source code itself and ending with a rich structure that can be reasoned about. This is a departure from the classic approach of \"black-box\" compilers, which are handed the input and only their output can be observed.
"},{"location":"user-guide/concepts/#language-versions","title":"Language Versions","text":"To use Slang, you start by initializing a Parser
object with a specific version of the language. The earliest Solidity version we support is 0.4.11
, and we plan on supporting all future versions as they are released.
From a Parser
object, you can analyze any source text according to the nonterminals of that specific version. Providing an accurate language version is important, as it affects the shape of the syntax tree, and possible errors produced. You can use the LanguageFacts::supportedVersions()
API to get a list of all supported versions for the current Slang release.
The Parser::parse()
API is the main entry point for the parser, and to generate concrete syntax trees (CSTs) that can be used for further analysis. Each parse()
operation accepts the input source code, and a NonterminalKind
variant. This allows callers to parse entire source files (NonterminalKind::SourceUnit
), individual contracts (NonterminalKind::ContractDefinition
), methods (NonterminalKind::FunctionDefinition
), or any other syntax nodes.
The resulting ParseOutput
object will contain syntax errors (if any), and the syntax tree corresponding to the input source code.
Slang is capable of parsing the source code into a Concrete Syntax Tree (CST; also sometimes called \"full-fidelity\"), which is a tree structure of the program that also includes things like punctuation or whitespace.
This is done by using the (standard) approach of lexical analysis followed by syntax analysis. The source text as a sequence of characters is recognized into a sequence of terminals (lexical analysis), which then in turn is parsed into the CST.
The resulting CST is a regular tree data structure that you can visit. The tree nodes are represented by the Node
structure, which can be one of two kinds:
NonterminalNode
represent sub-trees, containing a vector of other Node
children.TerminalNode
are leaves and represent a terminal (i.e. an identifier, keyword, punctuation) in the source.For many code analysis tasks, it is useful to traverse the parse tree and visit each node. The Cursor
object allows callers to traverse the parse tree in an efficient pre-order manner.
It provides several goTo*()
navigation functions, each returning true
if the cursor was successfully moved, and false
otherwise. There are three main ways to do it:
goToNext()
and goToPrevious()
,goToParent()
, goToFirstChild()
, goToNextNonDescendant()
goToNextTerminalWithKind(kind)
, goToNextNonterminalWithKind(kind)
As such, the cursor is stateful and keeps track of the path it has taken through the CST. It starts at the root it was created at and is completed when it reaches its root when navigating forward.
"},{"location":"user-guide/concepts/#cst-queries","title":"CST Queries","text":"The Cursor
API is a low-level API that allows you to traverse the CST in a procedural manner. However, it is often more convenient to use the declarative Query
API. Queries allow you to express your intent more concisely, and also allows you to reuse the same query in multiple places. Queries can largely replace the need for both internal (cursor), and external (visitor) iterator patterns.
The query language is based on pattern matching, and the execution semantics are closer to unification than to regular expression matching i.e. a query returns all possible matches, not just the longest/shortest/first/last match. There is no concept of a 'greedy' operator for example.
Query execution is based on Cursor
s, and the resulting matches and unification captures are returned as Cursor
s as well. This allows you to mix and match manual traversal, cursors, and queries.
Multiple queries can be executed in a batch, and efficiently traverse the tree looking for matches. This mode of operation can replace all visitor patterns.
"},{"location":"user-guide/concepts/#abstract-syntax-tree-ast","title":"Abstract Syntax Tree (AST)","text":"AST types are a set of abstractions that provide a typed view of the untyped CST nodes. You can convert any untyped CST node to its corresponding AST type using their constructors.
There is a corresponding type for each NonterminalKind
in the language. AST types are immutable. Additionally, their fields are constructed lazily as they are accessed for the first time.
AST nodes maintain a reference to the CST node they were constructed from, and can be used to navigate to the corresponding CST node.
"},{"location":"user-guide/introduction/","title":"Introduction","text":"Welcome to the Slang user guide! This aims to be an introduction to Slang itself, its concepts and also contains a collection of guides how you can achieve basic tasks with it.
"},{"location":"user-guide/introduction/#what-is-slang","title":"What is Slang?","text":"Slang is intended to be a modular Solidity compiler, specifically targeting code analysis and developer tooling. This means servicing tools with domain-specific APIs and, in general, facilitating working with and analyzing the Solidity source code. If you're in the editor writing Solidity or performing linting or additional validation, there's a chance that you are, or could be, running Slang!
To get a good grasp on the concepts used in Slang, see the Concepts section.
"},{"location":"user-guide/introduction/#what-slang-is-not","title":"What Slang is not?","text":"First and foremost, it is not a replacement for solc
, the standard Solidity compiler. We do not plan at the moment to support emitting optimized EVM bytecode for use in production. Secondly, it does not perform formal verification of contracts or Solidity logic in general. However, other tools that serve this purpose are intended to be built on top of it.
The Solidity programming language has evolved quite a bit since its inception. Some features were introduced, some changed, while some eventually became obsolete and were removed altogether.
While it's good for a programming language to evolve and better serve the needs of its users, not being able to easily upgrade or re-deploy existing contracts poses a unique challenge. Developer tooling must be able to understand and consume older contracts that are still being used on the blockchain, written in older versions of Solidity.
Because of that, Slang must be able to reason about different versions of Solidity; how the language grammar, name capture rules, and semantics have changed across different versions. One of our goals is to document differences as part of our Solidity Specification.
This is why, instead of having to download separate versions of the tool for each Solidity version, you can access the Slang language APIs by simply specifying the Solidity version that you want to work with.
"},{"location":"user-guide/introduction/#distributions","title":"Distributions","text":"Slang itself is written in Rust, compiled as a WASM component, and distributed as an npm package with a TypeScript interface. In the future, we are also looking into publishing it as a Rust crate, a Python library, and possibly more.
"},{"location":"user-guide/tree-query-language/","title":"The Tree Query Language","text":""},{"location":"user-guide/tree-query-language/#query-syntax","title":"Query Syntax","text":"A query is a pattern that matches a certain set of nodes in a tree. The expression to match a given node consists of a pair of brackets ([]
) containing two things: the node's kind, and optionally, a series of other patterns that match the node's children. For example, this pattern would match any MultiplicativeExpression
node that has two children Expression
nodes, with an Asterisk
node in between:
[MultiplicativeExpression [Expression] [Asterisk] [Expression]]\n
The children of a node can optionally be labeled. The label is a property of the edge from the node to the child, and is not a property of the child. For example, this pattern will match a MultiplicativeExpression
node with the two Expression
children labeled left_operand
and right_operand
:
[MultiplicativeExpression left_operand:[Expression] [Asterisk] right_operand:[Expression]]\n
You can also match a node's textual content using a string literal. For example, this pattern would match a MultiplicativeExpression
with a *
operator (for clarity):
[MultiplicativeExpression left_operand:[_] operator:[\"*\"] right_operand:[_]]\n
If you don't care about the kind of a node, you can use an underscore _
, which matches any kind. For example, this pattern will match a MultiplicativeExpression
node with two children, one of any kind labeled left_operand
and one of any kind:
[MultiplicativeExpression left_operand:[_] [_]]\n
Children can be elided. For example, this would produce multiple matches for a MultiplicativeExpression
where at least one of the children is an expression of a StringExpression
variant, where each match is associated with each of the StringExpression
children:
[MultiplicativeExpression [Expression [StringExpression]]]\n
Trivia nodes (whitespace, comments, etc.) will be skipped over when running a query. Furthermore, trivia nodes cannot be explicitly (or implicitly with _
) matched by queries.
When matching patterns, you may want to process specific nodes within the pattern. Captures allow you to associate names with specific nodes in a pattern, so that you can later refer to those nodes by those names. Capture names are written before the nodes that they refer to, and start with an @
character.
For example, this pattern would match any struct definition and it would associate the name struct_name
with the identifier:
[StructDefinition @struct_name name:[Identifier]]\n
And this pattern would match all event definitions for a contract, associating the name event_name
with the event name, contract_name
with the containing contract name:
[ContractDefinition\n @contract_name name:[Identifier]\n members:[ContractMembers\n [ContractMember\n [EventDefinition @event_name name:[Identifier]]\n ]\n ]\n]\n
"},{"location":"user-guide/tree-query-language/#quantification","title":"Quantification","text":"You can surround a sequence of patterns in parenthesis (()
), followed by a ?
, *
or +
operator. The ?
operator matches zero or one repetitions of a pattern, the *
operator matches zero or more, and the +
operator matches one or more.
For example, this pattern would match a sequence of one or more import directives at the top of the file:
[SourceUnit members:[_ ([_ @import [ImportDirective]])+]]\n
This pattern would match a structure definition with one or more members, capturing their names:
[StructDefinition\n @name name:[_]\n members:[_ ([_ @member [Identifier]])+]\n]\n
This pattern would match all function calls, capturing a string argument if one was present:
[FunctionCallExpression\n arguments:[ArgumentsDeclaration\n variant:[PositionalArgumentsDeclaration\n arguments:[PositionalArguments\n (@arg [Expression variant:[StringExpression]])?\n ]\n ]\n ]\n]\n
"},{"location":"user-guide/tree-query-language/#alternations","title":"Alternations","text":"An alternation is written as a sequence of patterns separated by |
and surrounded by parentheses.
For example, this pattern would match a call to either a variable or an object property. In the case of a variable, capture it as @function
, and in the case of a property, capture it as @method
:
[FunctionCallExpression\n operand:[Expression\n (@function variant:[Identifier]\n | @method variant:[MemberAccessExpression])\n ]\n]\n
This pattern would match a set of possible keyword terminals, capturing them as @keyword
:
@keyword (\n [\"break\"]\n | [\"delete\"]\n | [\"else\"]\n | [\"for\"]\n | [\"function\"]\n | [\"if\"]\n | [\"return\"]\n | [\"try\"]\n | [\"while\"]\n)\n
"},{"location":"user-guide/tree-query-language/#adjacency","title":"Adjacency","text":"By using the adjacency operator .
you can constrain a pattern to only match the first or the last child nodes.
For example, the following pattern would match only the first parameter declaration in a function definition:
[FunctionDefinition\n [ParametersDeclaration\n [Parameters . @first_param [Parameter]]\n ]\n]\n
And conversely the following will match only the last parameter:
[FunctionDefinition\n [ParametersDeclaration\n [Parameters @last_param [Parameter] .]\n ]\n]\n
If the adjacency operator is used in between two patterns it constrains matches on both patterns to occur consecutively, ie. without any other sibling node in between. For example, this pattern matches pairs of consecutive statements:
[Statements @stmt1 [Statement] . @stmt2 [Statement]]\n
"},{"location":"user-guide/npm-package/","title":"NPM Package","text":"You can install Slang NPM package simply by running the following npm
command:
npm install \"@nomicfoundation/slang\"\n
Or if you are using yarn
for package management:
yarn add \"@nomicfoundation/slang\"\n
"},{"location":"user-guide/npm-package/using-queries/","title":"Using Queries","text":"It's often more convenient to use the declarative Query
API to traverse the CST, as they allow you to express your intent more concisely and can largely replace the need for both internal (cursor), and external (visitor) iterator patterns.
The Tree Query Language is based on pattern matching, and the execution semantics are closer to unification than to regular expression matching. A query returns all possible matches, not just the longest/shortest/first/last match.
If not specified otherwise, let's assume we already parsed a Solidity source and have a cursor
pointing to the root node of the CST (created with createTreeCursor
, see Using the Cursor).
You can create a Query
object using Query.parse
, which accepts a string value. These can be then used by Cursor.query
to execute it.
You can pass multiple queries to a cursor to and efficiently traverse the tree looking for matches. They will be executed concurrently, returning matches in the order they appear in input.
// Any `Cursor` can be used to create a query.\nconst cursor = parseOutput.createTreeCursor();\n\nconst query = Query.parse(\"[ContractDefinition]\");\nconst matches: QueryMatchIterator = cursor.query([query]);\n
"},{"location":"user-guide/npm-package/using-queries/#iterating-over-node-patterns","title":"Iterating over node patterns","text":"Queries allow you to iterate over all node patterns that match the query, which can replace your need for manual iteration via cursors or visitors. In order to get a Cursor
that points to the matched node, you need to capture them with a name capture (@capture_name
) to a specific node in the query pattern.
Let's use this to list all the contract definitions in the source file:
input.solcontract Foo {}\ncontract Bar {}\ncontract Baz {}\n
const found = [];\n\nconst query = Query.parse(\"@contract [ContractDefinition]\");\nconst matches = cursor.query([query]);\n\nfor (const match of matches) {\n const cursor = match.captures[\"contract\"]![0]!;\n\n assertIsNonterminalNode(cursor.node);\n found.push(cursor.node.unparse().trim());\n}\n\nassert.deepStrictEqual(found, [\"contract Foo {}\", \"contract Bar {}\", \"contract Baz {}\"]);\n
"},{"location":"user-guide/npm-package/using-queries/#multiple-patterns-simultaneously","title":"Multiple patterns simultaneously","text":"We can also intersperse multiple patterns in a single query, which will return all the matches for each pattern. This can be useful when you want to match multiple types of nodes in a single pass.
const names = [];\n\nconst structDefinition = Query.parse(\"[StructDefinition @name [Identifier]]\");\nconst enumDefinition = Query.parse(\"[EnumDefinition @name [Identifier]]\");\nconst matches = cursor.query([structDefinition, enumDefinition]);\n\nfor (const match of matches) {\n const index = match.queryNumber;\n const cursor = match.captures[\"name\"]![0]!;\n\n names.push([index, cursor.node.unparse()]);\n}\n\nassert.deepStrictEqual(names, [\n [0, \"Foo\"],\n [1, \"Bar\"],\n [0, \"Baz\"],\n [1, \"Qux\"],\n]);\n
"},{"location":"user-guide/npm-package/using-queries/#matching-on-nodes-label","title":"Matching on node's label","text":"We can match not only on the node's kind, but also on its label. This can be useful if there may be two children with the same kind but different labels or to be more declarative.
To do so, we use [label: _]
syntax. Here, we also use _
to allow matching any kind of node, as long as it matches the given label.
contract Example {\n function foo() public {\n (uint a, uint16 b, uint64 c, uint256 d) = (1, 2, 3, 4);\n }\n}\n
const names = [];\n\nconst query = Query.parse(\"[TypedTupleMember @type type_name:[_]]\");\nconst matches = cursor.query([query]);\n\nfor (const match of matches) {\n const cursor = match.captures[\"type\"]![0]!;\n\n names.push(cursor.node.unparse());\n}\n\nassert.deepStrictEqual(names, [\"uint\", \" uint16\", \" uint64\", \" uint256\"]);\n
"},{"location":"user-guide/npm-package/using-queries/#matching-on-nodes-literal-content","title":"Matching on node's literal content","text":"Lastly, we can also match on the node's literal content. This can be useful when you want to match a specific identifier, string, or number.
Let's say we prefer our code to be explicit and prefer using uint256
instead of uint
. To find all instances of the uint
alias we could do the following:
contract Example {\n function foo() public {\n (uint a, uint16 b, uint64 c, uint256 d) = (1, 2, 3, 4);\n }\n}\n
const names = [];\n\nconst query = Query.parse(`[ElementaryType @uint_keyword variant:[\"uint\"]]`);\nconst matches = cursor.query([query]);\n\nfor (const match of matches) {\n const cursor = match.captures[\"uint_keyword\"]![0]!;\n\n names.push(cursor.node.unparse());\n}\n\nassert.deepStrictEqual(names, [\"uint\"]);\n
"},{"location":"user-guide/npm-package/using-queries/#example-finding-txorigin-patterns","title":"Example: Finding tx.origin
patterns","text":"As a more realistic example, let's say we want to write a linter that unconditionally lints against all tx.origin
accesses.
Let's use the motivating example from https://soliditylang.org:
input.sol// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.7.0 <0.9.0;\n// THIS CONTRACT CONTAINS A BUG - DO NOT USE\ncontract TxUserWallet {\n address owner;\n\n constructor() {\n owner = msg.sender;\n }\n\n function transferTo(address payable dest, uint amount) public {\n // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin\n require(tx.origin == owner);\n dest.transfer(amount);\n }\n}\n
Now, we can above features to write a query that matches all tx.origin
patterns:
const query = Query.parse(`\n@txorigin [MemberAccessExpression\n [Expression @start [\"tx\"]]\n [\"origin\"]\n]`);\n\nconst matches = cursor.query([query]);\nconst found = [];\n\nfor (const match of matches) {\n const cursor = match.captures[\"txorigin\"]![0]!;\n\n found.push([cursor.textOffset.utf8, cursor.node.unparse()]);\n}\n\nassert.deepStrictEqual(found, [[375, \"tx.origin\"]]);\n
"},{"location":"user-guide/npm-package/using-the-ast/","title":"Using the AST","text":"Let's try to analyze the following Solidity source file, containing a simple function:
input.solfunction add(uint256 a, uint256 b) public pure returns (uint256) {\n return a + b;\n}\n
We start as usual by parsing the input, and then we can use the ParseOutput
root to create the CST type. Since it is a node of kind FunctionDefinition
, we are using the AST type of the same name to analyze it:
import assert from \"node:assert\";\nimport { Parser } from \"@nomicfoundation/slang/parser\";\nimport { NonterminalKind } from \"@nomicfoundation/slang/cst\";\nimport { FunctionDefinition } from \"@nomicfoundation/slang/ast\";\n\nconst parser = Parser.create(\"0.8.0\");\n\nconst parseOutput = parser.parse(NonterminalKind.FunctionDefinition, source);\n
The FunctionDefinition
type has named fields to access all its children. For example, we can check the name of the function:
const $function = new FunctionDefinition(parseOutput.tree.asNonterminalNode()!);\n\nassert.equal($function.name.variant.unparse(), \"add\");\n
We can also list its parameters:
const parameters = $function.parameters.parameters.items.map((parameter) => {\n return parameter.name!.unparse();\n});\n\nassert.deepEqual(parameters, [\"a\", \"b\"]);\n
Or attributes:
const attributes = $function.attributes.items.map((attribute) => {\n return attribute.cst.unparse().trim();\n});\n\nassert.deepEqual(attributes, [\"public\", \"pure\"]);\n
"},{"location":"user-guide/npm-package/using-the-cursor/","title":"Using the Cursor","text":"This guide will walk you through the basics of using a CST cursor in your project. Let's start with this source file, that contains three contracts:
input.solcontract Foo {}\ncontract Bar {}\ncontract Baz {}\n
import assert from \"node:assert\";\nimport { Parser } from \"@nomicfoundation/slang/parser\";\nimport { assertIsTerminalNode, NonterminalKind, TerminalKind } from \"@nomicfoundation/slang/cst\";\n\nconst parser = Parser.create(\"0.8.0\");\n\nconst parseOutput = parser.parse(NonterminalKind.SourceUnit, source);\n
"},{"location":"user-guide/npm-package/using-the-cursor/#listing-contract-names","title":"Listing Contract Names","text":"The below example uses a cursor to list the names of all contracts in a source file:
const contracts = [];\n\nconst cursor = parseOutput.createTreeCursor();\n\nwhile (cursor.goToNextNonterminalWithKind(NonterminalKind.ContractDefinition)) {\n assert(cursor.goToFirstChild());\n assert(cursor.goToNextTerminalWithKind(TerminalKind.Identifier));\n\n assertIsTerminalNode(cursor.node);\n contracts.push(cursor.node.unparse());\n\n assert(cursor.goToParent());\n}\n\nassert.deepStrictEqual(contracts, [\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/npm-package/using-the-cursor/#visiting-only-a-sub-tree","title":"Visiting Only a Sub-tree","text":"In cases like the above, we needed to visit a sub-tree of the CST (to get the contract name). But we also need to remember to return the cursor to its original position after each read, which is inconvenient, and can lead to subtle bugs.
To avoid this, we can use the spawn()
API, which cheaply creates a new cursor that starts at the given node, without copying the previous path history. This lets us visit the sub-tree of each contract, without modifying the original cursor:
const contracts = [];\n\nconst cursor = parseOutput.createTreeCursor();\n\nwhile (cursor.goToNextNonterminalWithKind(NonterminalKind.ContractDefinition)) {\n const childCursor = cursor.spawn();\n assert(childCursor.goToNextTerminalWithKind(TerminalKind.Identifier));\n\n assertIsTerminalNode(childCursor.node);\n contracts.push(childCursor.node.unparse());\n}\n\nassert.deepStrictEqual(contracts, [\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/npm-package/using-the-cursor/#accessing-node-positions","title":"Accessing Node Positions","text":"The Cursor
API also tracks the position and range of the current node it is visiting. Here is an example that records the source range of each contract, along with its text:
const contracts = [];\n\nconst cursor = parseOutput.createTreeCursor();\n\nwhile (cursor.goToNextNonterminalWithKind(NonterminalKind.ContractDefinition)) {\n const range = cursor.textRange;\n\n const contractNode = cursor.node;\n\n contracts.push([\n range.start.line,\n range.start.column,\n range.end.line,\n range.end.column,\n contractNode.unparse().trim(),\n ]);\n}\n\nassert.deepStrictEqual(contracts, [\n [0, 0, 1, 0, \"contract Foo {}\"],\n [1, 0, 2, 0, \"contract Bar {}\"],\n [2, 0, 2, 15, \"contract Baz {}\"],\n]);\n
"},{"location":"user-guide/npm-package/using-the-parser/","title":"Using the Parser","text":"Using the API directly provides us with a more fine-grained control over the parsing process. It allows us to parse not just the input as a top-level source unit, but also individual constructs like contracts, various definitions, and even expressions.
"},{"location":"user-guide/npm-package/using-the-parser/#parsing-source-files","title":"Parsing Source Files","text":"Let's start with this simple source file, that contains a single contract:
input.solcontract Foo {}\n
We begin by creating a Parser
object with a specified version. This is an entry point for our parser API. Then we can use it to parse the source file, specifying the top-level nonterminal to parse:
import assert from \"node:assert\";\nimport { Parser } from \"@nomicfoundation/slang/parser\";\nimport {\n assertIsNonterminalNode,\n assertIsTerminalNode,\n NonterminalKind,\n TerminalKind,\n} from \"@nomicfoundation/slang/cst\";\n\nconst parser = Parser.create(\"0.8.0\");\n\nconst parseOutput = parser.parse(NonterminalKind.ContractDefinition, source);\n
"},{"location":"user-guide/npm-package/using-the-parser/#checking-for-syntax-errors","title":"Checking for Syntax Errors","text":"If the file has errors, we can get them from the ParseOutput
type, and print them out:
for (const error of parseOutput.errors()) {\n console.error(`Error at byte offset ${error.textRange.start.utf8}: ${error.message}`);\n}\n
Otherwise, we can check if input is valid using this helpful utility:
assert(parseOutput.isValid());\n
"},{"location":"user-guide/npm-package/using-the-parser/#inspecting-the-parse-tree","title":"Inspecting the Parse Tree","text":"Now, let's try to inspect the resulting CST, and iterate on its children:
const contract = parseOutput.tree;\nassertIsNonterminalNode(contract, NonterminalKind.ContractDefinition);\n\nconst contractChildren = contract.children();\nassert.equal(contractChildren.length, 7);\n\nconst [contractKeyword, firstSpace, contractName, secondSpace, openBrace, members, closeBrace] = contractChildren;\n\nassertIsTerminalNode(contractKeyword!.node, TerminalKind.ContractKeyword, \"contract\");\nassertIsTerminalNode(firstSpace!.node, TerminalKind.Whitespace, \" \");\nassertIsTerminalNode(contractName!.node, TerminalKind.Identifier, \"Foo\");\nassertIsTerminalNode(secondSpace!.node, TerminalKind.Whitespace, \" \");\nassertIsTerminalNode(openBrace!.node, TerminalKind.OpenBrace, \"{\");\nassertIsNonterminalNode(members!.node, NonterminalKind.ContractMembers);\nassertIsTerminalNode(closeBrace!.node, TerminalKind.CloseBrace, \"}\");\n
Additionally, we can convert the CST node back into the input string:
const contractSource = contract.unparse();\nassert.equal(contractSource, \"contract Foo {}\");\n
"},{"location":"user-guide/rust-crate/","title":"Rust Crate","text":"The Rust package is published to crates.io as slang_solidity
(docs). It can be used both as a regular Rust dependency and as a standalone CLI (installable with Cargo).
You can install the CLI as a cargo binary using:
cargo install \"slang_solidity_cli\"\n
Or you can add the API as a dependency to your project:
cargo add \"slang_solidity\"\n
"},{"location":"user-guide/rust-crate/using-queries/","title":"Using Queries","text":"It's often more convenient to use the declarative Query
API to traverse the CST, as they allow you to express your intent more concisely and can largely replace the need for both internal (cursor), and external (visitor) iterator patterns.
The query language is based on pattern matching, and the execution semantics are closer to unification than to regular expression matching. A query returns all possible matches, not just the longest/shortest/first/last match.
If not specified otherwise, let's assume we already parsed a Solidity source and have a cursor
pointing to the root node of the CST (created with create_tree_cursor
, see Using the Cursor).
You can create a Query
struct using Query::parse
, which accepts a &str
. These can be then used by Cursor::query
to execute it.
You can pass multiple queries to a cursor to and efficiently traverse the tree looking for matches. They will be executed concurrently, returning matches in the order they appear in input.
use slang_solidity::cst::Query;\n\n// Any `Cursor` can be used to create a query.\nlet cursor = parse_output.create_tree_cursor();\n\nlet query = Query::parse(\"[ContractDefinition]\").unwrap();\nlet result: QueryMatchIterator = cursor.query(vec![query]);\n
"},{"location":"user-guide/rust-crate/using-queries/#iterating-over-node-patterns","title":"Iterating over node patterns","text":"Queries allow you to iterate over all node patterns that match the query, which can replace your need for manual iteration via cursors or visitors. In order to get a Cursor
that points to the matched node, you need to capture them with a name capture (@capture_name
) to a specific node in the query pattern.
Let's use this to list all the contract definitions in the source file:
input.solcontract Foo {}\ncontract Bar {}\ncontract Baz {}\n
let mut found = vec![];\n\nlet query = Query::parse(\"@contract [ContractDefinition]\").unwrap();\n\nfor r#match in cursor.query(vec![query]) {\n let captures = r#match.captures;\n let cursors = captures.get(\"contract\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n found.push(cursor.node().unparse().trim().to_owned());\n}\n\nassert_eq!(\n found,\n [\"contract Foo {}\", \"contract Bar {}\", \"contract Baz {}\"]\n);\n
"},{"location":"user-guide/rust-crate/using-queries/#multiple-patterns-simultaneously","title":"Multiple patterns simultaneously","text":"We can also intersperse multiple patterns in a single query, which will return all the matches for each pattern. This can be useful when you want to match multiple types of nodes in a single pass.
let mut names = vec![];\n\nlet struct_def = Query::parse(\"[StructDefinition @name [Identifier]]\").unwrap();\nlet enum_def = Query::parse(\"[EnumDefinition @name [Identifier]]\").unwrap();\n\nfor r#match in cursor.query(vec![struct_def, enum_def]) {\n let index = r#match.query_number;\n let captures = r#match.captures;\n let cursors = captures.get(\"name\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n names.push((index, cursor.node().unparse()));\n}\n\nassert_eq!(\n names,\n &[\n (0, \"Foo\".to_string()),\n (1, \"Bar\".to_string()),\n (0, \"Baz\".to_string()),\n (1, \"Qux\".to_string())\n ]\n);\n
"},{"location":"user-guide/rust-crate/using-queries/#matching-on-nodes-label","title":"Matching on node's label","text":"We can match not only on the node's kind, but also on its label. This can be useful if there may be two children with the same kind but different labels or to be more declarative.
To do so, we use [label: _]
syntax. Here, we also use _
to allow matching any kind of node, as long as it matches the given label.
contract Example {\n function foo() public {\n (uint a, uint16 b, uint64 c, uint256 d) = (1, 2, 3, 4);\n }\n}\n
let mut names = vec![];\n\nlet query = Query::parse(\"[TypedTupleMember @type type_name:[_]]\").unwrap();\n\nfor r#match in cursor.query(vec![query]) {\n let captures = r#match.captures;\n let cursors = captures.get(\"type\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n names.push(cursor.node().unparse());\n}\n\nassert_eq!(names, &[\"uint\", \" uint16\", \" uint64\", \" uint256\"]);\n
"},{"location":"user-guide/rust-crate/using-queries/#matching-on-nodes-literal-content","title":"Matching on node's literal content","text":"Lastly, we can also match on the node's literal content. This can be useful when you want to match a specific identifier, string, or number.
Let's say we prefer our code to be explicit and prefer using uint256
instead of uint
. To find all instances of the uint
alias we could do the following:
contract Example {\n function foo() public {\n (uint a, uint16 b, uint64 c, uint256 d) = (1, 2, 3, 4);\n }\n}\n
let mut names = vec![];\n\nlet query = Query::parse(r#\"[ElementaryType @uint_keyword variant:[\"uint\"]]\"#).unwrap();\n\nfor r#match in cursor.query(vec![query]) {\n let captures = r#match.captures;\n let cursors = captures.get(\"uint_keyword\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n names.push(cursor.node().unparse());\n}\n\nassert_eq!(names, &[\"uint\"]);\n
"},{"location":"user-guide/rust-crate/using-queries/#example-finding-txorigin-patterns","title":"Example: Finding tx.origin
patterns","text":"As a more realistic example, let's say we want to write a linter that unconditionally lints against all tx.origin
accesses.
Let's use the motivating example from https://soliditylang.org:
input.sol// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.7.0 <0.9.0;\n// THIS CONTRACT CONTAINS A BUG - DO NOT USE\ncontract TxUserWallet {\n address owner;\n\n constructor() {\n owner = msg.sender;\n }\n\n function transferTo(address payable dest, uint amount) public {\n // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin\n require(tx.origin == owner);\n dest.transfer(amount);\n }\n}\n
Now, we can above features to write a query that matches all tx.origin
patterns:
let query = Query::parse(\n r#\"@txorigin [MemberAccessExpression\n [Expression @start [\"tx\"]]\n [\"origin\"]\n ]\"#,\n)\n.unwrap();\n\nlet mut results = vec![];\n\nfor r#match in cursor.query(vec![query]) {\n let captures = r#match.captures;\n let cursors = captures.get(\"txorigin\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n results.push((cursor.text_offset().utf8, cursor.node().unparse()));\n}\n\nassert_eq!(results, &[(375usize, \"tx.origin\".to_string())]);\n
"},{"location":"user-guide/rust-crate/using-the-cli/","title":"Using the CLI","text":""},{"location":"user-guide/rust-crate/using-the-cli/#parsing-source-files","title":"Parsing Source Files","text":"The parse
command will take a path to a Solidity file, and a --version
flag. Specifying the correct version is important, as it will affect the grammar used to parse inputs.
All parse errors are printed in a human-readable format; the command will succeed if there are no parse errors, and fail otherwise.
$ slang_solidity parse --help\n\nUsage: slang_solidity parse [OPTIONS] --version <VERSION> <FILE_PATH>\n\nArguments:\n <FILE_PATH>\n File path to the Solidity (*.sol) source file to parse\n\nOptions:\n -v, --version <VERSION>\n The Solidity language version to use for parsing\n --json\n Print the concrete syntax tree as JSON\n -h, --help\n Print help\n
Here is an example of the JSON output it can print:
// A Nonterminal node\n\"Nonterminal\": {\n // Name of the nonterminal kind\n \"kind\": \"SourceUnit\",\n // Length of the nonterminal in Unicode code points, depending on the encoding used\n \"text_len\": {\n \"utf8\": 24,\n \"utf16\": 24,\n \"char\": 24 // de facto utf32\n },\n \"children\": [/* Nonterminal or Terminal nodes */]\n}\n// A Terminal node\n\"Terminal\": {\n // Name of the terminal kind\n \"kind\": \"PragmaKeyword\",\n // Literal value, taken from the source code\n \"text\": \"pragma\"\n}\n
"},{"location":"user-guide/rust-crate/using-the-cli/#inspecting-json-output","title":"Inspecting JSON Output","text":"Now let's try to use that command to parse the following Solidity file, and inspect its contents:
input.solpragma solidity ^0.8.0;\n
slang_solidity parse --json --version \"0.8.0\" \"input.sol\" > \"output.json\"\n
Because the resulting structure is well-defined and recursive, we can use the popular jq
tool to quickly analyze the resulting output:
JQ_QUERY='recurse | select(.Terminal?) | .Terminal'\ncat output.json | jq \"$JQ_QUERY\"\n
This gives us a flat list of the Terminal nodes:
{\n \"kind\": \"PragmaKeyword\",\n \"text\": \"pragma\"\n}\n{\n \"kind\": \"Whitespace\",\n \"text\": \" \"\n}\n{\n \"kind\": \"SolidityKeyword\",\n \"text\": \"solidity\"\n}\n{\n \"kind\": \"Whitespace\",\n \"text\": \" \"\n}\n{\n \"kind\": \"Caret\",\n \"text\": \"^\"\n}\n{\n \"kind\": \"VersionPragmaValue\",\n \"text\": \"0\"\n}\n{\n \"kind\": \"Period\",\n \"text\": \".\"\n}\n{\n \"kind\": \"VersionPragmaValue\",\n \"text\": \"8\"\n}\n{\n \"kind\": \"Period\",\n \"text\": \".\"\n}\n{\n \"kind\": \"VersionPragmaValue\",\n \"text\": \"0\"\n}\n{\n \"kind\": \"Semicolon\",\n \"text\": \";\"\n}\n{\n \"kind\": \"EndOfLine\",\n \"text\": \"\\n\"\n}\n
Now, we can adapt the query to select the text
fields of the nodes and concatenate them, which gives us back the reconstructed source code! \ud83c\udf89
$ JQ_QUERY='[recurse | select(.Terminal?) | .Terminal.text] | join(\"\")'\n$ cat output.json | jq \"$JQ_QUERY\"\n\n\"pragma solidity ^0.8.0;\\n\"\n
"},{"location":"user-guide/rust-crate/using-the-cursor/","title":"Using the Cursor","text":"This guide will walk you through the basics of using a CST cursor in your project. Let's start with this source file, that contains three contracts:
input.solcontract Foo {}\ncontract Bar {}\ncontract Baz {}\n
use semver::Version;\nuse slang_solidity::cst::{EdgeLabel, NonterminalKind, TerminalKind, TextRangeExtensions};\nuse slang_solidity::parser::Parser;\n\nlet parser = Parser::create(Version::parse(\"0.8.0\")?)?;\n\nlet parse_output = parser.parse(NonterminalKind::SourceUnit, source);\n
"},{"location":"user-guide/rust-crate/using-the-cursor/#listing-contract-names","title":"Listing Contract Names","text":"The below example uses a cursor to list the names of all contracts in a source file:
let mut contracts = Vec::new();\n\nlet mut cursor = parse_output.create_tree_cursor();\n\nwhile cursor.go_to_next_nonterminal_with_kind(NonterminalKind::ContractDefinition) {\n assert!(cursor.go_to_first_child());\n assert!(cursor.go_to_next_terminal_with_kind(TerminalKind::Identifier));\n\n let terminal_node = cursor.node();\n contracts.push(terminal_node.as_terminal().unwrap().text.clone());\n\n // You have to make sure you return the cursor to its original position:\n assert!(cursor.go_to_parent());\n}\n\nassert_eq!(contracts, &[\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/rust-crate/using-the-cursor/#visiting-only-a-sub-tree","title":"Visiting Only a Sub-tree","text":"In cases like the above, we needed to visit a sub-tree of the CST (to get the contract name). But we also need to remember to return the cursor to its original position after each read, which is inconvenient, and can lead to subtle bugs.
To avoid this, we can use the spawn()
API, which cheaply creates a new cursor that starts at the given node, without copying the previous path history. This lets us visit the sub-tree of each contract, without modifying the original cursor:
let mut contracts = Vec::new();\n\nlet mut cursor = parse_output.create_tree_cursor();\n\nwhile cursor.go_to_next_nonterminal_with_kind(NonterminalKind::ContractDefinition) {\n let mut child_cursor = cursor.spawn();\n assert!(child_cursor.go_to_next_terminal_with_kind(TerminalKind::Identifier));\n\n let terminal_node = child_cursor.node();\n contracts.push(terminal_node.as_terminal().unwrap().text.clone());\n}\n\nassert_eq!(contracts, &[\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/rust-crate/using-the-cursor/#accessing-node-positions","title":"Accessing Node Positions","text":"The Cursor
API also tracks the position and range of the current node it is visiting. Here is an example that records the source range of each contract, along with its text:
let mut contracts = Vec::new();\n\nlet mut cursor = parse_output.create_tree_cursor();\n\nwhile cursor.go_to_next_nonterminal_with_kind(NonterminalKind::ContractDefinition) {\n let range = cursor.text_range().utf8();\n let text = cursor.node().unparse();\n\n contracts.push((range, text.trim().to_owned()));\n}\n\nassert_eq!(\n contracts,\n &[\n (0..16, \"contract Foo {}\".to_string()),\n (16..32, \"contract Bar {}\".to_string()),\n (32..47, \"contract Baz {}\".to_string()),\n ]\n);\n
"},{"location":"user-guide/rust-crate/using-the-cursor/#using-iterator-api","title":"Using Iterator API","text":"In addition to the procedural-style methods, the Cursor
struct also implements the Iterator
trait, which allows you to use it in a functional style.
Let's use that to extract all Identifier
nodes from the source text using that API:
let identifiers: Vec<_> = parse_output\n .tree()\n .clone()\n .descendants()\n .filter(|edge| edge.is_terminal_with_kind(TerminalKind::Identifier))\n .map(|identifier| identifier.unparse())\n .collect();\n\nassert_eq!(identifiers, &[\"Foo\", \"Bar\", \"Baz\"]);\n
Note
It's important to note that Iterator::next
first visits the current node, yields it, and then moves the cursor to the next node. As such, accessor associated functions called on the Cursor
that reference the \"current\" will point to the one that is not yet yielded by the iterator. This might be an important, when mixing the two styles.
The cursor also keeps track of the labels of the nodes it visits. Let's use that to extract all nodes that are labeled Name
:
let identifiers: Vec<_> = parse_output\n .tree()\n .clone()\n .descendants()\n .filter(|edge| edge.label == Some(EdgeLabel::Name))\n .filter(|edge| edge.is_terminal_with_kind(TerminalKind::Identifier))\n .map(|identifier| identifier.unparse())\n .collect();\n\nassert_eq!(identifiers, &[\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/rust-crate/using-the-parser/","title":"Using the Parser","text":"Using the API directly provides us with a more fine-grained control over the parsing process. It allows us to parse not just the input as a top-level source unit, but also individual nonterminals like contracts, various definitions, and even expressions.
"},{"location":"user-guide/rust-crate/using-the-parser/#parsing-source-files","title":"Parsing Source Files","text":"Let's start with this simple source file, that contains a single contract:
input.solcontract Foo {}\n
We begin by creating a Parser
object with a specified version. This is an entry point for our parser API. Then we can use it to parse the source file, specifying the top-level nonterminal to parse:
use semver::Version;\nuse slang_solidity::cst::{Node, NonterminalKind, TerminalKind};\nuse slang_solidity::parser::Parser;\n\nlet parser = Parser::create(Version::parse(\"0.8.0\")?)?;\n\nlet parse_output = parser.parse(NonterminalKind::ContractDefinition, source);\n
"},{"location":"user-guide/rust-crate/using-the-parser/#checking-for-syntax-errors","title":"Checking for Syntax Errors","text":"If the file has errors, we can get them from the ParseOutput
type, and print them out:
for error in parse_output.errors() {\n eprintln!(\n \"Error at byte offset {offset}: {message}\",\n offset = error.text_range().start.utf8,\n message = error.message()\n );\n}\n
Otherwise, we can check if input is valid using this helpful utility:
assert!(parse_output.is_valid());\n
"},{"location":"user-guide/rust-crate/using-the-parser/#inspecting-the-parse-tree","title":"Inspecting the Parse Tree","text":"Now, let's try to inspect the resulting CST, and iterate on its children:
let tree = parse_output.tree();\n\nlet contract = tree.as_nonterminal().unwrap();\nassert_eq!(contract.kind, NonterminalKind::ContractDefinition);\nassert_eq!(contract.children.len(), 7);\n\nlet children = &contract.children;\nassert!(\n matches!(&children[0].node, Node::Terminal(t) if t.kind == TerminalKind::ContractKeyword)\n);\nassert!(matches!(&children[1].node, Node::Terminal(t) if t.kind == TerminalKind::Whitespace));\nassert!(matches!(&children[2].node, Node::Terminal(t) if t.kind == TerminalKind::Identifier));\nassert!(matches!(&children[3].node, Node::Terminal(t) if t.kind == TerminalKind::Whitespace));\nassert!(matches!(&children[4].node, Node::Terminal(t) if t.kind == TerminalKind::OpenBrace));\nassert!(\n matches!(&children[5].node, Node::Nonterminal(r) if r.kind == NonterminalKind::ContractMembers)\n);\nassert!(matches!(&children[6].node, Node::Terminal(t) if t.kind == TerminalKind::CloseBrace));\n
Additionally, we can convert the CST node back into the input string:
assert_eq!(contract.unparse(), \"contract Foo {}\");\n
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"slang","text":""},{"location":"#solidity-compiler-tooling-by-nomicfoundation","title":"Solidity compiler tooling by @NomicFoundation","text":"A modular set of compiler APIs empowering the next generation of Solidity code analysis and developer tooling. Written in Rust and distributed in multiple languages.
This repository maintains the source code and release process for these projects:
\u2757 This project is still in alpha, and is under active development. If you are planning on using it, please reach out to us on Telegram so we can help you get started.
To make the developer experience as seamless and consistent as possible, we recommend using the VS Code devcontainer included in this repository. It is a light image that has the minimum required tools to build this project. If you are not familiar with containerized development, I recommend taking a look at the official VS Code guide. Using a devcontainer allows us to quickly setup/teardown the environment, and install/setup different dependencies for different projects, without polluting the local environment. In the future, it will enable us to include Windows and Mac OS specific images for cross-platform testing.
If you would like to still develop outside a container, this should still be possible, as the CI will guarantee that your changes are safe. We intend to keep the images to a bare minimum, and install most dependencies through scripts you can run locally. However, using a common development container means sharing and standardizing useful settings and extensions for the editor (VS Code), the terminal (zsh), and any other tools.
In the future, if we decide to enable code spaces, we can have a 1-click button to create and warm up a powerful dev machine to use in seconds, and running completely remote in a browser tab. It will make it trivial to switch between different versions and branches, or even use and debug multiple ones at the same time from different tabs.
"},{"location":"internals/development/#hermit","title":"Hermit","text":"To install language-specific binaries and packages, we use Hermit, which installs all tools only when it is first needed/invoked, so you can quickly setup and build different projects quickly. It also takes care of updating your $PATH
as you cd
in and out of different projects, to make sure you are using the right tools every time. Follow this guide to install it locally to your machine, or simply build any included project, and it will bootstrap itself if it is missing.
To ensure consistency, and a good experience for first-time developers, all build/test/run/debug commands should be written, versioned, and documented inside the infra_cli
crate. This means that any dev instructions are well documented, versioned, and verified/executed with every build. It also means that we can minimize any manual setup or teardown steps during development, and just rely on that cli.
You can access all such commands (from the hermit environment) by just running the infra
script, which just refers to $REPO_ROOT/scripts/bin/infra
. If this is your first time contributing, we recommend starting with infra --help
to familiarize yourself with its capabilities.
We manage versioning through changesets. Each pull request can describe what user facing changes it is introducing, and include this information as a \"changeset\" markdown file along with source changes. These changeset files are analyzed and used to create another pull request to bump the repository version and update dependencies. Once the version bump is merged, artifacts are built and released to all registries.
"},{"location":"internals/development/#managing-dependencies","title":"Managing Dependencies","text":"Our $REPO_ROOT/.github/dependabot.yml
config runs automatic updates to our dependencies on a weekly basis. This handles github-actions
, npm
, cargo
, and pip
packages. However, two kinds of dependencies still need to be updated manually for now:
$RUST_STABLE_VERSION
and $RUST_NIGHTLY_VERSION
defined in hermit.hcl
and updated via rustup install
.$REPO_ROOT/bin/XXX.pkg
, and updated via hermit install
.This repository is split into multiple projects at the root folder. Each project has its own dependencies and tools used to build, test, and ship different parts of the repository. For example, a Rust environment for the compiler, a Python environment for documentation, and a NodeJS environment for linters. This allows us to implement different binaries, APIs, and internal tools/scripts, and package/version them together, while having minimal inter-dependencies.
All dependencies should have exact full versions, and we can rely on tooling to automatically upgrade it over time. It allows us to have perfectly reproducible builds for every commit, a critical aspect for compilers, and developer tools in general.
"},{"location":"internals/repository-structure/#directory-structure","title":"Directory Structure","text":"Currently, the repository has the following projects:
.changeset/
: pending user visible changes not released yet..devcontainer/
: self-contained Docker image to develop, build, test, and publish.crates/
:infra/
: contains the CLI used for development, and utilities to build/test/run/debug all other projects.codegen/
: language analysis APs that convert input crates to output crates below.solidity/
:inputs/
: Solidity language definition.outputs/
: different packages and artifacts produced from it.documentation/
: mkdocs site to render project documentation.This document describes the new language definition model (AKA DSL v2), and the features/possibilities it enables for both syntax and semantic analysis. Each section describes a part of the definition model, and how it can affect the scanner, parser, CST, and AST.
This is a collection of different discussions we had over the last few weeks, and can (and should) be broken down into smaller work items if needed. It should be possible to map the current definition to the old one, so that we do incremental progress, instead of rewriting everything at once.
"},{"location":"internals/design-docs/language-definition-v2/#cst","title":"CST","text":"We currently produce an untyped tree of nodes. It holds all parts of the input (byte for byte), even whitespace, comments, and unrecognized (skipped) parts. We can reconstruct the original input back from the CST, just by iterating on nodes in order. For memory/performance reasons, we don't hold positions/location information in the tree, but they are calculated during iterating/visiting the tree.
The CST is useful for many use cases:
Here is an example of the node type, similar to what we have now:
pub enum Node {\n Terminal { node: Rc<TerminalNode> },\n Nonterminal { node: Rc<NonterminalNode> },\n}\n\npub struct TerminalNode {\n pub kind: TerminalKind,\n pub text: String,\n}\n\npub struct NonterminalNode {\n pub kind: NonterminalKind,\n pub text_length: TextIndex,\n pub children: Vec<Node>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#ast","title":"AST","text":"We intend to also produce a strongly typed tree (structs and enums). Having strong types provides safety/correctness guarantees for users. It also allows us to generate visitor and rewriter APIs automatically.
Each AST node should provide an API to get the underlying CST node, where users can iterate over the actual terminals as they appear in input, and get their position in the source code. However, this is a one-way operation. CST nodes don't hold references to their AST nodes.
Note: some compilers drop syntactic elements that don't carry semantic information from their AST (like semicolons, or commas). However, we don't make that distinction, as we intend to implement further analysis in the form of micro-passes, that each can rewrite and pick parts of the tree that are relevant to them. So our initial tree (AST) should be complete.
"},{"location":"internals/design-docs/language-definition-v2/#versioning","title":"Versioning","text":"The biggest benefit of the new language definition is that it allows scanners and parsers to attempt parsing input belonging to any language version, and report errors afterwards if the input is not valid for the selected version. This is a huge benefit over existing parsers, where they will either parse an inaccurate superset of all versions, or they parse a specific version, and produce unhelpful errors like Unrecognized 'abstract' keyword
when the current language version doesn't support it.
Not only we will be able to recover from such errors and continue parsing, producing an accurate/complete tree at the end, but we will also be able to produce much better errors like: The 'abstract' keyword is not supported in the current version X. Please upgrade to version Y instead to be able to use it
.
Tokens consist of one or more TokenDefinition
. Each definition is separate/unique, but produces the same TerminalKind
. This is useful for tokens like DecimalLiteral
and HexLiteral
who can have multiple forms, but each form is enabled or disabled in certain versions of the language.
All definitions have a unique Scanner
, and they can be combined in the same trie/FSM to produce a single token at each position in the input. Afterwards, the scanner can compare the definition's enabled
property with the current language version, adding an error if they don't match, but continue parsing anyway.
Keywords also contribute a TerminalKind
, and consist of one or more KeywordDefinition
. But they have additional semantics:
First, instead of defining a regular Scanner
, it defines a KeywordValue
that produces a finite set of possibilities. Most only produce one value (like abstract
or contract
), but some can produce multiple, like bytesN
or fixedMxN
, that can have different values for M
and N
. This is important for us to build hash sets and quickly check for membership.
Second, because keywords can also overlap with identifiers, each keyword has an identifier
property that refers to which identifier token they can match. Instead of being part of same trie/FSM as tokens, whenever we want to scan a keyword, we try to scan its identifier instead. Afterwards, we check if its contents match one of the values of the keyword.
Third, they have two additional reserved
property. We should use these when we scan identifiers, to make sure that the resulting identifier doesn't match a reserved keyword, and if so, we should report an error, but continue parsing.
Unique to Solidity, keywords can be reserved
in versions before or after the versions they are enabled
in. They can also be not reserved
in versions before or after the versions they stop being enabled
in. So we have to have these additional checks, to be able to catch cases like when a certain input can both be a keyword and an identifier, or neither.
We should also be able to generate a public API is_keyword(TerminalKind)
for users to conveniently detect them if needed.
Trivia items are similar tokens, contributing their own TerminalKind
. They are referred to from the language's top-level leading_trivia
and trailing_trivia
properties. Before and after each token, the scanner should try to scan these tokens, collecting them in a flat list.
Previously, we used to create many LeadingTrivia
and TrailingTrivia
nodes that hold whitespace/comments. Not only this is wasteful memory-wise, it is also unnatural/unexpected to wrap whitespace in nonterminal nodes. Instead, I propose treating them like any other token, and storing them as siblings to the tokens they belong to (in-order). Not only this is simpler, it is also more efficient, and is natural to how input is consumed and produced.
Fragments are not visible to users, and don't contribute a TerminalKind
. They are just a utility used to refactor common parts of the grammar, and avoid duplication. During processing the language definition, they are inlined wherever they are referenced.
Structs represent a flat list (sequence) of typed fields. They are the simplest nonterminal, and generate a struct
AST type. Their fields match 1-1 with the item fields. The struct name contributes a NonterminalKind
.
Each field can be either Required(T)
or Optional(T)
. Required fields are always present and parsed. Optional fields can be omitted if they don't exist, and are represented with Rust's Option<T>
type (or TypeScript T | undefined
). However, optional fields have an additional enabled
property. After parsing optional fields, we should compare them with the current language version, and produce errors if they don't match, but continue parsing normally.
The type of each field can be a Nonterminal(T)
or Terminal(Set<T>)
. A nonterminal field refers to another item, and holds its type. A terminal field refers to one or more terminal items (all valid in this position), and is of type TerminalNode
.
Additionally, the struct also stores the CST node that holds its contents:
DefinitionStruct(\n name = ParametersDeclaration,\n fields = (\n open_paren = Required(Terminal([OpenParen])),\n parameters = Required(Nonterminal(Parameters)),\n close_paren = Required(Terminal([CloseParen]))\n )\n)\n
AST Typepub struct ParametersDeclaration {\n pub open_paren: Rc<TerminalNode>,\n pub parameters: Rc<Parameters>,\n pub close_paren: Rc<TerminalNode>,\n\n pub cst: Rc<NonterminalNode>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#enum-items","title":"Enum Items","text":"Enums represent an ordered choice operator of multiple variants (possibilities). The enum name itself does NOT contribute a NonterminalKind
, since it will always result in one of its variants (each with a unique TerminalKind
or a NonterminalKind
. They only exist in the AST, and don't affect the CST at all.
We attempt to parse each variant (in-order), and choose the first one that succeeds. However, each variant can have an additional enabled
property. We should always try to parse the variants that are valid in the current version first, and if not, still parse the rest, but produce an error afterwards. The fields of each variant are parsed similar to a struct fields (example above).
Enum(\n name = FunctionBody,\n variants = [\n EnumVariant(name = Block, reference = Block),\n EnumVariant(name = Semicolon, reference = Semicolon)\n ]\n)\n
AST Typepub enum FunctionBody {\n Block {\n block: Rc<Block>,\n\n cst: Rc<NonterminalNode>,\n },\n Semicolon {\n semicolon: Rc<TerminalNode>,\n\n cst: Rc<NonterminalNode>,\n },\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#repeated-items","title":"Repeated Items","text":"Repeated items represent a list of items of the same kind. The item name contributes a NonterminalKind
. The AST type is a wrapper around a Vec<T>
, with any utilities we need to add for convenience.
It has an allow_empty
boolean property, which allows parsing zero items. If it is false
, we should still allow parsing zero items, but produce an error afterwards.
Repeated(\n name = FunctionAttributes,\n repeated = FunctionAttribute,\n allow_empty = true\n)\n
AST Typepub struct FunctionAttributes {\n pub items: Vec<Rc<FunctionAttribute>>\n\n pub cst: Rc<NonterminalNode>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#separated-items","title":"Separated Items","text":"Separated items represent a list of items of the same kind, separated by a delimiter. The item name contributes a NonterminalKind
. The AST type is a wrapper around two Vec<T>
for items and their delimiters, with any utilities we need to add for convenience. For example, we should add APIs to create iterators for only the separated items, the separators, or both (in-order).
It has an allow_empty
boolean property, which allows parsing zero items. If it is false
, we should still allow parsing zero items, but produce an error afterwards. We should also allow parsing a trailing separator at the end, but still produce an error afterwards.
Separated(\n name = EventParameters,\n separated = EventParameter,\n separator = Comma,\n allow_empty = true\n)\n
AST Typepub struct EventParameters {\n pub items: Vec<Rc<EventParameter>>\n pub separators: Vec<Rc<TerminalNode>>\n\n pub cst: Rc<NonterminalNode>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#precedence-items","title":"Precedence Items","text":"This is perhaps the most complex nonterminal. It still uses the same PRATT algorithm from the previous implementation (no changes there), but adapted for the new AST types. It has two lists:
First, a list of precedence_expressions
, with each expression having a list of operators. Each operator has its own versioning (enabled
property), a list of fields, and a model (prefix/postfix/binary).
The operators from all expressions are flattened and combined in the parent PRATT parser. That grouping is only used to indicate that some operators can produce the same PrecedenceExpression
name. However, we should exclude operators that don't match the current language version. This is useful for things like ExponentiationExpression
where it has two operators with different associativity, but defined in enabled/disabled in different versions.
Second, a list of primary_expressions
, with their own versioning (enabled
property) as well. We should try to parse them as an operator (similar to EnumItem
), and produce an error if the version doesn't match afterwards.
It is important to note that the item name doesn't contribute a NonterminalKind
, but each PrecedenceExpression
under it contributes one.
Precedence(\n name = Expression,\n precedence_expressions = [\n PrecedenceExpression(\n name = AdditionExpression,\n operators = [PrecedenceOperator(\n model = BinaryLeftAssociative,\n fields = (operator = Required(Terminal([Plus])))\n )]\n ),\n PrecedenceExpression(\n name = FunctionCallExpression,\n operators = [PrecedenceOperator(\n model = Postfix,\n fields = (\n open_paren = Required(Terminal([OpenParen])),\n arguments = Required(Nonterminal(Arguments)),\n close_paren = Required(Terminal([CloseParen]))\n )\n )]\n ),\n PrecedenceExpression(\n name = NegationExpression,\n operators = [PrecedenceOperator(\n model = Prefix,\n fields = (operator = Required(Terminal([Not])))\n )]\n )\n )],\n primary_expressions = [\n PrimaryExpression(expression = Identifier),\n PrimaryExpression(expression = NumberLiteral),\n PrimaryExpression(expression = StringLiteral)\n ]\n)\n
AST Typepub enum Expression {\n AdditionExpression { expression: Rc<AdditionExpression> },\n FunctionCallExpression { expression: Rc<FunctionCallExpression> },\n NegationExpression { expression: Rc<NegationExpression> },\n\n Identifier { expression: Rc<TerminalNode> },\n NumberLiteral { expression: Rc<TerminalNode> },\n StringLiteral { expression: Rc<TerminalNode> },\n}\n\npub struct AdditionExpression {\n // 'left_operand' auto-generated (before) because it is a binary expression, and same type as parent\n pub left_operand: Rc<Expression>,\n // operator 'fields' are flattened into the expression node here\n pub operator: Rc<TerminalNode>,\n // 'right_operand' auto-generated (after) because it is a binary expression, and same type as parent\n pub right_operand: Rc<Expression>,\n\n pub cst: Rc<NonterminalNode>,\n}\n\npub struct FunctionCallExpression {\n // 'operand' auto-generated (before) because it is a postfix expression, and same type as parent\n pub operand: Rc<Expression>,\n // operator 'fields' are flattened into the expression node here\n pub open_paren: Rc<TerminalNode>,\n pub arguments: Rc<Arguments>,\n pub close_paren: Rc<TerminalNode>,\n\n pub cst: Rc<NonterminalNode>,\n}\n\npub struct NegationExpression {\n // operator 'fields' are flattened into the expression node here\n pub operator: Rc<TerminalNode>,\n // 'operand' auto-generated (after) because it is a prefix expression, and same type as parent\n pub operand: Rc<Expression>,\n\n pub cst: Rc<NonterminalNode>,\n}\n
"},{"location":"internals/design-docs/language-definition-v2/#error-recovery","title":"Error Recovery","text":"For the CST, I think the current algorithms work well, and we should be able to keep them. Unrecognized (skipped) input is grouped into one token, and we can just add it as-is to the cst
node under its AST node.
During AST construction, we will simply check for TerminalKind::UNRECOGNIZED
nodes, and skip construction if there are any.
Based on the above, I propose the following changes to the current public API:
TokenKind
to TerminalKind
, since it will also refer to trivia.RuleKind
to NonterminalKind
, since \"rule\" is ambiguous.TerminalKind::SKIPPED
to UNRECOGNIZED
for clarity.LexicalContext
and fn scan(TokenKind)
from the public API, as it is a short-term workaround, and will be replaced later when we have language embedding.ProductionKind
completely, since it is no longer needed. We only need to expose fn parse(NonterminalKind)
.EndOFFileTrivia
no longer exists, ParseResult
should collect any remaining trivia at the end of the input, and include it in the ParseResult
returned, for any kind of node, not just SourceUnit
.The current CST visitors/cursors should still work as-is, since the CST tree will be unchanged. However, the new AST types allow us in the future to produce typed visitors and traits with named functions for every node type, similar to a lot of other AST processing libraries. I want to at least produce an immutable Visitor
and a mutable Rewriter
.
This specification compiles information from 83 publicly released versions of Solidity:
0.4.11
0.4.12
0.4.13
0.4.14
0.4.15
0.4.16
0.4.17
0.4.18
0.4.19
0.4.20
0.4.21
0.4.22
0.4.23
0.4.24
0.4.25
0.4.26
0.5.0
0.5.1
0.5.2
0.5.3
0.5.4
0.5.5
0.5.6
0.5.7
0.5.8
0.5.9
0.5.10
0.5.11
0.5.12
0.5.13
0.5.14
0.5.15
0.5.16
0.5.17
0.6.0
0.6.1
0.6.2
0.6.3
0.6.4
0.6.5
0.6.6
0.6.7
0.6.8
0.6.9
0.6.10
0.6.11
0.6.12
0.7.0
0.7.1
0.7.2
0.7.3
0.7.4
0.7.5
0.7.6
0.8.0
0.8.1
0.8.2
0.8.3
0.8.4
0.8.5
0.8.6
0.8.7
0.8.8
0.8.9
0.8.10
0.8.11
0.8.12
0.8.13
0.8.14
0.8.15
0.8.16
0.8.17
0.8.18
0.8.19
0.8.20
0.8.21
0.8.22
0.8.23
0.8.24
0.8.25
0.8.26
0.8.27
0.8.28
Among which, 34 versions have breaking changes:
0.4.11
0.4.12
0.4.14
0.4.16
0.4.21
0.4.22
0.4.25
0.5.0
0.5.3
0.5.5
0.5.8
0.5.10
0.5.12
0.5.14
0.6.0
0.6.2
0.6.5
0.6.7
0.6.8
0.6.11
0.7.0
0.7.1
0.7.4
0.8.0
0.8.4
0.8.7
0.8.8
0.8.13
0.8.18
0.8.19
0.8.22
0.8.24
0.8.25
0.8.27
This comment line declares that the source code is licensed under the GPL version 3.0. Machine-readable license specifiers are important in a setting where publishing the source code is the default. The comment is recognized by the compiler anywhere in the file at the file level, but it is recommended to put it at the top of the file.
// SPDX-License-Identifier: GPL-3.0\n
When omitted, the compiler produces a warning to add one. The compiler does not validate that the license is part of the list allowed by SPDX, but it does include the supplied string in the metadata.
If you do not want to specify a license or if the source code is not open-source, please use the special value UNLICENSED
. Note that UNLICENSED
(no usage allowed, not present in SPDX license list) is different from UNLICENSE
(grants all rights to everyone).
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/02-source-unit/","title":"1.2. Source Unit","text":""},{"location":"solidity-specification/01-file-structure/02-source-unit/#12-source-unit","title":"1.2. Source Unit","text":""},{"location":"solidity-specification/01-file-structure/02-source-unit/#syntax","title":"Syntax","text":"SourceUnit = (* members: *) SourceUnitMembers;
SourceUnitMembers = (* item: *) SourceUnitMember*;
SourceUnitMember = (* variant: *) PragmaDirective | (* variant: *) ImportDirective | (* variant: *) ContractDefinition | (* variant: *) InterfaceDefinition | (* variant: *) LibraryDefinition | (* variant: *) StructDefinition (* Introduced in 0.6.0 *) | (* variant: *) EnumDefinition (* Introduced in 0.6.0 *) | (* variant: *) FunctionDefinition (* Introduced in 0.7.1 *) | (* variant: *) ErrorDefinition (* Introduced in 0.8.4 *) | (* variant: *) UserDefinedValueTypeDefinition (* Introduced in 0.8.8 *) | (* variant: *) UsingDirective (* Introduced in 0.8.13 *) | (* variant: *) EventDefinition (* Introduced in 0.8.22 *) | (* variant: *) ConstantDefinition; (* Introduced in 0.7.4 *)"},{"location":"solidity-specification/01-file-structure/02-source-unit/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/","title":"1.3. Pragma Directives","text":""},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#13-pragma-directives","title":"1.3. Pragma Directives","text":""},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#syntax","title":"Syntax","text":"PragmaDirective = (* pragma_keyword: *) PRAGMA_KEYWORD (* pragma: *) Pragma (* semicolon: *) SEMICOLON;
Pragma = (* variant: *) AbicoderPragma | (* variant: *) ExperimentalPragma | (* variant: *) VersionPragma;
AbicoderPragma = (* abicoder_keyword: *) ABICODER_KEYWORD (* version: *) IDENTIFIER;
ExperimentalPragma = (* experimental_keyword: *) EXPERIMENTAL_KEYWORD (* feature: *) ExperimentalFeature;
ExperimentalFeature = (* variant: *) IDENTIFIER | (* variant: *) StringLiteral;
VersionPragma = (* solidity_keyword: *) SOLIDITY_KEYWORD (* sets: *) VersionExpressionSets;
VersionExpressionSets = (* item: *) VersionExpressionSet ((* separator: *) BAR_BAR (* item: *) VersionExpressionSet)*;
VersionExpressionSet = (* item: *) VersionExpression+;
VersionExpression = (* variant: *) VersionRange | (* variant: *) VersionTerm;
VersionRange = (* start: *) VersionLiteral (* minus: *) MINUS (* end: *) VersionLiteral;
VersionTerm = (* operator: *) VersionOperator? (* literal: *) VersionLiteral;
VersionOperator = (* variant: *) CARET | (* variant: *) TILDE | (* variant: *) EQUAL | (* variant: *) LESS_THAN | (* variant: *) GREATER_THAN | (* variant: *) LESS_THAN_EQUAL | (* variant: *) GREATER_THAN_EQUAL;
VersionLiteral = (* variant: *) SimpleVersionLiteral | (* variant: *) SINGLE_QUOTED_VERSION_LITERAL | (* variant: *) DOUBLE_QUOTED_VERSION_LITERAL;
SimpleVersionLiteral = (* item: *) VERSION_SPECIFIER ((* separator: *) PERIOD (* item: *) VERSION_SPECIFIER)*;
VERSION_SPECIFIER = \u00abVERSION_SPECIFIER_FRAGMENT\u00bb;
SINGLE_QUOTED_VERSION_LITERAL = \"'\" \u00abVERSION_SPECIFIER_FRAGMENT\u00bb (\".\" \u00abVERSION_SPECIFIER_FRAGMENT\u00bb)* \"'\";
DOUBLE_QUOTED_VERSION_LITERAL = '\"' \u00abVERSION_SPECIFIER_FRAGMENT\u00bb (\".\" \u00abVERSION_SPECIFIER_FRAGMENT\u00bb)* '\"';
\u00abVERSION_SPECIFIER_FRAGMENT\u00bb = (\"0\"\u2026\"9\" | \"x\" | \"X\" | \"*\")+;
(* Never reserved *)ABICODER_KEYWORD = \"abicoder\";
(* Never reserved *)EXPERIMENTAL_KEYWORD = \"experimental\";
(* Never reserved *)SOLIDITY_KEYWORD = \"solidity\";"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#version-pragma","title":"Version Pragma","text":"
This line declares which Solidity language version it was written for. This is to ensure that the contract is not compilable with a new (breaking) compiler version, where it could behave differently. An error is produced if the running compiler version does not match these requirements.
Note that multiple version pragmas are supported, and the compiler will verify each pragma separately.
For example, this line specifies that the source code is written for Solidity version 0.4.16
, or a newer version of the language up to, but not including version 0.9.0
:
pragma solidity >=0.4.16 <0.9.0;\n
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#abi-coder-pragma","title":"ABI Coder Pragma","text":"Used to instruct the compiler to choose a specific ABI encoder/decoder. The new ABI coder (v2) is able to encode and decode arbitrarily nested arrays and structs. It might produce less optimal code and has not received as much testing as the old encoder.
pragma abicoder v1;\n// OR\npragma abicoder v2;\n
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#experimental-pragma","title":"Experimental Pragma","text":"It can be used to enable features of the compiler or language that are not yet enabled by default. Compilers should produce an error on unrecognized pragmas (or earlier versions before they were released), and a warning before the stable version. After the stable version, this should not have an effect.
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#abiencoderv2","title":"ABIEncoderV2","text":"Please see the abicoder
pragma defined above.
pragma experimental ABIEncoderV2;\n
"},{"location":"solidity-specification/01-file-structure/03-pragma-directives/#smtchecker","title":"SMTChecker","text":"If you use SMTChecker
, then you get additional safety warnings which are obtained by querying an SMT solver. The component does not yet support all features of the Solidity language and likely outputs many warnings. In case it reports unsupported features, the analysis may not be fully sound.
pragma experimental SMTChecker;\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/04-import-directives/","title":"1.4. Import Directives","text":""},{"location":"solidity-specification/01-file-structure/04-import-directives/#14-import-directives","title":"1.4. Import Directives","text":""},{"location":"solidity-specification/01-file-structure/04-import-directives/#syntax","title":"Syntax","text":"ImportDirective = (* import_keyword: *) IMPORT_KEYWORD (* clause: *) ImportClause (* semicolon: *) SEMICOLON;
ImportClause = (* variant: *) PathImport | (* variant: *) NamedImport | (* variant: *) ImportDeconstruction;
PathImport = (* path: *) StringLiteral (* alias: *) ImportAlias?;
NamedImport = (* asterisk: *) ASTERISK (* alias: *) ImportAlias (* from_keyword: *) FROM_KEYWORD (* path: *) StringLiteral;
ImportDeconstruction = (* open_brace: *) OPEN_BRACE (* symbols: *) ImportDeconstructionSymbols (* close_brace: *) CLOSE_BRACE (* from_keyword: *) FROM_KEYWORD (* path: *) StringLiteral;
ImportDeconstructionSymbols = (* item: *) ImportDeconstructionSymbol ((* separator: *) COMMA (* item: *) ImportDeconstructionSymbol)*;
ImportDeconstructionSymbol = (* name: *) IDENTIFIER (* alias: *) ImportAlias?;
ImportAlias = (* as_keyword: *) AS_KEYWORD (* identifier: *) IDENTIFIER;"},{"location":"solidity-specification/01-file-structure/04-import-directives/#importing-files","title":"Importing Files","text":"
At a file level, you can use import statements of the following form:
import \"filename\";\n
This statement imports all global symbols from filename
(and symbols imported there) into the current global scope. This form is not recommended for use, because it unpredictably pollutes the namespace. If you add new top-level items inside filename
, they automatically appear in all files that import like this from \u201cfilename\u201d. It is better to import specific symbols explicitly, which results in all global symbols being available under the myFile
symbol:
import * as myFile from \"filename\";\n// OR\nimport \"filename\" as myFile;\n
"},{"location":"solidity-specification/01-file-structure/04-import-directives/#importing-specific-symbols","title":"Importing Specific Symbols","text":"You can import only the symbols you use from a specific file, using the syntax:
import {symbol1, symbol2} from \"filename\";\n
Which will create symbol1
and symbol1
to use in your code. If there is a naming collision, you can rename symbols while importing. For example, the code below creates new global symbols alias
and symbol2
which reference symbol1
and symbol2
from inside filename
, respectively:
import {symbol1 as alias, symbol2} from \"filename\";\n
"},{"location":"solidity-specification/01-file-structure/04-import-directives/#virtual-file-system","title":"Virtual File System","text":"In order to be able to support reproducible builds on all platforms, the Solidity compiler has to abstract away the details of the filesystem where source files are stored. For this reason import paths do not refer directly to files in the host filesystem. Instead the compiler maintains an internal database (virtual filesystem or VFS for short) where each source unit is assigned a unique source unit name which is an opaque and unstructured identifier. The import path specified in an import statement is translated into a source unit name and used to find the corresponding source unit in this database.
solc
binary CLI, you can pass disk file system paths to be used.solc
JSON API, you can pass the explicit file contents to be parsed.Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/05-using-directives/","title":"1.5. Using Directives","text":""},{"location":"solidity-specification/01-file-structure/05-using-directives/#15-using-directives","title":"1.5. Using Directives","text":""},{"location":"solidity-specification/01-file-structure/05-using-directives/#syntax","title":"Syntax","text":"UsingDirective = (* using_keyword: *) USING_KEYWORD (* clause: *) UsingClause (* for_keyword: *) FOR_KEYWORD (* target: *) UsingTarget (* global_keyword: *) GLOBAL_KEYWORD? (* Introduced in 0.8.13 *) (* semicolon: *) SEMICOLON;
UsingClause = (* variant: *) IdentifierPath | (* variant: *) UsingDeconstruction; (* Introduced in 0.8.13 *)
(* Introduced in 0.8.13 *)UsingDeconstruction = (* open_brace: *) OPEN_BRACE (* symbols: *) UsingDeconstructionSymbols (* close_brace: *) CLOSE_BRACE;
(* Introduced in 0.8.13 *)UsingDeconstructionSymbols = (* item: *) UsingDeconstructionSymbol ((* separator: *) COMMA (* item: *) UsingDeconstructionSymbol)*;
(* Introduced in 0.8.13 *)UsingDeconstructionSymbol = (* name: *) IdentifierPath (* alias: *) UsingAlias?; (* Introduced in 0.8.19 *)
(* Introduced in 0.8.19 *)UsingAlias = (* as_keyword: *) AS_KEYWORD (* operator: *) UsingOperator;
(* Introduced in 0.8.19 *)UsingOperator = (* variant: *) AMPERSAND | (* variant: *) ASTERISK | (* variant: *) BANG_EQUAL | (* variant: *) BAR | (* variant: *) CARET | (* variant: *) EQUAL_EQUAL | (* variant: *) GREATER_THAN | (* variant: *) GREATER_THAN_EQUAL | (* variant: *) LESS_THAN | (* variant: *) LESS_THAN_EQUAL | (* variant: *) MINUS | (* variant: *) PERCENT | (* variant: *) PLUS | (* variant: *) SLASH | (* variant: *) TILDE;
UsingTarget = (* variant: *) TypeName | (* variant: *) ASTERISK;"},{"location":"solidity-specification/01-file-structure/05-using-directives/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/06-trivia/","title":"1.6. Trivia","text":""},{"location":"solidity-specification/01-file-structure/06-trivia/#16-trivia","title":"1.6. Trivia","text":""},{"location":"solidity-specification/01-file-structure/06-trivia/#syntax","title":"Syntax","text":"WHITESPACE = (\" \" | \"\\t\")+;
END_OF_LINE = \"\\n\" | (\"\\r\" \"\\n\"?);
SINGLE_LINE_COMMENT = \"//\" (?!\"/\") (!(\"\\r\" | \"\\n\"))*;
MULTI_LINE_COMMENT = \"/*\" (?!\"*\" !\"/\") (!\"*\" | (\"*\" (?!\"/\")))* \"*/\";
SINGLE_LINE_NAT_SPEC_COMMENT = \"///\" (!(\"\\r\" | \"\\n\"))*;
MULTI_LINE_NAT_SPEC_COMMENT = \"/**\" (?!\"/\") (!\"*\" | (\"*\" (?!\"/\")))* \"*/\";"},{"location":"solidity-specification/01-file-structure/06-trivia/#single-line-comments","title":"Single Line Comments","text":"
A single-line comment is terminated by any unicode line terminator (LF
, VF
, FF
, CR
, NEL
, LS
or PS
) in UTF-8 encoding. The terminator is still part of the source code after the comment, so if it is not an ASCII symbol (these are NEL
, LS
and PS
), it will lead to a parser error.
// This is a single-line comment.\n
"},{"location":"solidity-specification/01-file-structure/06-trivia/#multi-line-comments","title":"Multi-line Comments","text":"Comments starting with /*
and ending with */
are allowed to range multiple lines:
/*\nThis is a\nmulti-line comment.\n*/\n
"},{"location":"solidity-specification/01-file-structure/06-trivia/#natspec-comments","title":"NatSpec Comments","text":"Additionally, there is another type of comment called a NatSpec comment. They are written with a triple slash ///
or a double asterisk block /**...*/
and they should be used directly above function declarations or statements. It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI).
/// @author My Team Name\n/// @title A simple contract example\ncontract MyContract {}\n
Please see the NatSpec Format section for further information.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/","title":"1.7. Nat Spec Format","text":""},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#17-nat-spec-format","title":"1.7. Nat Spec Format","text":""},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#what-is-natspec","title":"What is NatSpec?","text":"Solidity contracts can use a special form of comments to provide rich documentation for functions, return variables and more. This special form is named the Ethereum Natural Language Specification Format (NatSpec). It was inspired by Doxygen, and while it uses Doxygen-style comments and tags, there is no intention to keep strict compatibility with Doxygen.
It is recommended that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI). It is used in:
Documentation can be inserted above each contract
, interface
, library
, function
, event
and state variable
.
They can either exist in a single line format, starting with ///
:
/// @title An example contract\ncontract MyContract {}\n
And also in multi-line format, starting with /**
and ending with */
:
/**\n * @title An example contract\n */\ncontract MyContract {}\n
"},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#tags","title":"Tags","text":"Tags categorize different comments according to their purpose. The table below shows the different tags supported. Please note that they are optional, and without one, the entire comment will be interpreted as it had a @notice
tag.
@title
A title that should describe the contract/interface contract
, library
, interface
@author
The name of the author contract
, library
, interface
@notice
Explain to an end user what this does contract
, library
, interface
, function
, event
, state variable
@dev
Explain to a developer any extra details contract
, library
, interface
, function
, event
, state variable
@param
Documents a parameter just like in Doxygen (must be followed by parameter name) function
, event
@return
Documents the return variables of a contract's function function
, state variable
@inheritdoc
Copies all missing tags from the base function (must be followed by the contract name) function
, state variable
@custom:FOO
Custom tag, semantics is application-defined can be used everywhere"},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#function-return-types","title":"Function Return Types","text":"If your function returns multiple values, like (int quotient, int remainder)
then use multiple @return
statements in the same format as the @param
statements.
Custom tags start with @custom:
and must be followed by one or more lowercase letters or hyphens. It cannot start with a hyphen however. They can be used everywhere and are part of the developer documentation. For example, @custom:foo
or @custom:foo-bar
. A good use case is analysis and verification tools.
The Solidity compiler will pass through NatSpec documentation from your Solidity source code to the JSON output as described in this guide. The consumer of this JSON output may present this to the end-user directly or it may apply some pre-processing.
Specifying these dynamic expressions is outside the scope of the Solidity documentation. However, you can find one useful example in the RadSpec Project, where it evaluates references to function inputs to its values. For example, this line:
/// @notice This function will multiply `a` by 7\n
Can be evaluated as the following, where the value of a
is 10
:
This function will multiply 10 by 7\n
"},{"location":"solidity-specification/01-file-structure/07-nat-spec-format/#inheritance","title":"Inheritance","text":"Functions without NatSpec will automatically inherit the documentation of their base function. Exceptions to this are:
@inheritdoc
tag which specifies which contract should be used to inherit.Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/08-keywords/","title":"1.8. Keywords","text":""},{"location":"solidity-specification/01-file-structure/08-keywords/#18-keywords","title":"1.8. Keywords","text":""},{"location":"solidity-specification/01-file-structure/08-keywords/#syntax","title":"Syntax","text":"(* Introduced in 0.6.0 *)ABSTRACT_KEYWORD = \"abstract\";
(* Never reserved *)ADDRESS_KEYWORD = \"address\";
AFTER_KEYWORD = \"after\";
(* Reserved in 0.5.0 *)ALIAS_KEYWORD = \"alias\";
ANONYMOUS_KEYWORD = \"anonymous\";
(* Reserved in 0.5.0 *)APPLY_KEYWORD = \"apply\";
AS_KEYWORD = \"as\";
ASSEMBLY_KEYWORD = \"assembly\";
(* Reserved in 0.5.0 *)AUTO_KEYWORD = \"auto\";
BOOL_KEYWORD = \"bool\";
BREAK_KEYWORD = \"break\";
(* Deprecated in 0.8.0 *)BYTE_KEYWORD = \"byte\";
BYTES_KEYWORD = \"bytes\" (\"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"8\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"16\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"24\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"32\")?;
(* Introduced in 0.5.0 *)(* Reserved in 0.5.0 *)CALL_DATA_KEYWORD = \"calldata\";
CASE_KEYWORD = \"case\";
(* Introduced in 0.6.0 *)CATCH_KEYWORD = \"catch\";
CONSTANT_KEYWORD = \"constant\";
(* Introduced in 0.4.22 *)(* Reserved in 0.5.0 *)CONSTRUCTOR_KEYWORD = \"constructor\";
CONTINUE_KEYWORD = \"continue\";
CONTRACT_KEYWORD = \"contract\";
(* Reserved in 0.5.0 *)COPY_OF_KEYWORD = \"copyof\";
DAYS_KEYWORD = \"days\";
DEFAULT_KEYWORD = \"default\";
(* Reserved in 0.5.0 *)DEFINE_KEYWORD = \"define\";
DELETE_KEYWORD = \"delete\";
DO_KEYWORD = \"do\";
ELSE_KEYWORD = \"else\";
(* Introduced in 0.4.21 *)(* Reserved in 0.5.0 *)EMIT_KEYWORD = \"emit\";
ENUM_KEYWORD = \"enum\";
(* Introduced in 0.8.4 *)(* Never reserved *)ERROR_KEYWORD = \"error\";
ETHER_KEYWORD = \"ether\";
EVENT_KEYWORD = \"event\";
EXTERNAL_KEYWORD = \"external\";
(* Reserved in 0.6.0 *)FALLBACK_KEYWORD = \"fallback\";
FALSE_KEYWORD = \"false\";
FINAL_KEYWORD = \"final\";
(* Deprecated in 0.7.0 *)(* Reserved until 0.7.0 *)FINNEY_KEYWORD = \"finney\";
FIXED_KEYWORD = \"fixed\";FIXED_KEYWORD = \"fixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\") \"x\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\");FIXED_KEYWORD = \"fixed\" (\"184x8\" | \"184x16\" | \"184x24\" | \"184x32\" | \"184x40\" | \"184x48\" | \"184x56\" | \"184x64\" | \"184x72\" | \"192x8\" | \"192x16\" | \"192x24\" | \"192x32\" | \"192x40\" | \"192x48\" | \"192x56\" | \"192x64\" | \"200x8\" | \"200x16\" | \"200x24\" | \"200x32\" | \"200x40\" | \"200x48\" | \"200x56\" | \"208x8\" | \"208x16\" | \"208x24\" | \"208x32\" | \"208x40\" | \"208x48\" | \"216x8\" | \"216x16\" | \"216x24\" | \"216x32\" | \"216x40\" | \"224x8\" | \"224x16\" | \"224x24\" | \"224x32\" | \"232x8\" | \"232x16\" | \"232x24\" | \"240x8\" | \"240x16\" | \"248x8\");(* Reserved in 0.4.14 *)FIXED_KEYWORD = \"fixed\" (\"184x80\" | \"192x72\" | \"192x80\" | \"200x64\" | \"200x72\" | \"200x80\" | \"208x56\" | \"208x64\" | \"208x72\" | \"208x80\" | \"216x48\" | \"216x56\" | \"216x64\" | \"216x72\" | \"216x80\" | \"224x40\" | \"224x48\" | \"224x56\" | \"224x64\" | \"224x72\" | \"224x80\" | \"232x32\" | \"232x40\" | \"232x48\" | \"232x56\" | \"232x64\" | \"232x72\" | \"232x80\" | \"240x24\" | \"240x32\" | \"240x40\" | \"240x48\" | \"240x56\" | \"240x64\" | \"240x72\" | \"240x80\" | \"248x16\" | \"248x24\" | \"248x32\" | \"248x40\" | \"248x48\" | \"248x56\" | \"248x64\" | \"248x72\" | \"248x80\" | \"256x8\" | \"256x16\" | \"256x24\" | \"256x32\" | \"256x40\" | \"256x48\" | \"256x56\" | \"256x64\" | \"256x72\" | \"256x80\");(* Reserved in 0.4.14 *)FIXED_KEYWORD = \"fixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\") \"x\" (\"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"33\" | \"34\" | \"35\" | \"36\" | \"37\" | \"38\" | \"39\" | \"41\" | \"42\" | \"43\" | \"44\" | \"45\" | \"46\" | \"47\" | \"49\" | \"50\" | \"51\" | \"52\" | \"53\" | \"54\" | \"55\" | \"57\" | \"58\" | \"59\" | \"60\" | \"61\" | \"62\" | \"63\" | \"65\" | \"66\" | \"67\" | \"68\" | \"69\" | \"70\" | \"71\" | \"73\" | \"74\" | \"75\" | \"76\" | \"77\" | \"78\" | \"79\");
FOR_KEYWORD = \"for\";
(* Never reserved *)FROM_KEYWORD = \"from\";
FUNCTION_KEYWORD = \"function\";
(* Introduced in 0.8.13 *)(* Never reserved *)GLOBAL_KEYWORD = \"global\";
(* Introduced in 0.6.11 *)(* Reserved in 0.7.0 *)GWEI_KEYWORD = \"gwei\";
HEX_KEYWORD = \"hex\";
HOURS_KEYWORD = \"hours\";
IF_KEYWORD = \"if\";
(* Introduced in 0.6.5 *)(* Reserved in 0.5.0 *)IMMUTABLE_KEYWORD = \"immutable\";
(* Reserved in 0.5.0 *)IMPLEMENTS_KEYWORD = \"implements\";
IMPORT_KEYWORD = \"import\";
INDEXED_KEYWORD = \"indexed\";
IN_KEYWORD = \"in\";
INLINE_KEYWORD = \"inline\";
INTERFACE_KEYWORD = \"interface\";
INTERNAL_KEYWORD = \"internal\";
INT_KEYWORD = \"int\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\")?;
IS_KEYWORD = \"is\";
LET_KEYWORD = \"let\";
LIBRARY_KEYWORD = \"library\";
(* Reserved in 0.5.0 *)MACRO_KEYWORD = \"macro\";
MAPPING_KEYWORD = \"mapping\";
MATCH_KEYWORD = \"match\";
MEMORY_KEYWORD = \"memory\";
MINUTES_KEYWORD = \"minutes\";
MODIFIER_KEYWORD = \"modifier\";
(* Reserved in 0.5.0 *)MUTABLE_KEYWORD = \"mutable\";
NEW_KEYWORD = \"new\";
NULL_KEYWORD = \"null\";
OF_KEYWORD = \"of\";
(* Introduced in 0.6.0 *)(* Reserved in 0.5.0 *)OVERRIDE_KEYWORD = \"override\";
(* Reserved in 0.5.0 *)PARTIAL_KEYWORD = \"partial\";
PAYABLE_KEYWORD = \"payable\";
PRAGMA_KEYWORD = \"pragma\";
PRIVATE_KEYWORD = \"private\";
(* Reserved in 0.5.0 *)PROMISE_KEYWORD = \"promise\";
PUBLIC_KEYWORD = \"public\";
(* Introduced in 0.4.16 *)PURE_KEYWORD = \"pure\";
(* Reserved in 0.6.0 *)RECEIVE_KEYWORD = \"receive\";
(* Reserved in 0.5.0 *)REFERENCE_KEYWORD = \"reference\";
RELOCATABLE_KEYWORD = \"relocatable\";
RETURN_KEYWORD = \"return\";
RETURNS_KEYWORD = \"returns\";
(* Introduced in 0.8.4 *)(* Never reserved *)REVERT_KEYWORD = \"revert\";
(* Reserved in 0.5.0 *)SEALED_KEYWORD = \"sealed\";
SECONDS_KEYWORD = \"seconds\";
(* Reserved in 0.5.0 *)SIZE_OF_KEYWORD = \"sizeof\";
STATIC_KEYWORD = \"static\";
STORAGE_KEYWORD = \"storage\";
STRING_KEYWORD = \"string\";
STRUCT_KEYWORD = \"struct\";
SUPER_KEYWORD = \"super\";
(* Reserved in 0.5.0 *)SUPPORTS_KEYWORD = \"supports\";
SWITCH_KEYWORD = \"switch\";
(* Deprecated in 0.7.0 *)(* Reserved until 0.7.0 *)SZABO_KEYWORD = \"szabo\";
THIS_KEYWORD = \"this\";
(* Deprecated in 0.5.0 *)THROW_KEYWORD = \"throw\";
(* Introduced in 0.8.27 *)(* Never reserved *)TRANSIENT_KEYWORD = \"transient\";
TRUE_KEYWORD = \"true\";
(* Introduced in 0.6.0 *)TRY_KEYWORD = \"try\";
(* Reserved in 0.5.0 *)TYPE_DEF_KEYWORD = \"typedef\";
(* Introduced in 0.5.3 *)TYPE_KEYWORD = \"type\";
TYPE_OF_KEYWORD = \"typeof\";
UFIXED_KEYWORD = \"ufixed\";UFIXED_KEYWORD = \"ufixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\") \"x\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\");UFIXED_KEYWORD = \"ufixed\" (\"184x8\" | \"184x16\" | \"184x24\" | \"184x32\" | \"184x40\" | \"184x48\" | \"184x56\" | \"184x64\" | \"184x72\" | \"192x8\" | \"192x16\" | \"192x24\" | \"192x32\" | \"192x40\" | \"192x48\" | \"192x56\" | \"192x64\" | \"200x8\" | \"200x16\" | \"200x24\" | \"200x32\" | \"200x40\" | \"200x48\" | \"200x56\" | \"208x8\" | \"208x16\" | \"208x24\" | \"208x32\" | \"208x40\" | \"208x48\" | \"216x8\" | \"216x16\" | \"216x24\" | \"216x32\" | \"216x40\" | \"224x8\" | \"224x16\" | \"224x24\" | \"224x32\" | \"232x8\" | \"232x16\" | \"232x24\" | \"240x8\" | \"240x16\" | \"248x8\");(* Reserved in 0.4.14 *)UFIXED_KEYWORD = \"ufixed\" (\"184x80\" | \"192x72\" | \"192x80\" | \"200x64\" | \"200x72\" | \"200x80\" | \"208x56\" | \"208x64\" | \"208x72\" | \"208x80\" | \"216x48\" | \"216x56\" | \"216x64\" | \"216x72\" | \"216x80\" | \"224x40\" | \"224x48\" | \"224x56\" | \"224x64\" | \"224x72\" | \"224x80\" | \"232x32\" | \"232x40\" | \"232x48\" | \"232x56\" | \"232x64\" | \"232x72\" | \"232x80\" | \"240x24\" | \"240x32\" | \"240x40\" | \"240x48\" | \"240x56\" | \"240x64\" | \"240x72\" | \"240x80\" | \"248x16\" | \"248x24\" | \"248x32\" | \"248x40\" | \"248x48\" | \"248x56\" | \"248x64\" | \"248x72\" | \"248x80\" | \"256x8\" | \"256x16\" | \"256x24\" | \"256x32\" | \"256x40\" | \"256x48\" | \"256x56\" | \"256x64\" | \"256x72\" | \"256x80\");(* Reserved in 0.4.14 *)UFIXED_KEYWORD = \"ufixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\") \"x\" (\"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"33\" | \"34\" | \"35\" | \"36\" | \"37\" | \"38\" | \"39\" | \"41\" | \"42\" | \"43\" | \"44\" | \"45\" | \"46\" | \"47\" | \"49\" | \"50\" | \"51\" | \"52\" | \"53\" | \"54\" | \"55\" | \"57\" | \"58\" | \"59\" | \"60\" | \"61\" | \"62\" | \"63\" | \"65\" | \"66\" | \"67\" | \"68\" | \"69\" | \"70\" | \"71\" | \"73\" | \"74\" | \"75\" | \"76\" | \"77\" | \"78\" | \"79\");
UINT_KEYWORD = \"uint\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\")?;
(* Introduced in 0.8.0 *)(* Reserved in 0.5.0 *)UNCHECKED_KEYWORD = \"unchecked\";
USING_KEYWORD = \"using\";
(* Deprecated in 0.5.0 *)VAR_KEYWORD = \"var\";
(* Introduced in 0.4.16 *)VIEW_KEYWORD = \"view\";
(* Introduced in 0.6.0 *)(* Reserved in 0.6.0 *)VIRTUAL_KEYWORD = \"virtual\";
WEEKS_KEYWORD = \"weeks\";
WEI_KEYWORD = \"wei\";
WHILE_KEYWORD = \"while\";
(* Deprecated in 0.5.0 *)YEARS_KEYWORD = \"years\";"},{"location":"solidity-specification/01-file-structure/08-keywords/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/01-file-structure/09-punctuation/","title":"1.9. Punctuation","text":""},{"location":"solidity-specification/01-file-structure/09-punctuation/#19-punctuation","title":"1.9. Punctuation","text":""},{"location":"solidity-specification/01-file-structure/09-punctuation/#syntax","title":"Syntax","text":"OPEN_PAREN = \"(\";
CLOSE_PAREN = \")\";
OPEN_BRACKET = \"[\";
CLOSE_BRACKET = \"]\";
OPEN_BRACE = \"{\";
CLOSE_BRACE = \"}\";
COMMA = \",\";
PERIOD = \".\";
QUESTION_MARK = \"?\";
SEMICOLON = \";\";
COLON = \":\";
COLON_EQUAL = \":=\";
EQUAL = \"=\";
(* Deprecated in 0.5.0 *)EQUAL_COLON = \"=:\";
EQUAL_EQUAL = \"==\";
EQUAL_GREATER_THAN = \"=>\";
ASTERISK = \"*\";
ASTERISK_EQUAL = \"*=\";
ASTERISK_ASTERISK = \"**\";
BAR = \"|\";
BAR_EQUAL = \"|=\";
BAR_BAR = \"||\";
AMPERSAND = \"&\";
AMPERSAND_EQUAL = \"&=\";
AMPERSAND_AMPERSAND = \"&&\";
LESS_THAN = \"<\";
LESS_THAN_EQUAL = \"<=\";
LESS_THAN_LESS_THAN = \"<<\";
LESS_THAN_LESS_THAN_EQUAL = \"<<=\";
GREATER_THAN = \">\";
GREATER_THAN_EQUAL = \">=\";
GREATER_THAN_GREATER_THAN = \">>\";
GREATER_THAN_GREATER_THAN_EQUAL = \">>=\";
GREATER_THAN_GREATER_THAN_GREATER_THAN = \">>>\";
GREATER_THAN_GREATER_THAN_GREATER_THAN_EQUAL = \">>>=\";
PLUS = \"+\";
PLUS_EQUAL = \"+=\";
PLUS_PLUS = \"++\";
MINUS = \"-\";
MINUS_EQUAL = \"-=\";
MINUS_MINUS = \"--\";
MINUS_GREATER_THAN = \"->\";
SLASH = \"/\" (?!\"*\" | \"/\" | \"=\");
SLASH_EQUAL = \"/=\";
PERCENT = \"%\";
PERCENT_EQUAL = \"%=\";
BANG = \"!\";
BANG_EQUAL = \"!=\";
CARET = \"^\";
CARET_EQUAL = \"^=\";
TILDE = \"~\";"},{"location":"solidity-specification/01-file-structure/09-punctuation/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/","title":"2. Definitions","text":""},{"location":"solidity-specification/02-definitions/#2-definitions","title":"2. Definitions","text":"ContractDefinition = (* abstract_keyword: *) ABSTRACT_KEYWORD? (* Introduced in 0.6.0 *) (* contract_keyword: *) CONTRACT_KEYWORD (* name: *) IDENTIFIER (* inheritance: *) InheritanceSpecifier? (* open_brace: *) OPEN_BRACE (* members: *) ContractMembers (* close_brace: *) CLOSE_BRACE;
InheritanceSpecifier = (* is_keyword: *) IS_KEYWORD (* types: *) InheritanceTypes;
InheritanceTypes = (* item: *) InheritanceType ((* separator: *) COMMA (* item: *) InheritanceType)*;
InheritanceType = (* type_name: *) IdentifierPath (* arguments: *) ArgumentsDeclaration?;
ContractMembers = (* item: *) ContractMember*;
ContractMember = (* variant: *) UsingDirective | (* variant: *) FunctionDefinition | (* variant: *) ConstructorDefinition (* Introduced in 0.4.22 *) | (* variant: *) ReceiveFunctionDefinition (* Introduced in 0.6.0 *) | (* variant: *) FallbackFunctionDefinition (* Introduced in 0.6.0 *) | (* variant: *) UnnamedFunctionDefinition (* Deprecated in 0.6.0 *) | (* variant: *) ModifierDefinition | (* variant: *) StructDefinition | (* variant: *) EnumDefinition | (* variant: *) EventDefinition | (* variant: *) ErrorDefinition (* Introduced in 0.8.4 *) | (* variant: *) UserDefinedValueTypeDefinition (* Introduced in 0.8.8 *) | (* variant: *) StateVariableDefinition;"},{"location":"solidity-specification/02-definitions/01-contracts/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/02-interfaces/","title":"2.2. Interfaces","text":""},{"location":"solidity-specification/02-definitions/02-interfaces/#22-interfaces","title":"2.2. Interfaces","text":""},{"location":"solidity-specification/02-definitions/02-interfaces/#syntax","title":"Syntax","text":"InterfaceDefinition = (* interface_keyword: *) INTERFACE_KEYWORD (* name: *) IDENTIFIER (* inheritance: *) InheritanceSpecifier? (* open_brace: *) OPEN_BRACE (* members: *) InterfaceMembers (* close_brace: *) CLOSE_BRACE;
InterfaceMembers = (* item: *) ContractMember*;"},{"location":"solidity-specification/02-definitions/02-interfaces/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/03-libraries/","title":"2.3. Libraries","text":""},{"location":"solidity-specification/02-definitions/03-libraries/#23-libraries","title":"2.3. Libraries","text":""},{"location":"solidity-specification/02-definitions/03-libraries/#syntax","title":"Syntax","text":"LibraryDefinition = (* library_keyword: *) LIBRARY_KEYWORD (* name: *) IDENTIFIER (* open_brace: *) OPEN_BRACE (* members: *) LibraryMembers (* close_brace: *) CLOSE_BRACE;
LibraryMembers = (* item: *) ContractMember*;"},{"location":"solidity-specification/02-definitions/03-libraries/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/04-structs/","title":"2.4. Structs","text":""},{"location":"solidity-specification/02-definitions/04-structs/#24-structs","title":"2.4. Structs","text":""},{"location":"solidity-specification/02-definitions/04-structs/#syntax","title":"Syntax","text":"StructDefinition = (* struct_keyword: *) STRUCT_KEYWORD (* name: *) IDENTIFIER (* open_brace: *) OPEN_BRACE (* members: *) StructMembers (* close_brace: *) CLOSE_BRACE;
StructMembers = (* item: *) StructMember*;
StructMember = (* type_name: *) TypeName (* name: *) IDENTIFIER (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/02-definitions/04-structs/#struct-types","title":"Struct Types","text":"
Structs are custom defined types that can group several variables. They can be defined inside or outside contracts.
struct Voter {\n address delegate;\n uint vote;\n}\n
You can also create new objects of this struct using the following syntax:
contract MyContract {\n function create() public {\n Voter memory v = Voter({\n delegate: msg.sender,\n vote: 1\n });\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/05-enums/","title":"2.5. Enums","text":""},{"location":"solidity-specification/02-definitions/05-enums/#25-enums","title":"2.5. Enums","text":""},{"location":"solidity-specification/02-definitions/05-enums/#syntax","title":"Syntax","text":"EnumDefinition = (* enum_keyword: *) ENUM_KEYWORD (* name: *) IDENTIFIER (* open_brace: *) OPEN_BRACE (* members: *) EnumMembers (* close_brace: *) CLOSE_BRACE;
EnumMembers = ((* item: *) IDENTIFIER ((* separator: *) COMMA (* item: *) IDENTIFIER)*)?;"},{"location":"solidity-specification/02-definitions/05-enums/#enum-types","title":"Enum Types","text":"
Enums can be used to create custom types with a finite set of constant values. Enums can be declared on the file level, outside of contract or library definitions.
enum ActionChoices {\n One,\n Two\n}\n\ncontract MyContract {\n function choose() public pure returns (ActionChoices) {\n return ActionChoices.Two;\n }\n}\n
Enums require at least one member, and its default value when declared is the first member. Enums cannot have more than 256 members.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/06-constants/","title":"2.6. Constants","text":""},{"location":"solidity-specification/02-definitions/06-constants/#26-constants","title":"2.6. Constants","text":""},{"location":"solidity-specification/02-definitions/06-constants/#syntax","title":"Syntax","text":"(* Introduced in 0.7.4 *)ConstantDefinition = (* type_name: *) TypeName (* constant_keyword: *) CONSTANT_KEYWORD (* name: *) IDENTIFIER (* equal: *) EQUAL (* value: *) Expression (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/02-definitions/06-constants/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/07-state-variables/","title":"2.7. State Variables","text":""},{"location":"solidity-specification/02-definitions/07-state-variables/#27-state-variables","title":"2.7. State Variables","text":""},{"location":"solidity-specification/02-definitions/07-state-variables/#syntax","title":"Syntax","text":"StateVariableDefinition = (* type_name: *) TypeName (* attributes: *) StateVariableAttributes (* name: *) IDENTIFIER (* value: *) StateVariableDefinitionValue? (* semicolon: *) SEMICOLON;
StateVariableDefinitionValue = (* equal: *) EQUAL (* value: *) Expression;
StateVariableAttributes = (* item: *) StateVariableAttribute*;
StateVariableAttribute = (* variant: *) OverrideSpecifier (* Introduced in 0.6.0 *) | (* variant: *) CONSTANT_KEYWORD | (* variant: *) INTERNAL_KEYWORD | (* variant: *) PRIVATE_KEYWORD | (* variant: *) PUBLIC_KEYWORD | (* variant: *) IMMUTABLE_KEYWORD (* Introduced in 0.6.5 *) | (* variant: *) TRANSIENT_KEYWORD; (* Introduced in 0.8.27 *)"},{"location":"solidity-specification/02-definitions/07-state-variables/#state-variables","title":"State Variables","text":"
State variables are variables whose values are permanently stored in contract storage.
contract MyContract {\n uint myStateVariable;\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/08-functions/","title":"2.8. Functions","text":""},{"location":"solidity-specification/02-definitions/08-functions/#28-functions","title":"2.8. Functions","text":""},{"location":"solidity-specification/02-definitions/08-functions/#syntax","title":"Syntax","text":"FunctionDefinition = (* function_keyword: *) FUNCTION_KEYWORD (* name: *) FunctionName (* parameters: *) ParametersDeclaration (* attributes: *) FunctionAttributes (* returns: *) ReturnsDeclaration? (* body: *) FunctionBody;
FunctionName = (* variant: *) IDENTIFIER | (* variant: *) FALLBACK_KEYWORD | (* variant: *) RECEIVE_KEYWORD;
ParametersDeclaration = (* open_paren: *) OPEN_PAREN (* parameters: *) Parameters (* close_paren: *) CLOSE_PAREN;
Parameters = ((* item: *) Parameter ((* separator: *) COMMA (* item: *) Parameter)*)?;
Parameter = (* type_name: *) TypeName (* storage_location: *) StorageLocation? (* name: *) IDENTIFIER?;
FunctionAttributes = (* item: *) FunctionAttribute*;
FunctionAttribute = (* variant: *) ModifierInvocation | (* variant: *) OverrideSpecifier (* Introduced in 0.6.0 *) | (* variant: *) CONSTANT_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) INTERNAL_KEYWORD | (* variant: *) PAYABLE_KEYWORD | (* variant: *) PRIVATE_KEYWORD | (* variant: *) PUBLIC_KEYWORD | (* variant: *) PURE_KEYWORD (* Introduced in 0.4.16 *) | (* variant: *) VIEW_KEYWORD (* Introduced in 0.4.16 *) | (* variant: *) VIRTUAL_KEYWORD; (* Introduced in 0.6.0 *)
(* Introduced in 0.6.0 *)OverrideSpecifier = (* override_keyword: *) OVERRIDE_KEYWORD (* overridden: *) OverridePathsDeclaration?;
(* Introduced in 0.6.0 *)OverridePathsDeclaration = (* open_paren: *) OPEN_PAREN (* paths: *) OverridePaths (* close_paren: *) CLOSE_PAREN;
(* Introduced in 0.6.0 *)OverridePaths = (* item: *) IdentifierPath ((* separator: *) COMMA (* item: *) IdentifierPath)*;
ReturnsDeclaration = (* returns_keyword: *) RETURNS_KEYWORD (* variables: *) ParametersDeclaration;
FunctionBody = (* variant: *) Block | (* variant: *) SEMICOLON;
(* Introduced in 0.4.22 *)ConstructorDefinition = (* constructor_keyword: *) CONSTRUCTOR_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) ConstructorAttributes (* body: *) Block;
(* Introduced in 0.4.22 *)ConstructorAttributes = (* item: *) ConstructorAttribute*;
(* Introduced in 0.4.22 *)ConstructorAttribute = (* variant: *) ModifierInvocation | (* variant: *) INTERNAL_KEYWORD | (* variant: *) OVERRIDE_KEYWORD (* Introduced in 0.6.0 and deprecated in 0.6.7. *) | (* variant: *) PAYABLE_KEYWORD | (* variant: *) PUBLIC_KEYWORD | (* variant: *) VIRTUAL_KEYWORD; (* Introduced in 0.6.0 and deprecated in 0.6.7. *)
(* Deprecated in 0.6.0 *)UnnamedFunctionDefinition = (* function_keyword: *) FUNCTION_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) UnnamedFunctionAttributes (* body: *) FunctionBody;
(* Deprecated in 0.6.0 *)UnnamedFunctionAttributes = (* item: *) UnnamedFunctionAttribute*;
(* Deprecated in 0.6.0 *)UnnamedFunctionAttribute = (* variant: *) ModifierInvocation | (* variant: *) CONSTANT_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) INTERNAL_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) PAYABLE_KEYWORD | (* variant: *) PRIVATE_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) PUBLIC_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) PURE_KEYWORD (* Introduced in 0.4.16 and deprecated in 0.6.0. *) | (* variant: *) VIEW_KEYWORD; (* Introduced in 0.4.16 and deprecated in 0.6.0. *)
(* Introduced in 0.6.0 *)FallbackFunctionDefinition = (* fallback_keyword: *) FALLBACK_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) FallbackFunctionAttributes (* returns: *) ReturnsDeclaration? (* body: *) FunctionBody;
(* Introduced in 0.6.0 *)FallbackFunctionAttributes = (* item: *) FallbackFunctionAttribute*;
(* Introduced in 0.6.0 *)FallbackFunctionAttribute = (* variant: *) ModifierInvocation | (* variant: *) OverrideSpecifier | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) PAYABLE_KEYWORD | (* variant: *) PURE_KEYWORD | (* variant: *) VIEW_KEYWORD | (* variant: *) VIRTUAL_KEYWORD;
(* Introduced in 0.6.0 *)ReceiveFunctionDefinition = (* receive_keyword: *) RECEIVE_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) ReceiveFunctionAttributes (* body: *) FunctionBody;
(* Introduced in 0.6.0 *)ReceiveFunctionAttributes = (* item: *) ReceiveFunctionAttribute*;
(* Introduced in 0.6.0 *)ReceiveFunctionAttribute = (* variant: *) ModifierInvocation | (* variant: *) OverrideSpecifier | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) PAYABLE_KEYWORD | (* variant: *) VIRTUAL_KEYWORD;"},{"location":"solidity-specification/02-definitions/08-functions/#function-definitions","title":"Function Definitions","text":"
Functions are the executable units of code. Functions are usually defined inside a contract, but they can also be defined outside of contracts.
contract MyContract {\n function contractFunction() public {\n // Inside the contract\n }\n}\n\nfunction helperFunction() {\n // Outside the contract\n}\n
Functions can be overloaded, where multiple functions with the same name, but with different parameters, can co-exist.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/09-modifiers/","title":"2.9. Modifiers","text":""},{"location":"solidity-specification/02-definitions/09-modifiers/#29-modifiers","title":"2.9. Modifiers","text":""},{"location":"solidity-specification/02-definitions/09-modifiers/#syntax","title":"Syntax","text":"ModifierDefinition = (* modifier_keyword: *) MODIFIER_KEYWORD (* name: *) IDENTIFIER (* parameters: *) ParametersDeclaration? (* attributes: *) ModifierAttributes (* body: *) FunctionBody;
ModifierAttributes = (* item: *) ModifierAttribute*;
ModifierAttribute = (* variant: *) OverrideSpecifier (* Introduced in 0.6.0 *) | (* variant: *) VIRTUAL_KEYWORD; (* Introduced in 0.6.0 *)
ModifierInvocation = (* name: *) IdentifierPath (* arguments: *) ArgumentsDeclaration?;"},{"location":"solidity-specification/02-definitions/09-modifiers/#function-modifiers","title":"Function Modifiers","text":"
Function modifiers can be used to amend the semantics of functions in a declarative way:
contract MyContract {\n modifier onlySeller() {\n require(msg.sender == seller, \"Only seller can call this.\");\n _; // Function body will be inserted here\n }\n\n function myFunction() public view onlySeller {\n // Code here will be executed after `onlySeller` is executed.\n }\n}\n
Unlike functions, modifiers cannot be overloaded.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/10-events/","title":"2.10. Events","text":""},{"location":"solidity-specification/02-definitions/10-events/#210-events","title":"2.10. Events","text":""},{"location":"solidity-specification/02-definitions/10-events/#syntax","title":"Syntax","text":"EventDefinition = (* event_keyword: *) EVENT_KEYWORD (* name: *) IDENTIFIER (* parameters: *) EventParametersDeclaration (* anonymous_keyword: *) ANONYMOUS_KEYWORD? (* semicolon: *) SEMICOLON;
EventParametersDeclaration = (* open_paren: *) OPEN_PAREN (* parameters: *) EventParameters (* close_paren: *) CLOSE_PAREN;
EventParameters = ((* item: *) EventParameter ((* separator: *) COMMA (* item: *) EventParameter)*)?;
EventParameter = (* type_name: *) TypeName (* indexed_keyword: *) INDEXED_KEYWORD? (* name: *) IDENTIFIER?;"},{"location":"solidity-specification/02-definitions/10-events/#event-definitions","title":"Event Definitions","text":"
Events are convenient interfaces with the EVM logging facilities. They have to be defined inside a contract:
contract MyContract {\n // Defining an event\n event BidPlacedEvent(address bidder, uint amount);\n\n function bid() public payable {\n // Triggering an event\n emit BidPlacedEvent(msg.sender, msg.value);\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/11-user-defined-value-types/","title":"2.11. User Defined Value Types","text":""},{"location":"solidity-specification/02-definitions/11-user-defined-value-types/#211-user-defined-value-types","title":"2.11. User Defined Value Types","text":""},{"location":"solidity-specification/02-definitions/11-user-defined-value-types/#syntax","title":"Syntax","text":"(* Introduced in 0.8.8 *)UserDefinedValueTypeDefinition = (* type_keyword: *) TYPE_KEYWORD (* name: *) IDENTIFIER (* is_keyword: *) IS_KEYWORD (* value_type: *) ElementaryType (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/02-definitions/11-user-defined-value-types/#user-defined-value-types","title":"User Defined Value Types","text":"
A user defined value type allows creating a zero cost abstraction over an elementary value type. This is similar to a type alias. A user defined value type is defined using type C is V
, where C
is the name of the newly introduced type and V
has to be a built-in value type (the underlying type).
type MyInteger is uint256;\n\nlibrary MyLibrary {\n function add(MyInteger a, MyInteger b) internal pure returns (MyInteger) {\n return MyInteger.wrap(MyInteger.unwrap(a) + MyInteger.unwrap(b));\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/02-definitions/12-errors/","title":"2.12. Errors","text":""},{"location":"solidity-specification/02-definitions/12-errors/#212-errors","title":"2.12. Errors","text":""},{"location":"solidity-specification/02-definitions/12-errors/#syntax","title":"Syntax","text":"(* Introduced in 0.8.4 *)ErrorDefinition = (* error_keyword: *) ERROR_KEYWORD (* name: *) IDENTIFIER (* members: *) ErrorParametersDeclaration (* semicolon: *) SEMICOLON;
(* Introduced in 0.8.4 *)ErrorParametersDeclaration = (* open_paren: *) OPEN_PAREN (* parameters: *) ErrorParameters (* close_paren: *) CLOSE_PAREN;
(* Introduced in 0.8.4 *)ErrorParameters = ((* item: *) ErrorParameter ((* separator: *) COMMA (* item: *) ErrorParameter)*)?;
(* Introduced in 0.8.4 *)ErrorParameter = (* type_name: *) TypeName (* name: *) IDENTIFIER?;"},{"location":"solidity-specification/02-definitions/12-errors/#error-definitions","title":"Error Definitions","text":"
Errors allow you to define descriptive names and data for failure situations. Errors can be used in revert statements. In comparison to string descriptions, errors are much cheaper and allow you to encode additional data. You can use NatSpec to describe the error to the user. They can also be defined inside or outside contracts:
contract Token {\n error NotEnoughFunds(uint requested, uint available);\n\n function transfer(address to, uint amount) public {\n uint balance = balances[msg.sender];\n if (balance < amount)\n revert NotEnoughFunds(amount, balance);\n\n // Continue with the transfer...\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/03-types/","title":"3. Types","text":""},{"location":"solidity-specification/03-types/#3-types","title":"3. Types","text":"TypeName = (* variant: *) ArrayTypeName | (* variant: *) FunctionType | (* variant: *) MappingType | (* variant: *) ElementaryType | (* variant: *) IdentifierPath;
(* Postfix unary operator *)ArrayTypeName = (* operand: *) TypeName (* open_bracket: *) OPEN_BRACKET (* index: *) Expression? (* close_bracket: *) CLOSE_BRACKET;
FunctionType = (* function_keyword: *) FUNCTION_KEYWORD (* parameters: *) ParametersDeclaration (* attributes: *) FunctionTypeAttributes (* returns: *) ReturnsDeclaration?;
FunctionTypeAttributes = (* item: *) FunctionTypeAttribute*;
FunctionTypeAttribute = (* variant: *) INTERNAL_KEYWORD | (* variant: *) EXTERNAL_KEYWORD | (* variant: *) PRIVATE_KEYWORD | (* variant: *) PUBLIC_KEYWORD | (* variant: *) CONSTANT_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) PURE_KEYWORD (* Introduced in 0.4.16 *) | (* variant: *) VIEW_KEYWORD (* Introduced in 0.4.16 *) | (* variant: *) PAYABLE_KEYWORD;
MappingType = (* mapping_keyword: *) MAPPING_KEYWORD (* open_paren: *) OPEN_PAREN (* key_type: *) MappingKey (* equal_greater_than: *) EQUAL_GREATER_THAN (* value_type: *) MappingValue (* close_paren: *) CLOSE_PAREN;
MappingKey = (* key_type: *) MappingKeyType (* name: *) IDENTIFIER?; (* Introduced in 0.8.18 *)
MappingKeyType = (* variant: *) ElementaryType | (* variant: *) IdentifierPath;
MappingValue = (* type_name: *) TypeName (* name: *) IDENTIFIER?; (* Introduced in 0.8.18 *)"},{"location":"solidity-specification/03-types/01-advanced-types/#function-types","title":"Function Types","text":"
Function types are the types of functions. Variables of function type can be assigned from functions and function parameters of function type can be used to pass functions to and return functions from function calls. They come in two flavors, internal
and external
.
Function types are notated as follows:
function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]\n
In contrast to the parameter types, the return types cannot be empty. If the function type should not return anything, the whole returns (<return types>)
part has to be omitted.
By default, function types are internal, so the internal
keyword can be omitted. Note that this only applies to function types. Visibility has to be specified explicitly for functions defined in contracts, they do not have a default.
contract Oracle {\n Request[] private requests;\n\n function query(bytes memory data, function(uint) external callback) public {\n requests.push(Request(data, callback));\n }\n\n function reply(uint requestID, uint response) public {\n requests[requestID].callback(response);\n }\n}\n
"},{"location":"solidity-specification/03-types/01-advanced-types/#mapping-types","title":"Mapping Types","text":"Mapping types use the syntax mapping(_KeyType => _ValueType)
and variables of mapping type are declared using the syntax mapping(_KeyType => _ValueType) _VariableName
.
contract MappingExample {\n mapping(address => uint) public balances;\n\n function update(uint newBalance) public {\n balances[msg.sender] = newBalance;\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/03-types/02-elementary-types/","title":"3.2. Elementary Types","text":""},{"location":"solidity-specification/03-types/02-elementary-types/#32-elementary-types","title":"3.2. Elementary Types","text":""},{"location":"solidity-specification/03-types/02-elementary-types/#syntax","title":"Syntax","text":"ElementaryType = (* variant: *) BOOL_KEYWORD | (* variant: *) BYTE_KEYWORD (* Deprecated in 0.8.0 *) | (* variant: *) STRING_KEYWORD | (* variant: *) AddressType | (* variant: *) BYTES_KEYWORD | (* variant: *) INT_KEYWORD | (* variant: *) UINT_KEYWORD | (* variant: *) FIXED_KEYWORD | (* variant: *) UFIXED_KEYWORD;
AddressType = (* address_keyword: *) ADDRESS_KEYWORD (* payable_keyword: *) PAYABLE_KEYWORD?;"},{"location":"solidity-specification/03-types/02-elementary-types/#address-types","title":"Address Types","text":"
The address type comes in two flavours, which are largely identical:
address
: Holds a 20 byte value (size of an Ethereum address).address payable
: Same as address
, but with the additional members transfer
and send
.Hexadecimal literals that pass the address checksum test, for example 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF
are of address
type. Hexadecimal literals that are between 39 and 41 digits long and do not pass the checksum test produce an error. You can prepend (for int
types) or append (for bytesNN
types) zeros to remove the error.
The value types bytes1
, bytes2
, bytes3
, \u2026, bytes32
hold a sequence of bytes from one to up to 32.
The bytes
type is similar to bytes1[]
, but it is packed tightly in calldata and memory.
Variables of type string
are equal to bytes
but do not allow length or index access. If you want to access the byte-representation of a string s
, use bytes(s)
. Keep in mind that you are accessing the low-level bytes of the UTF-8 representation, and not the individual characters.
Memory arrays with dynamic length can be created using the new
keyword:
contract MyContract {\n function myFunction(uint length) public pure {\n bytes memory b = new bytes(length);\n }\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/04-statements/","title":"4. Statements","text":""},{"location":"solidity-specification/04-statements/#4-statements","title":"4. Statements","text":"Block = (* open_brace: *) OPEN_BRACE (* statements: *) Statements (* close_brace: *) CLOSE_BRACE;
Statements = (* item: *) Statement*;
Statement = (* variant: *) IfStatement | (* variant: *) ForStatement | (* variant: *) WhileStatement | (* variant: *) DoWhileStatement | (* variant: *) ContinueStatement | (* variant: *) BreakStatement | (* variant: *) ReturnStatement | (* variant: *) ThrowStatement (* Deprecated in 0.5.0 *) | (* variant: *) EmitStatement (* Introduced in 0.4.21 *) | (* variant: *) TryStatement (* Introduced in 0.6.0 *) | (* variant: *) RevertStatement (* Introduced in 0.8.4 *) | (* variant: *) AssemblyStatement | (* variant: *) Block | (* variant: *) UncheckedBlock (* Introduced in 0.8.0 *) | (* variant: *) TupleDeconstructionStatement | (* variant: *) VariableDeclarationStatement | (* variant: *) ExpressionStatement;
(* Introduced in 0.8.0 *)UncheckedBlock = (* unchecked_keyword: *) UNCHECKED_KEYWORD (* block: *) Block;
ExpressionStatement = (* expression: *) Expression (* semicolon: *) SEMICOLON;
AssemblyStatement = (* assembly_keyword: *) ASSEMBLY_KEYWORD (* label: *) StringLiteral? (* flags: *) AssemblyFlagsDeclaration? (* body: *) YulBlock;
AssemblyFlagsDeclaration = (* open_paren: *) OPEN_PAREN (* flags: *) AssemblyFlags (* close_paren: *) CLOSE_PAREN;
AssemblyFlags = (* item: *) StringLiteral ((* separator: *) COMMA (* item: *) StringLiteral)*;"},{"location":"solidity-specification/04-statements/01-blocks/#unchecked-blocks","title":"Unchecked Blocks","text":"
Starting with v0.8.0
, by default, all arithmetic operations are checked for underflow or overflow, which means that if the result of an operation falls outside the value range of the type, the call is reverted through a failing assertion. This can be disabled using the unchecked
block, resulting in wrapping arithmetic:
unchecked {\n i++;\n}\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/04-statements/02-declaration-statements/","title":"4.2. Declaration Statements","text":""},{"location":"solidity-specification/04-statements/02-declaration-statements/#42-declaration-statements","title":"4.2. Declaration Statements","text":""},{"location":"solidity-specification/04-statements/02-declaration-statements/#syntax","title":"Syntax","text":"TupleDeconstructionStatement = (* var_keyword: *) VAR_KEYWORD? (* Deprecated in 0.5.0 *) (* open_paren: *) OPEN_PAREN (* elements: *) TupleDeconstructionElements (* close_paren: *) CLOSE_PAREN (* equal: *) EQUAL (* expression: *) Expression (* semicolon: *) SEMICOLON;
TupleDeconstructionElements = (* item: *) TupleDeconstructionElement ((* separator: *) COMMA (* item: *) TupleDeconstructionElement)*;
TupleDeconstructionElement = (* member: *) TupleMember?;
TupleMember = (* variant: *) TypedTupleMember | (* variant: *) UntypedTupleMember;
TypedTupleMember = (* type_name: *) TypeName (* storage_location: *) StorageLocation? (* name: *) IDENTIFIER;
UntypedTupleMember = (* storage_location: *) StorageLocation? (* name: *) IDENTIFIER;
VariableDeclarationStatement = (* variable_type: *) VariableDeclarationType (* storage_location: *) StorageLocation? (* name: *) IDENTIFIER (* value: *) VariableDeclarationValue? (* semicolon: *) SEMICOLON;
VariableDeclarationType = (* variant: *) TypeName | (* variant: *) VAR_KEYWORD; (* Deprecated in 0.5.0 *)
VariableDeclarationValue = (* equal: *) EQUAL (* expression: *) Expression;
StorageLocation = (* variant: *) MEMORY_KEYWORD | (* variant: *) STORAGE_KEYWORD | (* variant: *) CALL_DATA_KEYWORD; (* Introduced in 0.5.0 *)"},{"location":"solidity-specification/04-statements/02-declaration-statements/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/04-statements/03-control-statements/","title":"4.3. Control Statements","text":""},{"location":"solidity-specification/04-statements/03-control-statements/#43-control-statements","title":"4.3. Control Statements","text":""},{"location":"solidity-specification/04-statements/03-control-statements/#syntax","title":"Syntax","text":"IfStatement = (* if_keyword: *) IF_KEYWORD (* open_paren: *) OPEN_PAREN (* condition: *) Expression (* close_paren: *) CLOSE_PAREN (* body: *) Statement (* else_branch: *) ElseBranch?;
ElseBranch = (* else_keyword: *) ELSE_KEYWORD (* body: *) Statement;
ForStatement = (* for_keyword: *) FOR_KEYWORD (* open_paren: *) OPEN_PAREN (* initialization: *) ForStatementInitialization (* condition: *) ForStatementCondition (* iterator: *) Expression? (* close_paren: *) CLOSE_PAREN (* body: *) Statement;
ForStatementInitialization = (* variant: *) TupleDeconstructionStatement | (* variant: *) VariableDeclarationStatement | (* variant: *) ExpressionStatement | (* variant: *) SEMICOLON;
ForStatementCondition = (* variant: *) ExpressionStatement | (* variant: *) SEMICOLON;
WhileStatement = (* while_keyword: *) WHILE_KEYWORD (* open_paren: *) OPEN_PAREN (* condition: *) Expression (* close_paren: *) CLOSE_PAREN (* body: *) Statement;
DoWhileStatement = (* do_keyword: *) DO_KEYWORD (* body: *) Statement (* while_keyword: *) WHILE_KEYWORD (* open_paren: *) OPEN_PAREN (* condition: *) Expression (* close_paren: *) CLOSE_PAREN (* semicolon: *) SEMICOLON;
ContinueStatement = (* continue_keyword: *) CONTINUE_KEYWORD (* semicolon: *) SEMICOLON;
BreakStatement = (* break_keyword: *) BREAK_KEYWORD (* semicolon: *) SEMICOLON;
ReturnStatement = (* return_keyword: *) RETURN_KEYWORD (* expression: *) Expression? (* semicolon: *) SEMICOLON;
(* Introduced in 0.4.21 *)EmitStatement = (* emit_keyword: *) EMIT_KEYWORD (* event: *) IdentifierPath (* arguments: *) ArgumentsDeclaration (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/04-statements/03-control-statements/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/04-statements/04-error-handling/","title":"4.4. Error Handling","text":""},{"location":"solidity-specification/04-statements/04-error-handling/#44-error-handling","title":"4.4. Error Handling","text":""},{"location":"solidity-specification/04-statements/04-error-handling/#syntax","title":"Syntax","text":"(* Introduced in 0.6.0 *)TryStatement = (* try_keyword: *) TRY_KEYWORD (* expression: *) Expression (* returns: *) ReturnsDeclaration? (* body: *) Block (* catch_clauses: *) CatchClauses;
(* Introduced in 0.6.0 *)CatchClauses = (* item: *) CatchClause+;
(* Introduced in 0.6.0 *)CatchClause = (* catch_keyword: *) CATCH_KEYWORD (* error: *) CatchClauseError? (* body: *) Block;
(* Introduced in 0.6.0 *)CatchClauseError = (* name: *) IDENTIFIER? (* parameters: *) ParametersDeclaration;
(* Introduced in 0.8.4 *)RevertStatement = (* revert_keyword: *) REVERT_KEYWORD (* error: *) IdentifierPath? (* arguments: *) ArgumentsDeclaration (* semicolon: *) SEMICOLON;
(* Deprecated in 0.5.0 *)ThrowStatement = (* throw_keyword: *) THROW_KEYWORD (* semicolon: *) SEMICOLON;"},{"location":"solidity-specification/04-statements/04-error-handling/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/","title":"5. Expressions","text":""},{"location":"solidity-specification/05-expressions/#5-expressions","title":"5. Expressions","text":"Expression = (* variant: *) AssignmentExpression | (* variant: *) ConditionalExpression | (* variant: *) OrExpression | (* variant: *) AndExpression | (* variant: *) EqualityExpression | (* variant: *) ComparisonExpression | (* variant: *) BitwiseOrExpression | (* variant: *) BitwiseXorExpression | (* variant: *) BitwiseAndExpression | (* variant: *) ShiftExpression | (* variant: *) AdditiveExpression | (* variant: *) MultiplicativeExpression | (* variant: *) ExponentiationExpression | (* variant: *) PostfixExpression | (* variant: *) PrefixExpression | (* variant: *) FunctionCallExpression | (* variant: *) CallOptionsExpression | (* variant: *) MemberAccessExpression | (* variant: *) IndexAccessExpression | (* variant: *) NewExpression | (* variant: *) TupleExpression | (* variant: *) TypeExpression (* Introduced in 0.5.3 *) | (* variant: *) ArrayExpression | (* variant: *) HexNumberExpression | (* variant: *) DecimalNumberExpression | (* variant: *) StringExpression | (* variant: *) ElementaryType | (* variant: *) PAYABLE_KEYWORD (* Introduced in 0.6.0 *) | (* variant: *) THIS_KEYWORD | (* variant: *) SUPER_KEYWORD | (* variant: *) TRUE_KEYWORD | (* variant: *) FALSE_KEYWORD | (* variant: *) IDENTIFIER;
(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) BAR_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) PLUS_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) MINUS_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) CARET_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) SLASH_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) PERCENT_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) ASTERISK_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) AMPERSAND_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) LESS_THAN_LESS_THAN_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_GREATER_THAN_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)AssignmentExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_GREATER_THAN_GREATER_THAN_EQUAL (* right_operand: *) Expression;
(* Postfix unary operator *)ConditionalExpression = (* operand: *) Expression (* question_mark: *) QUESTION_MARK (* true_expression: *) Expression (* colon: *) COLON (* false_expression: *) Expression;
(* Left-associative binary operator *)OrExpression = (* left_operand: *) Expression (* operator: *) BAR_BAR (* right_operand: *) Expression;
(* Left-associative binary operator *)AndExpression = (* left_operand: *) Expression (* operator: *) AMPERSAND_AMPERSAND (* right_operand: *) Expression;
(* Left-associative binary operator *)EqualityExpression = (* left_operand: *) Expression (* operator: *) EQUAL_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)EqualityExpression = (* left_operand: *) Expression (* operator: *) BANG_EQUAL (* right_operand: *) Expression;
(* Left-associative binary operator *)ComparisonExpression = (* left_operand: *) Expression (* operator: *) LESS_THAN (* right_operand: *) Expression;(* Left-associative binary operator *)ComparisonExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN (* right_operand: *) Expression;(* Left-associative binary operator *)ComparisonExpression = (* left_operand: *) Expression (* operator: *) LESS_THAN_EQUAL (* right_operand: *) Expression;(* Left-associative binary operator *)ComparisonExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_EQUAL (* right_operand: *) Expression;
(* Left-associative binary operator *)BitwiseOrExpression = (* left_operand: *) Expression (* operator: *) BAR (* right_operand: *) Expression;
(* Left-associative binary operator *)BitwiseXorExpression = (* left_operand: *) Expression (* operator: *) CARET (* right_operand: *) Expression;
(* Left-associative binary operator *)BitwiseAndExpression = (* left_operand: *) Expression (* operator: *) AMPERSAND (* right_operand: *) Expression;
(* Left-associative binary operator *)ShiftExpression = (* left_operand: *) Expression (* operator: *) LESS_THAN_LESS_THAN (* right_operand: *) Expression;(* Left-associative binary operator *)ShiftExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_GREATER_THAN (* right_operand: *) Expression;(* Left-associative binary operator *)ShiftExpression = (* left_operand: *) Expression (* operator: *) GREATER_THAN_GREATER_THAN_GREATER_THAN (* right_operand: *) Expression;
(* Left-associative binary operator *)AdditiveExpression = (* left_operand: *) Expression (* operator: *) PLUS (* right_operand: *) Expression;(* Left-associative binary operator *)AdditiveExpression = (* left_operand: *) Expression (* operator: *) MINUS (* right_operand: *) Expression;
(* Left-associative binary operator *)MultiplicativeExpression = (* left_operand: *) Expression (* operator: *) ASTERISK (* right_operand: *) Expression;(* Left-associative binary operator *)MultiplicativeExpression = (* left_operand: *) Expression (* operator: *) SLASH (* right_operand: *) Expression;(* Left-associative binary operator *)MultiplicativeExpression = (* left_operand: *) Expression (* operator: *) PERCENT (* right_operand: *) Expression;
(* Left-associative binary operator *)(* Deprecated in 0.8.0 *)ExponentiationExpression = (* left_operand: *) Expression (* operator: *) ASTERISK_ASTERISK (* right_operand: *) Expression;(* Right-associative binary operator *)(* Introduced in 0.8.0 *)ExponentiationExpression = (* left_operand: *) Expression (* operator: *) ASTERISK_ASTERISK (* right_operand: *) Expression;
(* Postfix unary operator *)PostfixExpression = (* operand: *) Expression (* operator: *) PLUS_PLUS;(* Postfix unary operator *)PostfixExpression = (* operand: *) Expression (* operator: *) MINUS_MINUS;
(* Prefix unary operator *)PrefixExpression = (* operator: *) PLUS_PLUS (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) MINUS_MINUS (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) TILDE (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) BANG (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) MINUS (* operand: *) Expression;(* Prefix unary operator *)(* Deprecated in 0.5.0 *)PrefixExpression = (* operator: *) PLUS (* operand: *) Expression;(* Prefix unary operator *)PrefixExpression = (* operator: *) DELETE_KEYWORD (* operand: *) Expression;
(* Postfix unary operator *)FunctionCallExpression = (* operand: *) Expression (* arguments: *) ArgumentsDeclaration;
(* Postfix unary operator *)(* Introduced in 0.6.2 *)CallOptionsExpression = (* operand: *) Expression (* open_brace: *) OPEN_BRACE (* options: *) CallOptions (* close_brace: *) CLOSE_BRACE;
(* Postfix unary operator *)MemberAccessExpression = (* operand: *) Expression (* period: *) PERIOD (* member: *) IDENTIFIER;
(* Postfix unary operator *)IndexAccessExpression = (* operand: *) Expression (* open_bracket: *) OPEN_BRACKET (* start: *) Expression? (* end: *) IndexAccessEnd? (* close_bracket: *) CLOSE_BRACKET;
IndexAccessEnd = (* colon: *) COLON (* end: *) Expression?;"},{"location":"solidity-specification/05-expressions/01-base-expressions/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/02-function-calls/","title":"5.2. Function Calls","text":""},{"location":"solidity-specification/05-expressions/02-function-calls/#52-function-calls","title":"5.2. Function Calls","text":""},{"location":"solidity-specification/05-expressions/02-function-calls/#syntax","title":"Syntax","text":"ArgumentsDeclaration = (* variant: *) PositionalArgumentsDeclaration | (* variant: *) NamedArgumentsDeclaration;
PositionalArgumentsDeclaration = (* open_paren: *) OPEN_PAREN (* arguments: *) PositionalArguments (* close_paren: *) CLOSE_PAREN;
PositionalArguments = ((* item: *) Expression ((* separator: *) COMMA (* item: *) Expression)*)?;
NamedArgumentsDeclaration = (* open_paren: *) OPEN_PAREN (* arguments: *) NamedArgumentGroup? (* close_paren: *) CLOSE_PAREN;
NamedArgumentGroup = (* open_brace: *) OPEN_BRACE (* arguments: *) NamedArguments (* close_brace: *) CLOSE_BRACE;
NamedArguments = ((* item: *) NamedArgument ((* separator: *) COMMA (* item: *) NamedArgument)*)?;
(* Introduced in 0.6.2 *)CallOptions = (* item: *) NamedArgument ((* separator: *) COMMA (* item: *) NamedArgument)*;
NamedArgument = (* name: *) IDENTIFIER (* colon: *) COLON (* value: *) Expression;"},{"location":"solidity-specification/05-expressions/02-function-calls/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/03-primary-expressions/","title":"5.3. Primary Expressions","text":""},{"location":"solidity-specification/05-expressions/03-primary-expressions/#53-primary-expressions","title":"5.3. Primary Expressions","text":""},{"location":"solidity-specification/05-expressions/03-primary-expressions/#syntax","title":"Syntax","text":"(* Introduced in 0.5.3 *)TypeExpression = (* type_keyword: *) TYPE_KEYWORD (* open_paren: *) OPEN_PAREN (* type_name: *) TypeName (* close_paren: *) CLOSE_PAREN;
NewExpression = (* new_keyword: *) NEW_KEYWORD (* type_name: *) TypeName;
TupleExpression = (* open_paren: *) OPEN_PAREN (* items: *) TupleValues (* close_paren: *) CLOSE_PAREN;
TupleValues = (* item: *) TupleValue ((* separator: *) COMMA (* item: *) TupleValue)*;
TupleValue = (* expression: *) Expression?;
ArrayExpression = (* open_bracket: *) OPEN_BRACKET (* items: *) ArrayValues (* close_bracket: *) CLOSE_BRACKET;
ArrayValues = (* item: *) Expression ((* separator: *) COMMA (* item: *) Expression)*;"},{"location":"solidity-specification/05-expressions/03-primary-expressions/#array-literals","title":"Array Literals","text":"
An array literal is a comma-separated list of one or more expressions, enclosed in square brackets ([...]
). For example [1, a, f(3)]
. It is always a statically-sized memory array whose length is the number of expressions.
contract MyContract {\n function someFunction() public pure {\n otherFunction([uint(1), 2, 3]);\n }\n}\n
"},{"location":"solidity-specification/05-expressions/03-primary-expressions/#array-slices","title":"Array Slices","text":"Array slices are a view on a contiguous portion of an array. They are written as x[start:end]
, where start
and end
are expressions resulting in a uint256 type (or implicitly convertible to it). The first element of the slice is x[start]
and the last element is x[end - 1]
.
Both start
and end
are optional: start
defaults to 0
and end
defaults to the length of the array.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/04-numbers/","title":"5.4. Numbers","text":""},{"location":"solidity-specification/05-expressions/04-numbers/#54-numbers","title":"5.4. Numbers","text":""},{"location":"solidity-specification/05-expressions/04-numbers/#syntax","title":"Syntax","text":"HexNumberExpression = (* literal: *) HEX_LITERAL (* unit: *) NumberUnit?; (* Deprecated in 0.5.0 *)
DecimalNumberExpression = (* literal: *) DECIMAL_LITERAL (* unit: *) NumberUnit?;
HEX_LITERAL = \"0x\" \u00abHEX_CHARACTER\u00bb+ (\"_\" \u00abHEX_CHARACTER\u00bb+)* (?!\u00abIDENTIFIER_START\u00bb);(* Deprecated in 0.5.0 *)HEX_LITERAL = \"0X\" \u00abHEX_CHARACTER\u00bb+ (\"_\" \u00abHEX_CHARACTER\u00bb+)* (?!\u00abIDENTIFIER_START\u00bb);
DECIMAL_LITERAL = \".\" \u00abDECIMAL_DIGITS\u00bb \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);DECIMAL_LITERAL = \u00abDECIMAL_DIGITS\u00bb (?!\".\") \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);(* Deprecated in 0.5.0 *)DECIMAL_LITERAL = \u00abDECIMAL_DIGITS\u00bb \".\" (?!\u00abDECIMAL_DIGITS\u00bb) \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);(* Deprecated in 0.5.0 *)DECIMAL_LITERAL = \u00abDECIMAL_DIGITS\u00bb \".\" \u00abDECIMAL_DIGITS\u00bb \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);(* Introduced in 0.5.0 *)DECIMAL_LITERAL = \u00abDECIMAL_DIGITS\u00bb (\".\" \u00abDECIMAL_DIGITS\u00bb)? \u00abDECIMAL_EXPONENT\u00bb? (?!\u00abIDENTIFIER_START\u00bb);
\u00abDECIMAL_DIGITS\u00bb = \"0\"\u2026\"9\"+ (\"_\" \"0\"\u2026\"9\"+)*;
\u00abDECIMAL_EXPONENT\u00bb = (\"e\" | \"E\") \"-\"? \u00abDECIMAL_DIGITS\u00bb;
NumberUnit = (* variant: *) WEI_KEYWORD | (* variant: *) GWEI_KEYWORD (* Introduced in 0.6.11 *) | (* variant: *) SZABO_KEYWORD (* Deprecated in 0.7.0 *) | (* variant: *) FINNEY_KEYWORD (* Deprecated in 0.7.0 *) | (* variant: *) ETHER_KEYWORD | (* variant: *) SECONDS_KEYWORD | (* variant: *) MINUTES_KEYWORD | (* variant: *) HOURS_KEYWORD | (* variant: *) DAYS_KEYWORD | (* variant: *) WEEKS_KEYWORD | (* variant: *) YEARS_KEYWORD; (* Deprecated in 0.5.0 *)"},{"location":"solidity-specification/05-expressions/04-numbers/#integers","title":"Integers","text":"
Signed (int8
..int256
) and unsigned (uint8
..uint256
) integers of various sizes, from 8 to 256 bits, moving up in steps of 8 bits. uint
and int
are aliases for uint256
and int256
, respectively.
Integers in Solidity are restricted to a certain range. For example, with uint32
, this is 0
up to 2**32 - 1
.
Integer literals are formed from a sequence of digits in the range 0-9
. They are interpreted as decimals. For example, 69
means sixty nine.
Octal literals do not exist in Solidity and leading zeros are invalid.
"},{"location":"solidity-specification/05-expressions/04-numbers/#decimals","title":"Decimals","text":"Decimal fractional literals are formed by a .
with at least one number on one side. Examples include 1.
, .1
and 1.3
.
Signed fixed
and unsigned fixed ufixed
point number of various sizes. Keywords ufixedMxN
and fixedMxN
, where M
represents the number of bits taken by the type and N
represents how many decimal points are available. M
must be divisible by 8 and goes from 8 to 256 bits. N
must be between 0 and 80, inclusive. ufixed
and fixed
are aliases for ufixed128x18
and fixed128x18
, respectively.
Fixed point numbers are not fully supported by Solidity yet. They can be declared, but cannot be assigned to or from.
"},{"location":"solidity-specification/05-expressions/04-numbers/#scientific-notation","title":"Scientific Notation","text":"Scientific notation in the form of 2e10
is also supported, where the mantissa can be fractional but the exponent has to be an integer. The literal MeE
is equivalent to M * 10**E
. Examples include 2e10
, -2e10
, 2e-10
, 2.5e1
.
Underscores can be used to separate the digits of a numeric literal to aid readability. For example, decimal 123_000
, hexadecimal 0x2eff_abcde
, scientific decimal notation 1_2e345_678
are all valid. Underscores are only allowed between two digits and only one consecutive underscore is allowed. There is no additional semantic meaning added to a number literal containing underscores, the underscores are ignored.
A literal number can take a suffix of wei
, gwei
or ether
to specify a sub-denomination of Ether, where Ether numbers without a postfix are assumed to be Wei.
assert(1 wei == 1);\nassert(1 gwei == 1e9);\nassert(1 szabo == 1e12);\nassert(1 finney == 1e15);\nassert(1 ether == 1e18);\n
"},{"location":"solidity-specification/05-expressions/04-numbers/#time-units","title":"Time Units","text":"Suffixes that can be used to specify units of time where seconds are the base unit and units are considered naively in the following way:
assert(1 == 1 seconds);\nassert(1 minutes == 60 seconds);\nassert(1 hours == 60 minutes);\nassert(1 days == 24 hours);\nassert(1 weeks == 7 days);\n
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/05-strings/","title":"5.5. Strings","text":""},{"location":"solidity-specification/05-expressions/05-strings/#55-strings","title":"5.5. Strings","text":""},{"location":"solidity-specification/05-expressions/05-strings/#syntax","title":"Syntax","text":"StringExpression = (* variant: *) StringLiteral (* Deprecated in 0.5.14 *) | (* variant: *) StringLiterals (* Introduced in 0.5.14 *) | (* variant: *) HexStringLiteral (* Deprecated in 0.5.14 *) | (* variant: *) HexStringLiterals (* Introduced in 0.5.14 *) | (* variant: *) UnicodeStringLiterals; (* Introduced in 0.7.0 *)
(* Introduced in 0.5.14 *)StringLiterals = (* item: *) StringLiteral+;
StringLiteral = (* variant: *) SINGLE_QUOTED_STRING_LITERAL | (* variant: *) DOUBLE_QUOTED_STRING_LITERAL;
(* Deprecated in 0.4.25 *)SINGLE_QUOTED_STRING_LITERAL = \"'\" (\u00abESCAPE_SEQUENCE_ARBITRARY\u00bb | !(\"'\" | \"\\\\\" | \"\\r\" | \"\\n\"))* \"'\";(* Introduced in 0.4.25 and deprecated in 0.7.0. *)SINGLE_QUOTED_STRING_LITERAL = \"'\" (\u00abESCAPE_SEQUENCE\u00bb | !(\"'\" | \"\\\\\" | \"\\r\" | \"\\n\"))* \"'\";SINGLE_QUOTED_STRING_LITERAL = \"'\" (\u00abESCAPE_SEQUENCE\u00bb | \" \"\u2026\"&\" | \"(\"\u2026\"[\" | \"]\"\u2026\"~\")* \"'\";
(* Deprecated in 0.4.25 *)DOUBLE_QUOTED_STRING_LITERAL = '\"' (\u00abESCAPE_SEQUENCE_ARBITRARY\u00bb | !('\"' | \"\\\\\" | \"\\r\" | \"\\n\"))* '\"';(* Introduced in 0.4.25 and deprecated in 0.7.0. *)DOUBLE_QUOTED_STRING_LITERAL = '\"' (\u00abESCAPE_SEQUENCE\u00bb | !('\"' | \"\\\\\" | \"\\r\" | \"\\n\"))* '\"';DOUBLE_QUOTED_STRING_LITERAL = '\"' (\u00abESCAPE_SEQUENCE\u00bb | \" \"\u2026\"!\" | \"#\"\u2026\"[\" | \"]\"\u2026\"~\")* '\"';
(* Introduced in 0.5.14 *)HexStringLiterals = (* item: *) HexStringLiteral+;
HexStringLiteral = (* variant: *) SINGLE_QUOTED_HEX_STRING_LITERAL | (* variant: *) DOUBLE_QUOTED_HEX_STRING_LITERAL;
SINGLE_QUOTED_HEX_STRING_LITERAL = \"hex'\" \u00abHEX_STRING_CONTENTS\u00bb? \"'\";
DOUBLE_QUOTED_HEX_STRING_LITERAL = 'hex\"' \u00abHEX_STRING_CONTENTS\u00bb? '\"';
\u00abHEX_STRING_CONTENTS\u00bb = \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb (\"_\"? \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb)*;
\u00abHEX_CHARACTER\u00bb = \"0\"\u2026\"9\" | \"a\"\u2026\"f\" | \"A\"\u2026\"F\";
(* Introduced in 0.7.0 *)UnicodeStringLiterals = (* item: *) UnicodeStringLiteral+;
(* Introduced in 0.7.0 *)UnicodeStringLiteral = (* variant: *) SINGLE_QUOTED_UNICODE_STRING_LITERAL | (* variant: *) DOUBLE_QUOTED_UNICODE_STRING_LITERAL;
(* Introduced in 0.7.0 *)SINGLE_QUOTED_UNICODE_STRING_LITERAL = \"unicode'\" (\u00abESCAPE_SEQUENCE\u00bb | !(\"'\" | \"\\\\\" | \"\\r\" | \"\\n\"))* \"'\";
(* Introduced in 0.7.0 *)DOUBLE_QUOTED_UNICODE_STRING_LITERAL = 'unicode\"' (\u00abESCAPE_SEQUENCE\u00bb | !('\"' | \"\\\\\" | \"\\r\" | \"\\n\"))* '\"';
\u00abESCAPE_SEQUENCE\u00bb = \"\\\\\" (\u00abASCII_ESCAPE\u00bb | \u00abHEX_BYTE_ESCAPE\u00bb | \u00abUNICODE_ESCAPE\u00bb);
(* Deprecated in 0.4.25 *)\u00abESCAPE_SEQUENCE_ARBITRARY\u00bb = \"\\\\\" (!(\"x\" | \"u\") | \u00abHEX_BYTE_ESCAPE\u00bb | \u00abUNICODE_ESCAPE\u00bb);
\u00abASCII_ESCAPE\u00bb = \"n\" | \"r\" | \"t\" | \"'\" | '\"' | \"\\\\\" | \"\\r\\n\" | \"\\r\" | \"\\n\";
\u00abHEX_BYTE_ESCAPE\u00bb = \"x\" \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb;
\u00abUNICODE_ESCAPE\u00bb = \"u\" \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb \u00abHEX_CHARACTER\u00bb;"},{"location":"solidity-specification/05-expressions/05-strings/#string-literals","title":"String Literals","text":"
String literals are written with either double or single-quotes (\"foo\"
or 'bar'
), and they can also be split into multiple consecutive parts (\"foo\" \"bar\"
is equivalent to \"foobar\"
) which can be helpful when dealing with long strings. They do not imply trailing zeroes as in C; \"foo\"
represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to bytes1
, ..., bytes32
if they fit.
String literals can only contain printable ASCII characters, which means the characters between 0x20
and 0x7E
inclusively.
While regular string literals can only contain ASCII, unicode literals (prefixed with the keyword unicode
) can contain any valid UTF-8 sequence. They also support the very same escape sequences as regular string literals.
string memory a = unicode\"Hello \ud83d\ude03\";\n
"},{"location":"solidity-specification/05-expressions/05-strings/#hexadecimal-literals","title":"Hexadecimal Literals","text":"Hexadecimal literals are prefixed with the keyword hex
and are enclosed in double or single-quotes (hex\"001122FF\"
, hex'0011_22_FF'
). Their content must be hexadecimal digits which can optionally use a single underscore as separator between byte boundaries. The value of the literal will be the binary representation of the hexadecimal sequence.
Hexadecimal literals behave like string literals and have the same convertibility restrictions. Additionally, multiple hexadecimal literals separated by whitespace are concatenated into a single literal: hex\"00112233\" hex\"44556677\"
is equivalent to hex\"0011223344556677\"
String literals also support the following escape characters:
\\<newline>
(escapes an actual newline)\\\\
(backslash)\\'
(single quote)\\\"
(double quote)\\n
(newline)break\\r
(carriage return)\\t
(tab)\\xNN
(hex escape, takes a hex value and inserts the appropriate byte)\\uNNNN
(unicode escape, takes a Unicode code point and inserts an UTF-8 sequence)Any Unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to terminate the string literal. Newline only terminates the string literal if it is not preceded by a \\
.
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/05-expressions/06-identifiers/","title":"5.6. Identifiers","text":""},{"location":"solidity-specification/05-expressions/06-identifiers/#56-identifiers","title":"5.6. Identifiers","text":""},{"location":"solidity-specification/05-expressions/06-identifiers/#syntax","title":"Syntax","text":"IdentifierPath = (* item: *) IDENTIFIER ((* separator: *) PERIOD (* item: *) IDENTIFIER)*;
IDENTIFIER = \u00abIDENTIFIER_START\u00bb \u00abIDENTIFIER_PART\u00bb*;
\u00abIDENTIFIER_START\u00bb = \"_\" | \"$\" | \"a\"\u2026\"z\" | \"A\"\u2026\"Z\";
\u00abIDENTIFIER_PART\u00bb = \u00abIDENTIFIER_START\u00bb | \"0\"\u2026\"9\";"},{"location":"solidity-specification/05-expressions/06-identifiers/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/06-yul/","title":"6. Yul","text":""},{"location":"solidity-specification/06-yul/#6-yul","title":"6. Yul","text":"YulBlock = (* open_brace: *) OPEN_BRACE (* statements: *) YulStatements (* close_brace: *) CLOSE_BRACE;
YulStatements = (* item: *) YulStatement*;
YulStatement = (* variant: *) YulBlock | (* variant: *) YulFunctionDefinition | (* variant: *) YulStackAssignmentStatement (* Deprecated in 0.5.0 *) | (* variant: *) YulIfStatement | (* variant: *) YulForStatement | (* variant: *) YulSwitchStatement | (* variant: *) YulLeaveStatement (* Introduced in 0.6.0 *) | (* variant: *) YulBreakStatement | (* variant: *) YulContinueStatement | (* variant: *) YulVariableAssignmentStatement | (* variant: *) YulLabel (* Deprecated in 0.5.0 *) | (* variant: *) YulVariableDeclarationStatement | (* variant: *) YulExpression;
YulFunctionDefinition = (* function_keyword: *) YUL_FUNCTION_KEYWORD (* name: *) YUL_IDENTIFIER (* parameters: *) YulParametersDeclaration (* returns: *) YulReturnsDeclaration? (* body: *) YulBlock;
YulParametersDeclaration = (* open_paren: *) OPEN_PAREN (* parameters: *) YulParameters (* close_paren: *) CLOSE_PAREN;
YulParameters = ((* item: *) YUL_IDENTIFIER ((* separator: *) COMMA (* item: *) YUL_IDENTIFIER)*)?;
YulReturnsDeclaration = (* minus_greater_than: *) MINUS_GREATER_THAN (* variables: *) YulVariableNames;
YulVariableNames = (* item: *) YUL_IDENTIFIER ((* separator: *) COMMA (* item: *) YUL_IDENTIFIER)*;
YulVariableDeclarationStatement = (* let_keyword: *) YUL_LET_KEYWORD (* variables: *) YulVariableNames (* value: *) YulVariableDeclarationValue?;
YulVariableDeclarationValue = (* assignment: *) YulAssignmentOperator (* expression: *) YulExpression;
YulVariableAssignmentStatement = (* variables: *) YulPaths (* assignment: *) YulAssignmentOperator (* expression: *) YulExpression;
YulAssignmentOperator = (* variant: *) COLON_EQUAL | (* variant: *) YulColonAndEqual; (* Deprecated in 0.5.5 *)
(* Deprecated in 0.5.5 *)YulColonAndEqual = (* colon: *) COLON (* equal: *) EQUAL;
(* Deprecated in 0.5.0 *)YulStackAssignmentStatement = (* assignment: *) YulStackAssignmentOperator (* variable: *) YUL_IDENTIFIER;
(* Deprecated in 0.5.0 *)YulStackAssignmentOperator = (* variant: *) EQUAL_COLON | (* variant: *) YulEqualAndColon;
(* Deprecated in 0.5.0 *)YulEqualAndColon = (* equal: *) EQUAL (* colon: *) COLON;
YulIfStatement = (* if_keyword: *) YUL_IF_KEYWORD (* condition: *) YulExpression (* body: *) YulBlock;
YulForStatement = (* for_keyword: *) YUL_FOR_KEYWORD (* initialization: *) YulBlock (* condition: *) YulExpression (* iterator: *) YulBlock (* body: *) YulBlock;
YulSwitchStatement = (* switch_keyword: *) YUL_SWITCH_KEYWORD (* expression: *) YulExpression (* cases: *) YulSwitchCases;
YulSwitchCases = (* item: *) YulSwitchCase+;
YulSwitchCase = (* variant: *) YulDefaultCase | (* variant: *) YulValueCase;
YulDefaultCase = (* default_keyword: *) YUL_DEFAULT_KEYWORD (* body: *) YulBlock;
YulValueCase = (* case_keyword: *) YUL_CASE_KEYWORD (* value: *) YulLiteral (* body: *) YulBlock;
(* Introduced in 0.6.0 *)YulLeaveStatement = (* leave_keyword: *) YUL_LEAVE_KEYWORD;
YulBreakStatement = (* break_keyword: *) YUL_BREAK_KEYWORD;
YulContinueStatement = (* continue_keyword: *) YUL_CONTINUE_KEYWORD;
(* Deprecated in 0.5.0 *)YulLabel = (* label: *) YUL_IDENTIFIER (* colon: *) COLON;"},{"location":"solidity-specification/06-yul/01-yul-statements/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/06-yul/02-yul-expressions/","title":"6.2. Yul Expressions","text":""},{"location":"solidity-specification/06-yul/02-yul-expressions/#62-yul-expressions","title":"6.2. Yul Expressions","text":""},{"location":"solidity-specification/06-yul/02-yul-expressions/#syntax","title":"Syntax","text":"YulExpression = (* variant: *) YulFunctionCallExpression | (* variant: *) YulLiteral | (* variant: *) YulBuiltInFunction | (* variant: *) YulPath;
(* Postfix unary operator *)YulFunctionCallExpression = (* operand: *) YulExpression (* open_paren: *) OPEN_PAREN (* arguments: *) YulArguments (* close_paren: *) CLOSE_PAREN;
YulArguments = ((* item: *) YulExpression ((* separator: *) COMMA (* item: *) YulExpression)*)?;
YulPaths = (* item: *) YulPath ((* separator: *) COMMA (* item: *) YulPath)*;
YulPath = (* item: *) YUL_IDENTIFIER ((* separator: *) PERIOD (* item: *) YUL_IDENTIFIER)*;
(* Introduced in 0.5.8 and deprecated in 0.7.0. *)YUL_IDENTIFIER = \u00abIDENTIFIER_START\u00bb (\u00abIDENTIFIER_PART\u00bb | \".\")*;YUL_IDENTIFIER = \u00abIDENTIFIER_START\u00bb \u00abIDENTIFIER_PART\u00bb*;
YulBuiltInFunction = (* variant: *) YUL_ADD_KEYWORD | (* variant: *) YUL_ADD_MOD_KEYWORD | (* variant: *) YUL_ADDRESS_KEYWORD | (* variant: *) YUL_AND_KEYWORD | (* variant: *) YUL_BALANCE_KEYWORD | (* variant: *) YUL_BLOCK_HASH_KEYWORD | (* variant: *) YUL_BYTE_KEYWORD | (* variant: *) YUL_CALL_CODE_KEYWORD | (* variant: *) YUL_CALL_DATA_COPY_KEYWORD | (* variant: *) YUL_CALL_DATA_LOAD_KEYWORD | (* variant: *) YUL_CALL_DATA_SIZE_KEYWORD | (* variant: *) YUL_CALLER_KEYWORD | (* variant: *) YUL_CALL_KEYWORD | (* variant: *) YUL_CALL_VALUE_KEYWORD | (* variant: *) YUL_COIN_BASE_KEYWORD | (* variant: *) YUL_CREATE_KEYWORD | (* variant: *) YUL_DELEGATE_CALL_KEYWORD | (* variant: *) YUL_DIV_KEYWORD | (* variant: *) YUL_EQ_KEYWORD | (* variant: *) YUL_EXP_KEYWORD | (* variant: *) YUL_EXT_CODE_COPY_KEYWORD | (* variant: *) YUL_EXT_CODE_SIZE_KEYWORD | (* variant: *) YUL_GAS_KEYWORD | (* variant: *) YUL_GAS_LIMIT_KEYWORD | (* variant: *) YUL_GAS_PRICE_KEYWORD | (* variant: *) YUL_GT_KEYWORD | (* variant: *) YUL_INVALID_KEYWORD | (* variant: *) YUL_IS_ZERO_KEYWORD | (* variant: *) YUL_JUMP_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) YUL_JUMPI_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) YUL_LOG_0_KEYWORD | (* variant: *) YUL_LOG_1_KEYWORD | (* variant: *) YUL_LOG_2_KEYWORD | (* variant: *) YUL_LOG_3_KEYWORD | (* variant: *) YUL_LOG_4_KEYWORD | (* variant: *) YUL_LT_KEYWORD | (* variant: *) YUL_M_LOAD_KEYWORD | (* variant: *) YUL_MOD_KEYWORD | (* variant: *) YUL_M_SIZE_KEYWORD | (* variant: *) YUL_M_STORE_8_KEYWORD | (* variant: *) YUL_M_STORE_KEYWORD | (* variant: *) YUL_MUL_KEYWORD | (* variant: *) YUL_MUL_MOD_KEYWORD | (* variant: *) YUL_NOT_KEYWORD | (* variant: *) YUL_NUMBER_KEYWORD | (* variant: *) YUL_ORIGIN_KEYWORD | (* variant: *) YUL_OR_KEYWORD | (* variant: *) YUL_POP_KEYWORD | (* variant: *) YUL_RETURN_KEYWORD | (* variant: *) YUL_REVERT_KEYWORD | (* variant: *) YUL_S_DIV_KEYWORD | (* variant: *) YUL_SELF_DESTRUCT_KEYWORD | (* variant: *) YUL_SGT_KEYWORD | (* variant: *) YUL_SIGN_EXTEND_KEYWORD | (* variant: *) YUL_S_LOAD_KEYWORD | (* variant: *) YUL_SLT_KEYWORD | (* variant: *) YUL_S_MOD_KEYWORD | (* variant: *) YUL_S_STORE_KEYWORD | (* variant: *) YUL_STOP_KEYWORD | (* variant: *) YUL_SUB_KEYWORD | (* variant: *) YUL_TIMESTAMP_KEYWORD | (* variant: *) YUL_XOR_KEYWORD | (* variant: *) YUL_KECCAK_256_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_SHA_3_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) YUL_SUICIDE_KEYWORD (* Deprecated in 0.5.0 *) | (* variant: *) YUL_RETURN_DATA_COPY_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_RETURN_DATA_SIZE_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_STATIC_CALL_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_CREATE_2_KEYWORD (* Introduced in 0.4.12 *) | (* variant: *) YUL_EXT_CODE_HASH_KEYWORD (* Introduced in 0.5.0 *) | (* variant: *) YUL_SAR_KEYWORD | (* variant: *) YUL_SHL_KEYWORD | (* variant: *) YUL_SHR_KEYWORD | (* variant: *) YUL_CHAIN_ID_KEYWORD | (* variant: *) YUL_SELF_BALANCE_KEYWORD | (* variant: *) YUL_BASE_FEE_KEYWORD (* Introduced in 0.8.7 *) | (* variant: *) YUL_DIFFICULTY_KEYWORD (* Deprecated in 0.8.18 *) | (* variant: *) YUL_PREV_RANDAO_KEYWORD (* Introduced in 0.8.18 *) | (* variant: *) YUL_BLOB_BASE_FEE_KEYWORD (* Introduced in 0.8.24 *) | (* variant: *) YUL_BLOB_HASH_KEYWORD (* Introduced in 0.8.24 *) | (* variant: *) YUL_T_LOAD_KEYWORD (* Introduced in 0.8.24 *) | (* variant: *) YUL_T_STORE_KEYWORD (* Introduced in 0.8.24 *) | (* variant: *) YUL_M_COPY_KEYWORD; (* Introduced in 0.8.24 *)
YulLiteral = (* variant: *) YUL_TRUE_KEYWORD | (* variant: *) YUL_FALSE_KEYWORD | (* variant: *) YUL_DECIMAL_LITERAL | (* variant: *) YUL_HEX_LITERAL | (* variant: *) HexStringLiteral | (* variant: *) StringLiteral;
YUL_DECIMAL_LITERAL = (\"0\" | (\"1\"\u2026\"9\" \"0\"\u2026\"9\"*)) (?!\u00abIDENTIFIER_START\u00bb);
YUL_HEX_LITERAL = \"0x\" \u00abHEX_CHARACTER\u00bb+ (?!\u00abIDENTIFIER_START\u00bb);"},{"location":"solidity-specification/06-yul/02-yul-expressions/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"solidity-specification/06-yul/03-yul-keywords/","title":"6.3. Yul Keywords","text":""},{"location":"solidity-specification/06-yul/03-yul-keywords/#63-yul-keywords","title":"6.3. Yul Keywords","text":""},{"location":"solidity-specification/06-yul/03-yul-keywords/#syntax","title":"Syntax","text":"(* Reserved until 0.7.1 *)YUL_ABSTRACT_KEYWORD = \"abstract\";
YUL_ADD_KEYWORD = \"add\";
YUL_ADD_MOD_KEYWORD = \"addmod\";
(* Never reserved *)YUL_ADDRESS_KEYWORD = \"address\";
(* Reserved until 0.7.1 *)YUL_AFTER_KEYWORD = \"after\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_ALIAS_KEYWORD = \"alias\";
YUL_AND_KEYWORD = \"and\";
(* Reserved until 0.7.1 *)YUL_ANONYMOUS_KEYWORD = \"anonymous\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_APPLY_KEYWORD = \"apply\";
(* Reserved until 0.7.1 *)YUL_AS_KEYWORD = \"as\";
(* Reserved until 0.7.1 *)YUL_ASSEMBLY_KEYWORD = \"assembly\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_AUTO_KEYWORD = \"auto\";
YUL_BALANCE_KEYWORD = \"balance\";
(* Introduced in 0.8.7 *)(* Reserved in 0.8.7 *)YUL_BASE_FEE_KEYWORD = \"basefee\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_BLOB_BASE_FEE_KEYWORD = \"blobbasefee\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_BLOB_HASH_KEYWORD = \"blobhash\";
YUL_BLOCK_HASH_KEYWORD = \"blockhash\";
(* Reserved until 0.5.10 *)YUL_BOOL_KEYWORD = \"bool\";
YUL_BREAK_KEYWORD = \"break\";
YUL_BYTE_KEYWORD = \"byte\";
(* Reserved until 0.7.1 *)YUL_BYTES_KEYWORD = \"bytes\" (\"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"8\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"16\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"24\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"32\")?;
YUL_CALL_CODE_KEYWORD = \"callcode\";
YUL_CALL_DATA_COPY_KEYWORD = \"calldatacopy\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_CALL_DATA_KEYWORD = \"calldata\";
YUL_CALL_DATA_LOAD_KEYWORD = \"calldataload\";
YUL_CALL_DATA_SIZE_KEYWORD = \"calldatasize\";
YUL_CALLER_KEYWORD = \"caller\";
YUL_CALL_KEYWORD = \"call\";
YUL_CALL_VALUE_KEYWORD = \"callvalue\";
YUL_CASE_KEYWORD = \"case\";
(* Reserved until 0.7.1 *)YUL_CATCH_KEYWORD = \"catch\";
(* Reserved in 0.5.12 *)YUL_CHAIN_ID_KEYWORD = \"chainid\";
YUL_COIN_BASE_KEYWORD = \"coinbase\";
(* Reserved until 0.7.1 *)YUL_CONSTANT_KEYWORD = \"constant\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_CONSTRUCTOR_KEYWORD = \"constructor\";
YUL_CONTINUE_KEYWORD = \"continue\";
(* Reserved until 0.7.1 *)YUL_CONTRACT_KEYWORD = \"contract\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_COPY_OF_KEYWORD = \"copyof\";
YUL_CREATE_KEYWORD = \"create\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_CREATE_2_KEYWORD = \"create2\";
(* Reserved until 0.7.1 *)YUL_DAYS_KEYWORD = \"days\";
YUL_DEFAULT_KEYWORD = \"default\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_DEFINE_KEYWORD = \"define\";
YUL_DELEGATE_CALL_KEYWORD = \"delegatecall\";
(* Reserved until 0.7.1 *)YUL_DELETE_KEYWORD = \"delete\";
(* Deprecated in 0.8.18 *)YUL_DIFFICULTY_KEYWORD = \"difficulty\";
YUL_DIV_KEYWORD = \"div\";
(* Reserved until 0.7.1 *)YUL_DO_KEYWORD = \"do\";
(* Reserved until 0.7.1 *)YUL_ELSE_KEYWORD = \"else\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_EMIT_KEYWORD = \"emit\";
(* Reserved until 0.7.1 *)YUL_ENUM_KEYWORD = \"enum\";
YUL_EQ_KEYWORD = \"eq\";
(* Reserved until 0.7.1 *)YUL_ETHER_KEYWORD = \"ether\";
(* Reserved until 0.7.1 *)YUL_EVENT_KEYWORD = \"event\";
YUL_EXP_KEYWORD = \"exp\";
YUL_EXT_CODE_COPY_KEYWORD = \"extcodecopy\";
(* Introduced in 0.5.0 *)(* Reserved in 0.5.0 *)YUL_EXT_CODE_HASH_KEYWORD = \"extcodehash\";
YUL_EXT_CODE_SIZE_KEYWORD = \"extcodesize\";
(* Reserved until 0.7.1 *)YUL_EXTERNAL_KEYWORD = \"external\";
(* Reserved from 0.6.0 until 0.7.1 *)YUL_FALLBACK_KEYWORD = \"fallback\";
YUL_FALSE_KEYWORD = \"false\";
(* Reserved until 0.7.1 *)YUL_FINAL_KEYWORD = \"final\";
(* Reserved until 0.7.0 *)YUL_FINNEY_KEYWORD = \"finney\";
(* Reserved until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\";(* Reserved until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\") \"x\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\");(* Reserved until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\" (\"184x8\" | \"184x16\" | \"184x24\" | \"184x32\" | \"184x40\" | \"184x48\" | \"184x56\" | \"184x64\" | \"184x72\" | \"192x8\" | \"192x16\" | \"192x24\" | \"192x32\" | \"192x40\" | \"192x48\" | \"192x56\" | \"192x64\" | \"200x8\" | \"200x16\" | \"200x24\" | \"200x32\" | \"200x40\" | \"200x48\" | \"200x56\" | \"208x8\" | \"208x16\" | \"208x24\" | \"208x32\" | \"208x40\" | \"208x48\" | \"216x8\" | \"216x16\" | \"216x24\" | \"216x32\" | \"216x40\" | \"224x8\" | \"224x16\" | \"224x24\" | \"224x32\" | \"232x8\" | \"232x16\" | \"232x24\" | \"240x8\" | \"240x16\" | \"248x8\");(* Reserved from 0.4.14 until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\" (\"184x80\" | \"192x72\" | \"192x80\" | \"200x64\" | \"200x72\" | \"200x80\" | \"208x56\" | \"208x64\" | \"208x72\" | \"208x80\" | \"216x48\" | \"216x56\" | \"216x64\" | \"216x72\" | \"216x80\" | \"224x40\" | \"224x48\" | \"224x56\" | \"224x64\" | \"224x72\" | \"224x80\" | \"232x32\" | \"232x40\" | \"232x48\" | \"232x56\" | \"232x64\" | \"232x72\" | \"232x80\" | \"240x24\" | \"240x32\" | \"240x40\" | \"240x48\" | \"240x56\" | \"240x64\" | \"240x72\" | \"240x80\" | \"248x16\" | \"248x24\" | \"248x32\" | \"248x40\" | \"248x48\" | \"248x56\" | \"248x64\" | \"248x72\" | \"248x80\" | \"256x8\" | \"256x16\" | \"256x24\" | \"256x32\" | \"256x40\" | \"256x48\" | \"256x56\" | \"256x64\" | \"256x72\" | \"256x80\");(* Reserved from 0.4.14 until 0.7.1 *)YUL_FIXED_KEYWORD = \"fixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\") \"x\" (\"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"33\" | \"34\" | \"35\" | \"36\" | \"37\" | \"38\" | \"39\" | \"41\" | \"42\" | \"43\" | \"44\" | \"45\" | \"46\" | \"47\" | \"49\" | \"50\" | \"51\" | \"52\" | \"53\" | \"54\" | \"55\" | \"57\" | \"58\" | \"59\" | \"60\" | \"61\" | \"62\" | \"63\" | \"65\" | \"66\" | \"67\" | \"68\" | \"69\" | \"70\" | \"71\" | \"73\" | \"74\" | \"75\" | \"76\" | \"77\" | \"78\" | \"79\");
YUL_FOR_KEYWORD = \"for\";
YUL_FUNCTION_KEYWORD = \"function\";
YUL_GAS_KEYWORD = \"gas\";
YUL_GAS_LIMIT_KEYWORD = \"gaslimit\";
YUL_GAS_PRICE_KEYWORD = \"gasprice\";
YUL_GT_KEYWORD = \"gt\";
(* Reserved from 0.7.0 until 0.7.1 *)YUL_GWEI_KEYWORD = \"gwei\";
YUL_HEX_KEYWORD = \"hex\";
(* Reserved until 0.7.1 *)YUL_HOURS_KEYWORD = \"hours\";
YUL_IF_KEYWORD = \"if\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_IMMUTABLE_KEYWORD = \"immutable\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_IMPLEMENTS_KEYWORD = \"implements\";
(* Reserved until 0.7.1 *)YUL_IMPORT_KEYWORD = \"import\";
(* Reserved until 0.7.1 *)YUL_INDEXED_KEYWORD = \"indexed\";
(* Reserved until 0.6.8 *)YUL_IN_KEYWORD = \"in\";
(* Reserved until 0.7.1 *)YUL_INLINE_KEYWORD = \"inline\";
(* Reserved until 0.7.1 *)YUL_INTERFACE_KEYWORD = \"interface\";
(* Reserved until 0.7.1 *)YUL_INTERNAL_KEYWORD = \"internal\";
(* Reserved until 0.7.1 *)YUL_INT_KEYWORD = \"int\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\")?;
YUL_INVALID_KEYWORD = \"invalid\";
(* Reserved until 0.7.1 *)YUL_IS_KEYWORD = \"is\";
YUL_IS_ZERO_KEYWORD = \"iszero\";
(* Deprecated in 0.5.0 *)YUL_JUMP_KEYWORD = \"jump\";
(* Deprecated in 0.5.0 *)YUL_JUMPI_KEYWORD = \"jumpi\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_KECCAK_256_KEYWORD = \"keccak256\";
(* Introduced in 0.6.0 *)(* Reserved in 0.7.1 *)YUL_LEAVE_KEYWORD = \"leave\";
YUL_LET_KEYWORD = \"let\";
(* Reserved until 0.7.1 *)YUL_LIBRARY_KEYWORD = \"library\";
YUL_LOG_0_KEYWORD = \"log0\";
YUL_LOG_1_KEYWORD = \"log1\";
YUL_LOG_2_KEYWORD = \"log2\";
YUL_LOG_3_KEYWORD = \"log3\";
YUL_LOG_4_KEYWORD = \"log4\";
YUL_LT_KEYWORD = \"lt\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_MACRO_KEYWORD = \"macro\";
(* Reserved until 0.7.1 *)YUL_MAPPING_KEYWORD = \"mapping\";
(* Reserved until 0.7.1 *)YUL_MATCH_KEYWORD = \"match\";
(* Reserved until 0.7.1 *)YUL_MEMORY_KEYWORD = \"memory\";
(* Reserved until 0.7.1 *)YUL_MINUTES_KEYWORD = \"minutes\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_M_COPY_KEYWORD = \"mcopy\";
YUL_M_LOAD_KEYWORD = \"mload\";
YUL_MOD_KEYWORD = \"mod\";
(* Reserved until 0.7.1 *)YUL_MODIFIER_KEYWORD = \"modifier\";
YUL_M_SIZE_KEYWORD = \"msize\";
YUL_M_STORE_KEYWORD = \"mstore\";
YUL_M_STORE_8_KEYWORD = \"mstore8\";
YUL_MUL_KEYWORD = \"mul\";
YUL_MUL_MOD_KEYWORD = \"mulmod\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_MUTABLE_KEYWORD = \"mutable\";
(* Reserved until 0.7.1 *)YUL_NEW_KEYWORD = \"new\";
YUL_NOT_KEYWORD = \"not\";
(* Reserved until 0.7.1 *)YUL_NULL_KEYWORD = \"null\";
YUL_NUMBER_KEYWORD = \"number\";
(* Reserved until 0.7.1 *)YUL_OF_KEYWORD = \"of\";
YUL_OR_KEYWORD = \"or\";
YUL_ORIGIN_KEYWORD = \"origin\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_OVERRIDE_KEYWORD = \"override\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_PARTIAL_KEYWORD = \"partial\";
(* Reserved until 0.7.1 *)YUL_PAYABLE_KEYWORD = \"payable\";
YUL_POP_KEYWORD = \"pop\";
(* Reserved until 0.7.1 *)YUL_PRAGMA_KEYWORD = \"pragma\";
(* Introduced in 0.8.18 *)(* Reserved in 0.8.18 *)YUL_PREV_RANDAO_KEYWORD = \"prevrandao\";
(* Reserved until 0.7.1 *)YUL_PRIVATE_KEYWORD = \"private\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_PROMISE_KEYWORD = \"promise\";
(* Reserved until 0.7.1 *)YUL_PUBLIC_KEYWORD = \"public\";
(* Reserved until 0.7.1 *)YUL_PURE_KEYWORD = \"pure\";
(* Reserved from 0.6.0 until 0.7.1 *)YUL_RECEIVE_KEYWORD = \"receive\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_REFERENCE_KEYWORD = \"reference\";
(* Reserved until 0.7.1 *)YUL_RELOCATABLE_KEYWORD = \"relocatable\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_RETURN_DATA_COPY_KEYWORD = \"returndatacopy\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_RETURN_DATA_SIZE_KEYWORD = \"returndatasize\";
YUL_RETURN_KEYWORD = \"return\";
(* Reserved until 0.7.1 *)YUL_RETURNS_KEYWORD = \"returns\";
YUL_REVERT_KEYWORD = \"revert\";
(* Reserved in 0.4.21 *)YUL_SAR_KEYWORD = \"sar\";
YUL_S_DIV_KEYWORD = \"sdiv\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_SEALED_KEYWORD = \"sealed\";
(* Reserved until 0.7.1 *)YUL_SECONDS_KEYWORD = \"seconds\";
(* Reserved in 0.5.12 *)YUL_SELF_BALANCE_KEYWORD = \"selfbalance\";
YUL_SELF_DESTRUCT_KEYWORD = \"selfdestruct\";
YUL_SGT_KEYWORD = \"sgt\";
(* Deprecated in 0.5.0 *)(* Reserved until 0.5.0 *)YUL_SHA_3_KEYWORD = \"sha3\";
(* Reserved in 0.4.21 *)YUL_SHL_KEYWORD = \"shl\";
(* Reserved in 0.4.21 *)YUL_SHR_KEYWORD = \"shr\";
YUL_SIGN_EXTEND_KEYWORD = \"signextend\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_SIZE_OF_KEYWORD = \"sizeof\";
YUL_S_LOAD_KEYWORD = \"sload\";
YUL_SLT_KEYWORD = \"slt\";
YUL_S_MOD_KEYWORD = \"smod\";
YUL_S_STORE_KEYWORD = \"sstore\";
(* Introduced in 0.4.12 *)(* Reserved in 0.4.12 *)YUL_STATIC_CALL_KEYWORD = \"staticcall\";
(* Reserved until 0.7.1 *)YUL_STATIC_KEYWORD = \"static\";
YUL_STOP_KEYWORD = \"stop\";
(* Reserved until 0.7.1 *)YUL_STORAGE_KEYWORD = \"storage\";
(* Reserved until 0.7.1 *)YUL_STRING_KEYWORD = \"string\";
(* Reserved until 0.7.1 *)YUL_STRUCT_KEYWORD = \"struct\";
YUL_SUB_KEYWORD = \"sub\";
(* Deprecated in 0.5.0 *)(* Reserved until 0.5.0 *)YUL_SUICIDE_KEYWORD = \"suicide\";
YUL_SUPER_KEYWORD = \"super\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_SUPPORTS_KEYWORD = \"supports\";
YUL_SWITCH_KEYWORD = \"switch\";
(* Reserved until 0.7.0 *)YUL_SZABO_KEYWORD = \"szabo\";
YUL_TIMESTAMP_KEYWORD = \"timestamp\";
YUL_THIS_KEYWORD = \"this\";
(* Reserved until 0.7.1 *)YUL_THROW_KEYWORD = \"throw\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_T_LOAD_KEYWORD = \"tload\";
YUL_TRUE_KEYWORD = \"true\";
(* Introduced in 0.8.24 *)(* Reserved in 0.8.25 *)YUL_T_STORE_KEYWORD = \"tstore\";
(* Reserved until 0.7.1 *)YUL_TRY_KEYWORD = \"try\";
(* Reserved from 0.5.0 until 0.7.1 *)YUL_TYPE_DEF_KEYWORD = \"typedef\";
(* Reserved until 0.7.1 *)YUL_TYPE_KEYWORD = \"type\";
(* Reserved until 0.7.1 *)YUL_TYPE_OF_KEYWORD = \"typeof\";
(* Reserved until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\";(* Reserved until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\") \"x\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\");(* Reserved until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\" (\"184x8\" | \"184x16\" | \"184x24\" | \"184x32\" | \"184x40\" | \"184x48\" | \"184x56\" | \"184x64\" | \"184x72\" | \"192x8\" | \"192x16\" | \"192x24\" | \"192x32\" | \"192x40\" | \"192x48\" | \"192x56\" | \"192x64\" | \"200x8\" | \"200x16\" | \"200x24\" | \"200x32\" | \"200x40\" | \"200x48\" | \"200x56\" | \"208x8\" | \"208x16\" | \"208x24\" | \"208x32\" | \"208x40\" | \"208x48\" | \"216x8\" | \"216x16\" | \"216x24\" | \"216x32\" | \"216x40\" | \"224x8\" | \"224x16\" | \"224x24\" | \"224x32\" | \"232x8\" | \"232x16\" | \"232x24\" | \"240x8\" | \"240x16\" | \"248x8\");(* Reserved from 0.4.14 until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\" (\"184x80\" | \"192x72\" | \"192x80\" | \"200x64\" | \"200x72\" | \"200x80\" | \"208x56\" | \"208x64\" | \"208x72\" | \"208x80\" | \"216x48\" | \"216x56\" | \"216x64\" | \"216x72\" | \"216x80\" | \"224x40\" | \"224x48\" | \"224x56\" | \"224x64\" | \"224x72\" | \"224x80\" | \"232x32\" | \"232x40\" | \"232x48\" | \"232x56\" | \"232x64\" | \"232x72\" | \"232x80\" | \"240x24\" | \"240x32\" | \"240x40\" | \"240x48\" | \"240x56\" | \"240x64\" | \"240x72\" | \"240x80\" | \"248x16\" | \"248x24\" | \"248x32\" | \"248x40\" | \"248x48\" | \"248x56\" | \"248x64\" | \"248x72\" | \"248x80\" | \"256x8\" | \"256x16\" | \"256x24\" | \"256x32\" | \"256x40\" | \"256x48\" | \"256x56\" | \"256x64\" | \"256x72\" | \"256x80\");(* Reserved from 0.4.14 until 0.7.1 *)YUL_UFIXED_KEYWORD = \"ufixed\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\") \"x\" (\"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"9\" | \"10\" | \"11\" | \"12\" | \"13\" | \"14\" | \"15\" | \"17\" | \"18\" | \"19\" | \"20\" | \"21\" | \"22\" | \"23\" | \"25\" | \"26\" | \"27\" | \"28\" | \"29\" | \"30\" | \"31\" | \"33\" | \"34\" | \"35\" | \"36\" | \"37\" | \"38\" | \"39\" | \"41\" | \"42\" | \"43\" | \"44\" | \"45\" | \"46\" | \"47\" | \"49\" | \"50\" | \"51\" | \"52\" | \"53\" | \"54\" | \"55\" | \"57\" | \"58\" | \"59\" | \"60\" | \"61\" | \"62\" | \"63\" | \"65\" | \"66\" | \"67\" | \"68\" | \"69\" | \"70\" | \"71\" | \"73\" | \"74\" | \"75\" | \"76\" | \"77\" | \"78\" | \"79\");
(* Reserved until 0.7.1 *)YUL_UINT_KEYWORD = \"uint\" (\"8\" | \"16\" | \"24\" | \"32\" | \"40\" | \"48\" | \"56\" | \"64\" | \"72\" | \"80\" | \"88\" | \"96\" | \"104\" | \"112\" | \"120\" | \"128\" | \"136\" | \"144\" | \"152\" | \"160\" | \"168\" | \"176\" | \"184\" | \"192\" | \"200\" | \"208\" | \"216\" | \"224\" | \"232\" | \"240\" | \"248\" | \"256\")?;
(* Reserved from 0.5.0 until 0.7.1 *)YUL_UNCHECKED_KEYWORD = \"unchecked\";
(* Reserved until 0.7.1 *)YUL_USING_KEYWORD = \"using\";
(* Reserved until 0.6.5 *)YUL_VAR_KEYWORD = \"var\";
(* Reserved until 0.7.1 *)YUL_VIEW_KEYWORD = \"view\";
(* Reserved from 0.6.0 until 0.7.1 *)YUL_VIRTUAL_KEYWORD = \"virtual\";
(* Reserved until 0.7.1 *)YUL_WEEKS_KEYWORD = \"weeks\";
(* Reserved until 0.7.1 *)YUL_WEI_KEYWORD = \"wei\";
(* Reserved until 0.7.1 *)YUL_WHILE_KEYWORD = \"while\";
(* Reserved until 0.7.1 *)YUL_YEARS_KEYWORD = \"years\";
YUL_XOR_KEYWORD = \"xor\";"},{"location":"solidity-specification/06-yul/03-yul-keywords/#documentation","title":"Documentation","text":"
Note
This section is under construction. You are more than welcome to contribute suggestions to our GitHub repository.
"},{"location":"user-guide/","title":"User Guide","text":"At its core, Slang is a collection of APIs that are meant to analyze the source code, starting with the source code itself and ending with a rich structure that can be reasoned about. This is a departure from the classic approach of \"black-box\" compilers, which are handed the input and only their output can be observed.
"},{"location":"user-guide/concepts/#language-versions","title":"Language Versions","text":"To use Slang, you start by initializing a Parser
object with a specific version of the language. The earliest Solidity version we support is 0.4.11
, and we plan on supporting all future versions as they are released.
From a Parser
object, you can analyze any source text according to the nonterminals of that specific version. Providing an accurate language version is important, as it affects the shape of the syntax tree, and possible errors produced. You can use the LanguageFacts::supportedVersions()
API to get a list of all supported versions for the current Slang release.
The Parser::parse()
API is the main entry point for the parser, and to generate concrete syntax trees (CSTs) that can be used for further analysis. Each parse()
operation accepts the input source code, and a NonterminalKind
variant. This allows callers to parse entire source files (NonterminalKind::SourceUnit
), individual contracts (NonterminalKind::ContractDefinition
), methods (NonterminalKind::FunctionDefinition
), or any other syntax nodes.
The resulting ParseOutput
object will contain syntax errors (if any), and the syntax tree corresponding to the input source code.
Slang is capable of parsing the source code into a Concrete Syntax Tree (CST; also sometimes called \"full-fidelity\"), which is a tree structure of the program that also includes things like punctuation or whitespace.
This is done by using the (standard) approach of lexical analysis followed by syntax analysis. The source text as a sequence of characters is recognized into a sequence of terminals (lexical analysis), which then in turn is parsed into the CST.
The resulting CST is a regular tree data structure that you can visit. The tree nodes are represented by the Node
structure, which can be one of two kinds:
NonterminalNode
represent sub-trees, containing a vector of other Node
children.TerminalNode
are leaves and represent a terminal (i.e. an identifier, keyword, punctuation) in the source.For many code analysis tasks, it is useful to traverse the parse tree and visit each node. The Cursor
object allows callers to traverse the parse tree in an efficient pre-order manner.
It provides several goTo*()
navigation functions, each returning true
if the cursor was successfully moved, and false
otherwise. There are three main ways to do it:
goToNext()
and goToPrevious()
,goToParent()
, goToFirstChild()
, goToNextNonDescendant()
goToNextTerminalWithKind(kind)
, goToNextNonterminalWithKind(kind)
As such, the cursor is stateful and keeps track of the path it has taken through the CST. It starts at the root it was created at and is completed when it reaches its root when navigating forward.
"},{"location":"user-guide/concepts/#cst-queries","title":"CST Queries","text":"The Cursor
API is a low-level API that allows you to traverse the CST in a procedural manner. However, it is often more convenient to use the declarative Query
API. Queries allow you to express your intent more concisely, and also allows you to reuse the same query in multiple places. Queries can largely replace the need for both internal (cursor), and external (visitor) iterator patterns.
The query language is based on pattern matching, and the execution semantics are closer to unification than to regular expression matching i.e. a query returns all possible matches, not just the longest/shortest/first/last match. There is no concept of a 'greedy' operator for example.
Query execution is based on Cursor
s, and the resulting matches and unification captures are returned as Cursor
s as well. This allows you to mix and match manual traversal, cursors, and queries.
Multiple queries can be executed in a batch, and efficiently traverse the tree looking for matches. This mode of operation can replace all visitor patterns.
"},{"location":"user-guide/concepts/#abstract-syntax-tree-ast","title":"Abstract Syntax Tree (AST)","text":"AST types are a set of abstractions that provide a typed view of the untyped CST nodes. You can convert any untyped CST node to its corresponding AST type using their constructors.
There is a corresponding type for each NonterminalKind
in the language. AST types are immutable. Additionally, their fields are constructed lazily as they are accessed for the first time.
AST nodes maintain a reference to the CST node they were constructed from, and can be used to navigate to the corresponding CST node.
"},{"location":"user-guide/introduction/","title":"Introduction","text":"Welcome to the Slang user guide! This aims to be an introduction to Slang itself, its concepts and also contains a collection of guides how you can achieve basic tasks with it.
"},{"location":"user-guide/introduction/#what-is-slang","title":"What is Slang?","text":"Slang is intended to be a modular Solidity compiler, specifically targeting code analysis and developer tooling. This means servicing tools with domain-specific APIs and, in general, facilitating working with and analyzing the Solidity source code. If you're in the editor writing Solidity or performing linting or additional validation, there's a chance that you are, or could be, running Slang!
To get a good grasp on the concepts used in Slang, see the Concepts section.
"},{"location":"user-guide/introduction/#what-slang-is-not","title":"What Slang is not?","text":"First and foremost, it is not a replacement for solc
, the standard Solidity compiler. We do not plan at the moment to support emitting optimized EVM bytecode for use in production. Secondly, it does not perform formal verification of contracts or Solidity logic in general. However, other tools that serve this purpose are intended to be built on top of it.
The Solidity programming language has evolved quite a bit since its inception. Some features were introduced, some changed, while some eventually became obsolete and were removed altogether.
While it's good for a programming language to evolve and better serve the needs of its users, not being able to easily upgrade or re-deploy existing contracts poses a unique challenge. Developer tooling must be able to understand and consume older contracts that are still being used on the blockchain, written in older versions of Solidity.
Because of that, Slang must be able to reason about different versions of Solidity; how the language grammar, name capture rules, and semantics have changed across different versions. One of our goals is to document differences as part of our Solidity Specification.
This is why, instead of having to download separate versions of the tool for each Solidity version, you can access the Slang language APIs by simply specifying the Solidity version that you want to work with.
"},{"location":"user-guide/introduction/#distributions","title":"Distributions","text":"Slang itself is written in Rust, compiled as a WASM component, and distributed as an npm package with a TypeScript interface. In the future, we are also looking into publishing it as a Rust crate, a Python library, and possibly more.
"},{"location":"user-guide/tree-query-language/","title":"The Tree Query Language","text":""},{"location":"user-guide/tree-query-language/#query-syntax","title":"Query Syntax","text":"A query is a pattern that matches a certain set of nodes in a tree. The expression to match a given node consists of a pair of brackets ([]
) containing two things: the node's kind, and optionally, a series of other patterns that match the node's children. For example, this pattern would match any MultiplicativeExpression
node that has two children Expression
nodes, with an Asterisk
node in between:
[MultiplicativeExpression [Expression] [Asterisk] [Expression]]\n
The children of a node can optionally be labeled. The label is a property of the edge from the node to the child, and is not a property of the child. For example, this pattern will match a MultiplicativeExpression
node with the two Expression
children labeled left_operand
and right_operand
:
[MultiplicativeExpression left_operand:[Expression] [Asterisk] right_operand:[Expression]]\n
You can also match a node's textual content using a string literal. For example, this pattern would match a MultiplicativeExpression
with a *
operator (for clarity):
[MultiplicativeExpression left_operand:[_] operator:[\"*\"] right_operand:[_]]\n
If you don't care about the kind of a node, you can use an underscore _
, which matches any kind. For example, this pattern will match a MultiplicativeExpression
node with two children, one of any kind labeled left_operand
and one of any kind:
[MultiplicativeExpression left_operand:[_] [_]]\n
Children can be elided. For example, this would produce multiple matches for a MultiplicativeExpression
where at least one of the children is an expression of a StringExpression
variant, where each match is associated with each of the StringExpression
children:
[MultiplicativeExpression [Expression [StringExpression]]]\n
Trivia nodes (whitespace, comments, etc.) will be skipped over when running a query. Furthermore, trivia nodes cannot be explicitly (or implicitly with _
) matched by queries.
When matching patterns, you may want to process specific nodes within the pattern. Captures allow you to associate names with specific nodes in a pattern, so that you can later refer to those nodes by those names. Capture names are written before the nodes that they refer to, and start with an @
character.
For example, this pattern would match any struct definition and it would associate the name struct_name
with the identifier:
[StructDefinition @struct_name name:[Identifier]]\n
And this pattern would match all event definitions for a contract, associating the name event_name
with the event name, contract_name
with the containing contract name:
[ContractDefinition\n @contract_name name:[Identifier]\n members:[ContractMembers\n [ContractMember\n [EventDefinition @event_name name:[Identifier]]\n ]\n ]\n]\n
"},{"location":"user-guide/tree-query-language/#quantification","title":"Quantification","text":"You can surround a sequence of patterns in parenthesis (()
), followed by a ?
, *
or +
operator. The ?
operator matches zero or one repetitions of a pattern, the *
operator matches zero or more, and the +
operator matches one or more.
For example, this pattern would match a sequence of one or more import directives at the top of the file:
[SourceUnit members:[_ ([_ @import [ImportDirective]])+]]\n
This pattern would match a structure definition with one or more members, capturing their names:
[StructDefinition\n @name name:[_]\n members:[_ ([_ @member [Identifier]])+]\n]\n
This pattern would match all function calls, capturing a string argument if one was present:
[FunctionCallExpression\n arguments:[ArgumentsDeclaration\n variant:[PositionalArgumentsDeclaration\n arguments:[PositionalArguments\n (@arg [Expression variant:[StringExpression]])?\n ]\n ]\n ]\n]\n
"},{"location":"user-guide/tree-query-language/#alternations","title":"Alternations","text":"An alternation is written as a sequence of patterns separated by |
and surrounded by parentheses.
For example, this pattern would match a call to either a variable or an object property. In the case of a variable, capture it as @function
, and in the case of a property, capture it as @method
:
[FunctionCallExpression\n operand:[Expression\n (@function variant:[Identifier]\n | @method variant:[MemberAccessExpression])\n ]\n]\n
This pattern would match a set of possible keyword terminals, capturing them as @keyword
:
@keyword (\n [\"break\"]\n | [\"delete\"]\n | [\"else\"]\n | [\"for\"]\n | [\"function\"]\n | [\"if\"]\n | [\"return\"]\n | [\"try\"]\n | [\"while\"]\n)\n
"},{"location":"user-guide/tree-query-language/#adjacency","title":"Adjacency","text":"By using the adjacency operator .
you can constrain a pattern to only match the first or the last child nodes.
For example, the following pattern would match only the first parameter declaration in a function definition:
[FunctionDefinition\n [ParametersDeclaration\n [Parameters . @first_param [Parameter]]\n ]\n]\n
And conversely the following will match only the last parameter:
[FunctionDefinition\n [ParametersDeclaration\n [Parameters @last_param [Parameter] .]\n ]\n]\n
If the adjacency operator is used in between two patterns it constrains matches on both patterns to occur consecutively, ie. without any other sibling node in between. For example, this pattern matches pairs of consecutive statements:
[Statements @stmt1 [Statement] . @stmt2 [Statement]]\n
"},{"location":"user-guide/npm-package/","title":"NPM Package","text":"You can install Slang NPM package simply by running the following npm
command:
npm install \"@nomicfoundation/slang\"\n
Or if you are using yarn
for package management:
yarn add \"@nomicfoundation/slang\"\n
"},{"location":"user-guide/npm-package/using-queries/","title":"Using Queries","text":"It's often more convenient to use the declarative Query
API to traverse the CST, as they allow you to express your intent more concisely and can largely replace the need for both internal (cursor), and external (visitor) iterator patterns.
The Tree Query Language is based on pattern matching, and the execution semantics are closer to unification than to regular expression matching. A query returns all possible matches, not just the longest/shortest/first/last match.
If not specified otherwise, let's assume we already parsed a Solidity source and have a cursor
pointing to the root node of the CST (created with createTreeCursor
, see Using the Cursor).
You can create a Query
object using Query.parse
, which accepts a string value. These can be then used by Cursor.query
to execute it.
You can pass multiple queries to a cursor to and efficiently traverse the tree looking for matches. They will be executed concurrently, returning matches in the order they appear in input.
// Any `Cursor` can be used to create a query.\nconst cursor = parseOutput.createTreeCursor();\n\nconst query = Query.parse(\"[ContractDefinition]\");\nconst matches: QueryMatchIterator = cursor.query([query]);\n
"},{"location":"user-guide/npm-package/using-queries/#iterating-over-node-patterns","title":"Iterating over node patterns","text":"Queries allow you to iterate over all node patterns that match the query, which can replace your need for manual iteration via cursors or visitors. In order to get a Cursor
that points to the matched node, you need to capture them with a name capture (@capture_name
) to a specific node in the query pattern.
Let's use this to list all the contract definitions in the source file:
input.solcontract Foo {}\ncontract Bar {}\ncontract Baz {}\n
const found = [];\n\nconst query = Query.parse(\"@contract [ContractDefinition]\");\nconst matches = cursor.query([query]);\n\nfor (const match of matches) {\n const cursor = match.captures[\"contract\"]![0]!;\n\n assertIsNonterminalNode(cursor.node);\n found.push(cursor.node.unparse().trim());\n}\n\nassert.deepStrictEqual(found, [\"contract Foo {}\", \"contract Bar {}\", \"contract Baz {}\"]);\n
"},{"location":"user-guide/npm-package/using-queries/#multiple-patterns-simultaneously","title":"Multiple patterns simultaneously","text":"We can also intersperse multiple patterns in a single query, which will return all the matches for each pattern. This can be useful when you want to match multiple types of nodes in a single pass.
const names = [];\n\nconst structDefinition = Query.parse(\"[StructDefinition @name [Identifier]]\");\nconst enumDefinition = Query.parse(\"[EnumDefinition @name [Identifier]]\");\nconst matches = cursor.query([structDefinition, enumDefinition]);\n\nfor (const match of matches) {\n const index = match.queryNumber;\n const cursor = match.captures[\"name\"]![0]!;\n\n names.push([index, cursor.node.unparse()]);\n}\n\nassert.deepStrictEqual(names, [\n [0, \"Foo\"],\n [1, \"Bar\"],\n [0, \"Baz\"],\n [1, \"Qux\"],\n]);\n
"},{"location":"user-guide/npm-package/using-queries/#matching-on-nodes-label","title":"Matching on node's label","text":"We can match not only on the node's kind, but also on its label. This can be useful if there may be two children with the same kind but different labels or to be more declarative.
To do so, we use [label: _]
syntax. Here, we also use _
to allow matching any kind of node, as long as it matches the given label.
contract Example {\n function foo() public {\n (uint a, uint16 b, uint64 c, uint256 d) = (1, 2, 3, 4);\n }\n}\n
const names = [];\n\nconst query = Query.parse(\"[TypedTupleMember @type type_name:[_]]\");\nconst matches = cursor.query([query]);\n\nfor (const match of matches) {\n const cursor = match.captures[\"type\"]![0]!;\n\n names.push(cursor.node.unparse());\n}\n\nassert.deepStrictEqual(names, [\"uint\", \" uint16\", \" uint64\", \" uint256\"]);\n
"},{"location":"user-guide/npm-package/using-queries/#matching-on-nodes-literal-content","title":"Matching on node's literal content","text":"Lastly, we can also match on the node's literal content. This can be useful when you want to match a specific identifier, string, or number.
Let's say we prefer our code to be explicit and prefer using uint256
instead of uint
. To find all instances of the uint
alias we could do the following:
contract Example {\n function foo() public {\n (uint a, uint16 b, uint64 c, uint256 d) = (1, 2, 3, 4);\n }\n}\n
const names = [];\n\nconst query = Query.parse(`[ElementaryType @uint_keyword variant:[\"uint\"]]`);\nconst matches = cursor.query([query]);\n\nfor (const match of matches) {\n const cursor = match.captures[\"uint_keyword\"]![0]!;\n\n names.push(cursor.node.unparse());\n}\n\nassert.deepStrictEqual(names, [\"uint\"]);\n
"},{"location":"user-guide/npm-package/using-queries/#example-finding-txorigin-patterns","title":"Example: Finding tx.origin
patterns","text":"As a more realistic example, let's say we want to write a linter that unconditionally lints against all tx.origin
accesses.
Let's use the motivating example from https://soliditylang.org:
input.sol// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.7.0 <0.9.0;\n// THIS CONTRACT CONTAINS A BUG - DO NOT USE\ncontract TxUserWallet {\n address owner;\n\n constructor() {\n owner = msg.sender;\n }\n\n function transferTo(address payable dest, uint amount) public {\n // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin\n require(tx.origin == owner);\n dest.transfer(amount);\n }\n}\n
Now, we can above features to write a query that matches all tx.origin
patterns:
const query = Query.parse(`\n@txorigin [MemberAccessExpression\n [Expression @start [\"tx\"]]\n [\"origin\"]\n]`);\n\nconst matches = cursor.query([query]);\nconst found = [];\n\nfor (const match of matches) {\n const cursor = match.captures[\"txorigin\"]![0]!;\n\n found.push([cursor.textOffset.utf8, cursor.node.unparse()]);\n}\n\nassert.deepStrictEqual(found, [[375, \"tx.origin\"]]);\n
"},{"location":"user-guide/npm-package/using-the-ast/","title":"Using the AST","text":"Let's try to analyze the following Solidity source file, containing a simple function:
input.solfunction add(uint256 a, uint256 b) public pure returns (uint256) {\n return a + b;\n}\n
We start as usual by parsing the input, and then we can use the ParseOutput
root to create the CST type. Since it is a node of kind FunctionDefinition
, we are using the AST type of the same name to analyze it:
import assert from \"node:assert\";\nimport { Parser } from \"@nomicfoundation/slang/parser\";\nimport { NonterminalKind } from \"@nomicfoundation/slang/cst\";\nimport { FunctionDefinition } from \"@nomicfoundation/slang/ast\";\n\nconst parser = Parser.create(\"0.8.0\");\n\nconst parseOutput = parser.parse(NonterminalKind.FunctionDefinition, source);\n
The FunctionDefinition
type has named fields to access all its children. For example, we can check the name of the function:
const $function = new FunctionDefinition(parseOutput.tree.asNonterminalNode()!);\n\nassert.equal($function.name.variant.unparse(), \"add\");\n
We can also list its parameters:
const parameters = $function.parameters.parameters.items.map((parameter) => {\n return parameter.name!.unparse();\n});\n\nassert.deepEqual(parameters, [\"a\", \"b\"]);\n
Or attributes:
const attributes = $function.attributes.items.map((attribute) => {\n return attribute.cst.unparse().trim();\n});\n\nassert.deepEqual(attributes, [\"public\", \"pure\"]);\n
"},{"location":"user-guide/npm-package/using-the-cursor/","title":"Using the Cursor","text":"This guide will walk you through the basics of using a CST cursor in your project. Let's start with this source file, that contains three contracts:
input.solcontract Foo {}\ncontract Bar {}\ncontract Baz {}\n
import assert from \"node:assert\";\nimport { Parser } from \"@nomicfoundation/slang/parser\";\nimport { assertIsTerminalNode, NonterminalKind, TerminalKind } from \"@nomicfoundation/slang/cst\";\n\nconst parser = Parser.create(\"0.8.0\");\n\nconst parseOutput = parser.parse(NonterminalKind.SourceUnit, source);\n
"},{"location":"user-guide/npm-package/using-the-cursor/#listing-contract-names","title":"Listing Contract Names","text":"The below example uses a cursor to list the names of all contracts in a source file:
const contracts = [];\n\nconst cursor = parseOutput.createTreeCursor();\n\nwhile (cursor.goToNextNonterminalWithKind(NonterminalKind.ContractDefinition)) {\n assert(cursor.goToFirstChild());\n assert(cursor.goToNextTerminalWithKind(TerminalKind.Identifier));\n\n assertIsTerminalNode(cursor.node);\n contracts.push(cursor.node.unparse());\n\n assert(cursor.goToParent());\n}\n\nassert.deepStrictEqual(contracts, [\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/npm-package/using-the-cursor/#visiting-only-a-sub-tree","title":"Visiting Only a Sub-tree","text":"In cases like the above, we needed to visit a sub-tree of the CST (to get the contract name). But we also need to remember to return the cursor to its original position after each read, which is inconvenient, and can lead to subtle bugs.
To avoid this, we can use the spawn()
API, which cheaply creates a new cursor that starts at the given node, without copying the previous path history. This lets us visit the sub-tree of each contract, without modifying the original cursor:
const contracts = [];\n\nconst cursor = parseOutput.createTreeCursor();\n\nwhile (cursor.goToNextNonterminalWithKind(NonterminalKind.ContractDefinition)) {\n const childCursor = cursor.spawn();\n assert(childCursor.goToNextTerminalWithKind(TerminalKind.Identifier));\n\n assertIsTerminalNode(childCursor.node);\n contracts.push(childCursor.node.unparse());\n}\n\nassert.deepStrictEqual(contracts, [\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/npm-package/using-the-cursor/#accessing-node-positions","title":"Accessing Node Positions","text":"The Cursor
API also tracks the position and range of the current node it is visiting. Here is an example that records the source range of each contract, along with its text:
const contracts = [];\n\nconst cursor = parseOutput.createTreeCursor();\n\nwhile (cursor.goToNextNonterminalWithKind(NonterminalKind.ContractDefinition)) {\n const range = cursor.textRange;\n\n const contractNode = cursor.node;\n\n contracts.push([\n range.start.line,\n range.start.column,\n range.end.line,\n range.end.column,\n contractNode.unparse().trim(),\n ]);\n}\n\nassert.deepStrictEqual(contracts, [\n [0, 0, 1, 0, \"contract Foo {}\"],\n [1, 0, 2, 0, \"contract Bar {}\"],\n [2, 0, 2, 15, \"contract Baz {}\"],\n]);\n
"},{"location":"user-guide/npm-package/using-the-parser/","title":"Using the Parser","text":"Using the API directly provides us with a more fine-grained control over the parsing process. It allows us to parse not just the input as a top-level source unit, but also individual constructs like contracts, various definitions, and even expressions.
"},{"location":"user-guide/npm-package/using-the-parser/#parsing-source-files","title":"Parsing Source Files","text":"Let's start with this simple source file, that contains a single contract:
input.solcontract Foo {}\n
We begin by creating a Parser
object with a specified version. This is an entry point for our parser API. Then we can use it to parse the source file, specifying the top-level nonterminal to parse:
import assert from \"node:assert\";\nimport { Parser } from \"@nomicfoundation/slang/parser\";\nimport {\n assertIsNonterminalNode,\n assertIsTerminalNode,\n NonterminalKind,\n TerminalKind,\n} from \"@nomicfoundation/slang/cst\";\n\nconst parser = Parser.create(\"0.8.0\");\n\nconst parseOutput = parser.parse(NonterminalKind.ContractDefinition, source);\n
"},{"location":"user-guide/npm-package/using-the-parser/#checking-for-syntax-errors","title":"Checking for Syntax Errors","text":"If the file has errors, we can get them from the ParseOutput
type, and print them out:
for (const error of parseOutput.errors()) {\n console.error(`Error at byte offset ${error.textRange.start.utf8}: ${error.message}`);\n}\n
Otherwise, we can check if input is valid using this helpful utility:
assert(parseOutput.isValid());\n
"},{"location":"user-guide/npm-package/using-the-parser/#inspecting-the-parse-tree","title":"Inspecting the Parse Tree","text":"Now, let's try to inspect the resulting CST, and iterate on its children:
const contract = parseOutput.tree;\nassertIsNonterminalNode(contract, NonterminalKind.ContractDefinition);\n\nconst contractChildren = contract.children();\nassert.equal(contractChildren.length, 7);\n\nconst [contractKeyword, firstSpace, contractName, secondSpace, openBrace, members, closeBrace] = contractChildren;\n\nassertIsTerminalNode(contractKeyword!.node, TerminalKind.ContractKeyword, \"contract\");\nassertIsTerminalNode(firstSpace!.node, TerminalKind.Whitespace, \" \");\nassertIsTerminalNode(contractName!.node, TerminalKind.Identifier, \"Foo\");\nassertIsTerminalNode(secondSpace!.node, TerminalKind.Whitespace, \" \");\nassertIsTerminalNode(openBrace!.node, TerminalKind.OpenBrace, \"{\");\nassertIsNonterminalNode(members!.node, NonterminalKind.ContractMembers);\nassertIsTerminalNode(closeBrace!.node, TerminalKind.CloseBrace, \"}\");\n
Additionally, we can convert the CST node back into the input string:
const contractSource = contract.unparse();\nassert.equal(contractSource, \"contract Foo {}\");\n
"},{"location":"user-guide/rust-crate/","title":"Rust Crate","text":"The Rust package is published to crates.io as slang_solidity
(docs). It can be used both as a regular Rust dependency and as a standalone CLI (installable with Cargo).
You can install the CLI as a cargo binary using:
cargo install \"slang_solidity_cli\"\n
Or you can add the API as a dependency to your project:
cargo add \"slang_solidity\"\n
"},{"location":"user-guide/rust-crate/using-queries/","title":"Using Queries","text":"It's often more convenient to use the declarative Query
API to traverse the CST, as they allow you to express your intent more concisely and can largely replace the need for both internal (cursor), and external (visitor) iterator patterns.
The query language is based on pattern matching, and the execution semantics are closer to unification than to regular expression matching. A query returns all possible matches, not just the longest/shortest/first/last match.
If not specified otherwise, let's assume we already parsed a Solidity source and have a cursor
pointing to the root node of the CST (created with create_tree_cursor
, see Using the Cursor).
You can create a Query
struct using Query::parse
, which accepts a &str
. These can be then used by Cursor::query
to execute it.
You can pass multiple queries to a cursor to and efficiently traverse the tree looking for matches. They will be executed concurrently, returning matches in the order they appear in input.
use slang_solidity::cst::Query;\n\n// Any `Cursor` can be used to create a query.\nlet cursor = parse_output.create_tree_cursor();\n\nlet query = Query::parse(\"[ContractDefinition]\").unwrap();\nlet result: QueryMatchIterator = cursor.query(vec![query]);\n
"},{"location":"user-guide/rust-crate/using-queries/#iterating-over-node-patterns","title":"Iterating over node patterns","text":"Queries allow you to iterate over all node patterns that match the query, which can replace your need for manual iteration via cursors or visitors. In order to get a Cursor
that points to the matched node, you need to capture them with a name capture (@capture_name
) to a specific node in the query pattern.
Let's use this to list all the contract definitions in the source file:
input.solcontract Foo {}\ncontract Bar {}\ncontract Baz {}\n
let mut found = vec![];\n\nlet query = Query::parse(\"@contract [ContractDefinition]\").unwrap();\n\nfor r#match in cursor.query(vec![query]) {\n let captures = r#match.captures;\n let cursors = captures.get(\"contract\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n found.push(cursor.node().unparse().trim().to_owned());\n}\n\nassert_eq!(\n found,\n [\"contract Foo {}\", \"contract Bar {}\", \"contract Baz {}\"]\n);\n
"},{"location":"user-guide/rust-crate/using-queries/#multiple-patterns-simultaneously","title":"Multiple patterns simultaneously","text":"We can also intersperse multiple patterns in a single query, which will return all the matches for each pattern. This can be useful when you want to match multiple types of nodes in a single pass.
let mut names = vec![];\n\nlet struct_def = Query::parse(\"[StructDefinition @name [Identifier]]\").unwrap();\nlet enum_def = Query::parse(\"[EnumDefinition @name [Identifier]]\").unwrap();\n\nfor r#match in cursor.query(vec![struct_def, enum_def]) {\n let index = r#match.query_number;\n let captures = r#match.captures;\n let cursors = captures.get(\"name\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n names.push((index, cursor.node().unparse()));\n}\n\nassert_eq!(\n names,\n &[\n (0, \"Foo\".to_string()),\n (1, \"Bar\".to_string()),\n (0, \"Baz\".to_string()),\n (1, \"Qux\".to_string())\n ]\n);\n
"},{"location":"user-guide/rust-crate/using-queries/#matching-on-nodes-label","title":"Matching on node's label","text":"We can match not only on the node's kind, but also on its label. This can be useful if there may be two children with the same kind but different labels or to be more declarative.
To do so, we use [label: _]
syntax. Here, we also use _
to allow matching any kind of node, as long as it matches the given label.
contract Example {\n function foo() public {\n (uint a, uint16 b, uint64 c, uint256 d) = (1, 2, 3, 4);\n }\n}\n
let mut names = vec![];\n\nlet query = Query::parse(\"[TypedTupleMember @type type_name:[_]]\").unwrap();\n\nfor r#match in cursor.query(vec![query]) {\n let captures = r#match.captures;\n let cursors = captures.get(\"type\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n names.push(cursor.node().unparse());\n}\n\nassert_eq!(names, &[\"uint\", \" uint16\", \" uint64\", \" uint256\"]);\n
"},{"location":"user-guide/rust-crate/using-queries/#matching-on-nodes-literal-content","title":"Matching on node's literal content","text":"Lastly, we can also match on the node's literal content. This can be useful when you want to match a specific identifier, string, or number.
Let's say we prefer our code to be explicit and prefer using uint256
instead of uint
. To find all instances of the uint
alias we could do the following:
contract Example {\n function foo() public {\n (uint a, uint16 b, uint64 c, uint256 d) = (1, 2, 3, 4);\n }\n}\n
let mut names = vec![];\n\nlet query = Query::parse(r#\"[ElementaryType @uint_keyword variant:[\"uint\"]]\"#).unwrap();\n\nfor r#match in cursor.query(vec![query]) {\n let captures = r#match.captures;\n let cursors = captures.get(\"uint_keyword\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n names.push(cursor.node().unparse());\n}\n\nassert_eq!(names, &[\"uint\"]);\n
"},{"location":"user-guide/rust-crate/using-queries/#example-finding-txorigin-patterns","title":"Example: Finding tx.origin
patterns","text":"As a more realistic example, let's say we want to write a linter that unconditionally lints against all tx.origin
accesses.
Let's use the motivating example from https://soliditylang.org:
input.sol// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.7.0 <0.9.0;\n// THIS CONTRACT CONTAINS A BUG - DO NOT USE\ncontract TxUserWallet {\n address owner;\n\n constructor() {\n owner = msg.sender;\n }\n\n function transferTo(address payable dest, uint amount) public {\n // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin\n require(tx.origin == owner);\n dest.transfer(amount);\n }\n}\n
Now, we can above features to write a query that matches all tx.origin
patterns:
let query = Query::parse(\n r#\"@txorigin [MemberAccessExpression\n [Expression @start [\"tx\"]]\n [\"origin\"]\n ]\"#,\n)\n.unwrap();\n\nlet mut results = vec![];\n\nfor r#match in cursor.query(vec![query]) {\n let captures = r#match.captures;\n let cursors = captures.get(\"txorigin\").unwrap();\n\n let cursor = cursors.first().unwrap();\n\n results.push((cursor.text_offset().utf8, cursor.node().unparse()));\n}\n\nassert_eq!(results, &[(375usize, \"tx.origin\".to_string())]);\n
"},{"location":"user-guide/rust-crate/using-the-cli/","title":"Using the CLI","text":""},{"location":"user-guide/rust-crate/using-the-cli/#parsing-source-files","title":"Parsing Source Files","text":"The parse
command will take a path to a Solidity file, and a --version
flag. Specifying the correct version is important, as it will affect the grammar used to parse inputs.
All parse errors are printed in a human-readable format; the command will succeed if there are no parse errors, and fail otherwise.
$ slang_solidity parse --help\n\nUsage: slang_solidity parse [OPTIONS] --version <VERSION> <FILE_PATH>\n\nArguments:\n <FILE_PATH>\n File path to the Solidity (*.sol) source file to parse\n\nOptions:\n -v, --version <VERSION>\n The Solidity language version to use for parsing\n --json\n Print the concrete syntax tree as JSON\n -h, --help\n Print help\n
Here is an example of the JSON output it can print:
// A Nonterminal node\n\"Nonterminal\": {\n // Name of the nonterminal kind\n \"kind\": \"SourceUnit\",\n // Length of the nonterminal in Unicode code points, depending on the encoding used\n \"text_len\": {\n \"utf8\": 24,\n \"utf16\": 24,\n \"char\": 24 // de facto utf32\n },\n \"children\": [/* Nonterminal or Terminal nodes */]\n}\n// A Terminal node\n\"Terminal\": {\n // Name of the terminal kind\n \"kind\": \"PragmaKeyword\",\n // Literal value, taken from the source code\n \"text\": \"pragma\"\n}\n
"},{"location":"user-guide/rust-crate/using-the-cli/#inspecting-json-output","title":"Inspecting JSON Output","text":"Now let's try to use that command to parse the following Solidity file, and inspect its contents:
input.solpragma solidity ^0.8.0;\n
slang_solidity parse --json --version \"0.8.0\" \"input.sol\" > \"output.json\"\n
Because the resulting structure is well-defined and recursive, we can use the popular jq
tool to quickly analyze the resulting output:
JQ_QUERY='recurse | select(.Terminal?) | .Terminal'\ncat output.json | jq \"$JQ_QUERY\"\n
This gives us a flat list of the Terminal nodes:
{\n \"kind\": \"PragmaKeyword\",\n \"text\": \"pragma\"\n}\n{\n \"kind\": \"Whitespace\",\n \"text\": \" \"\n}\n{\n \"kind\": \"SolidityKeyword\",\n \"text\": \"solidity\"\n}\n{\n \"kind\": \"Whitespace\",\n \"text\": \" \"\n}\n{\n \"kind\": \"Caret\",\n \"text\": \"^\"\n}\n{\n \"kind\": \"VersionPragmaValue\",\n \"text\": \"0\"\n}\n{\n \"kind\": \"Period\",\n \"text\": \".\"\n}\n{\n \"kind\": \"VersionPragmaValue\",\n \"text\": \"8\"\n}\n{\n \"kind\": \"Period\",\n \"text\": \".\"\n}\n{\n \"kind\": \"VersionPragmaValue\",\n \"text\": \"0\"\n}\n{\n \"kind\": \"Semicolon\",\n \"text\": \";\"\n}\n{\n \"kind\": \"EndOfLine\",\n \"text\": \"\\n\"\n}\n
Now, we can adapt the query to select the text
fields of the nodes and concatenate them, which gives us back the reconstructed source code! \ud83c\udf89
$ JQ_QUERY='[recurse | select(.Terminal?) | .Terminal.text] | join(\"\")'\n$ cat output.json | jq \"$JQ_QUERY\"\n\n\"pragma solidity ^0.8.0;\\n\"\n
"},{"location":"user-guide/rust-crate/using-the-cursor/","title":"Using the Cursor","text":"This guide will walk you through the basics of using a CST cursor in your project. Let's start with this source file, that contains three contracts:
input.solcontract Foo {}\ncontract Bar {}\ncontract Baz {}\n
use semver::Version;\nuse slang_solidity::cst::{EdgeLabel, NonterminalKind, TerminalKind, TextRangeExtensions};\nuse slang_solidity::parser::Parser;\n\nlet parser = Parser::create(Version::parse(\"0.8.0\")?)?;\n\nlet parse_output = parser.parse(NonterminalKind::SourceUnit, source);\n
"},{"location":"user-guide/rust-crate/using-the-cursor/#listing-contract-names","title":"Listing Contract Names","text":"The below example uses a cursor to list the names of all contracts in a source file:
let mut contracts = Vec::new();\n\nlet mut cursor = parse_output.create_tree_cursor();\n\nwhile cursor.go_to_next_nonterminal_with_kind(NonterminalKind::ContractDefinition) {\n assert!(cursor.go_to_first_child());\n assert!(cursor.go_to_next_terminal_with_kind(TerminalKind::Identifier));\n\n let terminal_node = cursor.node();\n contracts.push(terminal_node.as_terminal().unwrap().text.clone());\n\n // You have to make sure you return the cursor to its original position:\n assert!(cursor.go_to_parent());\n}\n\nassert_eq!(contracts, &[\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/rust-crate/using-the-cursor/#visiting-only-a-sub-tree","title":"Visiting Only a Sub-tree","text":"In cases like the above, we needed to visit a sub-tree of the CST (to get the contract name). But we also need to remember to return the cursor to its original position after each read, which is inconvenient, and can lead to subtle bugs.
To avoid this, we can use the spawn()
API, which cheaply creates a new cursor that starts at the given node, without copying the previous path history. This lets us visit the sub-tree of each contract, without modifying the original cursor:
let mut contracts = Vec::new();\n\nlet mut cursor = parse_output.create_tree_cursor();\n\nwhile cursor.go_to_next_nonterminal_with_kind(NonterminalKind::ContractDefinition) {\n let mut child_cursor = cursor.spawn();\n assert!(child_cursor.go_to_next_terminal_with_kind(TerminalKind::Identifier));\n\n let terminal_node = child_cursor.node();\n contracts.push(terminal_node.as_terminal().unwrap().text.clone());\n}\n\nassert_eq!(contracts, &[\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/rust-crate/using-the-cursor/#accessing-node-positions","title":"Accessing Node Positions","text":"The Cursor
API also tracks the position and range of the current node it is visiting. Here is an example that records the source range of each contract, along with its text:
let mut contracts = Vec::new();\n\nlet mut cursor = parse_output.create_tree_cursor();\n\nwhile cursor.go_to_next_nonterminal_with_kind(NonterminalKind::ContractDefinition) {\n let range = cursor.text_range().utf8();\n let text = cursor.node().unparse();\n\n contracts.push((range, text.trim().to_owned()));\n}\n\nassert_eq!(\n contracts,\n &[\n (0..16, \"contract Foo {}\".to_string()),\n (16..32, \"contract Bar {}\".to_string()),\n (32..47, \"contract Baz {}\".to_string()),\n ]\n);\n
"},{"location":"user-guide/rust-crate/using-the-cursor/#using-iterator-api","title":"Using Iterator API","text":"In addition to the procedural-style methods, the Cursor
struct also implements the Iterator
trait, which allows you to use it in a functional style.
Let's use that to extract all Identifier
nodes from the source text using that API:
let identifiers: Vec<_> = Rc::clone(parse_output.tree())\n .descendants()\n .filter(|edge| edge.is_terminal_with_kind(TerminalKind::Identifier))\n .map(|identifier| identifier.unparse())\n .collect();\n\nassert_eq!(identifiers, &[\"Foo\", \"Bar\", \"Baz\"]);\n
Note
It's important to note that Iterator::next
first visits the current node, yields it, and then moves the cursor to the next node. As such, accessor associated functions called on the Cursor
that reference the \"current\" will point to the one that is not yet yielded by the iterator. This might be an important, when mixing the two styles.
The cursor also keeps track of the labels of the nodes it visits. Let's use that to extract all nodes that are labeled Name
:
let identifiers: Vec<_> = Rc::clone(parse_output.tree())\n .descendants()\n .filter(|edge| edge.label == Some(EdgeLabel::Name))\n .filter(|edge| edge.is_terminal_with_kind(TerminalKind::Identifier))\n .map(|identifier| identifier.unparse())\n .collect();\n\nassert_eq!(identifiers, &[\"Foo\", \"Bar\", \"Baz\"]);\n
"},{"location":"user-guide/rust-crate/using-the-parser/","title":"Using the Parser","text":"Using the API directly provides us with a more fine-grained control over the parsing process. It allows us to parse not just the input as a top-level source unit, but also individual nonterminals like contracts, various definitions, and even expressions.
"},{"location":"user-guide/rust-crate/using-the-parser/#parsing-source-files","title":"Parsing Source Files","text":"Let's start with this simple source file, that contains a single contract:
input.solcontract Foo {}\n
We begin by creating a Parser
object with a specified version. This is an entry point for our parser API. Then we can use it to parse the source file, specifying the top-level nonterminal to parse:
use semver::Version;\nuse slang_solidity::cst::{Node, NonterminalKind, TerminalKind};\nuse slang_solidity::parser::Parser;\n\nlet parser = Parser::create(Version::parse(\"0.8.0\")?)?;\n\nlet parse_output = parser.parse(NonterminalKind::ContractDefinition, source);\n
"},{"location":"user-guide/rust-crate/using-the-parser/#checking-for-syntax-errors","title":"Checking for Syntax Errors","text":"If the file has errors, we can get them from the ParseOutput
type, and print them out:
for error in parse_output.errors() {\n eprintln!(\n \"Error at byte offset {offset}: {message}\",\n offset = error.text_range().start.utf8,\n message = error.message()\n );\n}\n
Otherwise, we can check if input is valid using this helpful utility:
assert!(parse_output.is_valid());\n
"},{"location":"user-guide/rust-crate/using-the-parser/#inspecting-the-parse-tree","title":"Inspecting the Parse Tree","text":"Now, let's try to inspect the resulting CST, and iterate on its children:
let contract = parse_output.tree();\n\nassert_eq!(contract.kind, NonterminalKind::ContractDefinition);\nassert_eq!(contract.children.len(), 7);\n\nlet children = &contract.children;\nassert!(\n matches!(&children[0].node, Node::Terminal(t) if t.kind == TerminalKind::ContractKeyword)\n);\nassert!(matches!(&children[1].node, Node::Terminal(t) if t.kind == TerminalKind::Whitespace));\nassert!(matches!(&children[2].node, Node::Terminal(t) if t.kind == TerminalKind::Identifier));\nassert!(matches!(&children[3].node, Node::Terminal(t) if t.kind == TerminalKind::Whitespace));\nassert!(matches!(&children[4].node, Node::Terminal(t) if t.kind == TerminalKind::OpenBrace));\nassert!(\n matches!(&children[5].node, Node::Nonterminal(r) if r.kind == NonterminalKind::ContractMembers)\n);\nassert!(matches!(&children[6].node, Node::Terminal(t) if t.kind == TerminalKind::CloseBrace));\n
Additionally, we can convert the CST node back into the input string:
assert_eq!(contract.unparse(), \"contract Foo {}\");\n
"}]}
\ No newline at end of file
diff --git a/main/sitemap.xml b/main/sitemap.xml
index 61329827c7..5e3f747caa 100644
--- a/main/sitemap.xml
+++ b/main/sitemap.xml
@@ -2,266 +2,266 @@
In addition to the procedural-style methods, the Cursor
struct also implements the Iterator
trait, which allows you to use it in a functional style.
Let's use that to extract all Identifier
nodes from the source text using that API:
let identifiers: Vec<_> = parse_output
- .tree()
- .clone()
- .descendants()
- .filter(|edge| edge.is_terminal_with_kind(TerminalKind::Identifier))
- .map(|identifier| identifier.unparse())
- .collect();
-
-assert_eq!(identifiers, &["Foo", "Bar", "Baz"]);
-
Note
It's important to note that Iterator::next
first visits the current node, yields it, and then moves the cursor to the next node. As such, accessor associated functions called on the Cursor
that reference the "current" will point to the one that is not yet yielded by the iterator. This might be an important, when mixing the two styles.
The cursor also keeps track of the labels of the nodes it visits. Let's use that to extract all nodes that are labeled Name
:
let identifiers: Vec<_> = parse_output
- .tree()
- .clone()
- .descendants()
- .filter(|edge| edge.label == Some(EdgeLabel::Name))
- .filter(|edge| edge.is_terminal_with_kind(TerminalKind::Identifier))
- .map(|identifier| identifier.unparse())
- .collect();
-
-assert_eq!(identifiers, &["Foo", "Bar", "Baz"]);
+
In addition to the procedural-style methods, the Cursor
struct also implements the Iterator
trait, which allows you to use it in a functional style.
Let's use that to extract all Identifier
nodes from the source text using that API:
let identifiers: Vec<_> = Rc::clone(parse_output.tree())
+ .descendants()
+ .filter(|edge| edge.is_terminal_with_kind(TerminalKind::Identifier))
+ .map(|identifier| identifier.unparse())
+ .collect();
+
+assert_eq!(identifiers, &["Foo", "Bar", "Baz"]);
+
Note
It's important to note that Iterator::next
first visits the current node, yields it, and then moves the cursor to the next node. As such, accessor associated functions called on the Cursor
that reference the "current" will point to the one that is not yet yielded by the iterator. This might be an important, when mixing the two styles.
The cursor also keeps track of the labels of the nodes it visits. Let's use that to extract all nodes that are labeled Name
:
let identifiers: Vec<_> = Rc::clone(parse_output.tree())
+ .descendants()
+ .filter(|edge| edge.label == Some(EdgeLabel::Name))
+ .filter(|edge| edge.is_terminal_with_kind(TerminalKind::Identifier))
+ .map(|identifier| identifier.unparse())
+ .collect();
+
+assert_eq!(identifiers, &["Foo", "Bar", "Baz"]);