Miscellaneous¶
Layout of State Variables in Storage¶
静的サイズの変数(マッピングと動的サイズの配列以外のもの)はストレージ内で 0
から始めて隣接して置かれます。複数の隣接した32バイト未満のものは可能であれば下記のルールに則り、1つのストレージスロットに詰め込まれます。
- ストレージスロットの最初の要素は低い桁の方にアラインされて保存されます
- 基本型は保存するのに必要な分だけのバイトを使います。
- 基本型がストレージのスロットの余っている部分に入りきらなかった場合は、次のストレージスロットに移動します。
- 構造体と配列データは常に新しいスロットで始まり、全てのスロットを占有します(ただし、構造体もしくは配列の中身の要素はこのルールに従ってタイトに保存されます)。
継承を使用するコントラクトに関しては、一番基本的なコントラクトで始まるC3線形化されたコントラクトの順番で状態変数の順序は決まります。上記のルール内であれば、異なるコントラクトからの状態変数でも同じストレージスロットを共有できます。
警告
32バイト未満の要素を使用すると、コントラクトのガス消費量が高くなるかもしれません。これはEVMが一度に32バイトで動作するため、もし要素が32バイト未満だと、そのエレメントを32バイトから理想的なサイズに減らすために、EVMは演算をしなければならなくなるからです。
ストレージの値を使っている時のみ、縮小したサイズの引数を使うメリットがあります。それはコンパイラが複数の要素をパックして1つのストレージスロットに入れ、複数の読み込みと書き出しをくっつけて1つの演算で行うからです。もしファンクションの引数やメモリの値を使うのであれば、コンパイラがそれらの値をパックしないので、メリットはありません。
最後に、EVMのその機能を最適化させるために、ストレージ変数と
struct
の要素の順番をきっちりとパックされるように整理して下さい。例えば、ストレージ変数の宣言をuint128、uint256、uint128
ではなく、uint128、uint128、uint256
とすると、前者では3スロット使いますが、後者では2スロットだけ使います。
注釈
ストレージのポインタはライブラリに渡される可能性があるので、ストレージ内の状態変数の設計はSolidityの外部インターフェースの一部と考えてください。 つまり、このセクションで並べらrたルールの変更は言語のブレーキングチェンジと考えてください。そして、この重要性から、実行する前にしっかりと設計を考えてください。
構造体の要素と配列は明示的に与えられたかの様にお互いの後に保存されます。
Mappings and Dynamic Arrays¶
サイズが予想できないため、マッピングと動的サイズ配列はKeccak-256のハッシュ演算を値や配列のデータの始めの位置を見つけるのに使います。この始めの位置は常にフルスタックのスロットです。
マッピングもしくは動的配列自体はストレージ内のある位置 p
のスロットを上記のルール(マッピングのマッピングや配列の配列には再帰的にそのルールを適用します)に従って占有します。動的配列に関しては、その配列内の要素の数をそのスロットに保存します(バイト配列と文字列は例外です。詳しくは 下記 を参照してください)。
マッピングに関しては、スロットは使われません(しかし、二つの同じマッピングが異なるハッシュを使うので必要です)。配列のデータは keccak256(p)
に入り、マッピングのキー k
に対応する値は keccak256(k . p)
に入ります。.
は連結を意味します。もし値が再び非基本型の場合は keccak256(k . p)
のオフセットを追加することによってその場所を見つけることができます。
下記がコントラクトの例です:
pragma solidity >=0.4.0 <0.6.0;
contract C {
struct s { uint a; uint b; }
uint x;
mapping(uint => mapping(uint => s)) data;
}
data[4][9].b
の場所は keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1
となります。
bytes
and string
¶
bytes
と string
は完全に同じようにエンコードされます。小さいバイト配列に関しては、長さとデータを同じスロットに保存します。特にデータが最大 31
バイトの場合、高い桁のバイト(左にアラインされたもの)側で保存され、一番小さい桁のバイトは length * 2` を保存します。
32
バイト以上のデータを保存している配列の場合、メインスロットは length * 2 + 1
を保存し、データは通常通り keccak256(slot)
で保存されます。つまり、短い配列と長い配列を一番下位のビットを調べることにより識別することができます。短いのはビットがセットされておらず、長いのはされています。
注釈
不正にエンコードされたスロットの操作は現在サポートされていませんが、将来的には追加されるかもしれません。
Layout in Memory¶
Solidityは4つの32バイトスロットを持ち、それは下記の様に使われる特定のバイトのレンジ(エンドポイントを含む)を持っています:
0x00
-0x3f
(64 bytes): ハッシュメソッド用のスクラッチスペース0x40
-0x5f
(32 bytes): 現在割り当てられているメモリのサイズ(フリーメモリポインタともいう)0x60
-0x7f
(32 bytes): ゼロスロット
スクラッチスペースは宣言の間で使えます(具体的にはインラインアセンブリ内)。ゼロスロットは動的メモリ配列の初期値に使われ、書き込みはされないべきです(フリーメモリポインタは最初に 0x80
を指しています)。
Solidityは常に新しいオブジェクトをフリーメモリポインタに置き、メモリはフリーになりません(将来的にはこれは変わるかもしれません)。
警告
Solidityでは64バイトより大きい一時メモリエリアが必要になる演算があるため、それはスクラッチスペースにはフィットしません。その演算はフリーメモリポインタが指し示した場所に置かれますが、その短いライフタイムを考えるとポインタはアップデートされません。 メモリはゼロになるかもしれないし、ならないかもしれません。 これらの理由によりフリーメモリがゼロになったメモリを指し示すと考えないほうが良いでしょう。
完全にゼロにされたメモリエリアに達するために msize
を使うのは良いアイデアと思えますが、フリーメモリポインタをアップデートしないで非一時的にその様なポインタを使うと、逆の結果となる可能性があります。
Layout of Call Data¶
ファンクションを呼び出すのに入力したデータは ABI specification で定義されたフォーマットになっていると考えられます。他のものでは、ABI specificationは引数を32バイトの倍数にパディングする様に要求しています。内部のファンクションコールでは異なった慣例を用いています。
コントラクトのコンストラクタ用の引数は直接コントラクトのコードの最後に追加されます。ABIエンコーディングでも同様です。コンストラクタはハードコードされたオフセットを通じて引数にアクセスします。データをコードに追加した時に変わってしまうので、codesize
opcodeは使わずにアクセスします。
Internals - Cleaning Up Variables¶
値が256ビットより短い場合、いくつかのケースでは余ったビットはクリア必要があります。 Solidityのコンパイラは、残ったビットにある不要なデータに影響を受けるかもしれない演算前にその様な残ったビットをクリアします。 例えば、メモリに値を書き込む前に、残ったビットはクリアされる必要があります。なぜなら、メモリの内容がハッシュの計算に使われたり、メッセージコールのデータとして送られる可能性があるためです。 同様によく分からない値があるかもしれないので、ストレージに値を保存する前に、残ったビットはクリアされる必要があります。
一方で、続く演算が影響をすぐに及ぼさないのであれば、ビットをクリアしません。例えば、JUMPI
命令ではゼロでない値は true
とされるので、JUMPI
用の条件として使われる前にはbooleanの値はクリアしません。
上記の設計原理に加えて、Solidityのコンパイラはデータがスタックの追加されたらそのデータをクリアします。
不正な値を削除するルールは型によって異なります。
Type | Valid Values | Invalid Values Mean |
---|---|---|
enum of n members | 0 until n - 1 | exception |
bool | 0 or 1 | 1 |
signed integers | sign-extended word | currently silently wraps; in the future exceptions will be thrown |
unsigned integers | higher bits zeroed | currently silently wraps; in the future exceptions will be thrown |
Internals - The Optimiser¶
Solidityのオプティマイザはアセンブリ上で動作するので、他の言語でも使えます。オプティマイザは命令のシーケンスを JUMPs
と JUMPDESTs
で基本的なブロックに分けます。このブロックの内側では、オプティマイザが命令を解析し、スタック、メモリ、もしくはストレージの変更を他の式を指し示している引数のリストと命令で構成されている式として記録しています。オプティマイザは"CommonSubexpressionEliminator"というコンポーネントを使用しています。それは他のタスクの中で、同じもの(入力に対して)ものを見つけて、それを1つの式クラスにまとめます。オプティマイザはまず最初に既知の式のリストから新しい式を見つけようとします。もしこれがうまく行かなかった場合、constant + constant = sum_of_constants
もしくは X * 1 = X
の様なルールに従い、式を簡略化します。これは再帰的な処理なので、もし2つ目の因数が最終的に1になる複雑な式だった場合、後者のルールが適用できます。ストレージとメモリの位置の修正は、異なると分かっていないストレージとメモリの位置の情報を消さなければいけません。もし最初に位置xに変数を書き込みし、その後に位置yに変数を書き込みをした場合、後者が前者を上書きしてしまう可能性があります。この場合yに書き込みした後はxに何が保存されているか分かりません。式x-yが0でない値だった場合、xに何が保存されているか分かります。
この処理の後、どの式が最後にスタックにのるか分かりますし、メモリとストレージの修正のリストも保持します。この情報は基本ブロックと一緒に保存され、それらをリンクさせるのに使われます。さらに、スタック、ストレージ、メモリの設定の情報は次のブロックに転送されます。もし全ての JUMP
と JUMPI
命令のターゲットを知っていれば、プログラムの完全な制御フローの図式を作ることができます。もし1つだけ分からないターゲットがあった場合(原理的にはありえます。jumpのターゲットは入力から計算されます)、未知の JUMP
のターゲットになりうるので、ブロックの入力ステートに関する全ての情報を削除する必要があります。オプティマイザが条件が定数になる JUMPI
を見つけた場合、無条件のjumpに変換します。
最終ステップとして、それぞれのブロックのコードは再生成されます。オプティマイザはブロックの最後で、スタック上の式から依存関係の図式を生成します。そしてこの図式に入っていない演算は省略します。オプティマイザはメモリとストレージの変更をオリジナルのコードで作られた順番で適用します(必要ない変更は省略します)。最後に、スタック上の正しい位置に置かれる必要がある全ての値を生成します。
これらのステップは基本ブロックに適用され、新しく生成されたコードはもしオリジナルより小さければ差し替えられます。もし基本ブロックが JUMPI
で分割され、分析中に条件文が定数と評価された場合、JUMPI
はその定数の値によって置換されます。
uint x = 7;
data[7] = 9;
if (data[x] != x + 2)
return 2;
else
return 1;
処理のはじめに命令がjumpを含んでいても、コンパイルできるコードに簡略化します。
data[7] = 9;
return 1;
Source Mappings¶
ASTアウトプットの一部として、コンパイラはASTの各ノードによって表されるソースコードの領域を提供します。これは色々な目的に使えます。例えば、ASTに基づいたエラーをレポートする静的解析ツールや、ローカル変数や用途をハイライトするデバッグツールです。
さらに、コンパイラはバイトコードから命令を生成するソースコード内の領域のマッピングも生成します。これはバイトコードレベルで動く静的解析ツールにとって重要です。デバッガー内でソースコードのどこに位置しているか表示するもしくはブレークポイントの操作にとっても重要です。
いずれのソースマッピングもソースファイルを参照するために整数識別子を使っています。
ソースファイルの識別子は output['sources'][sourceName]['id']
に保存されています。ここで、output
はJSONにパースされたstandard-jsonコンパイラインターフェースの出力です。
注釈
どのソースファイルにも関連していない命令の場合、ソースマッピングは -1
という整数識別子を割り当てます。これはコンパイラが生成したインラインアセンブリの宣言に起因するバイトコードセクションで発生する可能性があります。
AST内のソースマッピングは以下の注記を使います:
s:l:f
s
はソースファイル内の領域の始めに対するバイトオフセットで、l
はバイトでソース領域の長さを表し、f
は上記で言及したソースインデックスです。
バイトコード用のソースマッピング内のエンコードはもっと複雑です。
;
で分けられた s:l:f:j
のリストです。それぞれの要素は1つの命令に対応しており、つまり、バイトオフセットは使えませんが、命令のオフセットは使わなければいけないということです(命令のプッシュは1バイトより大きいです)。
s
、l
、f
フィールドは上記の通りで、j
は
i
、o
もしくは -
となります。それぞれ意味するのはjump命令が、ファンクションに入るかどうか、ファンクションから返ってくるかどうか、例えばループの一部としての標準的なjumpかどうか、です。
それらのソースマッピング、特にバイトコード用の、を圧縮するために、次のルールが使用されます。
- もしフィールドが空であれば、前の要素の値が使われます。
- もし、
:
がなかった場合、それに続く全てのフィールドは空であるとされます。
つまり、次のソースマッピングは同じものを表しています。
1:2:1;1:9:1;2:1:2;2:1:2;2:1:2
1:2:1;:9;2:1:2;;
Tips and Tricks¶
- 全ての要素を削除するには
delete
を使用して下さい。 - 構造体の要素としてより小さい型を使って、その型が一纏めになるようにソートして下さい。これは複数の
SSTORE
演算を1つの演算にまとめられる可能性があるので、ガスコストを低くすることができます(SSTORE
は5000もしくは20000ガスします。これが最適化です)。オプティマイザを使った状態でgas price estimatorを使ってチェックしてください! - 状態変数はpublicにしておいてください。コンパイラが自動的に getters を作ります。
- もしファンクションのはじめに入力やステートに対してチェックをたくさんかけているのであれば、Function Modifiers を使ってみて下さい。
- ストレージの構造体は1回の割り当てで初期化してください:
x = MyStruct({a: 1, b: 2});
注釈
もしストレージの構造体がプロパティで詰まっているなら、別々の割り当てで初期化して下さい: x.a = 1; x.b = 2;
。この方法の方がオプティマイザにとってストレージのアップデートが1回で済むため、値の割り当てが安くなります。
Cheatsheet¶
Order of Precedence of Operators¶
下記は演算子に対する命令の優先順位です。評価順にリスト化されています。
Precedence | Description | Operator |
---|---|---|
1 | Postfix increment and decrement | ++ , -- |
New expression | new <typename> |
|
Array subscripting | <array>[<index>] |
|
Member access | <object>.<member> |
|
Function-like call | <func>(<args...>) |
|
Parentheses | (<statement>) |
|
2 | Prefix increment and decrement | ++ , -- |
Unary minus | - |
|
Unary operations | delete |
|
Logical NOT | ! |
|
Bitwise NOT | ~ |
|
3 | Exponentiation | ** |
4 | Multiplication, division and modulo | * , / , % |
5 | Addition and subtraction | + , - |
6 | Bitwise shift operators | << , >> |
7 | Bitwise AND | & |
8 | Bitwise XOR | ^ |
9 | Bitwise OR | | |
10 | Inequality operators | < , > , <= , >= |
11 | Equality operators | == , != |
12 | Logical AND | && |
13 | Logical OR | || |
14 | Ternary operator | <conditional> ? <if-true> : <if-false> |
15 | Assignment operators | = , |= , ^= , &= , <<= ,
>>= , += , -= , *= , /= ,
%= |
16 | Comma operator | , |
Global Variables¶
abi.decode(bytes memory encodedData, (...)) returns (...)
: 与えられたデータを ABI-デコードします。カッコ内で2つ目の引数として型が与えられます。例:(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))
abi.encode(...) returns (bytes memory)
: 与えられた引数を ABI-エンコードしますabi.encodePacked(...) returns (bytes memory)
: 与えられた引数の packed encoding をします。abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)
: 与えられた引数の2つ目からを ABI-エンコードし、4バイトのセレクタを前につけますabi.encodeWithSignature(string memory signature, ...) returns (bytes memory)
:abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)
と同じですblock.coinbase
(address payable
): 現在のブロックのマイナーのアドレスblock.difficulty
(uint
): 現在のブロックのディフィカルティblock.gaslimit
(uint
): 現在のブロックのガスリミットblock.number
(uint
): 現在のブロックナンバーblock.timestamp
(uint
): 現在のブロックのタイムスタンプgasleft() returns (uint256)
: 残っているガスmsg.data
(bytes
): calldata全部msg.sender
(address payable
): 現在呼ばれているメッセージの送信者msg.value
(uint
): メッセージと一緒に送られたwei量now
(uint
): 現在のタイムスタンプ (block.timestamp
のエイリアス)tx.gasprice
(uint
): トランザクションガスプライスtx.origin
(address payable
): トランザクションの送信者(フルコールチェーン)assert(bool condition)
: もし条件文がfalse
の場合、実行を止め、状態変更を元に戻します (内部エラーに使う)require(bool condition)
: もし条件文がfalse
の場合、実行を止め、状態変更を元に戻します (不正な形式の入力や外部コンポーネントのエラーに使う)require(bool condition, string memory message)
: もし条件文がfalse
の場合、実行を止め、状態変更を元に戻します (不正な形式の入力や外部コンポーネントのエラーに使う)。エラーメッセージも出しますrevert()
: 実行を止め、状態変更を元に戻しますrevert(string memory message)
: 説明文を出し、実行を止め、状態変更を元に戻しますblockhash(uint blockNumber) returns (bytes32)
: 与えられたブロックのハッシュを返します(直帰256ブロックのみ有効です)keccak256(bytes memory) returns (bytes32)
: 入力のKeccak-256ハッシュを計算しますsha256(bytes memory) returns (bytes32)
: 入力のSHA-256ハッシュを計算しますripemd160(bytes memory) returns (bytes20)
: 入力のRIPEMD-160ハッシュを計算しますecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
: 楕円曲線の署名から公開鍵に関連したアドレスを復元する、もしくはエラーでゼロを返します。addmod(uint x, uint y, uint k) returns (uint)
: 任意の精度で(x + y) % k
の加算を行い、2**256
でラップアラウンドしません。バージョン0.5.0からはk != 0
のアサーションを行いますmulmod(uint x, uint y, uint k) returns (uint)
: 任意の精度で(x * y) % k
の加算を行い、2**256
でラップアラウンドしません。バージョン0.5.0からはk != 0
のアサーションを行いますthis
(current contract's type): 現在のコントラクト、明示的にaddress
もしくはaddress payable
に変換可能ですsuper
: 継承の階層で1つ上のコントラクトselfdestruct(address payable recipient)
: 現在のコントラクトを破壊し、与えられたaddressにファンドを送ります。<address>.balance
(uint256
): Address のバランス(Wei)<address payable>.send(uint256 amount) returns (bool)
: 与えられたWeiを Address に送ります。失敗するとfalse
を返します<address payable>.transfer(uint256 amount)
: 与えられたWeiを Address に送ります。失敗するとエラーを投げますtype(C).creationCode
(bytes memory
):
コントラクトの生成バイトコード。詳細は Type Information を参照ください
- type(C).runtimeCode
(bytes memory
):
コントラクトのランタイムバイトコード。詳細は Type Information をご覧ください
自分のコードで何をしているか把握していない限り、block.timestamp
、now
と blockhash
を乱数のソースとして信用しないでください。
タイムスタンプとブロックハッシュはある程度マイナーによって影響されます。悪意を持ったマイナーは例えばあるハッシュでカジノの支払いファンクションを呼び出し、もしお金を受け取れなかったら、また別のハッシュでそのファンクションを呼び出すことができます。
現在のブロックのタイムスタンプは最後のブロックより確実に大きい必要がありますが、保証されているのはタイムスタンプは2つの連続する標準ブロックの間であるということだけです。
注釈
ブロックハッシュはスケーラビリティの観点から全てのブロックに使うことはできません。 直近256ブロックのみアクセス可能で、他の値は全て0になります。
注釈
バージョン0.5.0では下記のエイリアスが廃止になりました。
suicide
: selfdestruct
のエイリアス
msg.gas
: gasleft
のエイリアス
block.blockhash
: blockhash
のエイリアス
sha3
: keccak256
のエイリアス
Function Visibility Specifiers¶
function myFunction() <visibility specifier> returns (bool) {
return true;
}
public
: 外部からも内部からもアクセス可能です(ストレージ/状態変数の getter function を生成します)private
: 現在のコントラクト内でのみ使えますexternal
: 外部からのみ使用可能です(ファンクションのみ)。つまりメッセージコールのみされます(this.func
を通じて)internal
: 内部でのみ使用可能です
Modifiers¶
pure
for functions: ステートの修正やアクセスができませんview
for functions: ステートの修正ができませんpayable
for functions: コールと一緒にEtherが受け取れますconstant
for state variables: 値の割り当てができません(初期化時を除く)。ストレージスロットは使いませんanonymous
for events: topicとしてイベントの署名は保存しませんindexed
for event parameters: topicとしてパラメータを保存します
Reserved Keywords¶
下記のキーワードはSolidityの予約語です。将来的にはシンタックスの一部となる可能性があります。
abstract
, after
, alias
, apply
, auto
, case
, catch
, copyof
, default
,
define
, final
, immutable
, implements
, in
, inline
, let
, macro
, match
,
mutable
, null
, of
, override
, partial
, promise
, reference
, relocatable
,
sealed
, sizeof
, static
, supports
, switch
, try
, typedef
, typeof
,
unchecked
.
Language Grammar¶
SourceUnit = (PragmaDirective | ImportDirective | ContractDefinition)*
// Pragma actually parses anything up to the trailing ';' to be fully forward-compatible.
PragmaDirective = 'pragma' Identifier ([^;]+) ';'
ImportDirective = 'import' StringLiteral ('as' Identifier)? ';'
| 'import' ('*' | Identifier) ('as' Identifier)? 'from' StringLiteral ';'
| 'import' '{' Identifier ('as' Identifier)? ( ',' Identifier ('as' Identifier)? )* '}' 'from' StringLiteral ';'
ContractDefinition = ( 'contract' | 'library' | 'interface' ) Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}'
ContractPart = StateVariableDeclaration | UsingForDeclaration
| StructDefinition | ModifierDefinition | FunctionDefinition | EventDefinition | EnumDefinition
InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* ')' )?
StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' )* Identifier ('=' Expression)? ';'
UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';'
StructDefinition = 'struct' Identifier '{'
( VariableDeclaration ';' (VariableDeclaration ';')* ) '}'
ModifierDefinition = 'modifier' Identifier ParameterList? Block
ModifierInvocation = Identifier ( '(' ExpressionList? ')' )?
FunctionDefinition = 'function' Identifier? ParameterList
( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' )*
( 'returns' ParameterList )? ( ';' | Block )
EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';'
EnumValue = Identifier
EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}'
ParameterList = '(' ( Parameter (',' Parameter)* )? ')'
Parameter = TypeName StorageLocation? Identifier?
EventParameterList = '(' ( EventParameter (',' EventParameter )* )? ')'
EventParameter = TypeName 'indexed'? Identifier?
FunctionTypeParameterList = '(' ( FunctionTypeParameter (',' FunctionTypeParameter )* )? ')'
FunctionTypeParameter = TypeName StorageLocation?
// semantic restriction: mappings and structs (recursively) containing mappings
// are not allowed in argument lists
VariableDeclaration = TypeName StorageLocation? Identifier
TypeName = ElementaryTypeName
| UserDefinedTypeName
| Mapping
| ArrayTypeName
| FunctionTypeName
| ( 'address' 'payable' )
UserDefinedTypeName = Identifier ( '.' Identifier )*
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
ArrayTypeName = TypeName '[' Expression? ']'
FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )*
( 'returns' FunctionTypeParameterList )?
StorageLocation = 'memory' | 'storage' | 'calldata'
StateMutability = 'pure' | 'view' | 'payable'
Block = '{' Statement* '}'
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
( DoWhileStatement | PlaceholderStatement | Continue | Break | Return |
Throw | EmitStatement | SimpleStatement ) ';'
ExpressionStatement = Expression
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
WhileStatement = 'while' '(' Expression ')' Statement
PlaceholderStatement = '_'
SimpleStatement = VariableDefinition | ExpressionStatement
ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
InlineAssemblyStatement = 'assembly' StringLiteral? AssemblyBlock
DoWhileStatement = 'do' Statement 'while' '(' Expression ')'
Continue = 'continue'
Break = 'break'
Return = 'return' Expression?
Throw = 'throw'
EmitStatement = 'emit' FunctionCall
VariableDefinition = (VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )?
// Precedence by order (see github.com/ethereum/solidity/pull/732)
Expression
= Expression ('++' | '--')
| NewExpression
| IndexAccess
| MemberAccess
| FunctionCall
| '(' Expression ')'
| ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression
| Expression '**' Expression
| Expression ('*' | '/' | '%') Expression
| Expression ('+' | '-') Expression
| Expression ('<<' | '>>') Expression
| Expression '&' Expression
| Expression '^' Expression
| Expression '|' Expression
| Expression ('<' | '>' | '<=' | '>=') Expression
| Expression ('==' | '!=') Expression
| Expression '&&' Expression
| Expression '||' Expression
| Expression '?' Expression ':' Expression
| Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression
| PrimaryExpression
PrimaryExpression = BooleanLiteral
| NumberLiteral
| HexLiteral
| StringLiteral
| TupleExpression
| Identifier
| ElementaryTypeNameExpression
ExpressionList = Expression ( ',' Expression )*
NameValueList = Identifier ':' Expression ( ',' Identifier ':' Expression )*
FunctionCall = Expression '(' FunctionCallArguments ')'
FunctionCallArguments = '{' NameValueList? '}'
| ExpressionList?
NewExpression = 'new' TypeName
MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expression? ']'
BooleanLiteral = 'true' | 'false'
NumberLiteral = ( HexNumber | DecimalNumber ) (' ' NumberUnit)?
NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether'
| 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years'
HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]*
HexNumber = '0x' [0-9a-fA-F]+
DecimalNumber = [0-9]+ ( '.' [0-9]* )? ( [eE] [0-9]+ )?
TupleExpression = '(' ( Expression? ( ',' Expression? )* )? ')'
| '[' ( Expression ( ',' Expression )* )? ']'
ElementaryTypeNameExpression = ElementaryTypeName
ElementaryTypeName = 'address' | 'bool' | 'string' | Int | Uint | Byte | Fixed | Ufixed
Int = 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256'
Uint = 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256'
Byte = 'byte' | 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32'
Fixed = 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ )
Ufixed = 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ )
AssemblyBlock = '{' AssemblyStatement* '}'
AssemblyStatement = AssemblyBlock
| AssemblyFunctionDefinition
| AssemblyVariableDeclaration
| AssemblyAssignment
| AssemblyIf
| AssemblyExpression
| AssemblySwitch
| AssemblyForLoop
| AssemblyBreakContinue
AssemblyFunctionDefinition =
'function' Identifier '(' AssemblyIdentifierList? ')'
( '->' AssemblyIdentifierList )? AssemblyBlock
AssemblyVariableDeclaration = 'let' AssemblyIdentifierList ( ':=' AssemblyExpression )?
AssemblyAssignment = AssemblyIdentifierList ':=' AssemblyExpression
AssemblyExpression = AssemblyFunctionCall | Identifier | Literal
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
AssemblySwitch = 'switch' AssemblyExpression ( Case+ AssemblyDefault? | AssemblyDefault )
AssemblyCase = 'case' Literal AssemblyBlock
AssemblyDefault = 'default' AssemblyBlock
AssemblyForLoop = 'for' AssemblyBlock AssemblyExpression AssemblyBlock AssemblyBlock
AssemblyBreakContinue = 'break' | 'continue'
AssemblyFunctionCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')'
AssemblyIdentifierList = Identifier ( ',' Identifier )*