mirror of
https://github.com/AleoHQ/leo.git
synced 2024-11-13 08:47:17 +03:00
Merge branch 'master' of github.com:AleoHQ/leo into feature/better-errors
This commit is contained in:
commit
4e32efb9e3
56
Cargo.lock
generated
56
Cargo.lock
generated
@ -76,9 +76,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.42"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
|
||||
checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
@ -2406,9 +2406,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.17"
|
||||
version = "0.8.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
|
||||
checksum = "039ba818c784248423789eec090aab9fb566c7b94d6ebbfa1814a9fd52c8afb2"
|
||||
dependencies = [
|
||||
"dtoa",
|
||||
"linked-hash-map",
|
||||
@ -2473,9 +2473,9 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-algorithms"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40caa98abd0a874a003d844d6a73562020fb5fa976daa14055f030d9803430b7"
|
||||
checksum = "3534da2cfa71894e4d28c5dcaf9f9c5c94315904c4ace6c86419a8f2ff8b1641"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitvec",
|
||||
@ -2501,9 +2501,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-curves"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "010ae5c95693279db519e4a8525a8a3145edc8e36198efdfaf250622f4540cc1"
|
||||
checksum = "83b4de7cd1533c67dd386058b631d7e5ea8b2063ebca212159b13fc1a94ff4a7"
|
||||
dependencies = [
|
||||
"derivative",
|
||||
"rand 0.8.4",
|
||||
@ -2517,9 +2517,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-derives"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e963ae4209f0166a619ab9266ce71c6f9228861ec55615a1b6154d673ffc158"
|
||||
checksum = "3e854888634305d8c5c79fe59adaae593971a8b29d3148834f698141b16c2c7b"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro-error",
|
||||
@ -2530,9 +2530,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-dpc"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1588a3d2f38307bd64838cf1761d8cef485461e5224831c9b9df11a1fa9864e7"
|
||||
checksum = "c922bb690fbe9c5919f67f2d887452376339a36bf5ffb50dc6b00d58a5650284"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base58",
|
||||
@ -2562,9 +2562,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-fields"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "761cb4c19d6685ac9d1d3a6eb1619600334bcf80dc6ac742cc38f4fe349ba871"
|
||||
checksum = "bca141a8eae489ea39600b04f0a642059ce076bd3a7d21704b38b90fc87c37f0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -2578,9 +2578,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-gadgets"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3b0cd11387a572f9a1c318c1d446dd46e0522c9be7681b8d6af6882d20880fd"
|
||||
checksum = "3138cfcaaa27e53cabc7a4fc596b3b0abb47c3cb46fee496e3ac7ce2cd07df9d"
|
||||
dependencies = [
|
||||
"derivative",
|
||||
"digest 0.9.0",
|
||||
@ -2598,9 +2598,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-marlin"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "206d633d6299dfb4f52640a3188567462dbf1f207de70cbb829707c07858c7e6"
|
||||
checksum = "12e6987cde7b734415afc7afe58a038d224393842ad5dfaba728a601c5c5473d"
|
||||
dependencies = [
|
||||
"blake2",
|
||||
"derivative",
|
||||
@ -2622,9 +2622,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-parameters"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f5c4d4233a623c46c483296aa24bf4d95a5f3344c669ce4f2956467fe6ea904"
|
||||
checksum = "506cbbbfe27acb4d4e8dd15e49b430dbfb920fd680cfe1c69ab87542083d0422"
|
||||
dependencies = [
|
||||
"curl",
|
||||
"hex",
|
||||
@ -2635,9 +2635,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-polycommit"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b5ca8d560cb6f49d9de9b4a955778cc14563b0a381c56db48ea6171788f30d4"
|
||||
checksum = "c23a7fa2aab25aa6fc3b7fb27b3d639936725a5c29b29936d1530b64bd011e67"
|
||||
dependencies = [
|
||||
"derivative",
|
||||
"digest 0.9.0",
|
||||
@ -2654,15 +2654,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-profiler"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29cb28d79c59db77774484dbc0f4e876dc8604085d8d7f43eee1b813ec0614b4"
|
||||
checksum = "f7d80cc343b9e19e0f38582fa7e17e6263c4c7449122cf9a606279567ff33d71"
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-r1cs"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a12e3b7acea34af74dbe5dea056714a9f3c9b1029a9ac888595c96a6e3c676e6"
|
||||
checksum = "537f2123c7da8a9d3482523156318479790c41e390d33f6936b89a3ed9eee0a8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"fxhash",
|
||||
@ -2675,9 +2675,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "snarkvm-utilities"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0842ff685e625cb7a2f9ddc7ca1b1560d2a3e3a7220099781a28ea54564133b"
|
||||
checksum = "a6d1565a7c2a320b711308188adf84980013872e579ae2e6abffe8796eb8e6fe"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
@ -82,7 +82,7 @@ version = "1.5.3"
|
||||
version = "0.7.6"
|
||||
|
||||
[dependencies.snarkvm-curves]
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-gadgets]
|
||||
|
@ -61,7 +61,7 @@ version = "1.5.3"
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.snarkvm-curves]
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-fields]
|
||||
@ -69,7 +69,7 @@ version = "0.7.7"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-dpc]
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-gadgets]
|
||||
|
284
docs/rfc/009-bit-byte-conversions.md
Normal file
284
docs/rfc/009-bit-byte-conversions.md
Normal file
@ -0,0 +1,284 @@
|
||||
# Leo RFC 009: Conversions with Bits and Bytes
|
||||
|
||||
## Authors
|
||||
|
||||
- Max Bruce
|
||||
- Collin Chin
|
||||
- Alessandro Coglio
|
||||
- Eric McCarthy
|
||||
- Jon Pavlik
|
||||
- Damir Shamanaev
|
||||
- Damon Sicore
|
||||
- Howard Wu
|
||||
|
||||
## Status
|
||||
|
||||
DRAFT
|
||||
|
||||
# Summary
|
||||
|
||||
This RFC proposes the addition of natively implemented global functions to perform conversions
|
||||
between Leo integer values and sequences of bits or bytes in big endian or little endian order.
|
||||
This RFC also proposes a future transition from these functions to methods associated to the integer types.
|
||||
|
||||
# Motivation
|
||||
|
||||
Conversions of integers to bits and bytes are fairly common in programming languages.
|
||||
Use case include communication with the external world
|
||||
(since external data is sometimes represented as bits and bytes rather than higher-level data structures),
|
||||
and serialization/deserialization for cryptographic purposes (e.g. hashing data).
|
||||
|
||||
# Design
|
||||
|
||||
## Concepts
|
||||
|
||||
The Leo integer values can be thought of sequences of bits.
|
||||
Therefore, it makes sense to convert between integer values and their corresponding sequences of bits;
|
||||
the sequences of bits can be in little or big endian order (i.e. least vs. most significant bit first),
|
||||
naturally leading to two possible conversions.
|
||||
Obviously, the bits represent the integers in base 2.
|
||||
|
||||
Since all the Leo integer values consist of multiples of 8 bits,
|
||||
it also makes sense to convert between integer values and squences of bytes,
|
||||
which represents the integers in base 256.
|
||||
Again, the bytes may be in little or big endian order.
|
||||
|
||||
It could also make sense to convert between integers consisting of `N` bits
|
||||
and sequences of "words" of `M` bits if `N` is a multiple of `M`,
|
||||
e.g. convert a `u32` into a sequence of two `u16`s, or convert a `u128` into a sequence of four `u32`s.
|
||||
However, the case in which `M` is 1 (bits) or 8 (bytes) is by far the most common,
|
||||
and therefore the initial focus of this RFC;
|
||||
nonetheless, it seems valuable to keep these possible generalizations in mind as we work though this initial design.
|
||||
|
||||
Another possible generalization is to lift these conversions to sequences,
|
||||
e.g. converting from a sequence of integer values to a sequence of bits or bytes
|
||||
by concatenating the results of converting the integer values,
|
||||
and converting from a sequence of bits or bytes to a sequence of integer values
|
||||
by grouping the bits or bytes into chunks and converting each chunk into an integer.
|
||||
For instance, a sequence of 4 `u32` values can be turned into a sequence of 32 bytes or a sequence of 128 bits.
|
||||
Note that, in these cases, the endianness only applies to the individual element conversion,
|
||||
not to the ordering of the integer values, which should be preserved by the conversion.
|
||||
|
||||
Besides integers, it could make sense to consider converting other Leo values between bits and bytes,
|
||||
namely characters, field elements, group elements, and addresses (but perhaps not booleans).
|
||||
If this is further extended to aggregate values (tuples, arrays, and circuits),
|
||||
then this moves towards a general serialization/deserialization library for Leo, which could be a separate feature.
|
||||
|
||||
## Representation of Bits
|
||||
|
||||
In Leo's current type system, bits can be represented as `bool` values.
|
||||
These are not quite the numbers 0 and 1, but they are isomorphic, and it is easy to convert between booleans and bits:
|
||||
```ts
|
||||
// convert a boolean x to a bit:
|
||||
(x ? 1 : 0)
|
||||
|
||||
// convert f bit y to a boolean:
|
||||
(y == 1)
|
||||
```
|
||||
|
||||
If Leo had a type `u1` for unsigned 1-bit integers, we could use that instead of `bool`.
|
||||
Separately from this RFC, such a type could be added.
|
||||
There is also an outstanding proposal (not in an RFC currently) to support types `uN` and `iN` for every positive `N`,
|
||||
in which case `u1` would be an instance of that.
|
||||
|
||||
## Representation of Bytes
|
||||
|
||||
The type `u8` is the natural way to represent a byte.
|
||||
The type `i8` is isomorphic to that, but we tend to think of bytes as unsigned.
|
||||
|
||||
## Representation of Sequences
|
||||
|
||||
This applies to the sequence of bits or bytes that a Leo integer converts to or from.
|
||||
E.g. a `u32` is converted to/from a sequence of bits or bytes.
|
||||
|
||||
Sequences in Leo may be ntaurally represented as arrays or tuples.
|
||||
Arrays are more flexible; in particular, they allow indexing via expressions rather than just numbers, unlike tuples.
|
||||
Thus, arrays are the natural choice to represent these sequences.
|
||||
|
||||
## Conversion Functions
|
||||
|
||||
We propose the following global functions,
|
||||
for which we write declarations without bodies below,
|
||||
since the implementation is native.
|
||||
(It is a separate issue whether the syntax below should be allowed,
|
||||
in order to represent natively implemented functions,
|
||||
or whether there should be a more explicit indication such as `native` in Java).
|
||||
|
||||
These are tentative names, which we can tweak.
|
||||
What is more important is the selection of operations, and their input/output types.
|
||||
|
||||
### Conversions between Integers and Bits
|
||||
|
||||
```ts
|
||||
// unsigned to bits, little and big endian
|
||||
function u8_to_bits_le(x: u8) -> [bool; 8];
|
||||
function u8_to_bits_be(x: u8) -> [bool; 8];
|
||||
function u16_to_bits_le(x: u16) -> [bool; 16];
|
||||
function u16_to_bits_be(x: u16) -> [bool; 16];
|
||||
function u32_to_bits_le(x: u32) -> [bool; 32];
|
||||
function u32_to_bits_be(x: u32) -> [bool; 32];
|
||||
function u64_to_bits_le(x: u64) -> [bool; 64];
|
||||
function u64_to_bits_be(x: u64) -> [bool; 64];
|
||||
function u128_to_bits_le(x: u128) -> [bool; 128];
|
||||
function u128_to_bits_be(x: u128) -> [bool; 128];
|
||||
|
||||
// signed to bits, little and big endian
|
||||
function i8_to_bits_le(x: i8) -> [bool; 8];
|
||||
function i8_to_bits_be(x: i8) -> [bool; 8];
|
||||
function i16_to_bits_le(x: i16) -> [bool; 16];
|
||||
function i16_to_bits_be(x: i16) -> [bool; 16];
|
||||
function i32_to_bits_le(x: i32) -> [bool; 32];
|
||||
function i32_to_bits_be(x: i32) -> [bool; 32];
|
||||
function i64_to_bits_le(x: i64) -> [bool; 64];
|
||||
function i64_to_bits_be(x: i64) -> [bool; 64];
|
||||
function i128_to_bits_le(x: i128) -> [bool; 128];
|
||||
function i128_to_bits_be(x: i128) -> [bool; 128];
|
||||
|
||||
// unsigned from bits, little and big endian
|
||||
function u8_from_bits_le(x: [bool; 8]) -> u8;
|
||||
function u8_from_bits_be(x: [bool; 8]) -> u8;
|
||||
function u16_from_bits_le(x: [bool; 16]) -> u16;
|
||||
function u16_from_bits_be(x: [bool; 16]) -> u16;
|
||||
function u32_from_bits_le(x: [bool; 32]) -> u32;
|
||||
function u32_from_bits_be(x: [bool; 32]) -> u32;
|
||||
function u64_from_bits_le(x: [bool; 64]) -> u64;
|
||||
function u64_from_bits_be(x: [bool; 64]) -> u64;
|
||||
function u128_from_bits_le(x: [bool; 128]) -> u128;
|
||||
function u128_from_bits_be(x: [bool; 128]) -> u128;
|
||||
|
||||
// signed from bits, little and big endian
|
||||
function i8_from_bits_le(x: [bool; 8]) -> i8;
|
||||
function i8_from_bits_be(x: [bool; 8]) -> i8;
|
||||
function i16_from_bits_le(x: [bool; 16]) -> i16;
|
||||
function i16_from_bits_be(x: [bool; 16]) -> i16;
|
||||
function i32_from_bits_le(x: [bool; 32]) -> i32;
|
||||
function i32_from_bits_be(x: [bool; 32]) -> i32;
|
||||
function i64_from_bits_le(x: [bool; 64]) -> i64;
|
||||
function i64_from_bits_be(x: [bool; 64]) -> i64;
|
||||
function i128_from_bits_le(x: [bool; 128]) -> i128;
|
||||
function i128_from_bits_be(x: [bool; 128]) -> i128;
|
||||
```
|
||||
|
||||
### Conversions between Integers and Bytes
|
||||
|
||||
```ts
|
||||
// unsigned to bytes, little and big endian
|
||||
function u16_to_bytes_le(x: u16) -> [u8; 2];
|
||||
function u16_to_bytes_be(x: u16) -> [u8; 2];
|
||||
function u32_to_bytes_le(x: u32) -> [u8; 4];
|
||||
function u32_to_bytes_be(x: u32) -> [u8; 4];
|
||||
function u64_to_bytes_le(x: u64) -> [u8; 8];
|
||||
function u64_to_bytes_be(x: u64) -> [u8; 8];
|
||||
function u128_to_bytes_le(x: u128) -> [u8; 16];
|
||||
function u128_to_bytes_be(x: u128) -> [u8; 16];
|
||||
|
||||
// signed to bytes, little and big endian
|
||||
function i16_to_bytes_le(x: i16) -> [u8; 2];
|
||||
function i16_to_bytes_be(x: i16) -> [u8; 2];
|
||||
function i32_to_bytes_le(x: i32) -> [u8; 4];
|
||||
function i32_to_bytes_be(x: i32) -> [u8; 4];
|
||||
function i64_to_bytes_le(x: i64) -> [u8; 8];
|
||||
function i64_to_bytes_be(x: i64) -> [u8; 8];
|
||||
function i128_to_bytes_le(x: i128) -> [u8; 16];
|
||||
function i128_to_bytes_be(x: i128) -> [u8; 16];
|
||||
|
||||
// unsigned from bytes, little and big endian
|
||||
function u16_from_bytes_le(x: [u8; 2]) -> u16;
|
||||
function u16_from_bytes_be(x: [u8; 2]) -> u16;
|
||||
function u32_from_bytes_le(x: [u8; 4]) -> u32;
|
||||
function u32_from_bytes_be(x: [u8; 4]) -> u32;
|
||||
function u64_from_bytes_le(x: [u8; 8]) -> u64;
|
||||
function u64_from_bytes_be(x: [u8; 8]) -> u64;
|
||||
function u128_from_bytes_le(x: [u8; 16]) -> u128;
|
||||
function u128_from_bytes_be(x: [u8; 16]) -> u128;
|
||||
|
||||
// signed from bytes, little and big endian
|
||||
function i16_from_bytes_le(x: [u8; 2]) -> i16;
|
||||
function i16_from_bytes_be(x: [u8; 2]) -> i16;
|
||||
function i32_from_bytes_le(x: [u8; 4]) -> i32;
|
||||
function i32_from_bytes_be(x: [u8; 4]) -> i32;
|
||||
function i64_from_bytes_le(x: [u8; 8]) -> i64;
|
||||
function i64_from_bytes_be(x: [u8; 8]) -> i64;
|
||||
function i128_from_bytes_le(x: [u8; 16]) -> i128;
|
||||
function i128_from_bytes_be(x: [u8; 16]) -> i128;
|
||||
```
|
||||
|
||||
## Handling of the Native Functions
|
||||
|
||||
Given the relatively large number and regular structure of the functions above,
|
||||
it makes sense to generate them programmatically (e.g. via Rust macros),
|
||||
rather than enumerating all of them explicitly in the implementation.
|
||||
It may also makes sense, at R1CS generation time,
|
||||
to use generated or suitably parameterized code to recognize them and turn them into the corresponding gadgets.
|
||||
|
||||
## Transition to Methods
|
||||
|
||||
Once a separate proposal for adding methods to Leo scalar types is realized,
|
||||
we may want to turn the global functions listed above into methods,
|
||||
deprecating the global functions, and eventually eliminating them.
|
||||
|
||||
Conversions to bits or bytes will be instance methods of the integer types,
|
||||
e.g. `u8` will include an instance method `to_bits_le` that takes no arguments and that returns a `[bool; 8]`.
|
||||
Example:
|
||||
```ts
|
||||
let int: u8 = 12;
|
||||
let bits: [bool; 8] = int.to_bits_le();
|
||||
console.assert(bits == [false, false, true, true, false, false, false, false]); // 00110000 (little endian)
|
||||
```
|
||||
|
||||
Conversions from bits or bytes will be static methods of the integer types,
|
||||
e.g. `u8` will include a static metod `from_bits_le` that takes a `[bool; 8]` argument and returns a `u8`.
|
||||
Example:
|
||||
```ts
|
||||
let bits: [bool; 8] = [false, false, true, true, false, false, false, false]; // 00110000 (little endian)
|
||||
let int = u8::from_bits_le(bits);
|
||||
console.assert(int == 12);
|
||||
```
|
||||
|
||||
# Drawbacks
|
||||
|
||||
This does not seem to bring any drawbacks.
|
||||
|
||||
# Effect on Ecosystem
|
||||
|
||||
None.
|
||||
|
||||
# Alternatives
|
||||
|
||||
## Pure Leo Implementation
|
||||
|
||||
These conversions can be realized in Leo (i.e. without native implementations),
|
||||
provided that Leo is extended with certain operations that are already separately planned:
|
||||
* Integer division and remainder, along with type casts, could be used.
|
||||
* Bitwise shifts and masks, along with type casts, could be used.
|
||||
|
||||
However, compiling the Leo code that realizes the conversions may result in less efficient R1CS than the native ones.
|
||||
|
||||
## Naming Bit and Byte Types Explicitly
|
||||
|
||||
Names like `u8_to_bits_le` and `u32_to_bytes_le` talk about bits and bytes,
|
||||
therefore relying on a choice of representation for bits and bytes,
|
||||
which is `bool` for bits and `u8` for bytes as explained above.
|
||||
An alternative is to have names like `u8_to_bools_le` and `u32_to_u8s_le`,
|
||||
which explicate the representation of bits and bytes in the name,
|
||||
and open the door to additional conversions to different representations.
|
||||
In particular, if and when Leo is extended with a type `u1` for bits,
|
||||
there could be additional operations like `u8_to_u1s_le`.
|
||||
|
||||
This more explicit naming scheme also provides a path towards extending
|
||||
bit and byte conversions to more generic "word" conversions,
|
||||
such as `u64_to_u16s_le`, which would turn a `u64` into a `[u16; 4]`.
|
||||
In general, it makes sense to convert between `uN` or `iN` and `[uM; P]` when `N == M * P`.
|
||||
If Leo were extended with types `uN` and `iN` for all positive `N` as proposed elsewhere,
|
||||
there could be a family of all such conversions.
|
||||
|
||||
## Methods Directly
|
||||
|
||||
Given that we eventually plan to use methods on scalar types for these conversions,
|
||||
it may make sense to do that right away.
|
||||
This is predicated on having support for methods on scalar types,
|
||||
for which a separate RFC is in the works.
|
||||
|
||||
If we decide for this approach, we will revise the above proposal to reflect that.
|
||||
The concepts and (essential) names and input/output types remain unchanged,
|
||||
but the conversions are packaged in slightly different form.
|
218
docs/rfc/010-native-functions.md
Normal file
218
docs/rfc/010-native-functions.md
Normal file
@ -0,0 +1,218 @@
|
||||
# Leo RFC 010: Improved Native Functions
|
||||
|
||||
## Authors
|
||||
|
||||
- Max Bruce
|
||||
- Collin Chin
|
||||
- Alessandro Coglio
|
||||
- Eric McCarthy
|
||||
- Jon Pavlik
|
||||
- Damir Shamanaev
|
||||
- Damon Sicore
|
||||
- Howard Wu
|
||||
|
||||
## Status
|
||||
|
||||
DRAFT
|
||||
|
||||
# Summary
|
||||
|
||||
This RFC proposes an improved approach to handling natively implemented functions ('native functions', for short) in Leo,
|
||||
that is functions implemented not via Leo code but (in essence) via Rust code.
|
||||
Currently there is just one such function, namely BLAKE2s.
|
||||
The scope of this proposal is limited to native functions defined by the developers of Leo itself,
|
||||
not by users of Leo (i.e. developers of applications written in Leo).
|
||||
|
||||
The approach proposed here is to allow (global and member) Leo functions to have no defining bodies,
|
||||
in which case they are regarded as natively implemented;
|
||||
this is only allowed in Leo files that contain standard/core libraries, provided with the Leo toolchain.
|
||||
Most of the compiler can work essentially in the same way as it does now;
|
||||
at R1CS generation time, native functions must be recognized, and turned into the known gadgets that implement them.
|
||||
|
||||
# Motivation
|
||||
|
||||
Many languages support native functions (here we generically use 'functions' to also denote methods),
|
||||
where 'native' refers to the fact that the functions are implemented not in the language under consideration,
|
||||
but rather in the language used to implement the language under consideration.
|
||||
For instance, Java supports native methods that are implemented in C rather than Java.
|
||||
|
||||
There are two main reasons for native functions in programming languages:
|
||||
1. The functionality cannot be expressed at all in the language under consideration,
|
||||
e.g. Java has no constructs to print on screen, making a native implementation necessary.
|
||||
2. The functionality can be realized more efficiently in the native language.
|
||||
|
||||
The first reason above may not apply to Leo, at least currently,
|
||||
as Leo's intended use is mainly for "pure computations" rather than interactions with the external world.
|
||||
However, we note that console statements could be regarded as native functions (as opposed to "ad hoc" statements),
|
||||
and this may be in fact the path to pursue if we extend the scope of console features (e.g. even to full GUIs),
|
||||
as has been recently proposed (we emphasize that the console code is not meant to be compiled to R1CS).
|
||||
|
||||
The second reason above applies to Leo right now.
|
||||
While there is currently just one native function supported in Leo, namely BLAKE2s,
|
||||
it is conceivable that there may be other cryptographic (or non-cryptographic) functions
|
||||
for which hand-crafted R1CS gadgets are available
|
||||
that are more efficient than what the Leo compiler would generate if their functionality were written in Leo.
|
||||
While we will continue working towards making the Leo compiler better,
|
||||
and perhaps eventually capable to generate R1CS whose efficiency is competitive with hand-crafted gadgets,
|
||||
this will take time, and in the meanwhile new and more native functions may be added,
|
||||
resulting in a sort of arms race.
|
||||
In other words, it is conceivable that Leo will need to support native functions in the foreseeable future.
|
||||
|
||||
Languages typically come with standard/core libraries that application developers can readily use.
|
||||
Even though the Leo standard/core libraries are currently limited (perhaps just to BLAKE2s),
|
||||
it seems likely that we will want to provide more extensive standard/core libraries,
|
||||
not just for cryptographic functions, but also for data structures and operations on them.
|
||||
|
||||
The just mentioned use case of data structures brings about an important point.
|
||||
Leo circuit types are reasonable ways to provide library data structures,
|
||||
as they support static and instance member functions that realize operations on those data structures.
|
||||
Just like some Java library classes provide a mix of native and non-native methods,
|
||||
we could imagine certain Leo library circuit types providing a mix of native and non-native member functions, e.g.:
|
||||
```ts
|
||||
circuit Point2D {
|
||||
x: u32;
|
||||
y: u32;
|
||||
function origin() -> Point2D { ... } // probably non-native
|
||||
function move(mut self, delta_x: u32, delta_y: u32) { ... } // probably non-native
|
||||
function distance(self, other:Point2D); // maybe native (involves square root)
|
||||
}
|
||||
```
|
||||
|
||||
Our initial motivation for naive functions is limited to Leo standard/core libraries,
|
||||
not to user-defined libraries or applications.
|
||||
That is, only the developers of the Leo language will be able to create native functions.
|
||||
Leo users, i.e. developers of Leo applications, will be able to use the provided native functions,
|
||||
but not to create their own.
|
||||
If support for user-defined native functions may become desired in the future, it will be discussed in a separate RFC.
|
||||
|
||||
# Design
|
||||
|
||||
## Background
|
||||
|
||||
### Current Approach to Native Functions
|
||||
|
||||
The BLAKE2s native function is currently implemented as follows (as a high-level view):
|
||||
1. Prior to type checking/inference, its declaration (without a defining body)
|
||||
is programmatically added to the program under consideration.
|
||||
This way, the input and output types of the BLAKE2s function can be used to type-check code that calls it.
|
||||
2. At R1CS generation time, when the BLAKE2s function is compiled, it is recognized as native and,
|
||||
instead of translating its body to R1CS (which is not possible as the function has no Leo body),
|
||||
a known BLAKE2s gadget is used.
|
||||
|
||||
This approach is fine for a single native function, but may not be the best for a richer collection of native functions.
|
||||
In particular, consider the `Point2D` example above, which has a mix of native and non-native functions:
|
||||
presumably, we would like to write at least the non-native functions of `Point2D` directly in a Leo file,
|
||||
as opposed to programmatically generating them prior to type checking/inference.
|
||||
|
||||
### Multi-File Compilation
|
||||
|
||||
Leo already supports the compilation of multiple files that form a program, via packages and importing.
|
||||
This capability is independent from native functions.
|
||||
|
||||
We note that, for the purpose of type checking code that calls a function `f`,
|
||||
the defining body of `f` is irrelevant: only the input and output types of `f` are relevant.
|
||||
The defining body is of course type-checked when `f` itself is type-checked,
|
||||
and furthermore the defining body is obviously needed to generate R1CS,
|
||||
but the point here is that only the input and output types of `f` are needed to type-check code that calls `f`.
|
||||
In particular, this means that, if a Leo file imports a package,
|
||||
only the type information from the package is needed to type-check the file that imports the package.
|
||||
Conceptually, each package exports a symbol table, used (and sufficient) to type-check files that import that package.
|
||||
|
||||
## Proposal
|
||||
|
||||
we propose to:
|
||||
1. Allow declarations of (global and member) functions to have no defining body, signaling that the function is native.
|
||||
2. Extend the AST and ASG to allow functions to have no bodies.
|
||||
3. Have the compiler allow empty function bodies only in standard/core library files, which should be known.
|
||||
4. Have type checking/inference "skip over" absent function bodies.
|
||||
5. At R1CS generation time, when a function without body is encountered, find and use the known gadget for it.
|
||||
|
||||
Currently the ABNF grammar requires function declarations to have a defining body (a block), i.e. to be implemented in Leo:
|
||||
```
|
||||
function-declaration = *annotation %s"function" identifier
|
||||
"(" [ function-parameters ] ")" [ "->" type ]
|
||||
block
|
||||
```
|
||||
We propose to relax the rule as follows:
|
||||
```
|
||||
function-declaration = *annotation %s"function" identifier
|
||||
"(" [ function-parameters ] ")" [ "->" type ]
|
||||
( block / ";" )
|
||||
```
|
||||
This allows a function declaration to have a terminating semicolon instead of a block.
|
||||
|
||||
Since we do not have anything like abstract methods in Leo, this is a workable way to indicate native functions.
|
||||
However, it is easy, if desired, to have a more promiment indication, such as a `native` keyword, or an annotation.
|
||||
|
||||
It may be necessary to extend the AST and ASG to accommodate function bodies to be optional,
|
||||
although this may be already the case for representing BLAKE2s in its current form described above.
|
||||
|
||||
The compiler should know which files are part of the Leo standard/core libraries and which ones are not.
|
||||
Functions without bodies will be only allowed to appear in those files.
|
||||
It will be an error if any other file (e.g. user-defined) contains functions without bodies.
|
||||
Type checking/inference may be where we make this check, or perhaps in some other phase.
|
||||
|
||||
Because of the already existing support for multi-file compilation described above,
|
||||
no essential change is needed in the compiler's type checking/inference.
|
||||
We just need to make sure that functions without bodies are expected and properly handled
|
||||
(i.e. their input and output types must be checked and added to the proper symbol tables,
|
||||
but their absent bodies must be skipped);
|
||||
this may already be the case, for the treatment of BLAKE2s described above.
|
||||
|
||||
The main change is in R1CS generation.
|
||||
Normally, when a function definition is encountered, its Leo body is translated to R1CS.
|
||||
For a native function, we need to find and use a known gadget instead.
|
||||
The compiler must know a mapping from native functions in the standard/core libraries
|
||||
to the R1CS gadgets that implement them, so it should be just a matter of selecting the appropriate one.
|
||||
Some of this logic must be already present, in order to detect and select the BLAkE2s gadget.
|
||||
|
||||
This approach is used in Java, where Java files may declare certain methods as `native`,
|
||||
without a body but with a declaration of input and output types.
|
||||
The actual native implementations, i.e. the native method bodies live in different files, as they are written in C.
|
||||
|
||||
# Drawbacks
|
||||
|
||||
This does not seem to bring any drawbacks.
|
||||
A capability for native functions (for BLAKE2s) already exists;
|
||||
this RFC proposes a way to make it more flexible,
|
||||
with mild (and likely simplifying) changes to the compiler.
|
||||
|
||||
# Effect on Ecosystem
|
||||
|
||||
This should help support richer standard/core libraries for Leo.
|
||||
|
||||
# Alternatives
|
||||
|
||||
## Programmatic Generation
|
||||
|
||||
Instead of storing declarations of native functions in standard/core files as proposed above,
|
||||
we could programmatically generate them as currently done for BLAKE2s.
|
||||
Macros may be used to generate families of similar function declarations.
|
||||
|
||||
However, consider `Point2D` above, which has a mix or native and non-native functions.
|
||||
One approach is to programmatically generate the whole `Point2D` declarative,
|
||||
with both native and non-native functions.
|
||||
But it seems that a Leo file would be clearer and more maintainable than a Rust string in the compiler.
|
||||
We could think of splitting the non-native and native functions of `Point2D`:
|
||||
the former in a Leo file, and the latter programmatically added.
|
||||
Again, this looks more complicated than just declaring native funcions in Leo files.
|
||||
|
||||
## Leo Code in Rust Files
|
||||
|
||||
It has been pointed out that it would be beneficial to have
|
||||
both the Leo code (for the non-native functions)
|
||||
and the Rust code (for the native functions)
|
||||
in the same place (i.e. file).
|
||||
This is not possible if the non-native code is in a Leo file, because Leo files cannot contain Rust code
|
||||
(and there is no plan to allow that, i.e. no inline Rust code).
|
||||
|
||||
However, we can turn things around and leverage Rust's macro system to accommodate Leo code in Rust files.
|
||||
That is, we can have Rust files that include both the non-native Leo code,
|
||||
written as Leo code (with some surrounding macro call or something like that),
|
||||
along with the Rust code that implements the naive functions.
|
||||
|
||||
This may turn out to be in fact the preferred design in the end,
|
||||
as it combines the advantage of writing non-native code in Leo
|
||||
with the advantage of having native and non-native code in the same place.
|
||||
In that case, we will revise this RFC to swap this design proposal with the one in the main section,
|
||||
moving the proposal for Leo files to this section as an alternative.
|
78
docs/rfc/011-scalar-type-accesses-and-methods.md
Normal file
78
docs/rfc/011-scalar-type-accesses-and-methods.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Leo RFC 011: Scalar Type Accesses And Methods
|
||||
|
||||
## Authors
|
||||
|
||||
- Max Bruce
|
||||
- Collin Chin
|
||||
- Alessandro Coglio
|
||||
- Eric McCarthy
|
||||
- Jon Pavlik
|
||||
- Damir Shamanaev
|
||||
- Damon Sicore
|
||||
- Howard Wu
|
||||
|
||||
## Status
|
||||
|
||||
DRAFT
|
||||
|
||||
## Summary
|
||||
|
||||
This RFC proposes two things:
|
||||
|
||||
1. The scalar types in Leo (integers, fields, etc.) can have static methods.
|
||||
2. The scalar types in Leo (integers, fields, etc.) can have static constants.
|
||||
3. Those values that have a scalar type can have methods directly on them.
|
||||
|
||||
## Motivation
|
||||
|
||||
This approach allows for a clean interface to provide built-in methods or static members for these basic types.
|
||||
|
||||
## Design
|
||||
|
||||
### Semantics
|
||||
|
||||
Firstly we would have to modify both the ABNF and parsing of Leo to allow static method calls onto a scalar type.
|
||||
|
||||
The ABNF would look as follows:
|
||||
|
||||
```abnf
|
||||
; This is an existing old rule
|
||||
scalar-type = boolean-type / arithmetic-type / address-type / character-type
|
||||
|
||||
; This is an existing old rule
|
||||
circuit-type = identifier / self-type
|
||||
|
||||
; Add this rule.
|
||||
named-type = circuit-type / scalar-type ; new rule
|
||||
|
||||
; Modify this rule:
|
||||
postfix-expression = primary-expression
|
||||
/ postfix-expression "." natural
|
||||
/ postfix-expression "." identifier
|
||||
/ identifier function-arguments
|
||||
/ postfix-expression "." identifier function-arguments
|
||||
/ named-type "::" identifier function-arguments ; this used to be a circuit-type
|
||||
/ named-type "::" identifier ; this is new to allow static members on
|
||||
/ postfix-expression "[" expression "]"
|
||||
/ postfix-expression "[" [expression] ".." [expression] "]"
|
||||
```
|
||||
|
||||
Now methods and static members would be first-class citizens of scalar types and their values. For example, the following could be done:
|
||||
|
||||
```ts
|
||||
let x = 1u8.to_bits(); // A method call on on a scalar value itself
|
||||
let x = u8::MAX; // A constant value on the scalar type
|
||||
let y = u8::to_bits(1u8); // A static method on the scalar type
|
||||
```
|
||||
|
||||
## Drawbacks
|
||||
|
||||
This change adds more complexity to the language.
|
||||
|
||||
## Effect on Ecosystem
|
||||
|
||||
None. The new parsing changes would not break any older programs.
|
||||
|
||||
## Alternatives
|
||||
|
||||
None.
|
@ -466,7 +466,7 @@ described above.
|
||||
newline = line-feed / carriage-return / carriage-return line-feed
|
||||
```
|
||||
|
||||
Go to: _[carriage-return](#user-content-carriage-return), [line-feed](#user-content-line-feed)_;
|
||||
Go to: _[line-feed](#user-content-line-feed), [carriage-return](#user-content-carriage-return)_;
|
||||
|
||||
|
||||
Line terminators form whitespace, along with spaces and horizontal tabs.
|
||||
@ -476,7 +476,7 @@ Line terminators form whitespace, along with spaces and horizontal tabs.
|
||||
whitespace = space / horizontal-tab / newline
|
||||
```
|
||||
|
||||
Go to: _[horizontal-tab](#user-content-horizontal-tab), [newline](#user-content-newline), [space](#user-content-space)_;
|
||||
Go to: _[space](#user-content-space), [horizontal-tab](#user-content-horizontal-tab), [newline](#user-content-newline)_;
|
||||
|
||||
|
||||
There are two kinds of comments in Leo, as in other languages.
|
||||
@ -494,7 +494,7 @@ the ones used in the Java language reference.
|
||||
comment = block-comment / end-of-line-comment
|
||||
```
|
||||
|
||||
Go to: _[end-of-line-comment](#user-content-end-of-line-comment), [block-comment](#user-content-block-comment)_;
|
||||
Go to: _[block-comment](#user-content-block-comment), [end-of-line-comment](#user-content-end-of-line-comment)_;
|
||||
|
||||
|
||||
<a name="block-comment"></a>
|
||||
@ -521,7 +521,7 @@ rest-of-block-comment-after-star = "/"
|
||||
/ not-star-or-slash rest-of-block-comment
|
||||
```
|
||||
|
||||
Go to: _[not-star-or-slash](#user-content-not-star-or-slash), [rest-of-block-comment](#user-content-rest-of-block-comment), [rest-of-block-comment-after-star](#user-content-rest-of-block-comment-after-star)_;
|
||||
Go to: _[rest-of-block-comment](#user-content-rest-of-block-comment), [rest-of-block-comment-after-star](#user-content-rest-of-block-comment-after-star), [not-star-or-slash](#user-content-not-star-or-slash)_;
|
||||
|
||||
|
||||
<a name="end-of-line-comment"></a>
|
||||
@ -589,7 +589,7 @@ lowercase-letter = %x61-7A ; a-z
|
||||
letter = uppercase-letter / lowercase-letter
|
||||
```
|
||||
|
||||
Go to: _[uppercase-letter](#user-content-uppercase-letter), [lowercase-letter](#user-content-lowercase-letter)_;
|
||||
Go to: _[lowercase-letter](#user-content-lowercase-letter), [uppercase-letter](#user-content-uppercase-letter)_;
|
||||
|
||||
|
||||
The following rules defines (ASCII) decimal, octal, and hexadecimal digits.
|
||||
@ -762,7 +762,7 @@ and Unicode escapes with one to six hexadecimal digits
|
||||
character-literal = single-quote character-literal-element single-quote
|
||||
```
|
||||
|
||||
Go to: _[single-quote](#user-content-single-quote), [character-literal-element](#user-content-character-literal-element)_;
|
||||
Go to: _[character-literal-element](#user-content-character-literal-element), [single-quote](#user-content-single-quote)_;
|
||||
|
||||
|
||||
<a name="character-literal-element"></a>
|
||||
@ -773,7 +773,7 @@ character-literal-element = not-single-quote-or-backslash
|
||||
/ unicode-character-escape
|
||||
```
|
||||
|
||||
Go to: _[not-single-quote-or-backslash](#user-content-not-single-quote-or-backslash), [ascii-character-escape](#user-content-ascii-character-escape), [unicode-character-escape](#user-content-unicode-character-escape), [simple-character-escape](#user-content-simple-character-escape)_;
|
||||
Go to: _[unicode-character-escape](#user-content-unicode-character-escape), [not-single-quote-or-backslash](#user-content-not-single-quote-or-backslash), [simple-character-escape](#user-content-simple-character-escape), [ascii-character-escape](#user-content-ascii-character-escape)_;
|
||||
|
||||
|
||||
<a name="single-quote-escape"></a>
|
||||
@ -828,7 +828,7 @@ simple-character-escape = single-quote-escape
|
||||
/ null-character-escape
|
||||
```
|
||||
|
||||
Go to: _[carriage-return-escape](#user-content-carriage-return-escape), [horizontal-tab-escape](#user-content-horizontal-tab-escape), [null-character-escape](#user-content-null-character-escape), [double-quote-escape](#user-content-double-quote-escape), [single-quote-escape](#user-content-single-quote-escape), [backslash-escape](#user-content-backslash-escape), [line-feed-escape](#user-content-line-feed-escape)_;
|
||||
Go to: _[carriage-return-escape](#user-content-carriage-return-escape), [horizontal-tab-escape](#user-content-horizontal-tab-escape), [double-quote-escape](#user-content-double-quote-escape), [backslash-escape](#user-content-backslash-escape), [null-character-escape](#user-content-null-character-escape), [single-quote-escape](#user-content-single-quote-escape), [line-feed-escape](#user-content-line-feed-escape)_;
|
||||
|
||||
|
||||
<a name="ascii-character-escape"></a>
|
||||
@ -864,7 +864,7 @@ string-literal-element = not-double-quote-or-backslash
|
||||
/ unicode-character-escape
|
||||
```
|
||||
|
||||
Go to: _[ascii-character-escape](#user-content-ascii-character-escape), [unicode-character-escape](#user-content-unicode-character-escape), [simple-character-escape](#user-content-simple-character-escape), [not-double-quote-or-backslash](#user-content-not-double-quote-or-backslash)_;
|
||||
Go to: _[not-double-quote-or-backslash](#user-content-not-double-quote-or-backslash), [simple-character-escape](#user-content-simple-character-escape), [unicode-character-escape](#user-content-unicode-character-escape), [ascii-character-escape](#user-content-ascii-character-escape)_;
|
||||
|
||||
|
||||
The ones above are all the atomic literals
|
||||
@ -884,7 +884,7 @@ atomic-literal = untyped-literal
|
||||
/ string-literal
|
||||
```
|
||||
|
||||
Go to: _[signed-literal](#user-content-signed-literal), [untyped-literal](#user-content-untyped-literal), [product-group-literal](#user-content-product-group-literal), [unsigned-literal](#user-content-unsigned-literal), [character-literal](#user-content-character-literal), [string-literal](#user-content-string-literal), [field-literal](#user-content-field-literal), [boolean-literal](#user-content-boolean-literal), [address-literal](#user-content-address-literal)_;
|
||||
Go to: _[field-literal](#user-content-field-literal), [untyped-literal](#user-content-untyped-literal), [address-literal](#user-content-address-literal), [unsigned-literal](#user-content-unsigned-literal), [character-literal](#user-content-character-literal), [boolean-literal](#user-content-boolean-literal), [signed-literal](#user-content-signed-literal), [string-literal](#user-content-string-literal), [product-group-literal](#user-content-product-group-literal)_;
|
||||
|
||||
|
||||
After defining the (mostly) alphanumeric tokens above,
|
||||
@ -928,7 +928,7 @@ token = keyword
|
||||
/ symbol
|
||||
```
|
||||
|
||||
Go to: _[identifier](#user-content-identifier), [keyword](#user-content-keyword), [symbol](#user-content-symbol), [package-name](#user-content-package-name), [annotation-name](#user-content-annotation-name), [atomic-literal](#user-content-atomic-literal)_;
|
||||
Go to: _[identifier](#user-content-identifier), [symbol](#user-content-symbol), [keyword](#user-content-keyword), [atomic-literal](#user-content-atomic-literal), [package-name](#user-content-package-name), [annotation-name](#user-content-annotation-name)_;
|
||||
|
||||
|
||||
Tokens, comments, and whitespace are lexemes, i.e. lexical units.
|
||||
@ -995,7 +995,7 @@ group-type = %s"group"
|
||||
arithmetic-type = integer-type / field-type / group-type
|
||||
```
|
||||
|
||||
Go to: _[integer-type](#user-content-integer-type), [field-type](#user-content-field-type), [group-type](#user-content-group-type)_;
|
||||
Go to: _[field-type](#user-content-field-type), [integer-type](#user-content-integer-type), [group-type](#user-content-group-type)_;
|
||||
|
||||
|
||||
The arithmetic types, along with the boolean, address, and character types,
|
||||
@ -1021,7 +1021,7 @@ character-type = %s"char"
|
||||
scalar-type = boolean-type / arithmetic-type / address-type / character-type
|
||||
```
|
||||
|
||||
Go to: _[character-type](#user-content-character-type), [arithmetic-type](#user-content-arithmetic-type), [boolean-type](#user-content-boolean-type), [address-type](#user-content-address-type)_;
|
||||
Go to: _[boolean-type](#user-content-boolean-type), [arithmetic-type](#user-content-arithmetic-type), [character-type](#user-content-character-type), [address-type](#user-content-address-type)_;
|
||||
|
||||
|
||||
Circuit types are denoted by identifiers and the keyword `Self`.
|
||||
@ -1038,7 +1038,7 @@ self-type = %s"Self"
|
||||
circuit-type = identifier / self-type
|
||||
```
|
||||
|
||||
Go to: _[identifier](#user-content-identifier), [self-type](#user-content-self-type)_;
|
||||
Go to: _[self-type](#user-content-self-type), [identifier](#user-content-identifier)_;
|
||||
|
||||
|
||||
A tuple type consists of zero, two, or more component types.
|
||||
@ -1082,7 +1082,7 @@ i.e. types whose values contain (sub-)values
|
||||
aggregate-type = tuple-type / array-type / circuit-type
|
||||
```
|
||||
|
||||
Go to: _[circuit-type](#user-content-circuit-type), [tuple-type](#user-content-tuple-type), [array-type](#user-content-array-type)_;
|
||||
Go to: _[array-type](#user-content-array-type), [tuple-type](#user-content-tuple-type), [circuit-type](#user-content-circuit-type)_;
|
||||
|
||||
|
||||
Scalar and aggregate types form all the types.
|
||||
@ -1128,7 +1128,7 @@ A literal is either an atomic one or an affine group literal.
|
||||
literal = atomic-literal / affine-group-literal
|
||||
```
|
||||
|
||||
Go to: _[affine-group-literal](#user-content-affine-group-literal), [atomic-literal](#user-content-atomic-literal)_;
|
||||
Go to: _[atomic-literal](#user-content-atomic-literal), [affine-group-literal](#user-content-affine-group-literal)_;
|
||||
|
||||
|
||||
The following rule is not directly referenced in the rules for expressions
|
||||
@ -1170,7 +1170,7 @@ primary-expression = identifier
|
||||
/ circuit-expression
|
||||
```
|
||||
|
||||
Go to: _[array-expression](#user-content-array-expression), [expression](#user-content-expression), [circuit-expression](#user-content-circuit-expression), [identifier](#user-content-identifier), [tuple-expression](#user-content-tuple-expression), [literal](#user-content-literal)_;
|
||||
Go to: _[tuple-expression](#user-content-tuple-expression), [expression](#user-content-expression), [circuit-expression](#user-content-circuit-expression), [identifier](#user-content-identifier), [array-expression](#user-content-array-expression), [literal](#user-content-literal)_;
|
||||
|
||||
|
||||
Tuple expressions construct tuples.
|
||||
@ -1318,7 +1318,7 @@ postfix-expression = primary-expression
|
||||
/ postfix-expression "[" [expression] ".." [expression] "]"
|
||||
```
|
||||
|
||||
Go to: _[identifier](#user-content-identifier), [circuit-type](#user-content-circuit-type), [function-arguments](#user-content-function-arguments), [natural](#user-content-natural), [primary-expression](#user-content-primary-expression), [expression](#user-content-expression), [postfix-expression](#user-content-postfix-expression)_;
|
||||
Go to: _[function-arguments](#user-content-function-arguments), [primary-expression](#user-content-primary-expression), [identifier](#user-content-identifier), [postfix-expression](#user-content-postfix-expression), [circuit-type](#user-content-circuit-type), [expression](#user-content-expression), [natural](#user-content-natural)_;
|
||||
|
||||
|
||||
Unary operators have the highest operator precedence.
|
||||
@ -1346,7 +1346,7 @@ exponential-expression = unary-expression
|
||||
/ unary-expression "**" exponential-expression
|
||||
```
|
||||
|
||||
Go to: _[unary-expression](#user-content-unary-expression), [exponential-expression](#user-content-exponential-expression)_;
|
||||
Go to: _[exponential-expression](#user-content-exponential-expression), [unary-expression](#user-content-unary-expression)_;
|
||||
|
||||
|
||||
Next in precedence come multiplication and division, both left-associative.
|
||||
@ -1428,12 +1428,12 @@ Finally we have conditional expressions.
|
||||
<a name="conditional-expression"></a>
|
||||
```abnf
|
||||
conditional-expression = disjunctive-expression
|
||||
/ conditional-expression
|
||||
/ disjunctive-expression
|
||||
"?" expression
|
||||
":" conditional-expression
|
||||
```
|
||||
|
||||
Go to: _[disjunctive-expression](#user-content-disjunctive-expression), [expression](#user-content-expression), [conditional-expression](#user-content-conditional-expression)_;
|
||||
Go to: _[conditional-expression](#user-content-conditional-expression), [disjunctive-expression](#user-content-disjunctive-expression), [expression](#user-content-expression)_;
|
||||
|
||||
|
||||
Those above are all the expressions.
|
||||
@ -1466,7 +1466,7 @@ statement = expression-statement
|
||||
/ block
|
||||
```
|
||||
|
||||
Go to: _[loop-statement](#user-content-loop-statement), [assignment-statement](#user-content-assignment-statement), [variable-declaration](#user-content-variable-declaration), [expression-statement](#user-content-expression-statement), [conditional-statement](#user-content-conditional-statement), [block](#user-content-block), [console-statement](#user-content-console-statement), [return-statement](#user-content-return-statement), [constant-declaration](#user-content-constant-declaration)_;
|
||||
Go to: _[constant-declaration](#user-content-constant-declaration), [conditional-statement](#user-content-conditional-statement), [expression-statement](#user-content-expression-statement), [return-statement](#user-content-return-statement), [console-statement](#user-content-console-statement), [block](#user-content-block), [loop-statement](#user-content-loop-statement), [assignment-statement](#user-content-assignment-statement), [variable-declaration](#user-content-variable-declaration)_;
|
||||
|
||||
|
||||
<a name="block"></a>
|
||||
@ -1509,7 +1509,7 @@ variable-declaration = %s"let" identifier-or-identifiers [ ":" type ]
|
||||
"=" expression ";"
|
||||
```
|
||||
|
||||
Go to: _[type](#user-content-type), [identifier-or-identifiers](#user-content-identifier-or-identifiers), [expression](#user-content-expression)_;
|
||||
Go to: _[identifier-or-identifiers](#user-content-identifier-or-identifiers), [expression](#user-content-expression), [type](#user-content-type)_;
|
||||
|
||||
|
||||
<a name="constant-declaration"></a>
|
||||
@ -1563,7 +1563,7 @@ The body is a block.
|
||||
loop-statement = %s"for" identifier %s"in" expression ".." [ "=" ] expression block
|
||||
```
|
||||
|
||||
Go to: _[expression](#user-content-expression), [block](#user-content-block), [identifier](#user-content-identifier)_;
|
||||
Go to: _[expression](#user-content-expression), [identifier](#user-content-identifier), [block](#user-content-block)_;
|
||||
|
||||
|
||||
An assignment statement is straightforward.
|
||||
@ -1649,7 +1649,7 @@ annotation = annotation-name
|
||||
[ "(" identifier *( "," identifier ) ")" ]
|
||||
```
|
||||
|
||||
Go to: _[annotation-name](#user-content-annotation-name), [identifier](#user-content-identifier)_;
|
||||
Go to: _[identifier](#user-content-identifier), [annotation-name](#user-content-annotation-name)_;
|
||||
|
||||
|
||||
A function declaration defines a function.
|
||||
@ -1666,7 +1666,7 @@ function-declaration = *annotation %s"function" identifier
|
||||
block
|
||||
```
|
||||
|
||||
Go to: _[identifier](#user-content-identifier), [function-parameters](#user-content-function-parameters), [type](#user-content-type), [block](#user-content-block)_;
|
||||
Go to: _[block](#user-content-block), [identifier](#user-content-identifier), [type](#user-content-type), [function-parameters](#user-content-function-parameters)_;
|
||||
|
||||
|
||||
<a name="function-parameters"></a>
|
||||
@ -1676,7 +1676,7 @@ function-parameters = self-parameter
|
||||
/ function-inputs
|
||||
```
|
||||
|
||||
Go to: _[self-parameter](#user-content-self-parameter), [function-inputs](#user-content-function-inputs)_;
|
||||
Go to: _[function-inputs](#user-content-function-inputs), [self-parameter](#user-content-self-parameter)_;
|
||||
|
||||
|
||||
<a name="self-parameter"></a>
|
||||
@ -1697,7 +1697,7 @@ Go to: _[function-input](#user-content-function-input)_;
|
||||
function-input = [ %s"const" ] identifier ":" type
|
||||
```
|
||||
|
||||
Go to: _[type](#user-content-type), [identifier](#user-content-identifier)_;
|
||||
Go to: _[identifier](#user-content-identifier), [type](#user-content-type)_;
|
||||
|
||||
|
||||
A circuit member variable declaration consists of
|
||||
@ -1745,7 +1745,7 @@ circuit-declaration = %s"circuit" identifier
|
||||
*member-function-declaration "}"
|
||||
```
|
||||
|
||||
Go to: _[member-variable-declarations](#user-content-member-variable-declarations), [identifier](#user-content-identifier)_;
|
||||
Go to: _[identifier](#user-content-identifier), [member-variable-declarations](#user-content-member-variable-declarations)_;
|
||||
|
||||
|
||||
An import declaration consists of the `import` keyword
|
||||
@ -1767,7 +1767,7 @@ by using an explicit package name before the package path.
|
||||
import-declaration = %s"import" package-name "." package-path ";"
|
||||
```
|
||||
|
||||
Go to: _[package-name](#user-content-package-name), [package-path](#user-content-package-path)_;
|
||||
Go to: _[package-path](#user-content-package-path), [package-name](#user-content-package-name)_;
|
||||
|
||||
|
||||
<a name="package-path"></a>
|
||||
|
@ -871,7 +871,7 @@ disjunctive-expression = conjunctive-expression
|
||||
; Finally we have conditional expressions.
|
||||
|
||||
conditional-expression = disjunctive-expression
|
||||
/ conditional-expression
|
||||
/ disjunctive-expression
|
||||
"?" expression
|
||||
":" conditional-expression
|
||||
|
||||
|
@ -55,10 +55,9 @@ impl<'a> ImportResolver<'a> for ImportParser<'a> {
|
||||
if let Some(program) = self.imports.get(&full_path) {
|
||||
return Ok(Some(program.clone()));
|
||||
}
|
||||
let mut imports = Self::default();
|
||||
let path = self.program_path.clone();
|
||||
|
||||
self.partial_imports.insert(full_path.clone());
|
||||
let mut imports = self.clone(); // Self::default() was previously
|
||||
let program = imports
|
||||
.parse_package(context, path, package_segments, span)
|
||||
.map_err(|x| -> LeoError { x })?;
|
||||
|
@ -33,11 +33,11 @@ version = "1.5.3"
|
||||
version = "0.7.6"
|
||||
|
||||
[dependencies.snarkvm-curves]
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-dpc]
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
features = [ "testnet1" ]
|
||||
|
||||
[dependencies.snarkvm-utilities]
|
||||
|
@ -18,7 +18,7 @@ license = "GPL-3.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies.snarkvm-curves]
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
default-features = false
|
||||
|
||||
[dependencies.leo-errors]
|
||||
|
Loading…
Reference in New Issue
Block a user