Michelson:Tezosのスマートコントラクト言語
目次
マイケルソン:Tezosのスマート契約の言語[編集]
言語はスタックベースで、高レベルのデータ型とプリミティブと厳格な静的型チェックがあります。そのデザインチェリーは、いくつかの言語科の特徴を選んでいます。注意深い読者は、Forth、Scheme、ML、Catへの直接参照に気付くでしょう。
マイケルソンプログラムは、順番に実行される一連の命令です。各命令は、前の命令の結果のスタックを入力として受け取り、次の命令のために書き換えます。スタックには、即値とヒープ割り当て構造の両方が含まれます。すべての値は不変で、ガーベジコレクションされます。
マイケルソンプログラムは、入力値と記憶空間の内容とを含む単一の要素スタックを入力として受け取る。出力値と格納領域の新しい内容を含む単一の要素スタックを返す必要があります。あるいは、マイケルソンプログラムは、特定のオペコードを明示的に使用して失敗したり、タイプシステムによって捕らえられなかった何かが間違ってしまったりすることがあります(ゼロ、ガス枯渇など)。
入力、出力、および記憶のタイプは固定であり、単相性であり、プログラムはシステムに導入される前にタイプチェックされる。予期しない長さや内容のスタック上で命令が実行されたため、スマートコントラクトの実行に失敗することはありません。
この仕様は、言語の完全な命令セット、タイプシステム、およびセマンティクスを示す。簡単な紹介ではなく、正確なリファレンスマニュアルを意味します。しかし、いくつかの例は文書の終わりに提供され、最初に、または仕様書と同時に読むことができます。
目次[編集]
- I - セマンティクス
- II - タイプシステム
- III - コアデータ型
- IV - コア命令
- V - 操作
- VI - ドメイン固有のデータ型
- VII - ドメイン固有の操作
- VIII - マクロ
- IX - 具体的な構文
- X - JSON構文
- XI - 例
- XII - 完全な文法
- XIII - リファレンス実装
I - セマンティクス[編集]
この仕様は、マイケルソン語の詳細な形式的意味論を提供する。与えられたプログラムと初期スタックでマイケルソンインタプリタによって実行された計算をシンボリックな方法で説明し、対応する結果スタックを生成する。 Michelsonインタプリタは、純粋な関数です。環境に影響を与えることなく、最初の要素から結果スタックを作成するだけです。このセマンティクスは、大きなステップフォームと呼ばれるもので自然に与えられます。これは、再帰的参照インタープリタのシンボリックな定義です。この定義は、インタプリタ(プログラムおよびスタック)のすべての可能な入力をカバーする規則のリストの形をとり、対応する結果のスタックの計算を記述する。
ルールの形式と選択 ? / sub>
ルールには、主に次のような形式があります。
> (構文パターン)/(初期スタックパターン)=> (結果スタックパターン) iff(条件) ここで(再帰) と(より多くの再帰)
=>
記号の左側がルールの選択に使用されます。プログラムと最初のスタックが与えられた場合、次のプロセスを使用して1つのルールのみを選択できます。まず、プログラムの最上位構造が構文パターンと一致していなければなりません。これは、命令シーケンスを処理するためのほんの些細なパターンが少なくて済むので、非常に単純であり、残りは、特定の1つの命令に一致する簡単なパターンから成り立っています。その後、最初のスタックは最初のスタックパターンと一致しなければなりません。最後に、 iff
キーワードに続くスタックの値に余分な条件を追加するルールもあります。時には、いくつかのルールが特定のコンテキストで適用されることがあります。この場合、この仕様の最初に現れるものが選択されます。ルールが適用されない場合、結果は明示的な FAIL
命令の結果と同等です。この場合は、次のセクションで説明するように、型付きのプログラムでは発生しません。
右側には、ルールが適用される場合のインタープリタの結果が記述されています。スタックパターンは、 =>
記号の左側に名前が付けられた定数(定数)とコンテキストの要素(プログラムと初期スタック)のいずれかです。
再帰的規則(大きなステップ形式) sub>? >
場合によっては、プログラムを解釈した結果が、別のプログラムを解釈した結果(条件呼び出しや関数呼び出しのように)から得られることもあります。このような場合、ルールには次の形式の句が含まれます。
ここで(中間プログラム)/(中間スタック)=> (部分的な結果)
これは、左の中間状態を解釈する場合にこのルールが適用されることを意味し、右のパターンを与える。
=>
記号の左側の記号は、初期状態の要素または他の部分的な結果から構成され、右側はルールの結果スタックを構築するために使用できる部分を示します。
部分的な結果パターンが解釈の結果と実際には一致しない場合、ルール全体の結果は明示的な FAIL
命令の結果と同等です。この場合も、次のセクションで説明するように、型付きのプログラムでは発生しません。
パターンの形式? ? ~~
コードパターンは、次の構文形式のいずれかです。
-
INSTR
(大文字の識別子)は単純な命令です(例:DROP
)。 -
INSTR(arg)...
は複合命令で、その引数はコード、データ、または型パターン(例:PUSH nat 3
)です。 -
{(instr); (例えば<code> IF {SWAP; DROP} {DROP}
)、ネストされたシーケンスは中カッコを削除することができます。 -
name
は、任意のプログラムと一致し、結果を構築するために使用できる一致したプログラムの一部を指定するパターンです。 -
_
は、どの命令にも一致するパターンです。
スタックパターンは、次の構文形式のいずれかです。
-
[FAIL]
は特別な失敗状態です。 -
[]
は空のスタックです。 -
(top):(rest)
は、左上のデータパターン(top)
によって一番上の要素がマッチし、残りの要素がスタックによってマッチしたスタックです右側の(rest)
パターン(x:y:rest
など)。 -
name
は、任意のスタックにマッチし、結果をビルドするためにスタックに名前を付けるパターンです。 -
_
は、どのスタックにも一致するパターンです。
データパターンは、次の構文形式のいずれかです。
- 整数/自然数リテラル(例:
3
); - 文字列リテラル(例:
"contents"
); -
タグ
、False
);タグ
は大文字の定数です(例:Unit
-
(Tag(arg)...)
タグ付き構築データ(例:(Pair 3 4)
); - 第1クラスのコード値のコードパターン。
-
name
を使用して値を命名し、結果を構築します。 -
_
を使用して任意の値に一致させることができます。
命令名、記号定数、およびデータコンストラクタの領域は、この仕様で修正されています。マイケルソンは、プログラマが独自の型を導入することはできません。
仕様で使用されている構文は、第IX節で示されている具体的な構文と少し異なる場合があることに注意してください。特に、いくつかの命令には、タイプチェッカーによって合成されるため、具体的な言語には存在しないタイプの注釈が付けられます。
ショートカット?? ?
時には、大きなステップのセマンティクスよりもプログラム書き換えの観点から考えるのが簡単です(書き込む方が簡単です)。それが当てはまり、両方が等価である場合は、次の形式のルールを記述します。
p / S =&gt; S '' ここで、p '/ S' =&gt; S ''
次のショートカットを使用します。
p / S =&gt; p '/ S'
具体的な言語には、いくつかの一般的な操作シーケンスを1つにまとめるシンタックス・シュガーがあります。これは、単純な正規表現スタイルの再帰的命令書き換えを使用して、この仕様書で説明されています。
II - 型システムと表記法の紹介[編集]
この仕様は、マイケルソンのタイプシステムについて説明します。特に正式なプログラミング言語仕様の読解に慣れていない読者にとっては、型チェックや推論のアルゴリズムを提供していません。それは、型付きの良いプログラムであると考えるものを意図的に定義するだけです。構文形式ごとに、型付きの良い入力と見なされるスタックとその結果の出力が記述されます。
型システムは健全です。つまり、プログラムに型を与えることができれば、適切に型付けされた入力スタック上で実行すると、予期しない長さや内容のスタックに解釈規則を適用することはありません。また、実行を継続するための適切なルールを選択できない状態にはなりません。よくタイプされたプログラムはブロックされず、間違っていません。
タイプ記法 ? ?
この仕様では、値、用語、スタックの種類の表記を紹介しています。言語全体のいくつかの場所で型注釈の形式で表示される値型のサブセットを除いて、この型言語は仕様にのみ存在することを理解することが重要です。
スタックタイプを記述することができます:
- 空のスタックの[
[]
;
最初の値のタイプが(top)
で、キューのスタックタイプが(rest)
のスタックの* (top):( rest)
言語の命令、プログラムおよびプリミティブもタイプされ、そのタイプは以下のように書かれる:
(前にスタックのタイプ) - &gt; (後のスタックの種類)
スタック内の値の型は次のようになります。
プリミティブデータ型(例: bool
)の* 識別子
(arg)
( list nat
)などのパラメータタイプを持つパラメトリックデータ型の場合、 identifier(arg)
いくつかのパラメータ(例: map string int
)を持つパラメトリックデータ型の* identifier(arg)...
-
[(前のスタックのタイプ) - &gt;コード引用(例:<code> [int:int:[] - > int:[]]
)の場合は、 -
lambda(arg)(ret)
は、[(arg):[] - &gt; (ret):[]]
。
メタタイプ変数 ? ??
型定義規則は、メタ型変数を導入する。明らかにするために、これはマイケルソンにはない多型性とは関係がありません。これらの変数は、仕様レベルでのみ存在し、プログラムの各部分間の一貫性を表現するために使用されます。たとえば、 IF
構文の型定義ルールは、両方のブランチが同じ型を持たなければならないことを表すメタ変数を導入します。
メタタイプ変数の表記法は次のとおりです。
- 型変数の
'
スタック型変数の場合は '
-
_
は、匿名型またはスタック型変数です。
入力ルール ? ?
システムはシンタックスディレクティブです。ここでは、シンタックスコンストラクトごとに1つのタイピングルールが定義されています。入力規則は、この構文構文に対して承認された入力スタックのタイプを制限し、出力タイプを入力タイプにリンクし、必要に応じてメタタイプ変数を使用してそれらの両方をサブ式にリンクします。
入力規則の形式は次のとおりです。
(構文パターン) ::(前にスタックのタイプ) - &gt; (スタックの種類)[ルール名] iff(敷地内)
(x)
という形式の(type)
という形式は、コード>(タイプ)</code>
プログラムはトップレベルプログラム式に適用されるルールのインスタンスを見つけることができ、すべてのメタタイプ変数が非変数タイプ式に置き換えられ、そのタイプのすべてのタイプ要件が十分に証明されている場合、同じ方法でタイプされます。正式型システムに精通していない読者にとっては、これを型定義派生と呼びます。
(x + 5)* 10
を計算する小さなプログラム上での派生型の例を次に示します。 x
PUSH </code>、 ADD
、およびシーケンスについては、次のセクションを参照してください。インスタンス化する際、 iff
を by
に置き換えます。
{プッシュナット5; ADD;プッシュナット10; SWAP; MUL} :: [nat:[] - &gt; nat:[]] ?によって{PUSH nat 5;追加} :: [nat:[] - &gt; nat:[]] プッシュナット5 :: [nat:[] - &gt; nat:nat:[]] 5 :: nat ADD :: [nat:nat:[] - &gt; nat:[]] および{プッシュナット10; SWAP; MUL} :: [nat:[] - &gt; nat:[]] プッシュナット10 :: [nat:[] - &gt; nat:nat:[]] 10 :: natで と{SWAP; MUL} :: [nat:nat:[] - &gt; nat:[]] スワップ :: [nat:nat:[] - &gt; nat:nat:[]] とMUL :: [nat:nat:[] - &gt; nat:[]]
このようなタイピングの導出を生成することは、統一または抽象的解釈のような多くの方法で行うことができる。マイケルソンの実装では、プログラマが提供する入力タイプを表す抽象スタック上でプログラムの再帰的な記号評価を実行し、結果のシンボリックスタックがプログラマによって提供される期待された結果。
注釈 ? ??
言語のほとんどの命令は、オプションでアノテーションを取ることができます。アノテーションを使用すると、データ、スタック、ペア、およびユニオン内のデータをより正確に追跡できます。
あるタイプのコンポーネントに追加された場合、注釈は型チェッカーのアクセス指示に従って型チェッカーによって伝播されます。
スタックに値を生成する命令に注釈を付けると、その型のトップレベルに注釈が書き換えられます。 値を生成しない命令に注釈を付けると、型チェックエラーが発生します。
プログラムの IF
、 IF_LEFT
、 IF_CONS
、 IF_NONE
、 LOOP
>)、注釈は互換性がなければなりません。注釈は、両方の要素が同じ注釈で注釈付けされている場合、または値/型の少なくとも1つに注釈が付けられていない場合に互換性があります。
プログラムの各タイプに関連付けられているマイケルソンのEmacsモードの印刷注釈のようなスタックビジュアライゼーションツールは、型チェッカーによって伝播されます。これはデバッグの助けとして役立ちます。
サイドノート?? ?
ほとんどの型システムと同様、不完全です。この型のシステムに型を与えることができないプログラムがありますが、それが実行されると間違ってはいけません。これは、型システムを使用可能にするために必要な妥協点です。また、マイケルソンの実装では、タイプ・システムが記述するタイプのプログラムほど多くのプログラムを受け入れないことを覚えておくことが重要です。これは、実装が単純なシングルパス型検査アルゴリズムを使用しており、多態性の形式を処理しないためです。
III - コアのデータ型と表記法[編集]
-
string
、nat
、int
:コアプリミティブ定数型。 -
True
とFalse
のブール値の型は、 -
unit
:結果やパラメータが不要な場合にプレースホルダとして使用するUnit
の型だけです。たとえば、契約の唯一の目的はストレージを更新することです。 -
(t)
の要素を持ち、{}
と書かれた単一で不変な同質のリンクリスト。 >空リストまたは{first; ...}
。セマンティクスでは、シェブロンを使用して要素のサブシーケンスを示します。たとえば、{head; &lt; tail&gt; }
-
(l)
型のa
とb
のペアと、(r)
、(Pair ab)
と書いてあります。 -
None
または(Some v)
に注意する(t)
。 -
(l)
の値a
を保持する値と、(左a)
または(右b)
という(r)
型のb
-
set(t)
:(t)
型の値の不変集合を{item; ...}
、要素は一意でソートされています。 -
(t)
型の値の(k)
型のキーからの不変のマップは、コード> {Eltキー値; ...} </code>、キーはソートされています。 -
(k)(t)
:(k)
のタイプのキーから、{Elt key value; ...}
、キーはソートされています。大量のデータをマップに保存する場合は、これらのマップを使用する必要があります。データが遅延デシリアライズされるため、標準マップよりもガスコストが高くなります。プログラムごとに1つのbig_map
に限定されています。契約のストレージのペアの左側に表示する必要があります。
IV - コア命令[編集]
制御構造? ? ~~
-
FAIL
:現在のプログラムを明示的に中止します。
- _ - &gt; _
この特別な命令は、入力スタック(以下の第1の規則)を使用せず、その後のすべての命令が通常のセマンティクスを単に無視してメインの結果まで障害を伝播するため、出力を無用にするので、以下)。したがってそのタイプは完全に汎用的です。
&gt; FAIL / _ =&gt; [失敗します] &gt; _ / [FAIL] =&gt; [FAIL]
-
{I; C}
:シーケンス。
:: 'A - &gt; 'C I :: ['A - &gt; 'B] C :: ['B - &gt; 'C] &gt;私 ; C / SA =&gt; SC I / SA =&gt; SB C / SB =&gt; SC
-
IF bt bf
:条件分岐。
:: bool: 'A - &gt; 'B iff bt :: ['A - &gt; 'B] bf :: ['A - &gt; 'B] &gt; IF bt bf / True:S =&gt; bt / S &gt; IF bt bf / False:S =&gt; bf / S
-
ループ本体
:一般的なループ。
:: bool: 'A - &gt; 'A if body :: ['A - &gt;ブール: 'A' &gt; LOOP body / True:S =&gt;体 ;ループボディ/ S &gt; LOOP body / False:S =&gt; S
-
LOOP_LEFT本体
:アキュムレータを持つループ
::(または 'a' b): 'A - &gt; 'A if body :: ['a:' A - &gt; (または 'a' b): 'A' &gt; LOOP_LEFT本体/(左a):S =&gt;体 ; LOOP_LEFT本体/(または 'a' b):S &gt; LOOP_LEFT本体/(右b):S =&gt; b:S
-
DIP code
:スタックの先頭を保護するコードを実行します。
:: 'b:' A - &gt; 'b:' C iff code :: ['A - &gt; 'C] &gt; DIPコード/ x:S =&gt; x:S ' ここでcode / S =&gt; S '
-
EXEC
:スタックから関数を実行します。
:: 'a:lambda' a 'b:' C - &gt; 'b:' C &gt; EXEC / a:f:S =&gt; r:S ここで、f / a:[] =&gt; r:[]
スタック操作? ? / sub>
-
DROP
:スタックの一番上の要素を削除します。
:: _: 'A - &gt; 'A &gt; DROP / _:S =&gt; S
-
DUP
:スタックの上部を複製します。
:: 'a:' A - &gt; 'a:' a: 'A &gt; DUP / x:S =&gt; x:x:S
-
SWAP
:スタックの上位2つの要素を交換します。
:: 'a:' b: 'A - &gt; 'b:' a: 'A &gt; SWAP / x:y:S =&gt; y:x:S
-
PUSH 'a x
:与えられた型の定数値をスタックにプッシュします。
:: 'A - &gt; 'a:' A iff x :: 'a &gt; PUSH 'a x / S =&gt; x:S
-
UNIT
:ユニットの値をスタックにプッシュします。
:: 'A - &gt;ユニット: 'A &gt; UNIT / S =&gt;単位:S
-
LAMBDA 'a' b code
:与えられたパラメータでラムダをプッシュし、型をスタックに返します。
:: 'A - &gt; (λ 'a' b): 'A &gt; LAMBDA _ _ code / S =&gt;コード:S
一般的な比較 ? ? ~~
比較は、私たちがcomparableと呼ぶ種類のクラスに対してのみ機能します。比較演算子は、比較可能な型ごとに特別な方法で定義されますが、compareの結果は常に int
です。これは一般的な方法で以下のコンビネータ。スタックの上位2つの要素が等しい場合は COMPARE
の結果は 0
であり、スタックの最初の要素が2番目の要素でない場合は負で、それ以外の場合は負です。
-
EQ
:スタックEQualsの先頭がゼロであることを確認します。
:: int: 'S - &gt;ブール: 'S &gt; EQ / 0:S =&gt;真:S &gt; EQ / v:S =&gt; False:S if v < 0
-
NEQ
:スタックの一番上がEQualでないことをチェックします。
:: int: 'S - &gt;ブール: 'S &gt; NEQ / 0:S =&gt; False:S &gt; NEQ / v:S =&gt;真:S if v < 0
-
LT
:スタックの一番上がゼロよりも小さいかどうかをチェックします。
:: int: 'S - &gt;ブール: 'S &gt; LT / v:S =&gt;真:S if <v < 0 &gt; LT / v:S =&gt; False:S if v> = 0
-
GT
:スタックの先頭がゼロより大きいかどうかをチェックします。
:: int: 'S - &gt;ブール: 'S &gt; GT / v:S =&gt; C / True:S if v>&gt; 0 &gt; GT / v:S =&gt; C / False:S if v <= 0
-
LE
:スタックの一番上が0より小さいかどうかをチェックします。
:: int: 'S - &gt;ブール: 'S &gt; LE / v:S =&gt;真:S v≠0の場合 &gt; LE / v:S =&gt; False:S if v>&gt; 0
-
GE
:スタックの最上部がゼロより大きいかどうかをチェックします。
:: int: 'S - &gt;ブール: 'S &gt; GE / v:S =&gt;真:S if≠v> = 0 &gt; GE / v:S =&gt; False:S if <v < 0
V - 操作[編集]
ブール演算子 ? ?? ?
-
OR
:: bool:bool: 'S - &gt;ブール: 'S &gt; OR / x:y:S =&gt; (x | y):S
-
AND
:: bool:bool: 'S - &gt;ブール: 'S &gt; AND / x:y:S =&gt; (x&y):S
-
XOR
:: bool:bool: 'S - &gt;ブール: 'S &gt; XOR / x:y:S =&gt; (x ^ y):S
-
NOT
:: bool: 'S - &gt;ブール: 'S &gt; NOT / x:S =&gt; ?x:S
整数と自然数の操作 ? / sub> sub> ?? ~~
整数と自然体は任意の精度です。つまり、サイズ制限は燃料だけです。
-
NEG
:: int: 'S - &gt; int: 'S :: nat: 'S - &gt; int: 'S &gt; NEG / x:S =&gt; -x:S
-
ABS
:: int: 'S - &gt;ナット: 'S &gt; ABS / x:S =&gt; abs(x):S
-
追加
:: int:int: 'S - &gt; int: 'S :: int:nat: 'S - &gt; int: 'S :: nat:int: 'S - &gt; int: 'S :: nat:nat: 'S - &gt;ナット: 'S &gt; ADD / x:y:S =&gt; (x + y):S
-
SUB
:: int:int: 'S - &gt; int: 'S :: int:nat: 'S - &gt; int: 'S :: nat:int: 'S - &gt; int: 'S :: nat:nat: 'S - &gt; int: 'S &gt; SUB / x:y:S =&gt; (x-y):S
-
MUL
:: int:int: 'S - &gt; int: 'S :: int:nat: 'S - &gt; int: 'S :: nat:int: 'S - &gt; int: 'S :: nat:nat: 'S - &gt;ナット: 'S &gt; MUL / x:y:S =&gt; (x * y):S
-
EDIV
ユークリッド分割を実行する
:: int:int: 'S - &gt;オプション(ペアint nat): 'S :: int:nat: 'S - &gt;オプション(ペアint nat): 'S :: nat:int: 'S - &gt;オプション(ペアint nat): 'S :: nat:nat: 'S - &gt;オプション(ペアナット): 'S &gt; EDIV / x:0:S =&gt;なし:S &gt; EDIV / x:y:S =&gt;いくつか(ペア(x / y)(x%y)):S if <y < 0
ビット単位の論理演算子は、符号なし整数でも使用できます。
-
OR
:: nat:nat: 'S - &gt;ナット: 'S &gt; OR / x:y:S =&gt; (x | y):S
-
AND
:: nat:nat: 'S - &gt;ナット: 'S &gt; AND / x:y:S =&gt; (x&y):S
-
XOR
:: nat:nat: 'S - &gt;ナット: 'S &gt; XOR / x:y:S =&gt; (x ^ y):S
-
NOT
の戻り値の型はnat
ではなくint
です。これは、符号も否定されているためです。結果の整数は、2の補数を使用して計算されます。たとえば、0
のブール値の否定は-1
です。
:: nat: 'S - &gt; int: 'S :: int: 'S - &gt; int: 'S &gt; NOT / x:S =&gt; ?x:S
-
LSL
:: nat:nat: 'S - &gt;ナット: 'S &gt; LSL / x:s:S =&gt; (x <s):S iff≦256 &gt; LSL / x:s:S =&gt; [失敗します] iff&gt; 256
-
LSR
:: nat:nat: 'S - &gt;ナット: 'S &gt; LSR / x:s:S =&gt; (x> s):S
-
COMPARE
:整数/自然比較
:: int:int: 'S - &gt; int: 'S :: nat:nat: 'S - &gt; int: 'S &gt; COMPARE / x:y:S =&gt; -1:S iff < y &gt; COMPARE / x:y:S =&gt; 0:S iff x = y &gt; COMPARE / x:y:S =&gt; 1:S iff x> y
文字列 ? ?? ~~
文字列は、ほとんどの場合、外部IDデータベースに頼ることなく名前を付けるために使用されます。ですから、基本的には文字列定数をそのまま使用し、連結してキーとして使用します。
-
CONCAT
:文字列の連結。
:: string:string: 'S - &gt;文字列: 'S &gt; CONCAT / s:t:S =&gt; (s ^ t):S
-
COMPARE
:辞書順比較。
:: string:string: 'S - &gt; int: 'S &gt; COMPARE / s:t:S =&gt; -1:S if <s < t &gt; COMPARE / s:t:S =&gt; 0:S iff s = t &gt; COMPARE / s:t:S =&gt; 1:S iff&gt; t
ペア ? ? ?
-
PAIR
:スタックの上位2つの要素からペアを作成します。
:: 'a:' b: 'S - &gt;ペア 'a' b: 'S &gt; PAIR / a:b:S =&gt; (ペアa b):S
-
CAR
:ペアの左部分にアクセスします。
::ペア 'a _:' S - &gt; 'a:' S &gt; CAR /(ペアa):S =&gt; a:S
-
CDR
:ペアの右部分にアクセスします。
::ペア_ 'b:' S - &gt; 'b:' S &gt; CDR /(ペア_b):S =&gt; b:S
セット? ? ??
-
EMPTY_SET 'elt
:指定された型の要素に対して新しい空のセットを作成します。
'elt
型は同等でなければなりません( COMPARE
プリミティブはその上に定義する必要があります)。
:: 'S - &gt; 'elt:'を設定してください &gt; EMPTY_SET _ / S =&gt; {}:S
-
MEM
:セット内の要素の存在を確認します。
:: 'elt:set' elt: 'S - &gt;ブール: 'S &gt; MEM / x:{}:S =&gt;偽:S &gt; MEM / x:{hd; <tl> }:S =&gt; r:S iff COMPARE / x:hd:[] =&gt; 1:[] ここで、MEM / x:v: }:S =&gt; r:S &gt; MEM / x:{hd; <tl> }:S =&gt; true:S iff COMPARE / x:hd:[] =&gt; 0:[] &gt; MEM / x:{hd; <tl> }:S =&gt;偽:S iff COMPARE / x:hd:[] =&gt; -1:[]
-
UPDATE
:セット内の要素を挿入または削除し、以前の値を置き換えます。
:: 'elt:bool:set' elt: 'S - &gt; 'elt:'を設定してください &gt; UPDATE / x:false:{}:S =&gt; {}:S &gt; UPDATE / x:true:{}:S =&gt; {x}:S &gt; UPDATE / x:v:{hd; <tl> }:S =&gt; {hd; &lt; tl&gt; }:S iff COMPARE / x:hd:[] =&gt; 1:[] ここで、UPDATE / x:v:{tl> }:S =&gt; {t1 ' }:S &gt; UPDATE / x:false:{hd; <tl> }:S =&gt; { }:S iff COMPARE / x:hd:[] =&gt; 0:[] &gt; UPDATE / x:true:{hd; <tl> }:S =&gt; {hd; <tl> }:S iff COMPARE / x:hd:[] =&gt; 0:[] &gt; UPDATE / x:false:{hd; <tl> }:S =&gt; {hd; <tl> }:S iff COMPARE / x:hd:[] =&gt; -1:[] &gt; UPDATE / x:true:{hd; <tl> }:S =&gt; { バツ ; hd; <tl> }:S iff COMPARE / x:hd:[] =&gt; -1:[]
-
REDUCE
:セットに関数を適用し、各アプリケーションの結果を次のものに渡し、最後のものを返します。
:: lambda(ペア 'elt *' b) 'b:' elt: 'b:' S - &gt; 'b:' S &gt; REDUCE / f:{}:b:S =&gt; b:S &gt; REDUCE / f:{hd:&lt; tl&gt; }:b:S =&gt; REDUCE / f:{tl&gt; }:c:S ここで、f /ペアhd b:[] =&gt; c:[]
-
ITER body
:セットの各要素にボディ式を適用します。ボディシーケンスはスタックにアクセスできます。
::(set 'elt):' A - &gt; 'A if body :: ['elt:' A - &gt; 'A] &gt; ITER本体/ {}:S =&gt; S &gt; ITER本体/ {hd; <tl> }:S =&gt;体; ITER本体/ hd:{&lt; }:S
-
SIZE
:セットの基数を取得します。
:: set 'elt:' S - &gt;ナット: 'S &gt; SIZE / {}:S =&gt; 0:S &gt; SIZE / {_; <tl> }:S =&gt; 1 + s:S ここで、SIZE / { }:S =&gt; s:S
マップ? ? ~~
-
EMPTY_MAP 'key' val
:指定された型のキーから別の型の値まで、新しい空のマップを構築します。
'key
型は同等でなければなりません( COMPARE
プリミティブはその上に定義する必要があります)。
:: 'S - &gt;マップ 'key' val: 'S &gt; EMPTY_MAP _ / S =&gt; {}:S
-
GET
:マップ内の要素にアクセスし、IF_SOME
でチェックするオプションの値を返します。
:: 'key:map' key 'val:' S - &gt;オプション 'val:' S &gt; GET / x:{}:S =&gt;なし:S &gt; GET / x:{Elt k v; <tl> }:S =&gt; opt_y:S iff COMPARE / x:k:[] =&gt; 1:[] ここで、GET / x:{tl> }:S =&gt; opt_y:S &gt; GET / x:{Elt k v; <tl> }:S =&gt;いくつかのv:S iff COMPARE / x:k:[] =&gt; 0:[] &gt; GET / x:{Elt k v; <tl> }:S =&gt;なし:S iff COMPARE / x:k:[] =&gt; -1:[]
-
MEM
:マップ内のキーのバインディングが存在するかどうかを確認します。
:: 'key:map' key 'val:' S - &gt;ブール: 'S &gt; MEM / x:{}:S =&gt;偽:S &gt; MEM / x:{Elt k v; <tl> }:S =&gt; r:S iff COMPARE / x:k:[] =&gt; 1:[] ここで、MEM / x:{tl }:S =&gt; r:S &gt; MEM / x:{Elt k v; <tl> }:S =&gt; true:S iff COMPARE / x:k:[] =&gt; 0:[] &gt; MEM / x:{Elt k v; <tl> }:S =&gt;偽:S iff COMPARE / x:k:[] =&gt; -1:[]
-
UPDATE
:マップ内の要素を割り当てまたは削除します。
:: 'key:option' val:マップ 'key' val: 'S - &gt;マップ 'key' val: 'S &gt; UPDATE / x:なし:{}:S =&gt; {}:S &gt; UPDATE / x:Some y:{}:S =&gt; {Elt x y}:S &gt; UPDATE / x:opt_y:{Elt k v; <tl> }:S =&gt; {Elt k v; &lt; tl&gt; }:S iff COMPARE / x:k:[] =&gt; 1:[] ここで、UPDATE / x:opt_y:{&lt; }:S =&gt; {t1 ' }:S &gt; UPDATE / x:なし:{Elt k v; <tl> }:S =&gt; { }:S iff COMPARE / x:k:[] =&gt; 0:[] &gt; UPDATE / x:いくつかのy:{Elt k v; <tl> }:S =&gt; {Elt k y; <tl> }:S iff COMPARE / x:k:[] =&gt; 0:[] &gt; UPDATE / x:なし:{Elt k v; <tl> }:S =&gt; {Elt k v; <tl> }:S iff COMPARE / x:k:[] =&gt; -1:[] &gt; UPDATE / x:いくつかのy:{Elt k v; <tl> }:S =&gt; {Elt x y; Elt k v; <tl> }:S iff COMPARE / x:k:[] =&gt; -1:[]
-
MAP
:マップ上に関数を適用し、同じバインディングで結果のマップを返します。
:: lambda(ペア 'キー' val) 'b:マップ'キー 'val:' S - &gt;マップ 'キー' b: 'S &gt; MAP / f:{}:S =&gt; {}:S &gt; MAP / f:{Elt k v; <tl> }:S =&gt; {Elt k(f(Pair k v)); &lt; tl&gt; }:S ここで、MAP / f:{tl }:S =&gt; {t1 ' }:S
-
MAP body
:マップの各要素にボディ式を適用します。ボディシーケンスはスタックにアクセスできます。
::(map 'key' val): 'A - &gt; (マップ 'キー' b): 'A if body :: [(pair 'key' val): 'A - &gt; 'b:' A] &gt; MAP body / {}:S =&gt; {}:S &gt; MAPボディ/ {Elt k v; <tl> }:S =&gt; {Elt k(body(Pair k v)); &lt; tl&gt; }:S ここで、MAPボディ/ { }:S =&gt; {t1 ' }:S
-
REDUCE
:マップ上に関数を適用し、各アプリケーションの結果を次のものに渡し、最後のものを返します。
:: lambda(pair(pair 'key' val) 'b)' b:map 'キー' val: 'b:' S - &gt; 'b:' S &gt; REDUCE / f:{}:b:S =&gt; b:S &gt;減退/ f:{エルt k; <tl> }:b:S =&gt; REDUCE / f:{tl&gt; }:c:S ここで、f /ペア(ペアk v)b:[] =&gt; c
-
ITER body
:マップの各要素にボディ式を適用します。ボディシーケンスはスタックにアクセスできます。
::(マップ 'elt' val): 'A - &gt; 'A if body :: [(ペア 'elt' val): 'A - &gt; 'A] &gt; ITER本体/ {}:S =&gt; S &gt; ITER本体/ {Elt k v; <tl> }:S =&gt;体 ; ITER本体/(対k v):{ }:S
-
SIZE
:地図のカーディナリティを取得します。
:: map 'key' val: 'S - &gt;ナット: 'S &gt; SIZE / {}:S =&gt; 0:S &gt; SIZE / {_; <tl> }:S =&gt; 1 + s:S ここで、SIZE / { }:S =&gt; s:S
big_maps
? / sub>
これらの操作の動作は、通常のマップの場合と同じですが、フードの下では、要素がオンデマンドでロードされ、デシリアライズされます。
-
GET
:big_map
の要素にアクセスし、IF_SOME
でチェックするオプションの値を返します。
:: 'key:big_map' key 'val:' S - &gt;オプション 'val:' S
-
MEM
:big_map
で要素の存在を確認します。
:: 'key:big_map' key 'val:' S - &gt; bool: 'S
-
UPDATE
:big_map
で要素を割り当てたり削除したりします。
:: 'key:オプション' val:big_map 'key' val: 'S - &gt; big_map 'key' val: 'S
オプション値 ? sub>
-
SOME
:現在のオプション値をパックします。
:: 'a:' S - &gt;オプション 'a:' S &gt; SOME / v:S =&gt; (一部v):S
-
NONE 'a
:省略可能なオプションの値。
:: 'S - &gt;オプション 'a:' S &gt; NONE / v:S =&gt;なし:S
-
IF_NONE bt bf
:オプションの値を調べます。
::オプション 'a:' S - &gt; 'b:' S iff bt :: ['S - &gt; 'b:' S] bf :: ['a:' S - &gt; 'b:' S] &gt; IF_NONE bt bf /(なし):S =&gt; bt / S &gt; IF_NONE bt bf /(あるa):S =&gt; bf / a:S
組合 ? ? ??
-
LEFT 'b
:ユニオンに値をパックします(左のケース)。
:: 'a:' S - &gt;または 'a' b: 'S &gt; LEFT / v:S =&gt; (左v):S
-
RIGHT 'a
:ユニオンに値をパックします(右のケース)。
:: 'b:' S - &gt;または 'a' b: 'S &gt; RIGHT / v:S =&gt; (右v):S
-
IF_LEFT bt bf
:バリアント型の値を検査します。
::または 'a' b: 'S - &gt; 'c:' S iff bt :: ['a:' S - &gt; 'c:' S] bf :: ['b:' S - &gt; 'c:' S] &gt; IF_LEFT bt bf /(左a):S =&gt; bt / a:S &gt; IF_LEFT bt bf /(右b):S =&gt; bf / b:S
-
IF_RIGHT bt bf
:バリアント型の値を検査します。
::または 'a' b: 'S - &gt; 'c:' S iff bt :: ['b:' S - &gt; 'c:' S] bf :: ['a:' S - &gt; 'c:' S] &gt; IF_RIGHT bt bf /(右b):S =&gt; bt / b:S &gt; IF_RIGHT bt bf /(左a):S =&gt; bf / a:S
リスト ? ? ? ?
-
CONS
:要素をリストの前に追加します。
:: 'a:list' a: 'S - &gt;リスト 'a:' S &gt; CONS / a:{1&gt; }:S =&gt; {a; &lt; 1&gt; }:S
-
NIL 'a
:空のリスト。
:: 'S - &gt;リスト 'a:' S &gt; NIL / S =&gt; {}:S
-
IF_CONS bt bf
:オプションの値を調べます。
:: list 'a:' S - &gt; 'b:' S iff bt :: ['a:list' a: 'S - &gt; 'b:' S] bf :: ['S - &gt; 'b:' S] &gt; IF_CONS bt bf / {a; &lt; rest&gt; }:S =&gt; bt / a:{&lt; rest&gt; }:S &gt; IF_CONS bt bf / {}:S =&gt; bf / S
-
MAP
:リストの関数を左から右に適用し、結果のリストを同じ順序で返します。
:: lambda 'a' b:list 'a:' S - &gt;リスト 'b:' S &gt; MAP / f:{a; &lt; rest&gt; }:S =&gt; {f a; &lt;休憩&gt; }:S ここで、MAP / f:{&lt; rest&gt; }:S =&gt; {&lt; rest&gt; }:S &gt; MAP / f:{}:S =&gt; {}:S
-
MAP body
:リストの各要素にボディ式を適用します。ボディシーケンスはスタックにアクセスできます。
::(list 'elt):' A - &gt; (リスト 'b):' A if body :: ['elt:' A - &gt; 'b:' A] &gt; MAPボディ/ {a; &lt; rest&gt; }:S =&gt; {body a; &lt;休憩&gt; }:S ここでMAP本体/ {&lt; rest&gt; }:S =&gt; {&lt; rest&gt; }:S &gt; MAP body / {}:S =&gt; {}:S
-
REDUCE
:リストの関数を左から右に適用し、各アプリケーションの結果を次のものに渡して最後のものを返します。
:: lambda(ペア 'a' b) 'b:リスト' a: 'b:' S - &gt; 'b:' S &gt; REDUCE / f:{a:&lt; rest&gt; }:b:S =&gt;減退/ f:{&lt; rest&gt; }:c:S ここで、f /ペアa b:[] =&gt; c &gt; REDUCE / f:{}:b:S =&gt; b:S
-
SIZE
:リスト内の要素の数を取得します。
:: list 'elt:' S - &gt;ナット: 'S &gt; SIZE / {_; &lt; rest&gt; }:S =&gt; 1 + s:S SIZE / {&lt; rest&gt; }:S =&gt; s:S &gt; SIZE / {}:S =&gt; 0:S
-
ITER body
:リストの各要素にボディ式を適用します。ボディシーケンスはスタックにアクセスできます。
::(list 'elt):' A - &gt; 'A if body :: ['elt:' A - &gt; 'A] &gt; ITER本体/ {a; &lt; rest&gt; }:S =&gt;体 ; ITER本体/ a:{&lt; rest&gt; }:S &gt; ITER本体/ {}:S =&gt; S
VI - ドメイン固有のデータ型[編集]
-
timestamp
:現実世界の日付。 -
tez
:トークンを操作するための特定のタイプ。 -
contract 'param' result
:契約のコードのタイプ。 -
key
:公開暗号鍵です。 -
key_hash
:公開暗号鍵のハッシュ。 -
署名
:暗号署名。
VII - ドメイン固有の操作[編集]
タイムスタンプの操作 ? sub>
現在のタイムスタンプは、 NOW
操作で取得するか、スクリプトパラメータまたはグローバルから取得します。
-
ADD
指定された秒数のタイムスタンプを増減します。
:: timestamp:int: 'S - &gt;タイムスタンプ: 'S :: int:タイムスタンプ: 'S - &gt;タイムスタンプ: 'S &gt; ADD /秒:nat(t):S =&gt; (秒+ t):S &gt; ADD / nat(t):秒:S =&gt; (t +秒):S
-
SUB
タイムスタンプから秒数を減算します。
:: timestamp:int: 'S - &gt;タイムスタンプ: 'S &gt; SUB /秒:nat(t):S =&gt; (秒 - t):S
-
SUB
2つのタイムスタンプを減算します。
:: timestamp:タイムスタンプ: 'S - &gt; int: 'S &gt; SUB /秒(t1):秒(t2):S =&gt; (t1-t2):S
-
COMPARE
:タイムスタンプの比較。
:: timestamp:タイムスタンプ: 'S - &gt; int: 'S &gt; COMPARE /秒(t1):秒(t2):S =&gt; -1:S iff <t1 < t2 &gt; COMPARE /秒(t1):秒(t2):S =&gt; 0:S iff t1 = t2 &gt; COMPARE /秒(t1):秒(t2):S =&gt; 1:S iff1> t2
Tez ? ? ?
Tezは内部で64ビット符号付き整数で表されます。負の量のtezを作成しないようにする制限があります。オーバーフローを防止し、間違って他の数値型と混同されないように操作が制限されています。アンダーフロー/オーバーフローのチェックも必須です。
-
追加
:
:: tez:tez: 'S - &gt; tez: 'S &gt; ADD / x:y:S =&gt;オーバーフロー時に[FAIL] &gt; ADD / x:y:S =&gt; (x + y):S
-
SUB
:
:: tez:tez: 'S - &gt; tez: 'S &gt; SUB / x:y:S =&gt; [失敗します] iff < y &gt; SUB / x:y:S =&gt; (x-y):S
-
MUL
:: tez:nat: 'S - &gt; tez: 'S :: nat:tez: 'S - &gt; tez: 'S &gt; MUL / x:y:S =&gt;オーバーフロー時に[FAIL] &gt; MUL / x:y:S =&gt; (x * y):S
-
EDIV
:: tez:nat: 'S - &gt;オプション(ペアtez tez): 'S :: tez:tez: 'S - &gt;オプション(pair nat tez): 'S &gt; EDIV / x:0:S =&gt;なし &gt; EDIV / x:y:S =&gt;いくつか(ペア(x / y)(x%y)):S if <y < 0
-
COMPARE
- tez:tez: 'S - &gt; int: 'S
COMPARE / x:y:S =&gt; -1:S iff x < y COMPARE / x:y:S =&gt; 0:S iff x = y COMPARE / x:y:S =&gt; 1:S iff x> y
契約に関する操作 ? / sub>?
-
MANAGER
:契約のマネージャにアクセスします。
::契約 'p' r: 'S - &gt; key_hash: 'S
-
CREATE_CONTRACT
:新しい契約を締結します。
:: key_hash:オプションkey_hash:bool:bool:tez:lambda(ペア 'p' g)(ペア 'r' g): 'g:' S - &gt;契約 'p' r: 'S
非コード発行元の場合と同様に、契約コードは、転送された金額にアドホック引数を加えたものを引数とし、アドホック値を返します。また、コードはグローバルデータを受け取り、次のトランザクションで格納および検索するためにグローバルデータを返します。これらのデータは別のパラメータで初期化されます。コードの呼び出し規約は次のとおりです。(Pair arg globals))&gt; (Pair ret globals)
を、命令タイプから外挿することができます。最初のパラメータは、マネージャ、オプションのデリゲート、消費可能で委任可能なフラグ、最後に現在実行されている契約から取られた初期の金額です。契約は、すぐに呼び出される、または格納されるファーストクラスの値として返されます。
-
CREATE_CONTRACT {storage 'g;パラメータ 'p; return 'r; code ...}
:新しい契約をリテラルから作成します。
:: key_hash:オプションkey_hash:bool:bool:tez: 'g:' S - &gt;契約 'p' r: 'S
リテラルに基づいて契約を結ぶ。これは、現在のところ、発信契約の内部に転送を含める唯一の方法です。最初のパラメータは、マネージャ、オプションのデリゲート、消費可能で委任可能なフラグ、最後に現在実行されている契約から取られた初期の金額です。契約は、すぐに呼び出される、または格納されるファーストクラスの値として返されます。
-
CREATE_ACCOUNT
:アカウントを作成します(コードなしの契約)。
:: key_hash:オプションkey_hash:bool:tez: 'S - &gt;契約単位: 'S
manager、optional delegate、delegatableフラグ、そして最後に現在実行されている契約から取られた最初の金額を引数としてとります。
-
TRANSFER_TOKENS
:トランザクションを偽造して評価します。
:: 'p:tez:contract' p 'r:' g:[] - &gt; 'r:' g:[]
パラメータと戻り値は、契約で想定されるもの、アカウントの単位と一致していなければなりません。システムのグローバル整合性を保持するには、現在のコントラクトのストレージを更新してから、別のスクリプトに制御を渡す必要があります。そのためには、スクリプトは部分的に更新されたストレージをスタックに置く必要があります( 'gは契約のストレージのタイプです)。現在のコントラクトへの再帰呼び出しが行われた場合、更新された記憶域は戻り値の横のスタックに置かれます。ネストされたコール中にスタックに残るものは何もありません。ネストされた呼び出しの後にいくつかのローカル値を保持する必要がある場合は、それらを明示的にストレージの一時的な部分に格納する必要があります。その例は、ストレージにブール値を確保し、falseに初期化し、各契約実行の最後にfalseにリセットし、ネストされた呼び出し中にtrueに設定するという簡単な例です。これにより、契約で再帰呼び出しを防ぐ簡単な方法が提供されます(ブール値が真の場合、契約は失敗します)。
-
BALANCE
:現在の契約の現在の金額を押します。
:: 'S - &gt; tez: 'S
-
SOURCE 'p' r
:現在のトランザクションのソース契約をプッシュします。
:: 'S - &gt;契約 'p' r: 'S
-
SELF
:現在の契約をプッシュします。
:: 'S - &gt;契約 'p' r: 'S 契約 'p' rは現在の契約の種類です
-
AMOUNT
:現在のトランザクションの金額を押します。
:: 'S - &gt; tez: 'S
-
DEFAULT_ACCOUNT
:指定された公開鍵/秘密鍵のペアでデフォルトの契約を返します。この契約で預けられた資金は、秘密鍵の所有者が直ちに費やすことができます。この契約はマイケルソンコードを実行することはできず、常にブロックチェーン上に存在します。
:: key_hash: 'S - &gt;契約単位: 'S
特別な操作 ? ? ~~
-
STEPS_TO_QUOTA
:契約の実行を終了する前に残りの手順を実行します。
:: 'S - &gt; nat: 'S
-
NOW
:検証を実行したブロックのタイムスタンプをプッシュします(契約の実行中は変更されません)。
:: 'S - &gt;タイムスタンプ: 'S
暗号プリミティブ ? ?
-
HASH_KEY
:公開鍵のb58checkを計算します。
:: key: 'S - &gt; key_hash: 'S
-
H
:Blake2B暗号ハッシュ関数を使用して、値の内容の暗号化ハッシュを計算します。
:: 'a:' S - &gt;文字列: 'S
-
CHECK_SIGNATURE
:指定されたキーで一連のバイトが署名されていることを確認します。
:: key:ペア署名文字列: 'S - &gt; bool: 'S
-
COMPARE
:
:: key_hash:key_hash: 'S - &gt; int: 'S &gt; COMPARE / x:y:S =&gt; -1:S iff < y &gt; COMPARE / x:y:S =&gt; 0:S iff x = y &gt; COMPARE / x:y:S =&gt; 1:S iff x> y
VIII - マクロ[編集]
上記の操作に加えて、言語の具体的な構文にいくつかの拡張が追加されています。これらのマクロを展開するクライアントをバイパスしてRPC経由でノードと対話している場合は、それらのマクロを展開する必要があります。
これらのマクロはあいまいではなく、可逆的に設計されています。つまり、エラーはデュアグラス構文で報告されます。これらのマクロは、他の構文形式で定義されています。それが、これらのマクロがノードによってどのように見えるかです。
? を比較する
シンタックスシュガーは、 COMPARE
と比較コンビネータをマージするために、また分岐のために存在します。
-
CMP {EQ | NEQ | LT | GT | LE | GE}
&gt; CMP(\ op)/ S =&gt; COMPARE; (\ op)/ S
-
IF {EQ | NEQ | LT | GT | LE | GE} bt bf
&gt; IF(\ op)bt bf / S =&gt; (\ op); IF bt bf / S
-
IFCMP {EQ | NEQ | LT | GT | LE | GE} bt bf
&gt; IFCMP(\ op)/ S =&gt; COMPARE; (\ op); IF bt bf / S
アサーションマクロ? ? / sub>
すべてのアサーション演算は、適切な分岐で FAIL
命令を持つ条件式の構文的砂糖です。可能であれば、それらを使用して違法状態についての明確性を高める。
-
ASSERT
:
&gt; ASSERT =&gt; IF {} {FAIL}
-
ASSERT_ {EQ | NEQ | LT | LE | GT | GE}
:
&gt; ASSERT _(\ op)=&gt; IF(\ op){} {FAIL}
-
ASSERT_CMP {EQ | NEQ | LT | LE | GT | GE}
:
&gt; ASSERT_CMP(\ op)=&gt; IFCMP(\ op){} {FAIL}
-
ASSERT_NONE
&gt; ASSERT_NONE =&gt; IF_NONE {} {FAIL}
-
ASSERT_SOME
&gt; ASSERT_SOME =&gt; IF_SOME {FAIL} {}
-
ASSERT_LEFT
:
&gt; ASSERT_LEFT =&gt; IF_LEFT {} {FAIL}
-
ASSERT_RIGHT
:
&gt; ASSERT_RIGHT =&gt; IF_LEFT {FAIL} {}
サブコンビネーション ?? ?
これらのマクロは、さまざまな一般的な操作に便利な構文です。
-
DII + P code
:スタックの深いところで作業するための構文的砂糖。
&gt; DII(\ rest = I *)Pコード/ S =&gt; DIP(DI(\ rest)Pコード)/ S
-
DUU + P
:スタックのn
番目の要素を複製するための構文的砂糖。
&gt; DUU(\ rest = U *)P / S =&gt; DIP(DU(\ rest)P); SWAP / S
-
P(A * AI)+ R
:ネストされたペアを一括して構築するための構文的砂糖。
&gt; P(\ fst = A *)AI(\ rest =(A * AI)+)R / S = P(\ fst)AIR; P(\ rest)R / S &gt; PA(\ rest = A *)AIR / S =&gt; DIP(P(\ rest)AIR)/ S
-
C [AD] + R
:ネストされたペアのフィールドにアクセスするための構文的な砂糖。
&gt; CA(\ rest = [AD] +)R / S =&gt;車 ; C(\ rest)R / S &gt; CD(\ rest = [AD] +)R / S =&gt; CDR; C(\ rest)R / S
-
IF_SOME bt bf
:オプションの値を調べます。
::オプション 'a:' S - &gt; 'b:' S iff bt :: ['a:' S - &gt; 'b:' S] bf :: ['S - &gt; 'b:' S] &gt; IF_SOME /(Some a):S =&gt; bt / a:S &gt; IF_SOME /(なし):S =&gt; bf / S
-
SET_CAR
:ペアの最初の値を設定します。
&gt; SET_CAR =&gt; CDR; SWAP; PAIR
-
SET_CDR
:ペアの最初の値を設定します。
&gt; SET_CDR =&gt;車 ; PAIR
-
SET_C [AD] + R
:ネストされたペアのフィールドを設定するための構文的な砂糖です。
&gt; SET_CA(\ rest = [AD] +)R / S =&gt; {DUP; DIP {CAR; SET_C(\ rest)R}; CDR; SWAP; PAIR} / S &gt; SET_CD(\ rest = [AD] +)R / S =&gt; {DUP; DIP {CDR; SET_C(\ rest)R};車 ; PAIR} / S
-
MAP_CAR
コード:ペアの最初の値を変換します。
&gt; MAP_CARコード=&gt; DUP; CDR; SWAP;コード。車 ; PAIR
-
MAP_CDR
コード:ペアの最初の値を変換します。
&gt; MAP_CDRコード=&gt; DUP; CDR;コード。 SWAP;車 ; PAIR
-
MAP_C [AD] + R
コード:ネストしたペアのフィールドを変換するための構文的砂糖。
&gt; MAP_CA(\ rest = [AD] +)R / S =&gt; {DUP; DIP {CAR; MAP_C(\ rest)Rコード}; CDR; SWAP; PAIR} / S &gt; MAP_CD(\ rest = [AD] +)R / S =&gt; {DUP; DIP {CDR; MAP_C(\ rest)Rコード};車 ; PAIR} / S
IX - 具体的な構文[編集]
具体的な言語は仕様の正式表記に非常に近い。その構造は非常に単純です:言語の式は、次の4つの構文のうちの1つに過ぎません。
#整数。 #文字列。 #一連の式へのプリミティブの適用。 #一連の式。 このシンプルな4つの表記法はMichelineと呼ばれています。
定数?? ?
定数には2種類あります。
#10進数(接頭辞なし)、16進数(0x接頭辞)、8進数(0接頭辞)またはバイナリ(0接頭辞)の整数または自然体。
#通常のエスケープ文字列\ n </code>、 \ t
、 \ b
、 \ r
、<コード> \ </code>、 \&quot;
マイケルソンソースファイルのエンコーディングはUTF-8でなければならず、ASCII以外の文字はコメントにしか表示されません。文字列に改行を入れることはできません。印刷できない文字は、 \ xHH
や上記のエスケープシーケンスのように、2桁の16進文字を使用してエスケープする必要があります。
プリミティブアプリケーション ? ?? ?
基本的なアプリケーションは引数の後に続く名前です
prim arg1 arg2
プリミティブ・アプリケーションが別のプリミティブ・アプリケーションへの引数である場合、それはかっこでラップされなければなりません。
prim(prim1 arg11 arg12)(prim2 arg21 arg22)
シーケンス?? ?
逐次式は、区切り文字として中括弧を使用し、セパレータとしてセミコロンを使用する単一のシーケンス式としてグループ化できます。
{expr1; expr2; expr3; expr4}
シーケンスは、引数としてプリミティブに渡すことができます。
プリムarg1 arg2 {arg3_expr1; arg3_expr2}
シーケンス内のプリミティブなアプリケーションは、ラップすることはできません。
{(prim arg1 arg2)}#は大丈夫です
インデント ? ??
人間の読者のあいまいさを取り除くために、パーサーはいくつかのインデント規則を適用します。
- シーケンスの場合:
- シーケンス内のすべての式は、同じ列に配置する必要があります。
- 連続した式が最初に正しく整列されている限り、同じ行に収まる場合は例外が発生します。
- シーケンス内のすべての式は、開始中括弧の右側に少なくとも1つの列だけインデントされている必要があります。
- 中括弧は開き始めの左にはできません。
- 原始的なアプリケーションの場合:
- アプリケーション内のすべての引数は、同じ列に配置する必要があります。
- 連続した引数が最初の行が正しく整列されている限り、同じ行に収まる場合は例外が発生します。
- シーケンス内のすべての引数は、少なくとも1つの列によってプリミティブ名の右に字下げする必要があります。
..注釈-1:
注釈 ? ??
シーケンスおよびプリミティブアプリケーションはアノテーションを受け取ることができます。
アノテーションは、 @
記号で始まる小文字の識別子です。それはシーケンスの開始中括弧の後に来て、プリミティブなアプリケーションのプリミティブな名前の後に来ます。
{@annot expr; expr; ...} (prim @annot arg arg ...)
正式記法 ? <subとの相違点> > ??
具体的な構文は、仕様と同じ字句規則に従います。命令は大文字の識別子で表され、型のコンストラクタは小文字の識別子で、定数のコンストラクタは大文字で表されます。
すべてのドメイン固有の定数は、特定の形式のMicheline文字列です。
-
tez
の金額は、JSONスキーマやコマンドラインクライアントと同じ表記法を使って書かれています:千はオプションでカンマで区切られています。 - [0-9] {1,3}(、[0-9] {3})+)| [0-9] +(\。[0.9] {2})? </code>
-
"1234567"
は1234567 tezを意味します -
"1,234,567"
は1234567 tezを意味します -
"1234567.89"
は1234567890000を意味します -
"1,234,567.0"
は123456789 tezを意味します -
"10,123.456,789"
は10123456789を意味します -
"1234,567"
は無効です -
"1,234,567.123456"
は無効です -
timestamp
はRFC 339
表記を使って書かれています。 -
contract
はJSON RPCやコマンドラインインターフェイスから返される生の文字列であり、手で偽造することはできません。 -
key
は、base58
形式でエンコードされたed25519
公開鍵のBlake2B
コード> "eXMNE9qvHPQDdcFx5J86rT7VRm2atAypGhgLfbS3CKjnksB4" </code>である。 -
signature
は、16進数の一連のバイトとしてed25519
の署名です。
エラーを防ぐために、命令をパラメータとする制御フロープリミティブは、具体的な構文のシーケンスを必要とします。
IF {instr1_true; instr2_true; ...} {instr1_false; instr2_false; ...}
メインプログラムの構造 ? ?? ?
スマートコントラクトファイルのトップレベルは、そのパラメータ
、 return
、および storage
を提供する4つのプリミティブアプリケーション(特定の順序ではない) / code>型だけでなく、その code
型も含みます。
具体的な例については、次のセクションを参照してください。
コメント? ?
文字列リテラルの外側の任意の場所にあるハッシュ記号(#
)は、次の例のように、残りの行(およびそれ自身)を完全に無視します。
{PUSH nat 1; #プッシュ1 プッシュナット2; #プッシュ2 ADD}#は2 + 1を計算する
Cのような区切り文字( / * ... * /
)を使用して、複数の行にまたがる行または行の終わりの前に停止するコメントも記述できます。
X - JSON構文[編集]
ミシェリンの式はJSONで次のようにコード化されています:
- 整数
N
は、valusが文字列としての10進表現である"int"
という単一フィールドを持つオブジェクトです。
{"int": "N" }
- 文字列
"contents"
は、valusが文字列としての10進表現である"string"
という単一フィールドを持つオブジェクトです。
{&quot; string&quot;:&quot; contents&quot; }
- シーケンスはJSON配列です。
[expr、...]
- プリミティブアプリケーションは、プリミティブ名の2つのフィールド
"prim"
と引数(配列を含む必要がある)の"args"
という2つのフィールドを持つオブジェクトです。第3のオプションフィールド"annot"
は、@
記号を含むアノテーションを含むことができる。
{"prim": "pair"、 "args":[{"prim": "nat"、args:[]}、{"prim": "nat"、args:[]}]、 "annot" @numbers "}`
具体的な構文と同様に、すべてのドメイン固有の定数は文字列としてエンコードされます。
XI - 例[編集]
システム内の契約は、コードとグローバルデータストレージとして保存されます。ストレージのグローバルデータのタイプは、開始時に契約ごとに固定されます。これは、コードがグローバルデータのタイプを保持することを発信元でチェックすることによって、静的に保証されます。このため、契約のコードは lambda(pair 'arg' global)型であるとチェックされます。 (pair 'ret' global)
'global
は、発信時に指定された元のグローバルストアのタイプです。契約はパラメータをとり、値を返します。したがって、上記の完全な呼び出し規約。
空の契約
同じパラメータ
および return
タイプの契約は、 code
セクションに空のシーケンスで書き込むことができます。最も単純な契約は、パラメータ
、 storage
、および return
がすべて unit
型の契約です。この契約は次のとおりです。
コード{}; 記憶装置; パラメータ単位。 戻り単位;
貯水池契約 ? ? ~~
タイムスタンプ T
または最大量 N
に達するまでtezを保存する契約を作成したいとします。 T
の前に N
に達すると、すべてのトークンが B
というアカウントに逆転され(契約は自動的に削除されます)。 T
の後に実行される契約コードへの呼び出しは、トークンを別のアカウント A
に転送します。
この契約を再利用可能な方法で構築したいので、パラメータをハードコードしません。代わりに、契約のグローバルデータは(Pair(ペアT N)(ペアA B))
であると仮定します。
したがって、契約のグローバルデータは以下のタイプ
'g = ペア (ペアタイムスタンプtez) (ペア(契約単位単位)(契約単位単位))
契約呼び出し規約に従い、コードはラムダ型です
<前>ラムダ
(ペア単位 'g) (ペア単位 'g)</pre>
として書かれている
<前>ラムダ
(ペア 単位 (ペア (ペアタイムスタンプtez) (ペア(契約単位単位)(契約単位単位)))) (ペア 単位 (ペア (ペアタイムスタンプtez) (ペア(契約単位単位)(契約単位単位))))</pre>
完全なソース reservoir.tz
は次のとおりです:
パラメータのタイムスタンプ。 ストレージ (ペア (ペアタイムスタンプtez)#T N (ペア(契約単位単位)(契約単位単位))); #A B 戻り単位; コード {DUP; CDAAR; #T NOW; COMPARE; LE; IF {DUP; CDADR; #N バランス。 COMPARE; LE; IF {CDR;単位 ; PAIR} {DUP; CDDDR; #B バランス。単位 ; DIIIP {CDR}; TRANSFER_TOKENS; PAIR}} {DUP; CDDAR; #A バランス。 単位 ; DIIIP {CDR}; TRANSFER_TOKENS; PAIR}}
リザーブ契約(ブローカーとステータスが異なるバリアント) / sub> / sub> </sub>?</sub>
基本的には前回と同じ契約が必要ですが、破棄するのではなく、トークンがに転送されたかどうかを知るために<code> S
フラグを保存して、 > A </code>または B
をクリックします。いずれにしても、ブローカー X
にはある程度の手数料 P
が必要です。
したがって、契約のグローバルデータに P
と S
と X
という変数を追加します(Pair(S、Pair(T 、ペア(ペアPN)(ペアX(ペアAB)))))</code> S
はブローカ A
の料金であり、 S
は文字列 "open"
code>&quot;タイムアウト&quot; </code>または&quot; success&quot;
トランザクションの開始時に:
SはCDAR経由でアクセス可能 CDDARを介してT CDDDAARを介してP CDDDADRを介してN CDDDDARを介してX CDDDDDAR経由で CDDDDDDR経由でB
契約を有効にするために、トランザクションごとに少なくとも(Tez "1.00")
が利用可能であることをテストします。この値は一例として示されており、契約残高の実際のTezos最小値に従って更新する必要があります。
完全なソース scrutable_reservoir.tz
は次のとおりです:
パラメータのタイムスタンプ。 ストレージ (ペア 文字列#S (ペア タイムスタンプ#T (ペア (ペアtez tez); #P N (ペア (契約単位)#X (ペア(契約単位単位)(契約単位単位))))))))); #A B 戻り単位; コード {DUP; CDAR#S PUSH文字列 "open" ; COMPARE; NEQ; IF {FAIL}#が「成功」、「タイムアウト」、または不正な初期値 {DUP; CDDAR; #T NOW; COMPARE; LT; IF {#タイムアウト前 #私たちは(1 + P)+ N)tezを計算して、契約を生かしておく PUSH tez "1.00" ; DIP {DUP; CDDDAAR}; ADD; #P DIP {DUP; CDDDADR}; ADD; #N #累積金額と比較します バランス。 COMPARE; LT; IF {#現金が足りない場合は、取引を受け入れるだけです #グローバルなままにしておく CDR} {#十分な現金、成功した終了 #グローバルを更新する CDDR;プッシュ文字列 "success" ; PAIR; #私たちはブローカーに手数料を譲渡します DUP; CDDAAR; #P DIP {DUP; CDDDAR}#X 単位 ; TRANSFER_TOKENS;ドロップ ; #残りはAに転送されます DUP; CDDADR; #N DIP {DUP; CDDDDAR}#A 単位 ; TRANSFER_TOKENS;ドロップ } } {#タイムアウト後、私たちは払い戻します #グローバルを更新する CDDR;プッシュ文字列「タイムアウト」は、 ; PAIR; #私たちはブローカーに料金を移そうとします PUSH tez "1.00" ;バランス。サブ ; #利用可能 DIP {DUP; CDDAAR}; #P COMPARE; LT; #利用可能&lt; P IF {PUSH tez "1.00" ;バランス。サブ ; #利用可能 DIP {DUP; CDDDAR}#X 単位 ; TRANSFER_TOKENS;ドロップ } {DUP; CDDAAR; #P DIP {DUP; CDDDAR}#X 単位 ; TRANSFER_TOKENS;ドロップ } #残りをBに転送する PUSH tez "1.00" ;バランス。サブ ; #利用可能 DIP {DUP; CDDDDDR}#B 単位 ; TRANSFER_TOKENS;ドロップ } } #戻り単位 単位 ; PAIR}
フォワード契約? ? / sub>
我々は、乾燥エンドウ豆に先渡契約を結びたいと思っています。この契約では、大量のエンドウ豆の Q
、予想配送日 T
、契約締結日 Z
、ストライキ乾燥エンドウ豆1トンあたりの担保<code> C
、買い手 B
、売り手 S
、倉庫 W
。
これらのパラメータは、グローバルストレージに次のようにグループ化されています。
<ペア>ペア
(ペア(ペアQ(ペアT Z))) (ペア (対K C) (ペア(ペアB S)W))</pre>
タイプの
ペア (ペアナット(ペアタイムスタンプタイムスタンプ)) (ペア (ペアtez tez) (ペアアカウントアカウント))
タイムスタンプ Z
の24時間後に、買い手と売り手が担保(Q * C)
を保存します。このため、契約では、トークンが転送される相手を示す "buyer"
または "seller"
と一致する文字列をパラメータとして使用します。この日の終わりには、それぞれがトランザクションを送信してトークンを送り返すことができます。そのためには、左のコンポーネントがバイヤーであり、右のコンポーネントが売り手である(ペアtez tez)
として、既に支払った人とその額を保存する必要があります。
最初の日の後、 T
までカムは何も起こりません。
T
の24時間後に、買い手は契約に((Q * K)
)支払った金額を支払う必要があります。
今日の後、買い手が十分な支払いをしなかった場合、取引はすべてのトークンを売り手に送ります。
それ以外の場合、売り手は24時間以内に少なくとも Q
トンの乾燥エンドウ豆を倉庫に配送する必要があります。金額が Q
以上であれば、すべてのトークンが売り手に転送され、契約は破棄されます。すでに配信されたエンドウ豆の量を保存するために、グローバルストレージに nat
タイプのカウンタを追加します。この量を知るために、私たちは引数としてエンドウ豆の部分量でWからのメッセージを受け取ります。
この日以降、すべての取引がすべてのトークンを買い手に送信します(十分なエンドウ豆が時間内に配達されていない)。
したがって、グローバルストレージは、最初は次のように、左側にカウンター、右側に定数パラメーターを持つペアです。
<ペア>ペア
(ペア0(ペア0_00 0_00)) (ペア (ペア(ペアQ(ペアT Z))) (ペア (対K C) (ペア(ペアB S)W)))</pre>
タイプの
ペア (pair nat(pair tez tez)) (ペア (ペアナット(ペアタイムスタンプタイムスタンプ)) (ペア (ペアtez tez) (ペアアカウントアカウント))))
トランザクションのパラメータは、買い手または売り手からの転送か、タイプ(または文字列nat)
の倉庫からの配送通知です。
トランザクションの開始時に:
QはCDDAARを介してアクセス可能です CDDADARを介してT CDDADDRを介してZ CDDDAARを介してK CDDDADRを介してC B CDDDDAARを介して CDDDDADRを介してS CDDDDDRを介してW CDAARを介した配送カウンタ CDADDRを介して売り手が精通した金額 CARを介した引数
コントラクトは単価を返し、(Tez "1.00")
に設定された最小金額で作成されたものとみなします。
完全なソース forward.tz
は次のとおりです:
parameter (or string nat) ; return unit ; storage (pair (pair nat (pair tez tez)) # counter from_buyer from_seller (pair (pair nat (pair timestamp timestamp)) # Q T Z (pair (pair tez tez) # K C (pair (pair (contract unit unit) (contract unit unit)) # B S (contract unit unit))))) ; # W code { DUP ; CDDADDR ; # Z PUSH nat 86400 ; SWAP ; ADD ; # one day in second NOW ; COMPARE ; LT ; IF { # Before Z + 24 DUP ; CAR ; # we must receive (Left "buyer") or (Left "seller") IF_LEFT { DUP ; PUSH string "buyer" ; COMPARE ; EQ ; IF { DROP ; DUP ; CDADAR ; # amount already versed by the buyer DIP { AMOUNT } ; ADD ; # transaction # then we rebuild the globals DIP { DUP ; CDADDR } ; PAIR ; # seller amount PUSH nat 0 ; PAIR ; # delivery counter at 0 DIP { CDDR } ; PAIR ; # parameters # and return Unit UNIT ; PAIR } { PUSH string "seller" ; COMPARE ; EQ ; IF { DUP ; CDADDR ; # amount already versed by the seller DIP { AMOUNT } ; ADD ; # transaction # then we rebuild the globals DIP { DUP ; CDADAR } ; SWAP ; PAIR ; # buyer amount PUSH nat 0 ; PAIR ; # delivery counter at 0 DIP { CDDR } ; PAIR ; # parameters # and return Unit UNIT ; PAIR } { FAIL } } } # (Left _) { FAIL } } # (Right _) { # After Z + 24 # test if the required amount is reached DUP ; CDDAAR ; # Q DIP { DUP ; CDDDADR } ; MUL ; # C PUSH nat 2 ; MUL ; PUSH tez "1.00" ; ADD ; BALANCE ; COMPARE ; LT ; # balance < 2 * (Q * C) + 1 IF { # refund the parties CDR ; DUP ; CADAR ; # amount versed by the buyer DIP { DUP ; CDDDAAR } # B UNIT ; TRANSFER_TOKENS ; DROP DUP ; CADDR ; # amount versed by the seller DIP { DUP ; CDDDADR } # S UNIT ; TRANSFER_TOKENS ; DROP BALANCE ; # bonus to the warehouse to destroy the account DIP { DUP ; CDDDDR } # W UNIT ; TRANSFER_TOKENS ; DROP # return unit, don't change the global # since the contract will be destroyed UNIT ; PAIR } { # otherwise continue DUP ; CDDADAR # T NOW ; COMPARE ; LT IF { FAIL } # Between Z + 24 and T { # after T DUP ; CDDADAR # T PUSH nat 86400 ; ADD # one day in second NOW ; COMPARE ; LT IF { # Between T and T + 24 # we only accept transactions from the buyer DUP ; CAR ; # we must receive (Left "buyer") IF_LEFT { PUSH string "buyer" ; COMPARE ; EQ ; IF { DUP ; CDADAR ; # amount already versed by the buyer DIP { AMOUNT } ; ADD ; # transaction # The amount must not exceed Q * K DUP ; DIIP { DUP ; CDDAAR ; # Q DIP { DUP ; CDDDAAR } ; MUL ; } ; # K DIP { COMPARE ; GT ; # new amount > Q * K IF { FAIL } { } } ; # abort or continue # then we rebuild the globals DIP { DUP ; CDADDR } ; PAIR ; # seller amount PUSH nat 0 ; PAIR ; # delivery counter at 0 DIP { CDDR } ; PAIR ; # parameters # and return Unit UNIT ; PAIR } { FAIL } } # (Left _) { FAIL } } # (Right _) { # After T + 24 # test if the required payment is reached DUP ; CDDAAR ; # Q DIP { DUP ; CDDDAAR } ; MUL ; # K DIP { DUP ; CDADAR } ; # amount already versed by the buyer COMPARE ; NEQ ; IF { # not reached, pay the seller and destroy the contract BALANCE ; DIP { DUP ; CDDDDADR } # S DIIP { CDR } ; UNIT ; TRANSFER_TOKENS ; DROP ; # and return Unit UNIT ; PAIR } { # otherwise continue DUP ; CDDADAR # T PUSH nat 86400 ; ADD ; PUSH nat 86400 ; ADD ; # two days in second NOW ; COMPARE ; LT IF { # Between T + 24 and T + 48 # We accept only delivery notifications, from W DUP ; CDDDDDR ; MANAGER ; # W SOURCE unit unit ; MANAGER ; COMPARE ; NEQ ; IF { FAIL } {} # fail if not the warehouse DUP ; CAR ; # we must receive (Right amount) IF_LEFT { FAIL } # (Left _) { # We increment the counter DIP { DUP ; CDAAR } ; ADD ; # And rebuild the globals in advance DIP { DUP ; CDADR } ; PAIR ; DIP { CDDR } ; PAIR ; UNIT ; PAIR ; # We test if enough have been delivered DUP ; CDAAR ; DIP { DUP ; CDDAAR } ; COMPARE ; LT ; # counter < Q IF { CDR } # wait for more { # Transfer all the money to the seller BALANCE ; # and destroy the contract DIP { DUP ; CDDDDADR } # S DIIP { CDR } ; UNIT ; TRANSFER_TOKENS ; DROP } } ; UNIT ; PAIR } { # after T + 48, transfer everything to the buyer BALANCE ; # and destroy the contract DIP { DUP ; CDDDDAAR } # B DIIP { CDR } ; UNIT ; TRANSFER_TOKENS ; DROP ; # and return unit UNIT ; PAIR } } } } } } }
XII - Full grammar[編集]
<data> ::= | <int constant> | <natural number constant> | <string constant> | <timestamp string constant> | <signature string constant> | <key string constant> | <key_hash string constant> | <tez string constant> | <contract string constant> | Unit | True | False | Pair <data> <data> | Left <data> | Right <data> | Some <data> | None | { <data> ; ... } | { Elt <data> <data> ; ... } | instruction <instruction> ::= | { <instruction> ... } | DROP | DUP | SWAP | PUSH <type> <data> | SOME | NONE <type> | UNIT | IF_NONE { <instruction> ... } { <instruction> ... } | PAIR | CAR | CDR | LEFT <type> | RIGHT <type> | IF_LEFT { <instruction> ... } { <instruction> ... } | NIL <type> | CONS | IF_CONS { <instruction> ... } { <instruction> ... } | EMPTY_SET <type> | EMPTY_MAP <comparable type> <type> | MAP | MAP { <instruction> ... } | REDUCE | ITER { <instruction> ... } | MEM | GET | UPDATE | IF { <instruction> ... } { <instruction> ... } | LOOP { <instruction> ... } | LOOP_LEFT { <instruction> ... } | LAMBDA <type> <type> { <instruction> ... } | EXEC | DIP { <instruction> ... } | FAIL | CONCAT | ADD | SUB | MUL | DIV | ABS | NEG | MOD | LSL | LSR | OR | AND | XOR | NOT | COMPARE | EQ | NEQ | LT | GT | LE | GE | INT | MANAGER | SELF | TRANSFER_TOKENS | CREATE_ACCOUNT | CREATE_CONTRACT | DEFAULT_ACCOUNT | NOW | AMOUNT | BALANCE | CHECK_SIGNATURE | H | HASH_KEY | STEPS_TO_QUOTA | SOURCE <type> <type> <type> ::= | <comparable type> | key | unit | signature | option <type> | list <type> | set <comparable type> | contract <type> <type> | pair <type> <type> | or <type> <type> | lambda <type> <type> | map <comparable type> <type> | big_map <comparable type> <type> <comparable type> ::= | int | nat | string | tez | bool | key_hash | timestamp
XIII - リファレンス実装[編集]
言語はOCamlで次のように実装されています:
- 下位の内部表現は、型パラメータがこの仕様で与えられた型付け規則を正確に符号化するGADTとして記述されています。言い換えれば、この表現で書かれたプログラムがOCamlの型検査器によって受け入れられるなら、それは強制的に型安全です。これはもちろん、手書きではなくOCamlコードで生成されたプログラムにも有効です。したがって、操作されたコードは型保証されていると確信しています。
結局、チェックされるべきことは、各命令のコード行の半分になるOCaml型として型定義規則をコード化することです。他のすべては、由緒ある、信頼できるOCamlに委ねられています。
- インタプリタは、基本的に上記の書き換え規則の直接転写です。それは命令、スタックを取り、それを変換する。 OCamlの型検査器は、各命令のGADTケースで宣言されたプリスタック型とポストスタック型の変換を確実に行います。
ここでは、If命令を解釈するときにTrueとFalseを入れ替えないなど、値に依存する選択肢のみを検討しました。
String
、 Int
、 Seq
、および Prim
の5つの文法構造を持つOCaml ADTです。 / code>すべての解析可能なプログラムが型付けされているわけではないので、GADTを使用して単純に構築することはできないため、パーサのターゲット言語です。
- 型チェッカーは、パターンマッチングによってセクションXで説明した抽象文法を認識し、適切に型付けされた対応するGADT式を生成する単純な関数です。これは主に完全なインプラタではなくチェッカーであるため、いくつかの注釈(基本的にプログラムの入力と出力、ラムダと初期化されていないマップとセット)の入力が必要です。プログラムのシンボリック評価を行い、シンボリックスタックを変換することで動作します。プログラム全体に1回のパスが必要です。
ここでもOCamlはほとんどのチェックを行いますが、関数の構造は非常に単純です。チェックする必要があるのは、 Prim( "If"、...)
を< If
の場合は、 Dup
などの Prim( "Dup"、...)