1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

[Tolk] Get rid of ~tilda with mutate and self methods

This is a very big change.
If FunC has `.methods()` and `~methods()`, Tolk has only dot,
one and only way to call a `.method()`.
A method may mutate an object, or may not.
It's a behavioral and semantic difference from FunC.

- `cs.loadInt(32)` modifies a slice and returns an integer
- `b.storeInt(x, 32)` modifies a builder
- `b = b.storeInt()` also works, since it not only modifies, but returns
- chained methods also work, they return `self`
- everything works exactly as expected, similar to JS
- no runtime overhead, exactly same Fift instructions
- custom methods are created with ease
- tilda `~` does not exist in Tolk at all
This commit is contained in:
tolk-vm 2024-10-31 11:18:54 +04:00
parent 12ff28ac94
commit d9dba320cc
No known key found for this signature in database
GPG key ID: 7905DD7FE0324B12
85 changed files with 2710 additions and 1965 deletions

View file

@ -17,11 +17,7 @@ fun createEmptyTuple(): tuple
/// Appends a value to tuple, resulting in `Tuple t' = (x1, ..., xn, value)`.
/// If its size exceeds 255, throws a type check exception.
@pure
fun tuplePush<X>(t: tuple, value: X): tuple
asm "TPUSH";
@pure
fun ~tuplePush<X>(t: tuple, value: X): (tuple, ())
fun tuplePush<X>(mutate self: tuple, value: X): void
asm "TPUSH";
/// Returns the first element of a non-empty tuple.
@ -336,118 +332,109 @@ fun beginParse(c: cell): slice
asm "CTOS";
/// Checks if slice is empty. If not, throws an exception.
fun assertEndOfSlice(s: slice): void
fun assertEndOfSlice(self: slice): void
asm "ENDS";
/// Loads the next reference from the slice.
@pure
fun loadRef(s: slice): (slice, cell)
fun loadRef(mutate self: slice): cell
asm( -> 1 0) "LDREF";
/// Preloads the next reference from the slice.
@pure
fun preloadRef(s: slice): cell
fun preloadRef(self: slice): cell
asm "PLDREF";
/// Loads a signed [len]-bit integer from a slice.
@pure
fun loadInt(s: slice, len: int): (slice, int)
fun loadInt(mutate self: slice, len: int): int
builtin;
/// Loads an unsigned [len]-bit integer from a slice.
@pure
fun loadUint(s: slice, len: int): (slice, int)
fun loadUint(mutate self: slice, len: int): int
builtin;
/// Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice `s''`.
@pure
fun loadBits(s: slice, len: int): (slice, slice)
fun loadBits(mutate self: slice, len: int): slice
builtin;
/// Preloads a signed [len]-bit integer from a slice.
@pure
fun preloadInt(s: slice, len: int): int
fun preloadInt(self: slice, len: int): int
builtin;
/// Preloads an unsigned [len]-bit integer from a slice.
@pure
fun preloadUint(s: slice, len: int): int
fun preloadUint(self: slice, len: int): int
builtin;
/// Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice.
@pure
fun preloadBits(s: slice, len: int): slice
fun preloadBits(self: slice, len: int): slice
builtin;
/// Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`).
@pure
fun loadCoins(s: slice): (slice, int)
fun loadCoins(mutate self: slice): int
asm( -> 1 0) "LDGRAMS";
/// Loads bool (-1 or 0) from a slice
@pure
fun loadBool(s: slice): (slice, int)
fun loadBool(mutate self: slice): int
asm( -> 1 0) "1 LDI";
/// Shifts a slice pointer to [len] bits forward, mutating the slice.
@pure
fun skipBits(s: slice, len: int): slice
asm "SDSKIPFIRST"; // todo make mutating
@pure
fun ~skipBits(s: slice, len: int): (slice, ())
fun skipBits(mutate self: slice, len: int): self
asm "SDSKIPFIRST";
/// Returns the first `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun getFirstBits(s: slice, len: int): slice
fun getFirstBits(self: slice, len: int): slice
asm "SDCUTFIRST";
/// Returns all but the last `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun removeLastBits(s: slice, len: int): slice
asm "SDSKIPLAST"; // todo make mutating
@pure
fun ~removeLastBits(s: slice, len: int): (slice, ())
fun removeLastBits(mutate self: slice, len: int): self
asm "SDSKIPLAST";
/// Returns the last `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun getLastBits(s: slice, len: int): slice
fun getLastBits(self: slice, len: int): slice
asm "SDCUTLAST";
/// Loads a dictionary (TL HashMapE structure, represented as TVM cell) from a slice.
/// Returns `null` if `nothing` constructor is used.
@pure
fun loadDict(s: slice): (slice, cell)
fun loadDict(mutate self: slice): cell
asm( -> 1 0) "LDDICT";
/// Preloads a dictionary (cell) from a slice.
@pure
fun preloadDict(s: slice): cell
fun preloadDict(self: slice): cell
asm "PLDDICT";
/// Loads a dictionary as [loadDict], but returns only the remainder of the slice.
@pure
fun skipDict(s: slice): slice
asm "SKIPDICT"; // todo make mutating
@pure
fun ~skipDict(s: slice): (slice, ())
fun skipDict(mutate self: slice): self
asm "SKIPDICT";
/// Loads (Maybe ^Cell) from a slice.
/// In other words, loads 1 bit: if it's true, loads the first ref, otherwise returns `null`.
@pure
fun loadMaybeRef(s: slice): (slice, cell)
fun loadMaybeRef(mutate self: slice): cell
asm( -> 1 0) "LDOPTREF";
/// Preloads (Maybe ^Cell) from a slice.
@pure
fun preloadMaybeRef(s: slice): cell
fun preloadMaybeRef(self: slice): cell
asm "PLDOPTREF";
/// Loads (Maybe ^Cell), but returns only the remainder of the slice.
@pure
fun ~skipMaybeRef(s: slice): (slice, ())
fun skipMaybeRef(mutate self: slice): self
asm "SKIPOPTREF";
/**
@ -464,62 +451,60 @@ fun beginCell(): builder
/// Converts a builder into an ordinary `cell`.
@pure
fun endCell(b: builder): cell
fun endCell(self: builder): cell
asm "ENDC";
/// Stores a reference to a cell into a builder.
@pure
fun storeRef(b: builder, c: cell): builder
asm(c b) "STREF";
fun storeRef(mutate self: builder, c: cell): self
asm(c self) "STREF";
/// Stores a signed [len]-bit integer into a builder (`0 ≤ len ≤ 257`).
@pure
fun storeInt(b: builder, x: int, len: int): builder
fun storeInt(mutate self: builder, x: int, len: int): self
builtin;
/// Stores an unsigned [len]-bit integer into a builder (`0 ≤ len ≤ 256`).
@pure
fun storeUint(b: builder, x: int, len: int): builder
fun storeUint(mutate self: builder, x: int, len: int): self
builtin;
/// Stores a slice into a builder.
@pure
fun storeSlice(b: builder, s: slice): builder
fun storeSlice(mutate self: builder, s: slice): self
asm "STSLICER";
/// Stores amount of Toncoins into a builder.
@pure
fun storeCoins(b: builder, x: int): builder
fun storeCoins(mutate self: builder, x: int): self
asm "STGRAMS";
/// Stores bool (-1 or 0) into a builder.
/// Attention: true value is `-1`, not 1! If you pass `1` here, TVM will throw an exception.
@pure
fun storeBool(b: builder, x: int): builder
asm(x b) "1 STI";
fun storeBool(mutate self: builder, x: int): self
asm(x self) "1 STI";
/// Stores dictionary (represented by TVM `cell` or `null`) into a builder.
/// In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise.
@pure
fun storeDict(b: builder, c: cell): builder
asm(c b) "STDICT";
fun storeDict(mutate self: builder, c: cell): self
asm(c self) "STDICT";
/// Stores (Maybe ^Cell) into a builder.
/// In other words, if cell is `null`, store '0' bit; otherwise, store '1' and a ref to [c].
@pure
fun storeMaybeRef(b: builder, c: cell): builder
asm(c b) "STOPTREF";
fun storeMaybeRef(mutate self: builder, c: cell): self
asm(c self) "STOPTREF";
/// Concatenates two builders.
@pure
fun storeBuilder(to: builder, from: builder): builder
fun storeBuilder(mutate self: builder, from: builder): self
asm "STBR";
/// Stores a slice representing TL addr_none$00 (two `0` bits).
@pure
fun storeAddressNone(b: builder): builder
asm "0 PUSHINT" "SWAP" "2 STU";
@pure
fun ~storeAddressNone(b: builder): (builder, ())
fun storeAddressNone(mutate self: builder): self
asm "b{00} STSLICECONST";
@ -529,47 +514,47 @@ fun ~storeAddressNone(b: builder): (builder, ())
/// Returns the number of references in a slice.
@pure
fun getRemainingRefsCount(s: slice): int
fun getRemainingRefsCount(self: slice): int
asm "SREFS";
/// Returns the number of data bits in a slice.
@pure
fun getRemainingBitsCount(s: slice): int
fun getRemainingBitsCount(self: slice): int
asm "SBITS";
/// Returns both the number of data bits and the number of references in a slice.
@pure
fun getRemainingBitsAndRefsCount(s: slice): (int, int)
fun getRemainingBitsAndRefsCount(self: slice): (int, int)
asm "SBITREFS";
/// Checks whether a slice is empty (i.e., contains no bits of data and no cell references).
@pure
fun isEndOfSlice(s: slice): int
fun isEndOfSlice(self: slice): int
asm "SEMPTY";
/// Checks whether a slice has no bits of data.
@pure
fun isEndOfSliceBits(s: slice): int
fun isEndOfSliceBits(self: slice): int
asm "SDEMPTY";
/// Checks whether a slice has no references.
@pure
fun isEndOfSliceRefs(s: slice): int
fun isEndOfSliceRefs(self: slice): int
asm "SREMPTY";
/// Checks whether data parts of two slices coinside.
@pure
fun isSliceBitsEqual(a: slice, b: slice): int
fun isSliceBitsEqual(self: slice, b: slice): int
asm "SDEQ";
/// Returns the number of cell references already stored in a builder.
@pure
fun getBuilderRefsCount(b: builder): int
fun getBuilderRefsCount(self: builder): int
asm "BREFS";
/// Returns the number of data bits already stored in a builder.
@pure
fun getBuilderBitsCount(b: builder): int
fun getBuilderBitsCount(self: builder): int
asm "BBITS";
@ -613,8 +598,8 @@ fun getBuilderBitsCount(b: builder): int
/// Loads from slice [s] the only prefix that is a valid `MsgAddress`,
/// and returns both this prefix `s'` and the remainder `s''` of [s] as slices.
@pure
fun loadAddress(s: slice): (slice, slice)
asm( -> 1 0) "LDMSGADDR"; // todo make mutating
fun loadAddress(mutate self: slice): slice
asm( -> 1 0) "LDMSGADDR";
/// Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`.
/// If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown.
@ -686,7 +671,7 @@ const NON_BOUNCEABLE = 0x10;
/// Load msgFlags from incoming message body (4 bits).
@pure
fun loadMessageFlags(s: slice): (slice, int)
fun loadMessageFlags(mutate self: slice): int
asm( -> 1 0) "4 LDU";
/// Having msgFlags (4 bits), check that a message is bounced.
@ -697,38 +682,34 @@ fun isMessageBounced(msgFlags: int): int
/// Skip 0xFFFFFFFF prefix (when a message is bounced).
@pure
fun ~skipBouncedPrefix(s: slice): (slice, ())
fun skipBouncedPrefix(mutate self: slice): self
asm "32 PUSHINT" "SDSKIPFIRST";
/// The guideline recommends to start the body of an internal message with uint32 `op` and uint64 `queryId`.
@pure
fun loadMessageOp(s: slice): (slice, int)
fun loadMessageOp(mutate self: slice): int
asm( -> 1 0) "32 LDU";
@pure
fun ~skipMessageOp(s: slice): (slice, ())
fun skipMessageOp(mutate self: slice): self
asm "32 PUSHINT" "SDSKIPFIRST";
@pure
fun storeMessageOp(b: builder, op: int): builder
asm(op b) "32 STU";
fun ~storeMessageOp(b: builder, op: int): (builder, ())
asm(op b) "32 STU";
fun storeMessageOp(mutate self: builder, op: int): self
asm(op self) "32 STU";
/// The guideline recommends that uint64 `queryId` should follow uint32 `op`.
@pure
fun loadMessageQueryId(s: slice): (slice, int)
fun loadMessageQueryId(mutate self: slice): int
asm( -> 1 0) "64 LDU";
@pure
fun ~skipMessageQueryId(s: slice): (slice, ())
fun skipMessageQueryId(mutate self: slice): self
asm "64 PUSHINT" "SDSKIPFIRST";
@pure
fun storeMessageQueryId(b: builder, queryId: int): builder
asm(queryId b) "64 STU";
fun ~storeMessageQueryId(b: builder, queryId: int): (builder, ())
asm(queryId b) "64 STU";
fun storeMessageQueryId(mutate self: builder, queryId: int): self
asm(queryId self) "64 STU";
/// SEND MODES - https://docs.ton.org/tvm.pdf page 137, SENDRAWMSG

View file

@ -24,7 +24,7 @@ fun listSplit<X>(list: tuple): (X, tuple)
/// Extracts the tail and the head of lisp-style list.
@pure
fun ~listNext<X>(list: tuple): (tuple, X)
fun listNext<X>(mutate self: tuple): X
asm( -> 1 0) "UNCONS";
/// Returns the head of lisp-style list.

View file

@ -19,415 +19,279 @@ fun createEmptyDict(): cell
/// Checks whether a dictionary is empty.
@pure
fun dictIsEmpty(c: cell): int
fun dictIsEmpty(self: cell): int
asm "DICTEMPTY";
@pure
fun iDictGet(dict: cell, keyLen: int, key: int): (slice, int)
asm(key dict keyLen) "DICTIGET" "NULLSWAPIFNOT";
fun iDictGet(self: cell, keyLen: int, key: int): (slice, int)
asm(key self keyLen) "DICTIGET" "NULLSWAPIFNOT";
@pure
fun uDictGet(dict: cell, keyLen: int, key: int): (slice, int)
asm(key dict keyLen) "DICTUGET" "NULLSWAPIFNOT";
fun uDictGet(self: cell, keyLen: int, key: int): (slice, int)
asm(key self keyLen) "DICTUGET" "NULLSWAPIFNOT";
@pure
fun sDictGet(dict: cell, keyLen: int, key: slice): (slice, int)
asm(key dict keyLen) "DICTGET" "NULLSWAPIFNOT";
fun sDictGet(self: cell, keyLen: int, key: slice): (slice, int)
asm(key self keyLen) "DICTGET" "NULLSWAPIFNOT";
@pure
fun iDictSet(dict: cell, keyLen: int, key: int, value: slice): cell
asm(value key dict keyLen) "DICTISET";
fun iDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
asm(value key self keyLen) "DICTISET";
@pure
fun ~iDictSet(dict: cell, keyLen: int, key: int, value: slice): (cell, ())
asm(value key dict keyLen) "DICTISET";
fun uDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
asm(value key self keyLen) "DICTUSET";
@pure
fun uDictSet(dict: cell, keyLen: int, key: int, value: slice): cell
asm(value key dict keyLen) "DICTUSET";
@pure
fun ~uDictSet(dict: cell, keyLen: int, key: int, value: slice): (cell, ())
asm(value key dict keyLen) "DICTUSET";
@pure
fun sDictSet(dict: cell, keyLen: int, key: slice, value: slice): cell
asm(value key dict keyLen) "DICTSET";
@pure
fun ~sDictSet(dict: cell, keyLen: int, key: slice, value: slice): (cell, ())
asm(value key dict keyLen) "DICTSET";
@pure
fun iDictSetRef(dict: cell, keyLen: int, key: int, value: cell): cell
asm(value key dict keyLen) "DICTISETREF";
@pure
fun ~iDictSetRef(dict: cell, keyLen: int, key: int, value: cell): (cell, ())
asm(value key dict keyLen) "DICTISETREF";
@pure
fun uDictSetRef(dict: cell, keyLen: int, key: int, value: cell): cell
asm(value key dict keyLen) "DICTUSETREF";
@pure
fun ~uDictSetRef(dict: cell, keyLen: int, key: int, value: cell): (cell, ())
asm(value key dict keyLen) "DICTUSETREF";
@pure
fun sDictSetRef(dict: cell, keyLen: int, key: slice, value: cell): cell
asm(value key dict keyLen) "DICTSETREF";
@pure
fun ~sDictSetRef(dict: cell, keyLen: int, key: slice, value: cell): (cell, ())
asm(value key dict keyLen) "DICTSETREF";
@pure
fun iDictSetIfNotExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTIADD";
@pure
fun ~iDictSetIfNotExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTIADD";
@pure
fun uDictSetIfNotExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTUADD";
@pure
fun ~uDictSetIfNotExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTUADD";
fun sDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): void
asm(value key self keyLen) "DICTSET";
@pure
fun iDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTIREPLACE";
fun iDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
asm(value key self keyLen) "DICTISETREF";
@pure
fun ~iDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTIREPLACE";
fun uDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
asm(value key self keyLen) "DICTUSETREF";
@pure
fun uDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTUREPLACE";
@pure
fun ~uDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTUREPLACE";
@pure
fun iDictGetRef(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTIGETREF" "NULLSWAPIFNOT";
@pure
fun uDictGetRef(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTUGETREF" "NULLSWAPIFNOT";
@pure
fun sDictGetRef(dict: cell, keyLen: int, key: slice): (cell, int)
asm(key dict keyLen) "DICTGETREF" "NULLSWAPIFNOT";
@pure
fun iDictGetRefOrNull(dict: cell, keyLen: int, key: int): cell
asm(key dict keyLen) "DICTIGETOPTREF";
@pure
fun uDictGetRefOrNull(dict: cell, keyLen: int, key: int): cell
asm(key dict keyLen) "DICTUGETOPTREF";
@pure
fun sDictGetRefOrNull(dict: cell, keyLen: int, key: slice): cell
asm(key dict keyLen) "DICTGETOPTREF";
fun sDictSetRef(mutate self: cell, keyLen: int, key: slice, value: cell): void
asm(value key self keyLen) "DICTSETREF";
@pure
fun iDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTIDEL";
fun iDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int
asm(value key self keyLen) "DICTIADD";
@pure
fun ~iDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTIDEL";
fun uDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int
asm(value key self keyLen) "DICTUADD";
@pure
fun uDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTUDEL";
@pure
fun ~uDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTUDEL";
fun iDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int
asm(value key self keyLen) "DICTIREPLACE";
@pure
fun sDictDelete(dict: cell, keyLen: int, key: slice): (cell, int)
asm(key dict keyLen) "DICTDEL";
fun uDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int
asm(value key self keyLen) "DICTUREPLACE";
@pure
fun ~sDictDelete(dict: cell, keyLen: int, key: slice): (cell, int)
asm(key dict keyLen) "DICTDEL";
@pure
fun iDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, slice, int)
asm(value key dict keyLen) "DICTISETGET" "NULLSWAPIFNOT";
@pure
fun ~iDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, (slice, int))
asm(value key dict keyLen) "DICTISETGET" "NULLSWAPIFNOT";
fun iDictGetRef(self: cell, keyLen: int, key: int): (cell, int)
asm(key self keyLen) "DICTIGETREF" "NULLSWAPIFNOT";
@pure
fun uDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, slice, int)
asm(value key dict keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
fun uDictGetRef(self: cell, keyLen: int, key: int): (cell, int)
asm(key self keyLen) "DICTUGETREF" "NULLSWAPIFNOT";
@pure
fun ~uDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, (slice, int))
asm(value key dict keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
fun sDictGetRef(self: cell, keyLen: int, key: slice): (cell, int)
asm(key self keyLen) "DICTGETREF" "NULLSWAPIFNOT";
@pure
fun sDictSetAndGet(dict: cell, keyLen: int, key: slice, value: slice): (cell, slice, int)
asm(value key dict keyLen) "DICTSETGET" "NULLSWAPIFNOT";
@pure
fun ~sDictSetAndGet(dict: cell, keyLen: int, key: slice, value: slice): (cell, (slice, int))
asm(value key dict keyLen) "DICTSETGET" "NULLSWAPIFNOT";
fun iDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
asm(key self keyLen) "DICTIGETOPTREF";
@pure
fun iDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
asm(value key dict keyLen) "DICTISETGETOPTREF";
fun uDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
asm(key self keyLen) "DICTUGETOPTREF";
@pure
fun ~iDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
asm(value key dict keyLen) "DICTISETGETOPTREF";
fun sDictGetRefOrNull(self: cell, keyLen: int, key: slice): cell
asm(key self keyLen) "DICTGETOPTREF";
@pure
fun uDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
asm(value key dict keyLen) "DICTUSETGETOPTREF";
@pure
fun ~uDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
asm(value key dict keyLen) "DICTUSETGETOPTREF";
fun iDictDelete(mutate self: cell, keyLen: int, key: int): int
asm(key self keyLen) "DICTIDEL";
@pure
fun iDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, slice, int)
asm(key dict keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
fun uDictDelete(mutate self: cell, keyLen: int, key: int): int
asm(key self keyLen) "DICTUDEL";
@pure
fun ~iDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, (slice, int))
asm(key dict keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
fun sDictDelete(mutate self: cell, keyLen: int, key: slice): int
asm(key self keyLen) "DICTDEL";
@pure
fun uDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, slice, int)
asm(key dict keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
@pure
fun ~uDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, (slice, int))
asm(key dict keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
fun iDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int)
asm(value key self keyLen) "DICTISETGET" "NULLSWAPIFNOT";
@pure
fun sDictDeleteAndGet(dict: cell, keyLen: int, key: slice): (cell, slice, int)
asm(key dict keyLen) "DICTDELGET" "NULLSWAPIFNOT";
fun uDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int)
asm(value key self keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
@pure
fun ~sDictDeleteAndGet(dict: cell, keyLen: int, key: slice): (cell, (slice, int))
asm(key dict keyLen) "DICTDELGET" "NULLSWAPIFNOT";
fun sDictSetAndGet(mutate self: cell, keyLen: int, key: slice, value: slice): (slice, int)
asm(value key self keyLen) "DICTSETGET" "NULLSWAPIFNOT";
@pure
fun iDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): cell
asm(value key dict keyLen) "DICTISETB";
fun iDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
asm(value key self keyLen) "DICTISETGETOPTREF";
@pure
fun ~iDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): (cell, ())
asm(value key dict keyLen) "DICTISETB";
fun uDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
asm(value key self keyLen) "DICTUSETGETOPTREF";
@pure
fun uDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): cell
asm(value key dict keyLen) "DICTUSETB";
@pure
fun ~uDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): (cell, ())
asm(value key dict keyLen) "DICTUSETB";
fun iDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int)
asm(key self keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
@pure
fun sDictSetBuilder(dict: cell, keyLen: int, key: slice, value: builder): cell
asm(value key dict keyLen) "DICTSETB";
fun uDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int)
asm(key self keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
@pure
fun ~sDictSetBuilder(dict: cell, keyLen: int, key: slice, value: builder): (cell, ())
asm(value key dict keyLen) "DICTSETB";
fun sDictDeleteAndGet(mutate self: cell, keyLen: int, key: slice): (slice, int)
asm(key self keyLen) "DICTDELGET" "NULLSWAPIFNOT";
@pure
fun iDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTIADDB";
fun iDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
asm(value key self keyLen) "DICTISETB";
@pure
fun ~iDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTIADDB";
fun uDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
asm(value key self keyLen) "DICTUSETB";
@pure
fun uDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTUADDB";
fun sDictSetBuilder(mutate self: cell, keyLen: int, key: slice, value: builder): void
asm(value key self keyLen) "DICTSETB";
@pure
fun ~uDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTUADDB";
@pure
fun iDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTIREPLACEB";
fun iDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int
asm(value key self keyLen) "DICTIADDB";
@pure
fun ~iDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTIREPLACEB";
fun uDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int
asm(value key self keyLen) "DICTUADDB";
@pure
fun uDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTUREPLACEB";
fun iDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int
asm(value key self keyLen) "DICTIREPLACEB";
@pure
fun ~uDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTUREPLACEB";
fun uDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int
asm(value key self keyLen) "DICTUREPLACEB";
@pure
fun iDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
fun iDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int)
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
@pure
fun ~iDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
@pure
fun uDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
fun uDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int)
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
@pure
fun ~uDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
@pure
fun sDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, slice, slice, int)
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
@pure
fun ~sDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, (slice, slice, int))
fun sDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (slice, slice, int)
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
@pure
fun iDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
fun iDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int)
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
@pure
fun ~iDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
@pure
fun uDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
fun uDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int)
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
@pure
fun ~uDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
@pure
fun sDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, slice, slice, int)
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
@pure
fun ~sDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, (slice, slice, int))
fun sDictDeleteLastAndGet(mutate self: cell, keyLen: int): (slice, slice, int)
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
@pure
fun iDictGetFirst(dict: cell, keyLen: int): (int, slice, int)
fun iDictGetFirst(self: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
@pure
fun uDictGetFirst(dict: cell, keyLen: int): (int, slice, int)
fun uDictGetFirst(self: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
@pure
fun sDictGetFirst(dict: cell, keyLen: int): (slice, slice, int)
fun sDictGetFirst(self: cell, keyLen: int): (slice, slice, int)
asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2";
@pure
fun iDictGetFirstAsRef(dict: cell, keyLen: int): (int, cell, int)
fun iDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
@pure
fun uDictGetFirstAsRef(dict: cell, keyLen: int): (int, cell, int)
fun uDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
@pure
fun sDictGetFirstAsRef(dict: cell, keyLen: int): (slice, cell, int)
fun sDictGetFirstAsRef(self: cell, keyLen: int): (slice, cell, int)
asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2";
@pure
fun iDictGetLast(dict: cell, keyLen: int): (int, slice, int)
fun iDictGetLast(self: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
@pure
fun uDictGetLast(dict: cell, keyLen: int): (int, slice, int)
fun uDictGetLast(self: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
@pure
fun sDictGetLast(dict: cell, keyLen: int): (slice, slice, int)
fun sDictGetLast(self: cell, keyLen: int): (slice, slice, int)
asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2";
@pure
fun iDictGetLastAsRef(dict: cell, keyLen: int): (int, cell, int)
fun iDictGetLastAsRef(self: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
@pure
fun uDictGetLastAsRef(dict: cell, keyLen: int): (int, cell, int)
fun uDictGetLastAsRef(self: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
@pure
fun sDictGetLastAsRef(dict: cell, keyLen: int): (slice, cell, int)
fun sDictGetLastAsRef(self: cell, keyLen: int): (slice, cell, int)
asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2";
@pure
fun iDictGetNext(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
fun iDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
@pure
fun uDictGetNext(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
fun uDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
@pure
fun iDictGetNextOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
fun iDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
@pure
fun uDictGetNextOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
fun uDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
@pure
fun iDictGetPrev(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
fun iDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
@pure
fun uDictGetPrev(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
fun uDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
@pure
fun iDictGetPrevOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
fun iDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
@pure
fun uDictGetPrevOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
/**
@ -435,13 +299,13 @@ fun uDictGetPrevOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
*/
@pure
fun prefixDictGet(dict: cell, keyLen: int, key: slice): (slice, slice, slice, int)
asm(key dict keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2";
fun prefixDictGet(self: cell, keyLen: int, key: slice): (slice, slice, slice, int)
asm(key self keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2";
@pure
fun prefixDictSet(dict: cell, keyLen: int, key: slice, value: slice): (cell, int)
asm(value key dict keyLen) "PFXDICTSET";
fun prefixDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): int
asm(value key self keyLen) "PFXDICTSET";
@pure
fun prefixDictDelete(dict: cell, keyLen: int, key: slice): (cell, int)
asm(key dict keyLen) "PFXDICTDEL";
fun prefixDictDelete(mutate self: cell, keyLen: int, key: slice): int
asm(key self keyLen) "PFXDICTDEL";

View file

@ -21,9 +21,5 @@ fun transformSliceToContinuation(s: slice): continuation
/// Moves a variable or a value [x] to the top of the stack.
@pure
fun stackMoveToTop<X>(x: X): X
fun stackMoveToTop<X>(mutate self: X): void
asm "NOP";
/// Mark a variable as used, such that the code which produced it won't be deleted even if it is not impure.
fun stackMoveToTopImpure<X>(x: X): void // todo needs to be deleted, check verified contracts
asm "DROP";

View file

@ -1,3 +1,5 @@
import "@stdlib/tvm-lowlevel"
fun pair_first<X, Y>(p: [X, Y]): X asm "FIRST";
fun one(dummy: tuple) {
@ -35,13 +37,34 @@ fun test88(x: int) {
@method_id(89)
fun test89(last: int) {
var t: tuple = createEmptyTuple();
t~tuplePush(1);
t~tuplePush(2);
t~tuplePush(3);
t~tuplePush(last);
t.tuplePush(1);
t.tuplePush(2);
t.tuplePush(3);
t.tuplePush(last);
return (t.tupleAt(0), t.tupleAt(t.tupleSize() - 1), t.tupleFirst(), t.tupleLast());
}
@pure fun get10() { return 10; }
@method_id(91)
fun touchCodegen2() {
var f = get10();
f.stackMoveToTop();
return f;
}
@method_id(92)
fun testDumpDontPolluteStack() {
var f = get10();
f.debugPrint();
debugPrint(10);
var s = "asdf";
s.debugPrintString();
debugDumpStack();
debugPrintString("my");
return (f, getRemainingBitsCount(s));
}
@method_id(93)
fun testStartBalanceCodegen1() {
var t = getMyOriginalBalanceWithExtraCurrencies();
@ -65,7 +88,27 @@ fun testStartBalanceCodegen2() {
@testcase | 88 | 5 | 234
@testcase | 88 | 50 | 0
@testcase | 89 | 4 | 1 4 1 4
@testcase | 91 | | 10
@testcase | 92 | | 10 32
@fif_codegen
"""
touchCodegen2 PROC:<{
//
get10 CALLDICT // f
}>
"""
@fif_codegen
"""
testDumpDontPolluteStack PROC:<{
...
DUMPSTK
x{6d79} PUSHSLICE // f s _9
STRDUMP DROP
SBITS // f _11
}>
"""
@fif_codegen
"""

View file

@ -4,8 +4,6 @@ fun f(a: int, b: int, c: int, d: int, e: int, f: int): (int, int) {
return (Dx/D,Dy/D);
};;;;
fun mulDivR(x: int, y: int, z: int): int { return mulDivRound(x, y, z); }
fun calc_phi(): int {
var n = 1;
repeat (70) { n*=10; };
@ -14,7 +12,7 @@ fun calc_phi(): int {
do {
(p,q)=(q,p+q);
} while (q <= n); //;;
return mulDivR(p, n, q);
return mulDivRound(p, n, q);
}
fun calc_sqrt2(): int {
@ -26,7 +24,7 @@ fun calc_sqrt2(): int {
var t = p + q;
(p, q) = (q, t + q);
} while (q <= n);
return mulDivR(p, n, q);
return mulDivRound(p, n, q);
}
fun calc_root(m: auto): auto {
@ -63,18 +61,14 @@ fun ataninv(base: int, q: int): int { // computes base*atan(1/q)
return sum;
}
fun arctanInv(base: int, q: int): int { return ataninv(base, q); }
fun calc_pi(): int {
var base: int = 64;
repeat (70) { base *= 10; }
return (arctanInv(base << 2, 5) - arctanInv(base, 239))~>>4;
return (ataninv(base << 2, 5) - ataninv(base, 239))~>>4;
}
fun calcPi(): int { return calc_pi(); }
fun main(): int {
return calcPi();
return calc_pi();
}
/**

View file

@ -5,12 +5,18 @@ fun main(a: int, b: int, c: int, d: int, e: int, f: int): (int, int) {
return (Dx / D, Dy / D);
}
@method_id(101)
fun testDivMod(x: int, y: int) {
return [divMod(x, y), modDiv(x, y), mulDivMod(x, y, 10)];
}
/**
method_id | in | out
@testcase | 0 | 1 1 1 -1 10 6 | 8 2
@testcase | 0 | 817 -31 624 -241 132272 272276 | 132 -788
@testcase | 0 | -886 562 498 -212 -36452 -68958 | -505 -861
@testcase | 0 | 448 -433 -444 792 150012 -356232 | -218 -572
@testcase | 0 | -40 -821 433 -734 -721629 -741724 | -206 889
@testcase | 0 | -261 -98 -494 868 -166153 733738 | 263 995
method_id | in | out
@testcase | 0 | 1 1 1 -1 10 6 | 8 2
@testcase | 0 | 817 -31 624 -241 132272 272276 | 132 -788
@testcase | 0 | -886 562 498 -212 -36452 -68958 | -505 -861
@testcase | 0 | 448 -433 -444 792 150012 -356232 | -218 -572
@testcase | 0 | -40 -821 433 -734 -721629 -741724 | -206 889
@testcase | 0 | -261 -98 -494 868 -166153 733738 | 263 995
@testcase | 101 | 112 3 | [ 37 1 1 37 33 6 ]
*/

View file

@ -4,35 +4,32 @@ fun unsafe_tuple<X>(x: X): tuple
fun inc(x: int, y: int): (int, int) {
return (x + y, y * 10);
}
fun ~inc(x: int, y: int): (int, int) {
(x, y) = inc(x, y);
return (x, y);
}
fun ~incWrap(x: int, y: int): (int, int) {
return ~inc(x, y);
fun `~inc`(mutate self: int, y: int): int {
val (newX, newY) = inc(self, y);
self = newX;
return newY;
}
@method_id(11)
fun test_return(x: int): (int, int, int, int, int, int, int) {
return (x, x~incWrap(x / 20), x, x = x * 2, x, x += 1, x);
return (x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x);
}
@method_id(12)
fun test_assign(x: int): (int, int, int, int, int, int, int) {
var (x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int) = (x, x~inc(x / 20), x, x=x*2, x, x+=1, x);
var (x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int) = (x, x.`~inc`(x / 20), x, x=x*2, x, x+=1, x);
return (x1, x2, x3, x4, x5, x6, x7);
}
@method_id(13)
fun test_tuple(x: int): tuple {
var t: tuple = unsafe_tuple([x, x~incWrap(x / 20), x, x = x * 2, x, x += 1, x]);
var t: tuple = unsafe_tuple([x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x]);
return t;
}
@method_id(14)
fun test_tuple_assign(x: int): (int, int, int, int, int, int, int) {
var [x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int] = [x, x~inc(x / 20), x, x = x * 2, x, x += 1, x];
var [x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int] = [x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x];
return (x1, x2, x3, x4, x5, x6, x7);
}
@ -42,7 +39,7 @@ fun foo1(x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int): (int, i
@method_id(15)
fun test_call_1(x: int): (int, int, int, int, int, int, int) {
return foo1(x, x~inc(x / 20), x, x = x * 2, x, x += 1, x);
return foo1(x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x);
}
fun foo2(x1: int, x2: int, x3456: (int, int, int, int), x7: int): (int, int, int, int, int, int, int) {
@ -52,7 +49,7 @@ fun foo2(x1: int, x2: int, x3456: (int, int, int, int), x7: int): (int, int, int
@method_id(16)
fun test_call_2(x: int): (int, int, int, int, int, int, int) {
return foo2(x, x~incWrap(x / 20), (x, x = x * 2, x, x += 1), x);
return foo2(x, x.`~inc`(x / 20), (x, x = x * 2, x, x += 1), x);
}
fun asm_func(x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int): (int, int, int, int, int, int, int)
@ -61,28 +58,28 @@ asm
@method_id(17)
fun test_call_asm_old(x: int): (int, int, int, int, int, int, int) {
return asm_func(x, x += 1, x, x, x~inc(x / 20), x, x = x * 2);
return asm_func(x, x += 1, x, x, x.`~inc`(x / 20), x, x = x * 2);
}
@method_id(18)
fun test_call_asm_new(x: int): (int, int, int, int, int, int, int) {
return asm_func(x, x~incWrap(x / 20), x, x = x * 2, x, x += 1, x);
return asm_func(x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x);
}
global xx: int;
@method_id(19)
fun test_global(x: int): (int, int, int, int, int, int, int) {
xx = x;
return (xx, xx~incWrap(xx / 20), xx, xx = xx * 2, xx, xx += 1, xx);
return (xx, xx.`~inc`(xx / 20), xx, xx = xx * 2, xx, xx += 1, xx);
}
@method_id(20)
fun test_if_else(x: int): (int, int, int, int, int) {
if (x > 10) {
return (x~inc(8), x + 1, x = 1, x <<= 3, x);
return (x.`~inc`(8), x + 1, x = 1, x <<= 3, x);
} else {
xx = 9;
return (x, x~inc(-4), x~inc(-1), x >= 1, x = x + xx);
return (x, x.`~inc`(-4), x.`~inc`(-1), x >= 1, x = x + xx);
}
}
@ -103,6 +100,12 @@ fun main() {
@testcase | 20 | 80 | 80 89 1 8 8
@testcase | 20 | 9 | 9 -40 -10 -1 13
@fif_codegen_avoid ~incWrap
@fif_codegen
"""
~inc PROC:<{
// self y
inc CALLDICT // self newY
}>
"""
@code_hash 97139400653362069936987769894397430077752335662822462908581556703209313861576
*/

View file

@ -2,10 +2,8 @@
fun empty_tuple2(): tuple
asm "NIL";
@pure
fun tpush2<X>(t: tuple, x: X): (tuple, ())
fun tpush2<X>(mutate self: tuple, x: X): void
asm "TPUSH";
fun myEmptyTuple(): tuple { return empty_tuple2(); }
fun myTuplePush<X>(t: tuple, value: X): (tuple, ()) { return tpush2(t, value); }
@pure
fun asm_func_1(x: int, y: int, z: int): tuple
@ -20,31 +18,27 @@ asm (y z x -> 0) "3 TUPLE";
fun asm_func_4(a: int, b: (int, (int, int)), c: int): tuple
asm (b a c -> 0) "5 TUPLE";
fun asmFunc1(x: int, y: int, z: int): tuple { return asm_func_1(x, y, z); }
fun asmFunc3(x: int, y: int, z: int): tuple { return asm_func_3(x, y, z); }
@pure
fun asm_func_modify(a: tuple, b: int, c: int): (tuple, ())
asm (c b a -> 0) "SWAP TPUSH SWAP TPUSH";
fun asmFuncModify(a: tuple, b: int, c: int): (tuple, ()) { return asm_func_modify(a, b, c); }
fun asm_func_modify(mutate self: tuple, b: int, c: int): void
asm (c b self) "SWAP TPUSH SWAP TPUSH";
global t: tuple;
fun foo(x: int): int {
t~myTuplePush(x);
t.tpush2(x);
return x * 10;
}
@method_id(11)
fun test_old_1(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asmFunc1(foo(11), foo(22), foo(33));
var t2: tuple = asm_func_1(foo(11), foo(22), foo(33));
return (t, t2);
}
@method_id(12)
fun test_old_2(): (tuple, tuple) {
t = myEmptyTuple();
t = empty_tuple2();
var t2: tuple = asm_func_2(foo(11), foo(22), foo(33));
return (t, t2);
}
@ -58,7 +52,7 @@ fun test_old_3(): (tuple, tuple) {
@method_id(14)
fun test_old_4(): (tuple, tuple) {
t = myEmptyTuple();
t = empty_tuple2();
var t2: tuple = empty_tuple2();
// This actually computes left-to-right even without compute-asm-ltr
t2 = asm_func_4(foo(11), (foo(22), (foo(33), foo(44))), foo(55));
@ -69,21 +63,21 @@ fun test_old_4(): (tuple, tuple) {
fun test_old_modify(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = empty_tuple2();
t2~asmFuncModify(foo(22), foo(33));
t2.asm_func_modify(foo(22), foo(33));
return (t, t2);
}
@method_id(16)
fun test_old_dot(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = foo(11).asmFunc3(foo(22), foo(33));
var t2: tuple = foo(11).asm_func_3(foo(22), foo(33));
return (t, t2);
}
@method_id(21)
fun test_new_1(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = asmFunc1(foo(11), foo(22), foo(33));
var t2: tuple = asm_func_1(foo(11), foo(22), foo(33));
return (t, t2);
}
@ -112,7 +106,7 @@ fun test_new_4(): (tuple, tuple) {
fun test_new_modify(): (tuple, tuple) {
t = empty_tuple2();
var t2: tuple = empty_tuple2();
t2~asm_func_modify(foo(22), foo(33));
t2.asm_func_modify(foo(22), foo(33));
return (t, t2);
}

View file

@ -1,250 +0,0 @@
// Here we test "functions that just wrap other functions" (camelCase in particular):
// > builder beginCell() { return begin_cell(); }
// Such functions, when called, are explicitly inlined during code generation (even without `inline` modifier).
// It means, that `beginCell()` is replaced to `begin_cell()` (and effectively to `NEWC`).
// Moreover, body of `beginCell` is NOT codegenerated at all.
// Hence, we can write camelCase wrappers (as well as more intelligible namings around stdlib functions)
// without affecting performance and even bytecode hashes.
// This works with ~functions also. And even works with wrappers of wrappers.
// Moreover, such wrappers can reorder input parameters, see a separate test camel2.tolk.
import "@stdlib/tvm-dicts"
fun myBeginCell(): builder { return beginCell(); }
fun myEndCell(b: builder): cell { return endCell(b); }
fun myStoreRef(b: builder, c: cell): builder { return storeRef(b, c); }
fun myStoreUint(b: builder, i: int, bw: int): builder { return storeUint(b, i, bw); }
// 'inline' is not needed actually, but if it exists, it's just ignored
@inline
@pure
fun myBeginParse(c: cell): slice { return beginParse(c); }
@inline
@pure
fun mySkipBits(s: slice, len: int): slice { return skipBits(s, len); }
@inline
@pure
fun ~mySkipBits(s: slice, len: int): (slice, ()) { return ~skipBits(s, len); }
@inline
@pure
fun ~myLoadUint(s: slice, len: int): (slice, int) { return loadUint(s, len); }
fun myComputeDataSize(c: cell, maxCells: int): (int, int, int) { return calculateCellSizeStrict(c, maxCells); }
fun dict__new(): cell { return createEmptyDict(); }
fun dict__iset(dict: cell, keyLen: int, index: int, value: slice): cell { return iDictSet(dict, keyLen, index, value); }
fun ~dict__iset(dict: cell, keyLen: int, index: int, value: slice): (cell, ()) { return ~iDictSet(dict, keyLen, index, value); }
fun dict__tryIGet(dict: cell, keyLen: int, index: int): (slice, int) { return iDictGet(dict, keyLen, index); }
fun dict__tryIGetMin(dict: cell, keyLen: int): (int, slice, int) { return iDictGetFirst(dict, keyLen); }
@pure
fun triple_second<X, Y, Z>(p: [X, Y, Z]): Y
asm "SECOND";
fun myEmptyTuple(): tuple { return createEmptyTuple(); }
fun emptyTuple1(): tuple { return myEmptyTuple(); }
fun emptyTuple11(): tuple { return emptyTuple1(); }
fun myTuplePush<X>(t: tuple, value: X): tuple { return tuplePush(t, value); }
fun ~myTuplePush<X>(t: tuple, value: X): (tuple, ()) { return ~tuplePush(t, value); }
fun myTupleAt<X>(t: tuple, index: int): X { return tupleAt(t, index); }
fun tripleSecond<X1, Y2, Z3>(p: [X1, Y2, Z3]): Y2 { return triple_second(p); }
@pure
fun nullValue<X>(): X
asm "PUSHNULL";
fun initial1(x: tuple): tuple { return x; }
fun initial2(x: tuple): tuple { return initial1(x); }
// int add(int x, int y) { return x + y; } // this is also a wrapper, as its body is _+_(x,y)
fun fake1(a: int, b: int, c: int): void
asm(a b c) "DROP DROP DROP";
fun fake2(a: int, b: int, c: int): void
asm(b c a) "DROP DROP DROP";
fun fake3(a: int, b: int, c: int): ()
asm(c a b) "DROP DROP DROP";
fun fake4(a: int, b: int, c: int): ()
asm(c b a) "DROP DROP DROP";
fun fake1Wrapper(a: int, b: int, c: int) { return fake1(a, b, c); }
fun fake2Wrapper(a: int, b: int, c: int) { return fake2(a, b, c); }
fun fake3Wrapper(a: int, b: int, c: int) { return fake3(a, b, c); }
fun fake4Wrapper(a: int, b: int, c: int) { return fake4(a, b, c); }
@method_id(101)
fun test1(): [int, int, int] {
var x: int = 1;
var y: int = 1;
var to_be_ref: cell = myBeginCell().myEndCell();
var in_c: builder = myBeginCell().myStoreUint(123, 8);
in_c = myStoreRef(in_c, to_be_ref);
var (a, b, c) = myComputeDataSize(in_c.myEndCell(), 10);
assert(!(b != 8)) throw 101;
assert(!(c != 1), 101);
return [a, b + x, c + y];
}
@method_id(102)
fun test2(): [[int, int, int], int, int, int] {
var dict: cell = dict__new();
dict = dict__iset(dict, 32, 456, myBeginCell().myStoreUint(4560, 32).myEndCell().myBeginParse());
dict.dict__iset(32, 789, myBeginCell().myStoreUint(7890, 32).myEndCell().myBeginParse());
dict~dict__iset(32, 123, myBeginCell().myStoreUint(0, 64).myStoreUint(1230, 32).myStoreUint(1231, 32).myStoreUint(1232, 32).myEndCell().myBeginParse());
var (mink, minv, _) = dict__tryIGetMin(dict, 32);
// skip 64 bits
minv~mySkipBits(16);
minv = minv.mySkipBits(16);
minv.mySkipBits(11); // does nothing
(minv, _) = ~mySkipBits(minv, 16);
mySkipBits(minv, 11); // does nothing
minv~mySkipBits(16);
// load 3*32
var minv1 = minv~myLoadUint(32);
var minv2 = minv~myLoadUint(32);
var minv3 = minv~myLoadUint(32);
var (_, found123) = dict__tryIGet(dict, 32, 123);
var (_, found456) = dict__tryIGet(dict, 32, 456);
var (_, found789) = dict__tryIGet(dict, 32, 789);
return [[minv1, minv2, minv3], found123, found456, found789];
}
@method_id(103)
fun test3(): tuple {
var with34: tuple = initial2(emptyTuple1());
with34~myTuplePush(34);
var t: tuple = emptyTuple11();
t = myTuplePush(t, 12);
myTuplePush(t, emptyTuple11()); // does nothing
t~myTuplePush(emptyTuple1());
t~myTuplePush(with34.myTupleAt(0));
t.myTuplePush("123"s); // does nothing
var tri: [cell, int, cell] = [nullValue(), 90 + 1, null];
var f: int = tripleSecond(tri);
(t, _) = ~myTuplePush(t, f);
return t;
}
@method_id(104)
fun test4(a: int, b: int, c: int): int {
fake1Wrapper(a, b, c);
fake2Wrapper(a, b, c);
fake3Wrapper(a, b, c);
fake4Wrapper(a, b, c);
return 10;
}
fun main(): int {
var x: int = now();
return 30;
}
/**
method_id | in | out
@testcase | 101 | | [ 2 9 2 ]
@testcase | 102 | | [ [ 1230 1231 1232 ] -1 -1 0 ]
@testcase | 103 | | [ 12 [] 34 91 ]
@fif_codegen
"""
main PROC:<{
//
30 PUSHINT
}>
"""
@fif_codegen
"""
test1 PROC:<{
//
NEWC // _5
ENDC // to_be_ref
NEWC // to_be_ref _8
123 PUSHINT // to_be_ref _8 _9=123
SWAP // to_be_ref _9=123 _8
8 STU // to_be_ref in_c
STREF // in_c
ENDC // _16
10 PUSHINT // _16 _17=10
CDATASIZE // a b c
OVER // a b c b
8 NEQINT // a b c _21
101 THROWIF
DUP // a b c c
1 NEQINT // a b c _26
101 THROWIF
SWAP // a c b
INC // a c _30
SWAP // a _30 c
INC // a _30 _31
TRIPLE // _29
}>
"""
@fif_codegen
"""
test2 PROC:<{
...
16 PUSHINT // dict minv _45=16
SDSKIPFIRST // dict minv
16 PUSHINT // dict minv _47=16
SDSKIPFIRST // dict minv
16 PUSHINT // dict minv _52=16
SDSKIPFIRST // dict minv
16 PUSHINT // dict minv _57=16
SDSKIPFIRST // dict minv
...
32 PUSHINT // dict minv1 minv2 minv3 found123 found456 _83=32
789 PUSHINT // dict minv1 minv2 minv3 found123 found456 _83=32 _84=789
s0 s7 s7 XCHG3 // found456 minv1 minv2 minv3 found123 _84=789 dict _83=32
DICTIGET
NULLSWAPIFNOT // found456 minv1 minv2 minv3 found123 _101 _102
NIP // found456 minv1 minv2 minv3 found123 found789
...
4 TUPLE // _86
}>
"""
@fif_codegen
"""
test3 PROC:<{
//
NIL // _1
initial1 CALLDICT // with34
...
TRIPLE // t tri
SECOND // t f
TPUSH // t
}>
"""
@fif_codegen
"""
test4 PROC:<{
// a b c
s2 s1 s0 PUSH3 // a b c a b c
DROP DROP DROP
s1 s0 s2 PUSH3 // a b c b c a
DROP DROP DROP
s0 s2 s1 PUSH3 // a b c c a b
DROP DROP DROP
s0 s2 XCHG // c b a
DROP DROP DROP
10 PUSHINT // _7=10
}>
"""
@fif_codegen_avoid DECLPROC myBeginCell
@fif_codegen_avoid DECLPROC myStoreUint
@fif_codegen_avoid DECLPROC myStoreRef
@fif_codegen_avoid DECLPROC myComputeDataSize
@fif_codegen_avoid DECLPROC tryIdictGet
@fif_codegen_avoid DECLPROC myEmptyTuple
@fif_codegen_avoid DECLPROC myStoreUint
@fif_codegen_avoid DECLPROC initial2
@fif_codegen_avoid DECLPROC add
@fif_codegen_avoid DECLPROC increase
*/

View file

@ -1,204 +0,0 @@
// Here we also test "functions that just wrap other functions" like in camel1.tolk,
// but when they reorder arguments, e.g.
// > T f(x,y) { return anotherF(y,x); }
// This also works, even for wrappers of wrappers, even if anotherF is asm(with reorder).
// But swapping arguments may sometimes lead to bytecode changes (see test2),
// both with compute-asm-ltr and without it.
fun myBeginCell(): builder { return beginCell(); }
fun myEndCell(b: builder): cell { return endCell(b); }
fun myStoreRef1(b: builder, c: cell): builder { return storeRef(b, c); }
fun myStoreRef2(c: cell, b: builder): builder { return storeRef(b, c); }
fun myStoreUint1(b: builder, x: int, bw: int): builder { return storeUint(b, x, bw); }
fun myStoreUint2(b: builder, bw: int, x: int): builder { return storeUint(b, x, bw); }
fun computeDataSize1(c: cell, maxCells: int): (int, int, int) { return calculateCellSizeStrict(c, maxCells); }
fun computeDataSize2(maxCells: int, c: cell): (int, int, int) { return calculateCellSizeStrict(c, maxCells); }
fun fake(a: int, b: int, c: int): void
asm "DROP DROP DROP";
fun fake2(b: int, c: int, a: int) { return fake(a,b,c); }
fun fake3(c: int, a: int, b: int) { return fake(a,b,c); }
fun fake4(c: int, b: int, a: int) { return fake(a,b,c); }
@method_id(101)
fun test1(): (int, int, int) {
var x: int = 1;
var y: int = 1;
var to_be_ref: cell = myBeginCell().myEndCell();
var in_c: builder = myBeginCell().myStoreUint1(123, 8);
in_c = myStoreRef1(in_c, to_be_ref);
var (a, b, c) = computeDataSize1(in_c.myEndCell(), 10);
assert(!0, 101);
return (a, b + x, c + y);
}
@method_id(102)
fun test2(): (int, int, int) {
var x: int = 1;
var y: int = 1;
var to_be_ref: cell = myBeginCell().myEndCell();
var in_c: builder = myBeginCell().myStoreUint2(8, 123);
in_c = myStoreRef2(to_be_ref, in_c);
var (a, b, c) = computeDataSize2(10, in_c.myEndCell());
return (a, b + x, c + y);
}
@method_id(103)
fun test3(): (int, int, int) {
var x: int = 1;
var y: int = 1;
var to_be_ref: cell = beginCell().endCell();
var in_c: builder = beginCell().storeUint(123, 8);
in_c = storeRef(in_c, to_be_ref);
var (a, b, c) = calculateCellSizeStrict(in_c.endCell(), 10);
return (a, b + x, c + y);
}
fun beginCell1(): builder { return beginCell(); }
fun beginCell11(): builder { return beginCell1(); }
fun beginCell111(): builder { return beginCell11(); }
fun endCell1(b: builder): cell { return endCell(b); }
fun endCell11(b: builder): cell { return endCell1(b); }
fun beginParse1(c: cell): slice { return beginParse(c); }
fun beginParse11(c: cell): slice { return beginParse1(c); }
fun storeInt1(b: builder, bw: int, x: int): builder { return storeInt(b, x, bw); }
fun storeInt11(bw: int, x: int, b: builder): builder { return storeInt1(b, bw, x); }
fun storeInt111(b: builder, x: int, bw: int): builder { return storeInt11(bw, x, b); }
@method_id(104)
fun test4(): slice {
var b: builder = beginCell111();
b = storeInt11(32, 1, b);
b = storeInt111(b, 2, 32).storeInt111(3, 32);
return b.endCell11().beginParse11();
}
@method_id(105)
fun test5(a: int, b: int, c: int): int {
fake(a, b, c);
fake2(b, c, a);
fake3(c, a, b);
fake4(c, b, a);
return a;
}
fun main() {
throw 0;
}
/**
method_id | in | out
@testcase | 101 | | 2 9 2
@testcase | 102 | | 2 9 2
@testcase | 103 | | 2 9 2
@testcase | 104 | | CS{Cell{0018000000010000000200000003} bits: 0..96; refs: 0..0}
test1 and test3 fif code is absolutely identical, test2 (due to reorder) is a bit different:
@fif_codegen
"""
test1 PROC:<{
//
NEWC // _5
ENDC // to_be_ref
NEWC // to_be_ref _8
123 PUSHINT // to_be_ref _8 _9=123
SWAP // to_be_ref _9=123 _8
8 STU // to_be_ref in_c
STREF // in_c
ENDC // _16
10 PUSHINT // _16 _17=10
CDATASIZE // a b c
SWAP // a c b
INC // a c _23
SWAP // a _23 c
INC // a _23 _24
}>
"""
@fif_codegen
"""
test2 PROC:<{
//
NEWC // _5
ENDC // to_be_ref
NEWC // to_be_ref _8
123 PUSHINT // to_be_ref _8 _10=123
SWAP // to_be_ref _10=123 _8
8 STU // to_be_ref in_c
STREF // in_c
10 PUSHINT
SWAP
ENDC
SWAP
CDATASIZE // a b c
SWAP // a c b
INC // a c _19
SWAP // a _19 c
INC // a _19 _20
}>
"""
@fif_codegen
"""
test3 PROC:<{
//
NEWC // _5
ENDC // to_be_ref
NEWC // to_be_ref _8
123 PUSHINT // to_be_ref _8 _9=123
SWAP // to_be_ref _9=123 _8
8 STU // to_be_ref in_c
STREF // in_c
ENDC // _16
10 PUSHINT // _16 _17=10
CDATASIZE // a b c
SWAP // a c b
INC // a c _19
SWAP // a _19 c
INC // a _19 _20
}>
"""
@fif_codegen
"""
test4 PROC:<{
//
NEWC // b
1 PUSHINT // b _3=1
SWAP // _3=1 b
32 STI // b
2 PUSHINT
SWAP // _5=2 b
32 STI
3 PUSHINT
SWAP
32 STI // b
ENDC // _11
CTOS // _12
}>
"""
@fif_codegen
"""
test5 PROC:<{
// a b c
s2 s1 s0 PUSH3 // a b c a b c
DROP DROP DROP
s2 s1 s0 PUSH3 // a b c a b c
DROP DROP DROP
s2 s1 s0 PUSH3 // a b c a b c
DROP DROP DROP
s2 PUSH
-ROT // a a b c
DROP DROP DROP
}>
"""
@fif_codegen_avoid myStoreUint1
@fif_codegen_avoid myStoreUint2
*/

View file

@ -1,95 +0,0 @@
// Here we test that if you declare a wrapper like
// > builder beginCell() { return begin_cell(); }
// but use it NOT only as a direct call, BUT as a 1-st class function
// (save to a variable, return from a function, etc.)
// it also works, since a function becomes codegenerated (though direct calls are expectedly inlined).
fun myBeginCell(): builder { return beginCell(); }
fun myEndCell(b: builder): cell { return endCell(b); }
fun myStoreRef(b: builder, c: cell): builder { return storeRef(b, c); }
fun myStoreUint3(i: int, bw: int, b: builder): builder { return storeUint(b, i, bw); }
fun computeDataSize2(maxCells: int, c: cell): (int, int, int) { return calculateCellSizeStrict(c, maxCells); }
fun myEmptyTuple(): tuple { return createEmptyTuple(); }
fun myTuplePush<X>(t: tuple, value: X): tuple { return tuplePush(t, value); }
fun ~myTuplePush<X>(t: tuple, value: X): (tuple, ()) { return ~tuplePush(t, value); }
fun myTupleGetFirst<X>(t: tuple): X { return tupleFirst(t); }
@inline
fun getBeginEnd(): (auto, auto) {
return (myBeginCell, myEndCell);
}
fun begAndStore(beg: auto, store: auto, x: int): builder {
return store(x, 8, beg());
}
fun test1(): (int, int, int) {
var (_, computer) = (0, computeDataSize2);
var (beg, end) = getBeginEnd();
var t: tuple = myEmptyTuple();
t~myTuplePush(myStoreRef);
var refStorer = myTupleGetFirst(t);
var x: int = 1;
var y: int = 1;
var to_be_ref: cell = myBeginCell().myEndCell();
var in_c: builder = begAndStore(beg, myStoreUint3, 123);
in_c = refStorer(in_c, to_be_ref);
var (a, b, c) = computer(10, end(in_c));
return (a, b + x, c + y);
}
fun main(): (int, int, int) {
return test1();
}
/**
method_id | in | out
@testcase | 0 | | 2 9 2
@fif_codegen DECLPROC myBeginCell
@fif_codegen DECLPROC computeDataSize2
@fif_codegen
"""
myStoreUint3 PROC:<{
// i bw b
SWAP // i b bw
STUX // _3
}>
"""
@fif_codegen
"""
myStoreRef PROC:<{
// b c
SWAP // c b
STREF // _2
}>
"""
@fif_codegen
"""
CONT:<{
computeDataSize2 CALLDICT
}> // computer
getBeginEnd INLINECALLDICT // computer beg end
NIL // computer beg end t
...
NEWC // computer beg end refStorer _19
ENDC // computer beg end refStorer to_be_ref
...
CONT:<{
myStoreUint3 CALLDICT
}>
...
begAndStore CALLDICT // computer to_be_ref end refStorer in_c
"""
@fif_codegen_avoid myEmptyTuple
@fif_codegen_avoid myTuplePush
*/

View file

@ -1,145 +0,0 @@
// Here we test that a just-return function is not a valid wrapper, it will not be inlined.
// (doesn't use all arguments, has different pureness, has method_id, etc.)
fun myStoreUint(b: builder, x: int, unused: int): builder { return storeUint(b, x, x); }
fun throwIf(excNo: int, cond: int) { assert(!cond) throw excNo; }
fun initial1(x: auto) { return x; }
fun initial2(x: auto) { return initial1(x); }
@pure
fun asm_func_4(a: int, b: (int, (int, int)), c: int): tuple
asm (b a c -> 0) "5 TUPLE";
fun asmFunc4(a: int, b: (int, (int, int)), c: int): tuple { return asm_func_4(a, b, c); }
fun postpone_elections(): int {
return false;
}
fun setAndGetData(ret: int): int {
var c: cell = beginCell().storeUint(ret, 8).endCell();
setContractData(c);
var s: slice = getContractData().beginParse();
throwIf(101, 0);
return s~loadUint(8);
}
fun setAndGetDataWrapper(ret: int): int {
return setAndGetData(ret);
}
@method_id(101)
fun test1(): int {
var c: cell = beginCell().myStoreUint(32, 10000000).endCell();
var s: slice = c.beginParse();
return s~loadUint(32);
}
get fun test2(ret: int): int {
return setAndGetDataWrapper(ret);
}
@method_id(103)
fun test3(): int {
return initial2(10);
}
global t: tuple;
fun foo(x: int): int {
t~tuplePush(x);
return x * 10;
}
@method_id(104)
fun test4(): (tuple, tuple) {
t = createEmptyTuple();
var t2: tuple = asmFunc4(foo(11), (foo(22), (foo(33), foo(44))), foo(55));
return (t, t2);
}
@method_id(105)
fun test5(): int {
if (1) {
return postpone_elections();
}
return 123;
}
@method_id(106)
fun test6(): int {
return add2(1, 2); // doesn't inline since declared below
}
fun main(ret: int): int {
return setAndGetDataWrapper(ret);
}
fun onExternalMessage(ret: int): int {
return setAndGetData(ret);
}
// currently, functions implemented after usage, can't be inlined, since inlining is legacy, not AST
fun add2(x: int, y: int): int { return x + y; }
/**
method_id | in | out
@testcase | 101 | | 32
@testcase | 103 | | 10
@testcase | 104 | | [ 11 22 33 44 55 ] [ 220 330 440 110 550 ]
@testcase | 105 | | 0
@testcase | 106 | | 3
@testcase | 74435 | 99 | 99
@testcase | 0 | 98 | 98
@testcase | -1 | 97 | 97
@fif_codegen DECLPROC myStoreUint
@fif_codegen DECLPROC throwIf
@fif_codegen DECLPROC postpone_elections
@fif_codegen DECLPROC add2
@fif_codegen 74435 DECLMETHOD test2
@fif_codegen
"""
test3 PROC:<{
//
10 PUSHINT // _0=10
initial2 CALLDICT // _1
}>
"""
@fif_codegen
"""
test2 PROC:<{
// ret
setAndGetData CALLDICT // _1
}>
"""
@fif_codegen
"""
11 PUSHINT
foo CALLDICT
22 PUSHINT
foo CALLDICT
33 PUSHINT
foo CALLDICT
44 PUSHINT
foo CALLDICT
55 PUSHINT
foo CALLDICT
asmFunc4 CALLDICT // t2
"""
@fif_codegen
"""
test6 PROC:<{
//
1 PUSHINT // _0=1
2 PUSHINT // _0=1 _1=2
add2 CALLDICT // _2
}>
"""
@fif_codegen_avoid setAndGetDataWrapper
*/

View file

@ -1,121 +1,136 @@
fun store_u32(b: builder, value: int): builder {
return b.storeUint(value, 32);
}
fun ~store_u32(b: builder, value: int): (builder, ()) {
return ~storeUint(b, value, 32);
fun store_u32(mutate self: builder, value: int): self {
return self.storeUint(value, 32);
}
fun load_u32(cs: slice): (slice, int) {
return cs.loadUint(32);
fun load_u32(mutate self: slice): int {
return self.loadUint(32);
}
fun my_loadInt(s: slice, len: int): (slice, int)
asm(s len -> 1 0) "LDIX"; // top is "value slice"
fun my_storeInt(b: builder, x: int, len: int): builder
asm(x b len) "STIX";
fun ~my_storeInt(b: builder, x: int, len: int): (builder, ())
asm(x b len) "STIX";
fun myLoadInt(mutate self: slice, len: int): int
asm(-> 1 0) "LDIX";
fun myStoreInt(mutate self: builder, x: int, len: int): self
asm(x self len) "STIX";
@method_id(101)
fun test1(): [int,int,int,int,int] {
var b: builder = beginCell().storeUint(1, 32);
b = b.storeUint(2, 32);
b~storeUint(3, 32);
b.storeUint(3, 32);
b = b.store_u32(4);
b~store_u32(5);
b.store_u32(5);
var cs: slice = b.endCell().beginParse();
var (cs redef, one: int) = cs.loadUint(32);
var (two: int, three: int) = (cs~loadUint(32), cs~load_u32());
var (cs redef, four: int) = cs.load_u32();
var five: int = cs~load_u32();
var one: int = cs.loadUint(32);
var (two: int, three: int) = (cs.loadUint(32), cs.load_u32());
var four: int = cs.load_u32();
var five: int = cs.load_u32();
return [one,two,three,four,five];
}
@method_id(102)
fun test2(): [int,int,int] {
var b: builder = beginCell().my_storeInt(1, 32);
b = b.my_storeInt(2, 32);
b~my_storeInt(3, 32);
var b: builder = beginCell().myStoreInt(1, 32);
b = b.myStoreInt(2, 32);
b.myStoreInt(3, 32);
var cs: slice = b.endCell().beginParse();
var (cs redef, one: int) = cs.my_loadInt(32);
var (two: int, three: int) = (cs~my_loadInt(32), cs~my_loadInt(32));
var one: int = cs.myLoadInt(32);
var (two: int, three: int) = (cs.myLoadInt(32), cs.myLoadInt(32));
return [one,two,three];
}
@method_id(103)
fun test3(ret: int): int {
var (_, same: int) = beginCell().storeUint(ret,32).endCell().beginParse().loadUint(32);
val same: int = beginCell().storeUint(ret,32).endCell().beginParse().loadUint(32);
return same;
}
@method_id(104)
fun test4(): [int,int] {
var b: builder = my_storeInt(beginCell(), 1, 32);
b = storeInt(storeInt(b, 2, 32), 3, 32);
var b: builder = beginCell().myStoreInt(1, 32);
b = b.storeInt(2, 32).storeInt(3, 32);
var cs: slice = b.endCell().beginParse();
var cs32: slice = cs.getFirstBits(32); // todo s.first_bits()~loadUint() doesn't work, 'lvalue expected'
var (one, _, three) = (cs32~loadInt(32), cs~skipBits(64), cs~load_u32());
var (one, _, three) = (cs.getFirstBits(32).loadUint(32), cs.skipBits(64), cs.load_u32());
return [one,three];
}
@method_id(105)
fun test5(): [int,int] {
var cref: cell = endCell(store_u32(beginCell(), 105));
var cref: cell = endCell(beginCell().store_u32(105));
var c: cell = beginCell().storeRef(cref).storeRef(cref).store_u32(1).endCell();
var cs: slice = beginParse(c);
// todo I want cs~loadRef().beginParse()~load_u32(), but 'lvalue expected'
var ref1 = cs~loadRef().beginParse();
var ref2 = cs~loadRef().beginParse();
var sto5x2: int = ref1~load_u32() + ref2~loadUint(32);
return [sto5x2, cs~load_u32()];
}
fun ~sumNumbersInSlice(s: slice): (slice, int) {
var result = 0;
while (!s.isEndOfSliceBits()) {
result += s~loadUint(32);
}
return (s, result);
var sto5x2: int = cs.loadRef().beginParse().load_u32() + cs.loadRef().beginParse().loadUint(32);
return [sto5x2, cs.load_u32()];
}
@method_id(106)
fun test6() {
var ref = beginCell().storeInt(100, 32).endCell();
var s: slice = beginCell().storeInt(1, 32).storeInt(2, 32).storeRef(ref).endCell().beginParse();
var result = (getRemainingBitsCount(s), s~sumNumbersInSlice(), getRemainingBitsCount(s), isEndOfSlice(s), isEndOfSliceBits(s), isEndOfSliceRefs(s));
var ref2: cell = s~loadRef();
var s2: slice = ref2.beginParse();
s.assertEndOfSlice();
return (result, s2~loadInt(32), s2.isEndOfSlice());
return beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32);
}
@method_id(107)
fun test7() {
// since .store() methods now mutate, this piece of code works not as earlier (mutates uri_builder)
var uri_builder = beginCell();
var uri_slice = uri_builder.storeSlice(".json").endCell().beginParse();
var image_slice = uri_builder.storeSlice(".png").endCell().beginParse();
return (uri_builder.getBuilderBitsCount(), uri_slice.getRemainingBitsCount(), image_slice.getRemainingBitsCount());
}
@method_id(108)
fun test8() {
var uri_builder = beginCell();
var fresh = uri_builder;
var uri_slice = fresh.storeSlice(".json").endCell().beginParse();
var fresh redef = uri_builder;
var image_slice = fresh.storeSlice(".png").endCell().beginParse();
return (uri_builder.getBuilderBitsCount(), uri_slice.getRemainingBitsCount(), image_slice.getRemainingBitsCount());
}
fun sumNumbersInSlice(mutate self: slice): int {
var result = 0;
while (!self.isEndOfSliceBits()) {
result += self.loadUint(32);
}
return result;
}
@method_id(110)
fun test10() {
var ref = beginCell().storeInt(100, 32).endCell();
var s: slice = beginCell().storeInt(1, 32).storeInt(2, 32).storeRef(ref).endCell().beginParse();
var result = (getRemainingBitsCount(s), s.sumNumbersInSlice(), getRemainingBitsCount(s), isEndOfSlice(s), isEndOfSliceBits(s), isEndOfSliceRefs(s));
var ref2: cell = s.loadRef();
var s2: slice = ref2.beginParse();
s.assertEndOfSlice();
return (result, s2.loadInt(32), s2.isEndOfSlice());
}
@method_id(111)
fun test11() {
var s: slice = beginCell().storeInt(1, 32).storeInt(2, 32).storeInt(3, 32).storeInt(4, 32).storeInt(5, 32).storeInt(6, 32).storeInt(7, 32).endCell().beginParse();
var size1 = getRemainingBitsCount(s);
s~skipBits(32);
s.skipBits(32);
var s1: slice = s.getFirstBits(64);
var n1 = s1~loadInt(32);
var n1 = s1.loadInt(32);
var size2 = getRemainingBitsCount(s);
s~loadInt(32);
s.loadInt(32);
var size3 = getRemainingBitsCount(s);
s~removeLastBits(32);
s.removeLastBits(32);
var size4 = getRemainingBitsCount(s);
var n2 = s~loadInt(32);
var n2 = s.loadInt(32);
var size5 = getRemainingBitsCount(s);
return (n1, n2, size1, size2, size3, size4, size5);
}
@method_id(108)
fun test108() {
@method_id(112)
fun test12() {
var (result1, result2) = (0, 0);
try {
beginCell().storeRef(beginCell().endCell()).endCell().beginParse().assertEndOfSlice();
@ -132,45 +147,45 @@ fun test108() {
return (result1, result2);
}
@method_id(109)
fun test109() {
@method_id(113)
fun test13() {
var ref2 = beginCell().storeInt(1, 32).endCell();
var ref1 = beginCell().storeInt(1, 32).storeRef(ref2).endCell();
var c = beginCell().storeInt(444, 32).storeRef(ref1).storeRef(ref1).storeRef(ref1).storeRef(ref2).storeInt(4, 32).endCell();
var (n_cells1, n_bits1, n_refs1) = c.calculateCellSizeStrict(10);
var s = c.beginParse();
s~loadRef();
s~loadRef();
var n = s~loadInt(32);
s.loadRef();
s.loadRef();
var n = s.loadInt(32);
var (n_cells2, n_bits2, n_refs2) = s.calculateSliceSizeStrict(10);
return ([n_cells1, n_bits1, n_refs1], [n_cells2, n_bits2, n_refs2], n);
}
@method_id(110)
@method_id(114)
fun test110(x: int) {
var s = beginCell().storeBool(x < 0).storeBool(0).storeBool(x).endCell().beginParse();
return (s~loadBool(), s~loadBool(), s~loadBool());
return (s.loadBool(), s.loadBool(), s.loadBool());
}
@method_id(111)
@method_id(115)
fun test111() {
var s = beginCell().storeMessageOp(123).storeMessageQueryId(456)
.storeAddressNone().storeAddressNone()
.storeUint(0, 32)
.storeUint(123, 32).storeUint(456, 64).storeUint(789, 64)
.endCell().beginParse();
var op1 = s~loadUint(32);
var q1 = s~loadUint(64);
var op1 = s.loadUint(32);
var q1 = s.loadUint(64);
if (s.addressIsNone()) {
s~skipBits(2);
s.skipBits(2);
}
if (s~loadBool() == 0) {
assert(s~loadBool() == 0) throw 444;
s~skipBits(32);
if (s.loadBool() == 0) {
assert(s.loadBool() == 0) throw 444;
s.skipBouncedPrefix();
}
var op2 = s~loadMessageOp();
var q2 = s~loadMessageQueryId();
s~skipBits(64);
var op2 = s.loadMessageOp();
var q2 = s.loadMessageQueryId();
s.skipBits(64);
s.assertEndOfSlice();
assert(isMessageBounced(0x001)) throw 444;
return (op1, q1, op2, q2);
@ -186,11 +201,31 @@ fun main(): int {
@testcase | 103 | 103 | 103
@testcase | 104 | | [ 1 3 ]
@testcase | 105 | | [ 210 1 ]
@testcase | 106 | | 64 3 0 0 -1 0 100 -1
@testcase | 107 | | 2 3 224 192 160 128 96
@testcase | 108 | | 9 100
@testcase | 109 | | [ 3 128 5 ] [ 2 96 3 ] 444
@testcase | 110 | -1 | -1 0 -1
@testcase | 110 | 0 | 0 0 0
@testcase | 111 | | 123 456 123 456
@testcase | 107 | | 72 40 72
@testcase | 108 | | 0 40 32
@testcase | 110 | | 64 3 0 0 -1 0 100 -1
@testcase | 111 | | 2 3 224 192 160 128 96
@testcase | 112 | | 9 100
@testcase | 113 | | [ 3 128 5 ] [ 2 96 3 ] 444
@testcase | 114 | -1 | -1 0 -1
@testcase | 114 | 0 | 0 0 0
@testcase | 115 | | 123 456 123 456
Note, that since 'compute-asm-ltr' became on be default, chaining methods codegen is not quite optimal.
@fif_codegen
"""
test6 PROC:<{
//
NEWC // _1
1 PUSHINT // _1 _2=1
SWAP // _2=1 _1
32 STU // _0
2 PUSHINT // _0 _6=2
SWAP // _6=2 _0
32 STU // _0
3 PUSHINT // _0 _10=3
SWAP // _10=3 _0
32 STU // _0
}>
"""
*/

View file

@ -43,9 +43,6 @@ asm "SDEQ";
fun stslicer(b: builder, s: slice): builder
asm "STSLICER";
fun myStoreUint(b: builder, x: int, len: int): builder { return storeUint(b, x, len); }
fun endSlice(b: builder): slice { return endcs(b); }
fun main() {
var i1: int = iget1();
var i2: int = iget2();
@ -59,8 +56,8 @@ fun main() {
var s2: slice = sget2();
var s3: slice = newc().stslicer(str1).stslicer(str2r).endcs();
assert(sdeq(s1, newc().myStoreUint(str1int, 12 * nibbles).endcs())) throw int111;
assert(sdeq(s2, newc().storeUint(str2int, 6 * nibbles).endSlice())) throw 112;
assert(sdeq(s1, newc().storeUint(str1int, 12 * nibbles).endcs())) throw int111;
assert(sdeq(s2, newc().storeUint(str2int, 6 * nibbles).endcs())) throw 112;
assert(sdeq(s3, newc().storeUint(0x636f6e737431AABBCC, 18 * nibbles).endcs())) throw 113;
var i4: int = iget240();

View file

@ -1,8 +1,7 @@
import "@stdlib/tvm-dicts"
fun ~addIntToIDict(iDict: cell, key: int, number: int): (cell, ()) {
iDict~iDictSetBuilder(32, key, beginCell().storeInt(number, 32));
return (iDict, ());
fun addIntToIDict(mutate self: cell, key: int, number: int): void {
return self.iDictSetBuilder(32, key, beginCell().storeInt(number, 32));
}
fun calculateDictLen(d: cell) {
@ -15,40 +14,40 @@ fun calculateDictLen(d: cell) {
return len;
}
fun ~loadTwoDigitNumberFromSlice(s: slice): (slice, int) {
var n1 = s~loadInt(8);
var n2 = s~loadInt(8);
return (s, (n1 - 48) * 10 + (n2 - 48));
fun loadTwoDigitNumberFromSlice(mutate self: slice): int {
var n1 = self.loadInt(8);
var n2 = self.loadInt(8);
return (n1 - 48) * 10 + (n2 - 48);
}
@method_id(101)
fun test101(getK1: int, getK2: int, getK3: int) {
var dict = createEmptyDict();
dict~uDictSetBuilder(32, 1, beginCell().storeUint(1, 32));
var (old1: slice, found1) = dict~uDictSetAndGet(32, getK1, beginCell().storeUint(2, 32).endCell().beginParse());
var (old2: slice, found2) = dict~uDictSetAndGet(32, getK2, beginCell().storeUint(3, 32).endCell().beginParse());
dict.uDictSetBuilder(32, 1, beginCell().storeUint(1, 32));
var (old1: slice, found1) = dict.uDictSetAndGet(32, getK1, beginCell().storeUint(2, 32).endCell().beginParse());
var (old2: slice, found2) = dict.uDictSetAndGet(32, getK2, beginCell().storeUint(3, 32).endCell().beginParse());
var (cur3: slice, found3) = dict.uDictGet(32, getK3);
return (
found1 ? old1~loadUint(32) : -1,
found2 ? old2~loadUint(32) : -1,
found3 ? cur3~loadUint(32) : -1
found1 ? old1.loadUint(32) : -1,
found2 ? old2.loadUint(32) : -1,
found3 ? cur3.loadUint(32) : -1
);
}
@method_id(102)
fun test102() {
var dict = createEmptyDict();
dict~addIntToIDict(2, 102);
dict~addIntToIDict(1, 101);
dict~addIntToIDict(4, 104);
dict~addIntToIDict(3, 103);
dict.addIntToIDict(2, 102);
dict.addIntToIDict(1, 101);
dict.addIntToIDict(4, 104);
dict.addIntToIDict(3, 103);
var deleted = createEmptyTuple();
var shouldBreak = false;
while (!shouldBreak) {
var (kDel, kVal, wasDel) = dict~iDictDeleteLastAndGet(32);
var (kDel, kVal, wasDel) = dict.iDictDeleteLastAndGet(32);
if (wasDel) {
deleted~tuplePush([kDel, kVal~loadInt(32)]);
deleted.tuplePush([kDel, kVal.loadInt(32)]);
} else {
shouldBreak = true;
}
@ -59,38 +58,38 @@ fun test102() {
@method_id(103)
fun test103() {
var dict = createEmptyDict();
dict~uDictSetBuilderIfNotExists(32, 1,beginCell().storeInt(1, 32));
dict~uDictSetBuilderIfNotExists(32, 1,beginCell().storeInt(1, 32));
dict.uDictSetBuilderIfNotExists(32, 1,beginCell().storeInt(1, 32));
dict.uDictSetBuilderIfNotExists(32, 1,beginCell().storeInt(1, 32));
var len1 = calculateDictLen(dict);
dict~uDictSetBuilderIfExists(32, 2,beginCell().storeInt(1, 32));
dict~uDictSetBuilderIfExists(32, 2,beginCell().storeInt(1, 32));
dict.uDictSetBuilderIfExists(32, 2,beginCell().storeInt(1, 32));
dict.uDictSetBuilderIfExists(32, 2,beginCell().storeInt(1, 32));
var len2 = calculateDictLen(dict);
dict~uDictSetBuilder(32, 3,beginCell().storeInt(1, 32));
dict~uDictSetBuilderIfExists(32, 3,beginCell().storeInt(1, 32));
dict.uDictSetBuilder(32, 3,beginCell().storeInt(1, 32));
dict.uDictSetBuilderIfExists(32, 3,beginCell().storeInt(1, 32));
var len3 = calculateDictLen(dict);
var (delK1, _, _) = dict~uDictDeleteFirstAndGet(32);
var (delK2, _, _) = dict~uDictDeleteFirstAndGet(32);
var (delK3, _, _) = dict~uDictDeleteFirstAndGet(32);
var (delK1, _, _) = dict.uDictDeleteFirstAndGet(32);
var (delK2, _, _) = dict.uDictDeleteFirstAndGet(32);
var (delK3, _, _) = dict.uDictDeleteFirstAndGet(32);
return (len1, len2, len3, delK1, delK2, delK3);
}
@method_id(104)
fun test104() {
var dict = createEmptyDict();
dict~sDictSetBuilder(32, "7800", beginCell().storeUint(5 + 48, 8).storeUint(6 + 48, 8));
dict~sDictSet(32, "key1", "12");
var (old1, _) = dict~sDictSetAndGet(32, "key1", "34");
var (old2, _) = dict~sDictDeleteAndGet(32, "key1");
dict.sDictSetBuilder(32, "7800", beginCell().storeUint(5 + 48, 8).storeUint(6 + 48, 8));
dict.sDictSet(32, "key1", "12");
var (old1, _) = dict.sDictSetAndGet(32, "key1", "34");
var (old2, _) = dict.sDictDeleteAndGet(32, "key1");
var (restK, restV, _) = dict.sDictGetFirst(32);
var (restK1, restV1, _) = dict~sDictDeleteLastAndGet(32);
var (restK1, restV1, _) = dict.sDictDeleteLastAndGet(32);
assert (restK.isSliceBitsEqual(restK1)) throw 123;
assert (restV.isSliceBitsEqual(restV1)) throw 123;
return (
old1~loadTwoDigitNumberFromSlice(),
old2~loadTwoDigitNumberFromSlice(),
restV~loadTwoDigitNumberFromSlice(),
restK~loadTwoDigitNumberFromSlice(),
restK~loadTwoDigitNumberFromSlice()
old1.loadTwoDigitNumberFromSlice(),
old2.loadTwoDigitNumberFromSlice(),
restV.loadTwoDigitNumberFromSlice(),
restK.loadTwoDigitNumberFromSlice(),
restK.loadTwoDigitNumberFromSlice()
);
}

View file

@ -1,18 +1,18 @@
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
var dict: cell = createEmptyDict();
dict~idict_set_builder(32, 3, begin_cell().store_int(30, 32));
dict~idict_set_builder(32, 4, begin_cell().store_int(40, 32));
dict~idict_set_builder(32, 5, begin_cell().store_int(valueAt5, 32));
dict.idict_set_builder(32, 3, begin_cell().store_int(30, 32));
dict.idict_set_builder(32, 4, begin_cell().store_int(40, 32));
dict.idict_set_builder(32, 5, begin_cell().store_int(valueAt5, 32));
return dict;
}
fun lookupIdxByValue(idict32: cell, value: int): int {
var cur_key = -1;
do {
var (cur_key redef, cs: slice, found: int) = idict32.idict_get_next?(32, cur_key);
var (cur_key redef, cs: slice, found: int) = idict32.idictGetNext(32, cur_key);
// one-line condition (via &) doesn't work, since right side is calculated immediately
if (found) {
if (cs~load_int(32) == value) {
if (cs.loadInt(32) == value) {
return cur_key;
}
}

View file

@ -2,9 +2,9 @@ import "@stdlib/tvm-dicts"
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
var dict: cell = createEmptyDict();
dict~iDictSetBuilder(32, 3, beginCell().storeInt(30, 32));
dict~iDictSetBuilder(32, 4, beginCell().storeInt(40, 32));
dict~iDictSetBuilder(32, 5, beginCell().storeInt(valueAt5, 32));
dict.iDictSetBuilder(32, 3, beginCell().storeInt(30, 32));
dict.iDictSetBuilder(32, 4, beginCell().storeInt(40, 32));
dict.iDictSetBuilder(32, 5, beginCell().storeInt(valueAt5, 32));
return dict;
}
@ -14,7 +14,7 @@ fun lookupIdxByValue(idict32: cell, value: int): int {
var (cur_key redef, cs: slice, found: int) = idict32.iDictGetNext(32, cur_key);
// one-line condition (via &) doesn't work, since right side is calculated immediately
if (found) {
if (cs~loadInt(32) == value) {
if (cs.loadInt(32) == value) {
return cur_key;
}
}

View file

@ -0,0 +1,9 @@
fun main() {
return true();
}
/**
@compilation_should_fail
The message is weird now, but later I'll rework error messages anyway.
@stderr cannot apply expression of type int to an expression of type (): cannot unify type () -> ??3 with int
*/

View file

@ -0,0 +1,14 @@
fun add1(x: int) {
return x + 1;
}
fun main() {
val adder_fn = add1;
var x = 10;
return adder_fn(mutate x);
}
/**
@compilation_should_fail
@stderr `mutate` used for non-mutate argument
*/

View file

@ -0,0 +1,12 @@
fun with2Params(x: int, y: int) {
}
fun main() {
return with2Params(1);
}
/**
@compilation_should_fail
@stderr too few arguments in call to `with2Params`, expected 2, have 1
*/

View file

@ -0,0 +1,13 @@
fun methodWith1Param(self: int, param: int) {
}
fun main() {
val x = 10;
x.methodWith1Param(2, "asdf");
}
/**
@compilation_should_fail
@stderr too many arguments in call to `methodWith1Param`, expected 1, have 2
*/

View file

@ -0,0 +1,13 @@
fun inc(x: int) {
return x + 1;
}
fun main() {
return inc(_);
}
/**
@compilation_should_fail
@stderr rvalue expected
@stderr inc(_)
*/

View file

@ -0,0 +1,12 @@
fun nothing() {
}
fun main() {
val x = 0;
return x.nothing();
}
/**
@compilation_should_fail
@stderr `nothing` has no parameters and can not be called as method
*/

View file

@ -0,0 +1,14 @@
fun main() {
beginCell()
.storeAddressNone()
.storeUint(3, 32)
.storeUnexisting()
.storeInt(1, 32)
.endCell();
}
/**
@compilation_should_fail
@stderr undefined symbol `storeUnexisting`
@stderr .storeUnexisting()
*/

View file

@ -0,0 +1,8 @@
fun main() {
var incoming_ton: int = get_incoming_value().3();
}
/**
@compilation_should_fail
@stderr expected method name, got `3`
*/

View file

@ -1,5 +1,5 @@
fun main(): int {
;; this is not a comment
;; here is not a comment
}
/**

View file

@ -1,8 +1,8 @@
fun main() {
val imm = 10;
get seqno(self: int) {
return 0;
}
/**
@compilation_should_fail
@stderr immutable variables are not supported yet
*/
@stderr get methods can't have `mutate` and `self` params
*/

View file

@ -0,0 +1,11 @@
fun f(x: int) {}
fun cantAssignToVal() {
val x = 10;
f(x += 1);
}
/**
@compilation_should_fail
@stderr modifying an immutable variable `x`
*/

View file

@ -0,0 +1,16 @@
fun increment(mutate x: int) {
x = x + 1;
}
fun cantCallMutatingAsAMember() {
var x = 0;
x.increment();
return x;
}
/**
@compilation_should_fail
@stderr function `increment` mutates parameter `x`
@stderr consider calling `increment(mutate x)`, not `x.increment`()
@stderr alternatively, rename parameter to `self` to make it a method
*/

View file

@ -0,0 +1,8 @@
fun load32(self: slice): int {
return self.loadUint(32);
}
/**
@compilation_should_fail
@stderr modifying `self` (call a mutating method), which is immutable by default
*/

View file

@ -0,0 +1,14 @@
fun increment(mutate x: int) {
}
fun main() {
var x = 0;
var inc = increment;
inc(x);
}
/**
@compilation_should_fail
@stderr saving `increment` into a variable is impossible, since it has `mutate` parameters and thus can only be called directly
*/

View file

@ -0,0 +1,8 @@
fun onInternalMessage(mutate in_msg_body: slice) {
}
/**
@compilation_should_fail
@stderr invalid declaration of a reserved function
*/

View file

@ -0,0 +1,8 @@
fun main(cs: slice) {
return loadInt(cs, 32);
}
/**
@compilation_should_fail
@stderr `loadInt` is a mutating method; consider calling `cs.loadInt()`, not `loadInt(cs)`
*/

View file

@ -0,0 +1,12 @@
fun asdf(mutate cs: slice) {}
fun main(cs: slice) {
cs.asdf();
}
/**
@compilation_should_fail
@stderr function `asdf` mutates parameter `cs`
@stderr consider calling `asdf(mutate cs)`, not `cs.asdf`()
@stderr alternatively, rename parameter to `self` to make it a method
*/

View file

@ -0,0 +1,10 @@
fun cantAssignToVal() {
val x = 10;
var y = 20;
[y, x] = [30, 40];
}
/**
@compilation_should_fail
@stderr modifying an immutable variable `x`
*/

View file

@ -0,0 +1,11 @@
const op_increase = 0x123;
fun cantAssignToConst() {
var x = 10;
(x, op_increase) = (20, 30);
}
/**
@compilation_should_fail
@stderr modifying an immutable variable `op_increase`
*/

View file

@ -0,0 +1,14 @@
fun inc(mutate x: int) {
x += 1;
}
fun cantPassToMutatingFunction() {
val myVal = 10;
inc(mutate myVal);
}
/**
@compilation_should_fail
@stderr modifying an immutable variable `myVal`
*/

View file

@ -0,0 +1,14 @@
fun cantCallMutatingMethod(c: cell) {
val s: slice = c.beginParse();
if (1) {
var s: slice = c.beginParse();
s.loadRef(); // this is ok, 's' is another variable
}
val i = s.loadUint(32);
}
/**
@compilation_should_fail
@stderr modifying an immutable variable `s` (call a mutating method)
@stderr s.loadUint
*/

View file

@ -0,0 +1,16 @@
const op_increase = 0x123;
fun inc(mutate x: int): int {
x += 10;
return x + 1;
}
fun cantCallMutatingFunctionWithImmutable() {
return inc(mutate op_increase);
}
/**
@compilation_should_fail
@stderr modifying an immutable variable `op_increase` (call a mutating function)
@stderr inc(mutate op_increase)
*/

View file

@ -0,0 +1,15 @@
fun incBoth(mutate x: int, mutate y: int) {
x += 10;
y += 10;
}
fun cantCallMutatingFunctionWithRvalue() {
var x = 10;
incBoth(mutate x, mutate 30);
}
/**
@compilation_should_fail
@stderr lvalue expected (call a mutating function)
@stderr incBoth(mutate x, mutate 30)
*/

View file

@ -0,0 +1,10 @@
fun cantRedefImmutable() {
val x = 10;
var (y: int, x redef) = (20, 30);
return (y, x);
}
/**
@compilation_should_fail
@stderr modifying an immutable variable `x` (left side of assignment)
*/

View file

@ -0,0 +1,9 @@
fun increment(self: int) {
self = self + 1;
}
/**
@compilation_should_fail
@stderr modifying `self` (left side of assignment), which is immutable by default
@stderr probably, you want to declare `mutate self`
*/

View file

@ -0,0 +1,8 @@
fun cantReturnFromSelf(mutate self: int): self {
return 2;
}
/**
@compilation_should_fail
@stderr invalid return from `self` function
*/

View file

@ -0,0 +1,8 @@
fun cantUseSelfAsType(mutate x: int) {
var y: self = x;
}
/**
@compilation_should_fail
@stderr `self` type can be used only as a return type of a function (enforcing it to be chainable)
*/

View file

@ -0,0 +1,10 @@
fun cantReturnSelf(mutate x: int): int {
x += 1;
return self;
}
/**
@compilation_should_fail
@stderr using `self` in a non-member function (it does not accept the first `self` parameter)
@stderr return self
*/

View file

@ -0,0 +1,9 @@
fun cantReturnNothingFromSelf(mutate self: int): self {
self = self + 1;
}
/**
@compilation_should_fail
@stderr missing return; forgot `return self`?
@stderr }
*/

View file

@ -0,0 +1,15 @@
fun increment(mutate self: int): self {
self = self + 1;
return self;
}
fun cantReturnAnotherSelf(mutate self: int): self {
self = self + 1;
var x = 0;
return x.increment();
}
/**
@compilation_should_fail
@stderr invalid return from `self` function
*/

View file

@ -0,0 +1,8 @@
fun increment(x: int, self: int): int {
return x + self;
}
/**
@compilation_should_fail
@stderr `self` can only be the first parameter
*/

View file

@ -0,0 +1,8 @@
fun increment(x: int): int {
return self + 1;
}
/**
@compilation_should_fail
@stderr using `self` in a non-member function
*/

View file

@ -0,0 +1,19 @@
fun incInt(mutate self: int): self {
self += 1;
return self;
}
fun appendBuilder(mutate self: builder): self {
self.storeUint(1, 32);
return self;
}
fun cantMixDifferentThis() {
var x = 0;
return x.incInt().appendBuilder().incInt();
}
/**
@compilation_should_fail
@stderr cannot apply function appendBuilder : builder -> (builder, ()) to arguments of type int: cannot unify type int with builder
*/

View file

@ -0,0 +1,14 @@
fun incNotChained(mutate self: int) {
self = self + 1;
}
fun cantCallNotChainedMethodsInAChain(x: int) {
return x.incNotChained().incNotChained();
}
/**
The error is very weird, but nevertheless, the type system prevents of doing such errors.
@compilation_should_fail
@stderr cannot apply function incNotChained : int -> (int, ()) to arguments of type (): cannot unify type () with int
*/

View file

@ -0,0 +1,14 @@
fun incNotChained(mutate self: int) {
self = self + 1;
}
fun failWhenReturnANotChainedValue(x: int): int {
return x.incNotChained();
}
/**
The error is very weird, but nevertheless, the type system prevents of doing such errors.
@compilation_should_fail
@stderr previous function return type int cannot be unified with return statement expression type (): cannot unify type () with int
*/

View file

@ -0,0 +1,27 @@
fun increment(mutate x: int): int {
x = x + 1;
return x;
}
@method_id(101)
fun bugWithModifyingMethodInsideSameExpression() {
/*
The same bug existed in FunC:
#pragma allow-post-modification;
(int, int) ~increment(int x) { x = x + 5; return (x, x); }
int main() { int x = 0; x += x~increment(); return x; }
It's related to using a variable modified by ~method inside the same expression.
*/
var x = 0;
x = x + increment(mutate x);
return x;
}
fun main() {
}
/**
// correct: 2
@testcase | 101 | | 1
*/

View file

@ -0,0 +1,337 @@
fun incrementInPlace(mutate self: int, byValue: int): void {
self = self + byValue;
}
fun incrementTwoInPlace(mutate self: int, mutate y: int, byValue: int): int {
self.incrementInPlace(byValue);
y += byValue;
return self + y;
}
@method_id(101)
fun testIncrement1() {
var x = 50;
var y = 30;
incrementInPlace(mutate x, 10);
incrementInPlace(mutate x, 10);
incrementInPlace(mutate y, 10);
y.incrementInPlace(10);
incrementInPlace(mutate y, 10);
return (x, y);
}
@method_id(102)
fun testIncrement2() {
var x = 50;
var y = 30;
val sum1 = incrementTwoInPlace(mutate x, mutate y, 10);
val sum2 = x.incrementTwoInPlace(mutate y, 10);
return (x, y, sum1, sum2);
}
fun load_next(mutate cs: slice): int {
return loadInt(mutate cs, 32);
}
fun myLoadInt(mutate self: slice, len: int): int
asm(-> 1 0) "LDIX";
fun myStoreInt(mutate self: builder, x: int, len: int): self
asm(x self len) "STIX";
@inline_ref
fun unpack_utils_info(mutate utils_info_sl: slice): (int, int) {
return (
utils_info_sl.myLoadInt(32),
utils_info_sl.myLoadInt(32)
);
}
@method_id(103)
fun testSlices1() {
var b: builder = beginCell().storeInt(1, 32).myStoreInt(2, 32);
b.myStoreInt(3, 32);
var c: cell = b.myStoreInt(4, 32).storeInt(5, 32).endCell();
var cs = c.beginParse();
var first = cs.preloadInt(32);
unpack_utils_info(mutate cs);
return (first, cs.myLoadInt(32), cs.loadInt(32));
}
fun load_decimal_symbol(mutate self: slice): int {
// load decimal from bits using utf-8 table
var n: int = self.loadUint(8);
n = n - 48;
assert(n >= 0) throw 400;
assert(n <= 9) throw 400;
return n;
}
@method_id(104)
fun testSlices2() {
var cs = "123";
return (cs.load_decimal_symbol(), cs.load_decimal_symbol(), cs.load_decimal_symbol());
}
global v1: int;
global v2: int;
global v3: int;
@method_id(105)
fun testGlobals() {
v1 = 0;
v2 = 0;
v3 = 100;
v3 += incrementTwoInPlace(mutate v1, mutate v2, 5);
return (v1, v2, v3);
}
fun withNameShadowing(mutate x: int, pivot: int, extra: int) {
x += pivot;
if (pivot < 100) {
var x = 100 + extra;
if (pivot < 50) {
var x = 50 + extra;
return x + extra;
} else {
x += extra;
return x + extra;
}
} else {
x += extra;
return -100 + extra;
}
}
@method_id(106)
fun testNameShadowing() {
var x = 0;
var sum = 0;
sum += withNameShadowing(mutate x, 100, 10);
sum += withNameShadowing(mutate x, 50, 10);
sum += withNameShadowing(mutate x, 0, 10);
return (x, sum);
}
fun updateTwoItems(mutate self: (int, int), byValue: int) {
val (first, second) = self;
self = (first + byValue, second + byValue);
}
@method_id(107)
fun testMutableTensor() {
var t = (40, 50);
t.updateTwoItems(10);
updateTwoItems(mutate t, 10);
return t;
}
@pure
fun myStoreUint(mutate self: builder, x: int, len: int): self
asm(x self len) "STIX";
@pure
fun myStoreU32(mutate self: builder, x: int): self {
return self.storeUint(x, 32);
}
fun getSumOfNumbersInCell(c: cell): int {
var sum = 0;
var s = c.beginParse();
var n_numbers = s.getRemainingBitsCount() / 32;
repeat (n_numbers) {
sum += s.loadUint(32);
}
return sum;
}
@method_id(110)
fun testStoreChaining() {
var b = beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32);
b.storeUint(4, 32);
b.myStoreUint(5, 32).storeUint(6, 32);
storeUint(mutate b, 7, 32);
b = b.storeUint(8, 32);
b = b.storeUint(9, 32).storeUint(10, 32);
return getBuilderBitsCount(b);
}
@method_id(111)
fun testStoreChainingCustom() {
var b = beginCell().myStoreUint(1, 32).myStoreUint(2, 32).myStoreUint(3, 32);
b.myStoreUint(4, 32);
b.myStoreUint(5, 32).myStoreUint(6, 32);
myStoreUint(mutate b, 7, 32);
b = b.myStoreUint(8, 32);
b = b.myStoreUint(9, 32).myStoreUint(10, 32);
val sum1 = getSumOfNumbersInCell(b.endCell());
b = beginCell().myStoreU32(1).storeUint(2, 32).myStoreU32(3);
b.myStoreU32(4);
b.myStoreU32(5).myStoreU32(6);
myStoreU32(mutate b, 7);
b = b.myStoreU32(8);
b = b.storeUint(9, 32).myStoreU32(10);
val sum2 = getSumOfNumbersInCell(b.endCell());
return (sum1, sum2);
}
fun myStoreU32_and_mutate_x(mutate self: builder, mutate x: int): void {
return myStoreUint(mutate self, x += 10, 32);
}
@method_id(112)
fun testStoreAndMutateBoth() {
var x = 3;
var b: builder = beginCell().myStoreUint(1, 32);
b.myStoreU32_and_mutate_x(mutate x);
b.myStoreU32(3).myStoreU32_and_mutate_x(mutate x);
b.myStoreU32_and_mutate_x(mutate x);
var cs: slice = b.endCell().beginParse();
var (n1,n2,n3,n4,n5) = (cs.loadUint(32),cs.loadUint(32),cs.loadUint(32),cs.loadUint(32),cs.loadUint(32));
assert(n5 == x) throw 100;
return [n1,n2,n3,n4,n5];
}
global ccc: builder;
@method_id(113)
fun testStoreChainingForGlobal() {
ccc = beginCell().storeUint(1, 32).myStoreUint(2, 32).myStoreU32(3);
ccc.storeUint(4, 32);
ccc.storeUint(5, 32).myStoreU32(6);
storeUint(mutate ccc, 7, 32);
ccc = ccc.myStoreU32(8);
ccc = ccc.storeUint(9, 32).myStoreUint(10, 32);
return getBuilderBitsCount(ccc);
}
fun alwaysThrows(): int { throw 123; return 123; }
fun loadIntFromCell(c: cell, len: int) { return c.beginParse().loadUint(len); }
@method_id(114)
fun testLoadIntForTemporaryObject() {
val c0 = beginCell().storeUint(0, 32).endCell();
val c4 = beginCell().storeUint(4, 32).endCell();
return (
beginCell().storeUint(1, 32).endCell().beginParse().loadUint(32),
beginCell().storeUint(2, 32).endCell().beginParse().loadUint(32),
c0.beginParse().loadUint(32) ? alwaysThrows() : loadIntFromCell(c4, 32)
);
}
@pure
fun myStoreUint_pure(mutate self: builder): void
asm "STIX";
fun myStoreUint_impure(mutate self: builder): void
asm "STIX";
fun testStoreUintPureUnusedResult() {
var b = beginCell();
b.myStoreUint_pure();
var s = b.endCell().beginParse();
val ii = s.loadUint(32);
return 0;
}
fun testStoreUintImpureUnusedResult() {
var b = beginCell();
b.myStoreUint_impure();
var s = b.endCell().beginParse();
val ii = s.loadUint(32);
return 0;
}
global counter: int;
fun writeNext2(mutate self: builder): self {
return self.storeUint(counter += 1, 32).storeUint(counter += 1, 32);
}
fun resetCounter(mutate self: builder): self {
counter = 0;
return self;
}
@method_id(115)
fun testExplicitReturn() {
counter = 0;
return (
beginCell().writeNext2().writeNext2().resetCounter().writeNext2().endCell().getSumOfNumbersInCell(),
counter
);
}
fun main(){}
/**
@testcase | 101 | | 70 60
@testcase | 102 | | 70 50 100 120
@testcase | 103 | | 1 3 4
@testcase | 104 | | 1 2 3
@testcase | 105 | | 5 5 110
@testcase | 106 | | 160 110
@testcase | 107 | | 60 70
@testcase | 110 | | 320
@testcase | 111 | | 55 55
@testcase | 112 | | [ 1 13 3 23 33 ]
@testcase | 113 | | 320
@testcase | 114 | | 1 2 4
@testcase | 115 | | 13 2
@fif_codegen
"""
incrementInPlace PROC:<{
// self byValue
ADD // self
}>
"""
@fif_codegen
"""
testIncrement2 PROC:<{
...
incrementTwoInPlace CALLDICT // x y sum1
-ROT
10 PUSHINT // sum1 x y _9=10
incrementTwoInPlace CALLDICT // sum1 x y sum2
s1 s3 s0 XCHG3 // x y sum1 sum2
}>
"""
@fif_codegen
"""
load_next PROC:<{
// cs
32 LDI // _1 cs
SWAP // cs _1
}>
"""
@fif_codegen
"""
testStoreUintPureUnusedResult PROC:<{
//
0 PUSHINT // _12=0
}>
"""
@fif_codegen
"""
testStoreUintImpureUnusedResult PROC:<{
//
NEWC // b
STIX // _2
DROP //
0 PUSHINT // _12=0
}>
"""
*/

View file

@ -30,13 +30,11 @@ global `some()var`:int;
}
@method_id(113)fun`unary+bitwise-constant`():[int,int,int]{
// todo spaces are still not allowed before ~
return [~-~~+-3, ~+3-~ 9, -(-~+-20-~ 10+3+~ 38&39)];
return [~-~~+-3, ~+3-~9, -(-~+-20-~ 10+3+~38&39)];
}
@method_id(114)fun`unary+bitwize-parametrized`(c3:int, c9:int, c20:int, c10:int, c38:int):[int,int,int]{
// todo spaces are still not allowed before ~
return [~-~~+-c3, ~+c3-~ `c9`, -(-~+-c20-~ c10+c3+~ c38&39)];
return [~-~~+-c3, ~+c3-~`c9`, -(-~+-c20-~c10+c3+~c38&39)];
}
fun add3(a: int, b: int, c: int) { return a+b+c; }
@ -49,16 +47,16 @@ fun add3(a: int, b: int, c: int) { return a+b+c; }
return [add3(fst2,snd2,trd2),add3(fst1,snd1,trd1)];
}
fun `load:u32`(cs: slice): (slice, int) {
return cs.loadUint(32);
fun `load:u32`(mutate self: slice): int {
return self.loadUint(32);
}
@method_id(116) fun `call_~_via_backticks`():[int,int,int,int] {
var b:builder = beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32).storeUint(4, 32);
var `cs`:slice = b.endCell().beginParse();
var (`cs` redef,one:int) = `cs`.`loadUint`(32);
var (two:int,three:int) = (`cs`~`loadUint`(32), cs~`load:u32`());
var (cs redef,four:int) = cs.`load:u32`();
val one:int=`cs`.`loadUint`(32);
val (two:int,three:int) = (`cs`.`loadUint`(32), cs.`load:u32`());
val four:int = cs.`load:u32`();
return [one,two,three,four];
}

View file

@ -12,8 +12,8 @@ fun test1() {
var t = createEmptyTuple();
do {
var num = numbers~listNext();
t~tuplePush(num);
var num = numbers.listNext();
t.tuplePush(num);
} while (numbers != null);
return (h, numbers == null, t);
@ -63,7 +63,7 @@ fun test4() {
@method_id(105)
fun test5() {
var n = getUntypedNull();
return !(null == n) ? n~loadInt(32) : 100;
return !(null == n) ? n.loadInt(32) : 100;
}
@method_id(106)
@ -75,7 +75,7 @@ fun test6(x: int) {
fun test7() {
var b = beginCell().storeMaybeRef(null);
var s = b.endCell().beginParse();
var c = s~loadMaybeRef();
var c = s.loadMaybeRef();
return (null == c) * 10 + (b != null);
}
@ -145,14 +145,14 @@ fun main() {
"""
test7 PROC:<{
...
LDOPTREF // b _17 _16
LDOPTREF // b _20 _19
DROP // b c
ISNULL // b _10
10 MULCONST // b _12
SWAP // _12 b
ISNULL // _12 _13
0 EQINT // _12 _14
ADD // _15
ISNULL // b _13
10 MULCONST // b _15
SWAP // _15 b
ISNULL // _15 _16
0 EQINT // _15 _17
ADD // _18
}>
"""
*/

View file

@ -0,0 +1,113 @@
const cc1 = "0:ca6e321c7cce9ecedf0a8ca2492ec8592494aa5fb5ce0387dff96ef6af982a3e"a;
const cc2 = "EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPrHF"a;
fun verifyAddr(addr: slice, workchain: int, number: int) {
assert (addr.getRemainingBitsCount() == 3 + 8 + 256) throw 112;
addr.skipBits(3);
assert (addr.loadUint(8) == workchain) throw 111;
assert (addr.loadUint(256) == number) throw 111;
}
fun main() {
verifyAddr("Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF"a, 255, 23158417847463239084714197001737581570653996933128112807891516801582625927987);
verifyAddr("EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c"a, 0, 0);
verifyAddr("EQCRDM9h4k3UJdOePPuyX40mCgA4vxge5Dc5vjBR8djbEKC5"a, 0, 65607996509792174074532427555986248720836864382484024657400295821210434460432);
verifyAddr("UQCOgxbCOjOLH_cEuQdGgS23zBM5SrQQepMFedjK-oixYbis"a, 0, 64460038539088394980732229180523693489682583665805557562964506609821558550881);
verifyAddr("EQDa4VOnTYlLvDJ0gZjNYm5PXfSmmtL6Vs6A_CZEtXCNICq_"a, 0, 99002318936150612861744867526221033858534811876886359650897405270877291973920);
verifyAddr("Ef8BtXO9bcTMXjg9bgivKh4lhJmZWQPP6_rb9vfjlTP5FJtM"a, 255, 772910975127952880303441415761050161913031788763061162001556772893733681428);
verifyAddr("Ef89xh-uy860-mCcvS8zcAUs8bApmxLGygDLEKjUk5RL-311"a, 255, 27941138149036269893630478666581900122707382189183906805784676408403709676539);
verifyAddr("Ef_vA6yRfmt2P4UHnxlrQUZFcBnKux8mL2eMqBgpeMFPorr4"a, 255, 108109262375472472702582493362335418330829651067377177643099076957184687427490);
verifyAddr("Ef8o6AM9sUZ8rOqLFY8PYeaC3gbopZR1BMkE8fcD0r5NnmCi"a, 255, 18502444830824300068094395885436326119386947594392869497312068745716154912158);
verifyAddr("Ef_fvrd0hBoVJUxoi7wH173Zk8NPiyVvxh5IoYSjEYZbOhsu"a, 255, 101202732337223525952216789200341489000836292542250083765062769181728788863802);
verifyAddr("Ef9nzj6RBc4mQ6p3ng7mGJ7tp7MbzERhe7obkM9A0wnCCEcf"a, 255, 46952625717497919357580310066854892621799390294920450816077086267929711460872);
verifyAddr("Ef9rU-_AAnBkHB71TIC3QvUf5LcAsvj0B4IoYzAXLpEFd5CA"a, 255, 48545777798729612074233611768739897492467685225150339217043102685589809464695);
verifyAddr("Ef9LynHHKgBxY6-l-W_dWN-CtGT2_ji5rN3EzOI-p9zWEfq6"a, 255, 34281152017620085319078796986198022632548048219136747083019177301186013091345);
verifyAddr("Ef9hMd78gzSiVsK0zz0AHtEja8x1UoB_NDZMjn-l86NQK_2Y"a, 255, 43962460814164090767878334494257755557842170134382045184921495822637115592747);
verifyAddr("Ef80FNJ5NJO4-0QwlVAWckUZXdk-PfYDexDZ1-ju9SxhF0A6"a, 255, 23557057702048801338698514499604413540742716310574705490458593067566768087319);
verifyAddr("Ef_fdIbThooPs4_r2DE_Z6ZsWycJdHLnsuKAJHTcbaZaipez"a, 255, 101071650030310556115830521522496708686577365303530257137459798093298869361290);
verifyAddr("Ef_lva0qEiZhWrrZJl-IJxyCcTQmmTo71fIWyQ31HxJ8NurV"a, 255, 103914771557158282349484109182290824591675204108148026180964788916630125182006);
verifyAddr("Ef8sMGKypw006AeRYqimLjmY2Ufp-SHk8C0ZJBNgVBlzw_Nr"a, 255, 19987255184378161380023126214650814972824352533523055905552702178965809886147);
verifyAddr("EQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff+W72r5gqPrHF"a, 0, 91561894446285001782438967260723928368560331318344957259023550817453781559870);
verifyAddr("EQCaSCHVak-jIc9ANutTAfHpZNM3YdGky7yaDzsTrg0WhFlm"a, 0, 69783625181781015447914682554083924798054947959007050695795761257887453484676);
verifyAddr("EQBS9U3AfD15fGmOtRMXQAxcPVBwNuItfLcDni9fkbTyyNX0"a, 0, 37523067738561024305547433298623118197038688994386001017161816416175242146504);
verifyAddr("EQBiMNL9qNWMAkJHuM0BFneYcuHL17kzS4pswpaEO-NGWrFG"a, 0, 44412924025649114419413541526870954696667907029239618728289150652715284776538);
verifyAddr("EQAUzE-Nef80O9dLZy91HfPiOb6EEQ8YqyWKyIU-KeaYLNUi"a, 0, 9407242825041766837311851458322335726136775042891143504070507665010681354284);
verifyAddr("EQD-nhrinjv0B4LTgr0dRHTHwH1MOsgGhKBXJZd7vESMZUf1"a, 0, 115166810931401616117484448645661180241548402534908005320733783571353775148133);
verifyAddr("EQAVD3Fni9I6j8XeSIl-wAGBEhqhame6OtAY0GScKT0D9X6f"a, 0, 9525855215156855607080079714361451576383963668563135377495902959388099150837);
verifyAddr("EQC6ACq3VANZjqfRBy7JMHkpLwqQ9qyYJsCIGx1mYbQgxaKw"a, 0, 84130484652351964071210477536969520113177637645401392541565606610268614566085);
verifyAddr("EQCIJLNFIko5CvpKn9oAkrDgLocDOoD4vwmHxNx_fsG_LkwW"a, 0, 61579391178099797614367237687950512448308156724136883899001108680249616482094);
verifyAddr("EQCe4AYIBce1pAk2qJJPSs1OzyZRlKjkfq8zuC8D7erv6DUP"a, 0, 71861245445432818728925844931259040612664802586395398157190478191760507596776);
verifyAddr("EQCtrtTXEAoSpoERmiqOnICe9LHxn2N89N4BH9qdHlrG-U0i"a, 0, 78559023162479717496981724991265882229440558807791659796411897368395464230649);
verifyAddr("EQBBlraAps0OZaB9Q8ePQn2wVAaL1G411A-dNppyWe3X3GIT"a, 0, 29666621803903557832193058147214384979915773445007872807927344851911086823388);
verifyAddr("EQBiASqUqaVizrozLRbszkWC2kETbkhpO2qniDVDPPg2_0W8"a, 0, 44328719889509369519441680467651025944540360433148852643949783408843779749631);
verifyAddr("EQBu2Q1EO8gIoNA1qoGWnHUudKfmqlKEDTQE-DxN-_4sdg14"a, 0, 50137910719490808065414827264266674858051167131188257457782826342827836714102);
verifyAddr("EQA5bvxWd5-q2vJUVqR9AlbEIfdFysLR0PXGgVlBf8x5hWuF"a, 0, 25977927117604457079092522008276392864656238504700352770597256138254994667909);
verifyAddr("EQBguMSHjFv5bfoOdshr3ruS9ymSZzhRKMovoNrxGxZXvmee"a, 0, 43748489720571123896506696370504498290006245978262404519821633796370658121662);
verifyAddr("EQAxL0oF1-zNgimPKthbDnYS4xj94rHtfNRN7_Pd1r2LNNv3"a, 0, 22246882279393590648219842750911786376759362211171398419754426796438233910068);
verifyAddr("EQANX1uRKGZfyPIwEaIXrR0ZOqadct5q10dvKxWIxx7SQqzW"a, 0, 6048549475100840191738010856156544571222758030966479209409932714701987172930);
verifyAddr("EQBitdFDoU5DWSjfKq7AsO29RIwAnBzzvcVVSn5ekQoB9Liv"a, 0, 44647902768175374073183447303109856895983123510911038181495138821771906122228);
verifyAddr("EQBgbux7VSjqJHP7ByRK1q4QuVZbpSCesNgvz5qad3lfXX_B"a, 0, 43618018778298854282398238948198420936771670943015013768514626213000552996701);
verifyAddr("EQDisBd8U7M3CEOZ8gcWCdetdmJi3AI31zIT5qBwOdmUbsxY"a, 0, 102533830955233207294921564956803510155400341370448300698800842506363763004526);
verifyAddr("EQAZpn_eynVlf7Ii2d6jP_p1URPrdF9F3S7DiudQyelkjzwE"a, 0, 11602000355550451044739442929923326898313570892134000961608306166632391730319);
verifyAddr("EQDE0HBgfkOiqHezLtExBGTvOs8eitthHQosBjW3BmDy1y2K"a, 0, 89021598108837008984355105304701054698583123510131754065320641619941010764503);
verifyAddr("EQDyT36zktBN9PVWvZ1joRxhIfEUgCPt4F2isa-enUA_d6CP"a, 0, 109600164736599393471831241268953938618560132398692391517933933264745646800759);
verifyAddr("EQDSMUGwt25IQd3_yHjI03F71G8Kp2GMaMEv2TiWoTKbsyRH"a, 0, 95072727086440754059372943502908629555499501854161516009430039520728770059187);
verifyAddr("EQAgK1EcrvEuL9sCtoj3cNhVNOuf3lo5GIPE2gn1fwZZYB3j"a, 0, 14550545393206146289454646242321274637527057595221202748348667645886114191712);
verifyAddr("EQCDKqL5w_6MD-Z7AOButu-uR-ZJTsgNU1fu464hn9grY81U"a, 0, 59328315557704100696483472039557119625141880163887490602190749720459366378339);
verifyAddr("EQB1aVMyFBhnlYXmQjsma0S63kvxKU7ccZKFNCFTwX7ASPv4"a, 0, 53106696421104300082516512931084483581353095629408473618166869610568148238408);
verifyAddr("EQBbjrXHoxDyh1ZYGBdBoQgLaScxW6pZR1hEhJC8BqF-5Kgq"a, 0, 41412616102566803060532874463898939692666425753852274254609049615175463829220);
verifyAddr("EQC-QeZ13QP0lszxNKt380fCWuaV94vwC/bfuqmrlg1/fJPA"a, 0, 86055876869280374285292827775555707420719385459150221433115419095878595346300);
verifyAddr("EQAiUwpF27vXCngqNhf_TQ5E_06ah0G4zuSrnfU7CLLaht5H"a, 0, 15525356059048115813946213102829493539706126913595626308144289257869196581510);
verifyAddr("EQBqiVjmhe2iVGmgOSDO1FGjSiz_AMtb1w7lLEiP4XIF_SFy"a, 0, 48187833566271418625754761625661652107159264793429628379411792200127405491709);
verifyAddr("EQDmwvaK2d_SbaPdpOM60effPWeKsksgDVwFPEyxuftM396K"a, 0, 104376425077737068747642645125299653296942252727305637929378053253273342397663);
verifyAddr("EQDWtPZZgF7wvIMUHZQojuD3utiuivsW7WslRJ33dgv-5yc8"a, 0, 97114682311034709685427168495629428400170984047839002197324103884924936519399);
verifyAddr("EQAA7z0JI0JKqbN-1uENKz9JrxIO5ZRY-ehMeg9fPncx50Ck"a, 0, 422697701361909095759185681783393186844038628935759044330165207027374567911);
verifyAddr("EQBVUHRoCq6coQYUwOAhGSoAmQ6Mpm7dFlDYon6HMgWV8Ftr"a, 0, 38588743302295548905191533977469452945717219128199196974980570837505276220912);
verifyAddr("EQCTdvDCf0bA5dOPI1-44tB2ZfNcMGiklzvg27TovgDEqM6E"a, 0, 66700138358140658950710678965721715920748906761125730971082529064117803730088);
verifyAddr("EQBDBKE5WGKIlnoi3OOzw7vkKKIX55eWjPvgxJWwek8AyL2J"a, 0, 30313140970524770883308749215942283658935592719811899513010665548955593408712);
verifyAddr("EQAvCSyLCo21GrqLAifdov4WkOxuGQCjMRxgF1cXSaNzLHZe"a, 0, 21274912932379789207153885262858116665851037273450532982121514600400844714796);
verifyAddr("EQCsLpDeHB2qpRbmsCb_0xmsYVNx1NgeYrvHGT1TDrHkDgL4"a, 0, 77880084760844670718511036557364450189323549135231036747480176919181282894862);
verifyAddr("EQCTQ8kPwyX92r48gCIL_pLN_RcQT9ghZygnmDTYkOkuW_j5"a, 0, 66609755171046741472463433430016629628609840960137226492652665879987546041947);
verifyAddr("EQCTrFRSHt-tfk7WxK9ZHQmqLcgxXxTK7wGfCEbqgY2W9Mcx"a, 0, 66794468397542182731534559853537484892417154018190804733043974345563210356468);
verifyAddr("EQCv28y49GdaLncoclv0ISdDlMUY_cxDPGNWFCPT8t4GuqUJ"a, 0, 79543100951881731989812212377176715376834409392116644269458867858071577560762);
verifyAddr("EQCVL-k6deDR56Z8pcb0Btg0lGfaivOGfdDCD1vvyRsyL9vS"a, 0, 67479265933941008511790471646564661743401752930295407567346938670637286896175);
verifyAddr("EQD6t2dXDjZxF1DqunKF-8dEWivJdliY_0FYiCXnthuqnDCa"a, 0, 113402258385556889021060606279033166272577193563727959698596277924908309916316);
verifyAddr("EQDE98XNzXiPq7VnbJJ2M4-Ht3tX_OWR0xUTTnDC8NObLmyU"a, 0, 89091094739778473356272490822716056624384395256388481953562551087642791090990);
verifyAddr("EQDfeRDE1TDhwt478CDR0Q7MDwqcTUhfjqyTT59mgoAaF6f7"a, 0, 101079669463449311486034260688909914923832300293253430457119371423825321269783);
verifyAddr("EQDijcEyUKa-QgCbeGlggQk1uBtt2ZRHyW4Y4gB4R6MN6RLW"a, 0, 102473162609487797404330889623966425536887610061087715571345738626121871855081);
verifyAddr("EQDOtFOt41skbjBkZF89oYXpoDECjlxIzD-ShWAOYyzuxqLA"a, 0, 93495056812773926196963707371665512785268729004579280701087533371037976424134);
verifyAddr("EQDuJKSFWU7AYqH6KLFfAbYvMuz346eWmJvG6_2NYE42_B4T"a, 0, 107715199938628393100813870735031557263256555616273999363057194279168054802172);
verifyAddr("EQDwGu4vFv1e3wn8min_iy7OPJXegOYTFQ5bZFZ5a5ZPiBpX"a, 0, 108602665568837301744601989570019709742180613578164394799026726718721456754568);
verifyAddr("EQC4G2ph6AS_mD_-cIv4aIYm1z5jAgCW_TTDEr72ygXOP2X-"a, 0, 83274003234732023403481554420859495155084746906198543572711543697320249249343);
verifyAddr("EQDpUkyAa6lZ12P3ZB2PL_rmWwI1I55BU4kxw_rssFL5dswA"a, 0, 105534303174146507629736518862713754948570412188900908177600861330298381728118);
verifyAddr("EQDoIA20MF1qEcSPtROdCu5ukGx9dVjgMeJh1oQ4A4cf_Jif"a, 0, 104993214557977037193613824776415934089204193426692473563548548423424814817276);
verifyAddr("EQDpUkyAa6lZ12P3ZB2PL_rmWwI1I55BU4kxw_rssFL5dswA"a, 0, 105534303174146507629736518862713754948570412188900908177600861330298381728118);
verifyAddr("EQClLO4EnZ_rTyV1GVpWy53pLgWJRki5c4ZzuM_1O_ClBkO9"a, 0, 74711004027159342540251007601464500186374346239921204216319145006974068892934);
verifyAddr("EQDmkj65Ab_m0aZaW8IpKw4kYqIgITw_HRstYEkVQ6NIYCyW"a, 0, 104290347741656803921830951060768893809692975574470790497562993373950614128736);
verifyAddr("EQCqNTwAYUNhPFS0RgqZoTLGJcQQxbAJ7csUo4YO3_TONLab"a, 0, 76987241268612358571638783428744566580605181728938801022059780105627411729972);
verifyAddr("EQCL3DmCynaRK7-vsfeNmd4Jj-UxAIHPvA4qS2xwaL6UpLbF"a, 0, 63260589232981964910240894899061676480139492286430589202252472895352724165796);
verifyAddr("EQDbU1SVEjBE73oUqgAoM9gDcShUkM5EC2PgoCjuwVUKo-Ee"a, 0, 99203745911752606845646497420891218522647962685916739950275357890977532807843);
verifyAddr("EQD02VdcF4TDbCKLLhZJ39NQTu6aWq2LnLjp0oXqbNu_BANK"a, 0, 110748343802097970709980079967961144373090790244250392237586606542170934198020);
verifyAddr("EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA"a, 0, 51839428943991432793039248316067731096592274748149794482308513726460953499586);
verifyAddr("UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA"a, 0, 91561894446285001782438967260723928368560331318344957259023550817453781559870);
verifyAddr("EQAUTbQiM522Y_XJ_T98QPhPhTmb4nV--VSPiha8kC6kRfPO"a, 0, 9183547432069678364603018431103042146626948674383548774683927217595824907333);
verifyAddr("EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE"a, 0, 45985353862647206060987594732861817093328871106941773337270673759241903247880);
verifyAddr("UQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPuwA"a, 0, 91561894446285001782438967260723928368560331318344957259023550817453781559870);
verifyAddr("kQDKbjIcfM6ezt8KjKJJLshZJJSqX7XOA4ff-W72r5gqPgpP"a, 0, 91561894446285001782438967260723928368560331318344957259023550817453781559870);
verifyAddr("kf-Dfdg-YQXaR2Q97gZJ4fGBtmV1DHOU1y1RPyyZZtRy_Ikh"a, 255, 59475331506450494976393625198911249698879029820580340449086829444312920781564);
verifyAddr("0:527964d55cfa6eb731f4bfc07e9d025098097ef8505519e853986279bd8400d8"a, 0, 37304138005561100291416421295333982606153966175434134130332440738068913455320);
verifyAddr("0:0000000000000000000000000000000000000000000000000000000000000000"a, 0, 0);
verifyAddr("0:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFfffffffffffffffffffffffffffff"a, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639935);
verifyAddr("0:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"a, 0, 23158417847463239084714197001737581570653996933128112807891516801582625927987);
verifyAddr("0:0000000000000000000000000000000000000000000000000000000000000000"a, 0, 0);
verifyAddr("1:527964d55cfa6eb731f4bfc07e9d025098097ef8505519e853986279bd8400d8"a, 1, 37304138005561100291416421295333982606153966175434134130332440738068913455320);
verifyAddr("9:527964d55cfa6eb731f4bfc07e9d025098097ef8505519e853986279bd8400d8"a, 9, 37304138005561100291416421295333982606153966175434134130332440738068913455320);
verifyAddr("99:527964d55cfa6eb731f4bfc07e9d025098097ef8505519e853986279bd8400d8"a, 99, 37304138005561100291416421295333982606153966175434134130332440738068913455320);
verifyAddr("-1:527964d55cfa6eb731f4bfc07e9d025098097ef8505519e853986279bd8400d8"a, 255, 37304138005561100291416421295333982606153966175434134130332440738068913455320);
return cc1.isSliceBitsEqual(cc2);
}
/**
@testcase | 0 | | -1
*/

View file

@ -13,8 +13,8 @@ fun f_pure2(): int {
fun get_contract_data(): (int, int) {
var c: cell = getContractData();
var cs: slice = c.beginParse();
cs~loadBits(32);
var value: int = cs~loadUint(16);
cs.loadBits(32);
var value: int = cs.loadUint(16);
return (1, value);
}

View file

@ -0,0 +1,213 @@
fun incChained(mutate self: int): self {
self = self + 1;
return self;
}
fun incChained2(mutate self: int): self {
return self.incChained();
}
fun incChained3(mutate self: int): self {
incChained(mutate self);
return self;
}
fun incChained4(mutate self: int): self {
self.incChained();
return self;
}
@method_id(101)
fun testIncChainedCodegen(x: int) {
return x.incChained().incChained2().incChained3().incChained4();
}
@method_id(102)
fun testIncChained() {
var x: int = 10;
incChained(mutate x);
x.incChained();
x.incChained2();
x.incChained2().incChained();
x = x.incChained();
x = x.incChained2().incChained().incChained2();
return x.incChained();
}
fun incChainedWithMiddleReturn(mutate self: int, maxValue: int): self {
if (self >= maxValue) {
return self;
}
self += 1;
return self;
}
@method_id(103)
fun testIncChainedWithMiddleReturn(x: int) {
x.incChainedWithMiddleReturn(10).incChainedWithMiddleReturn(10);
x = x.incChainedWithMiddleReturn(10).incChainedWithMiddleReturn(10);
return x.incChainedWithMiddleReturn(10).incChainedWithMiddleReturn(999);
}
fun incChainedMutatingBoth(mutate self: int, mutate y: int): self {
self += 1;
y += 1;
return self;
}
global c104: int;
@method_id(104)
fun testIncChainedMutatingBoth() {
var (x, y) = (0, 0);
c104 = 0;
x.incChainedMutatingBoth(mutate y).incChainedMutatingBoth(mutate y);
incChainedMutatingBoth(mutate x, mutate y);
x = x.incChainedMutatingBoth(mutate c104).incChainedMutatingBoth(mutate c104).incChainedMutatingBoth(mutate y);
return (x, y, c104);
}
fun incTensorChained(mutate self: (int, int)): self {
val (f, s) = self;
self = (f + 1, s + 1);
return self;
}
@method_id(105)
fun testIncTensorChained(f: int, s: int) {
var tens = (f, s);
tens.incTensorChained().incTensorChained();
return tens.incTensorChained().incTensorChained();
}
fun incConditionalChainable(mutate self: int, mutate another: int, ifLessThan: int): self {
another += 1;
return self.incChained() < ifLessThan ? self.incChained().incChained() : self;
}
@method_id(106)
fun testIncConditionalChainable(x: int) {
var y = 0;
x.incConditionalChainable(mutate y, 5).incConditionalChainable(mutate y, 5);
x = x.incConditionalChainable(mutate y, 5).incConditionalChainable(mutate y, 5);
return (x.incConditionalChainable(mutate y, 5), y);
}
fun checkNotEq(self: int, throwIfEq: int): void {
if (self == throwIfEq) {
throw 100 + throwIfEq;
}
}
@method_id(107)
fun testNotMutatingSelf(arg: int) {
try {
arg.checkNotEq(100);
arg.checkNotEq(101);
arg.checkNotEq(102);
return 0;
} catch (code) {
return code;
}
}
global c108: int;
fun checkNotEqChainable(self: int, throwIfEq: int): self {
c108 += 1;
if (self != throwIfEq) {
return self;
}
throw 100 + throwIfEq;
return self;
}
@method_id(108)
fun testNotMutatingChainableSelf(arg: int) {
c108 = 0;
try {
arg.checkNotEqChainable(100).checkNotEqChainable(101).checkNotEqChainable(102);
arg = arg.checkNotEqChainable(100).checkNotEqChainable(101).checkNotEqChainable(102);
return (arg, c108);
} catch (code) {
return (code, c108);
}
}
global onceFailed109: int;
fun checkNotEqChainableMutateAnother(self: int, throwIfEq: int, mutate toInc: int): self {
if (onceFailed109) { return self; }
toInc += 1;
try { return self.checkNotEqChainable(throwIfEq); }
catch { onceFailed109 = 1; return self; }
}
global c109: int;
@method_id(109)
fun testNotMutatingChainableSelfMutateAnother(initial: int) {
val arg = initial;
var x = 0;
c108 = 0;
c109 = 0;
onceFailed109 = 0;
arg.checkNotEqChainableMutateAnother(100, mutate x)
.checkNotEqChainableMutateAnother(101, mutate c109)
.checkNotEqChainableMutateAnother(102, mutate x);
return (arg, c108, c109, x);
}
fun main() { }
/**
@testcase | 101 | 5 | 9
@testcase | 102 | | 20
@testcase | 103 | 1 | 7
@testcase | 103 | 100 | 101
@testcase | 103 | 8 | 11
@testcase | 104 | | 6 4 2
@testcase | 105 | 1 2 | 5 6
@testcase | 106 | -20 | -5 5
@testcase | 106 | -1 | 8 5
@testcase | 106 | 7 | 12 5
@testcase | 107 | 200 | 0
@testcase | 107 | 102 | 202
@testcase | 108 | 200 | 200 6
@testcase | 108 | 101 | 201 0
@testcase | 109 | 200 | 200 3 1 2
@testcase | 109 | 100 | 100 0 0 1
@testcase | 109 | 102 | 102 2 1 2
@fif_codegen
"""
incChained PROC:<{
// self
INC // self
}>
incChained2 PROC:<{
// self
incChained CALLDICT // self
}>
incChained3 PROC:<{
// self
incChained CALLDICT // self
}>
incChained4 PROC:<{
// self
incChained CALLDICT // self
}>
"""
@fif_codegen
"""
testIncChainedCodegen PROC:<{
// x
incChained CALLDICT // x
incChained2 CALLDICT // x
incChained3 CALLDICT // x
incChained4 CALLDICT // x
}>
"""
*/

View file

@ -243,7 +243,7 @@ fun tanh_f258(x: int, n: int): int {
repeat (n) {
a = (c -= Two) + mulDivRound(x2, 1 << 239, a); // a := 2k+1+x^2/a as fixed250, k=n+1,n,...,2
}
a = (stackMoveToTop(3) << 254) + mulDivRound(x2, 1 << 243, a); // a := 3+x^2/a as fixed254
a = (3 << 254) + mulDivRound(x2, 1 << 243, a); // a := 3+x^2/a as fixed254
// y = x/(1+a') = x - x*a'/(1+a') = x - x*x^2/(a+x^2) where a' = x^2/a
return x - (mulDivRound(x, x2, a + (x2 ~>> 7)) ~>> 7);
}
@ -257,12 +257,12 @@ fun expm1_f257(x: int): int {
// (almost) compute tanh(x/2) first; x/2 as fixed258 = x as fixed257
var x2: int = mulDivRound(x, x, 1 << 255); // x^2 as fixed261
var Two: int = (1 << 251); // 2. as fixed250
var a: int = stackMoveToTop(39) << 250; // a=2n+5 as fixed250
var a: int = 39 << 250; // a=2n+5 as fixed250
var c = a;
repeat (17) {
a = (c -= Two) + mulDivRound(x2, 1 << 239, a); // a := 2k+1+x^2/a as fixed250, k=n+1,n,...,2
}
a = (stackMoveToTop(3) << 254) + mulDivRound(x2, 1 << 243, a); // a := 3+x^2/a as fixed254
a = (3 << 254) + mulDivRound(x2, 1 << 243, a); // a := 3+x^2/a as fixed254
// now tanh(x/2) = x/(1+a') where a'=x^2/a ; apply exp(x)-1=2*tanh(x/2)/(1-tanh(x/2))
var t: int = (x ~>> 4) - a; // t:=x-a as fixed254
return x - mulDivRound(x2, t / 2, a + mulrshiftr256(x, t) ~/ 4) ~/ 4; // x - x^2 * (x-a) / (a + x*(x-a))
@ -306,12 +306,12 @@ fun fixed248_exp2(x: int): int {
fun tan_f260_inlined(x: int): int {
var x2: int = mulrshiftr256(x, x); // x^2 as fixed264
var Two: int = (1 << 251); // 2. as fixed250
var a: int = stackMoveToTop(33) << 250; // a=2n+5 as fixed250
var a: int = 33 << 250; // a=2n+5 as fixed250
var c = a;
repeat (14) {
a = (c -= Two) - mulDivRound(x2, 1 << 236, a); // a := 2k+1-x^2/a as fixed250, k=n+1,n,...,2
}
a = (stackMoveToTop(3) << 254) - mulDivRound(x2, 1 << 240, a); // a := 3-x^2/a as fixed254
a = (3 << 254) - mulDivRound(x2, 1 << 240, a); // a := 3-x^2/a as fixed254
// y = x/(1-a') = x + x*a'/(1-a') = x + x*x^2/(a-x^2) where a' = x^2/a
return x + (mulDivRound(x / 2, x2, a - (x2 ~>> 10)) ~>> 9);
}
@ -330,12 +330,12 @@ fun tan_f260(x: int): int {
fun tan_f258_inlined(x: int): int {
var x2: int = mulrshiftr256(x, x); // x^2 as fixed260
var Two: int = (1 << 251); // 2. as fixed250
var a: int = stackMoveToTop(41) << 250; // a=2n+5 as fixed250
var a: int = 41 << 250; // a=2n+5 as fixed250
var c = a;
repeat (18) {
a = (c -= Two) - mulDivRound(x2, 1 << 240, a); // a := 2k+1-x^2/a as fixed250, k=n+1,n,...,2
}
a = (stackMoveToTop(3) << 254) - mulDivRound(x2, 1 << 244, a); // a := 3-x^2/a as fixed254
a = (3 << 254) - mulDivRound(x2, 1 << 244, a); // a := 3-x^2/a as fixed254
// y = x/(1-a') = x + x*a'/(1-a') = x + x*x^2/(a-x^2) where a' = x^2/a
return x + (mulDivRound(x / 2, x2, a - (x2 ~>> 6)) ~>> 5);
}
@ -546,9 +546,8 @@ fun atanh_f261(x: int, n: int): int {
fun log_aux_f257(x: int): (int, int) {
var s: int = log2_floor_p1(x);
x <<= 256 - s;
var t: int = stackMoveToTop(-1 << 256);
var t: int = -1 << 256;
if ((x >> 249) <= 90) {
// t~stackMoveToTop();
t >>= 1;
s -= 1;
}
@ -593,7 +592,7 @@ fun pow33b(m: int): int {
fun log_auxx_f260(x: int): (int, int, int) {
var s: int = log2_floor_p1(x) - 1;
x <<= 255 - s; // rescale to 1 <= x < 2 as fixed255
var t: int = stackMoveToTop(2873) << 244; // ~ (33/32)^11 ~ sqrt(2) as fixed255
var t: int = 2873 << 244; // ~ (33/32)^11 ~ sqrt(2) as fixed255
var x1: int = (x - t) >> 1;
var q: int = mulDivRound(x1, 65, x1 + t) + 11; // crude approximation to round(log(x)/log(33/32))
// t = 1; repeat (q) { t *= 33; } // t:=33^q, 0<=q<=22
@ -742,13 +741,14 @@ fun atan_aux_prereduce(x: int): (int, int, int) {
var tc: int = 7214596; // tan(13*theta) as fixed24 where theta=atan(1/32)
var t1: int = mulDivRound(xu - tc, 1 << 88, xu * tc + (1 << 48)); // tan(x') as fixed64 where x'=atan(x)-13*theta
// t1/(3+t1^2) * 3073/32 = x'/3 * 3072/32 = x' / (96/3072) = x' / theta
var q: int = mulDivRound(t1 * 3073, 1 << 59, t1 * t1 + (stackMoveToTop(3) << 128)) + 13; // approximately round(atan(x)/theta), 0<=q<=25
var q: int = mulDivRound(t1 * 3073, 1 << 59, t1 * t1 + (3 << 128)) + 13; // approximately round(atan(x)/theta), 0<=q<=25
var (pa, pb) = (33226912, 5232641); // (32+I)^5
var (qh, ql) = divMod(q, 5);
var (a, b) = (1 << (5 * (51 - q)), 0); // (1/32^q, 0) as fixed255
repeat (ql) {
// a+b*I *= 32+I
(a, b) = (sub_rev(stackMoveToTop(b), 32 * a), a + 32 * b); // same as (32 * a - b, 32 * b + a), but more efficient
b.stackMoveToTop();
(a, b) = (sub_rev(b, 32 * a), a + 32 * b); // same as (32 * a - b, 32 * b + a), but more efficient
}
repeat (qh) {
// a+b*I *= (32+I)^5 = pa + pb*I
@ -807,7 +807,7 @@ fun atan_auxx_f256(x: int): (int, int) {
@inline_ref
fun atan_f255(x: int): int {
var s: int = (x ~>> 256);
stackMoveToTop(x);
x.stackMoveToTop();
if (s) {
x = lshift256divr(-1 << 255, x); // x:=-1/x as fixed256
} else {
@ -880,7 +880,7 @@ fun fixed248_acos(x: int): int {
@inline_ref
fun fixed248_atan(x: int): int {
var s: int = (x ~>> 249);
stackMoveToTop(x);
x.stackMoveToTop();
if (s) {
s = sign(s);
x = lshift256divr(-1 << 248, x); // x:=-1/x as fixed256
@ -897,7 +897,7 @@ fun fixed248_atan(x: int): int {
@inline_ref
fun fixed248_acot(x: int): int {
var s: int = (x ~>> 249);
stackMoveToTop(x);
x.stackMoveToTop();
if (s) {
x = lshift256divr(-1 << 248, x); // x:=-1/x as fixed256
s = 0;
@ -918,7 +918,7 @@ fun fixed248_acot(x: int): int {
/// fixed252 nrand();
@inline_ref
fun nrand_f252(): int {
var (x, s, t, A, B, r0) = (nan(), stackMoveToTop(29483) << 236, stackMoveToTop(-3167) << 239, 12845, 16693, 9043);
var (x, s, t, A, B, r0) = (nan(), 29483 << 236, -3167 << 239, 12845, 16693, 9043);
// 4/sqrt(e*Pi) = 1.369 loop iterations on average
do {
var (u, v) = (random() / 16 + 1, mulDivRound(random() - (1 << 255), 7027, 1 << 16)); // fixed252; 7027=ceil(sqrt(8/e)*2^12)
@ -950,7 +950,7 @@ fun nrand_f252(): int {
/// fixed252 nrand_fast();
@inline_ref
fun nrand_fast_f252(): int {
var t: int = stackMoveToTop(-3) << 253; // -6. as fixed252
var t: int = -3 << 253; // -6. as fixed252
repeat (12) {
t += random() / 16; // add together 12 uniformly random numbers
}
@ -979,8 +979,8 @@ fun fixed248_nrand_fast(): int {
}
@pure
fun ~tset<X>(t: tuple, idx: int, value: X): (tuple, ())
asm(t value idx) "SETINDEXVAR";
fun tset<X>(mutate self: tuple, idx: int, value: X): void
asm(self value idx) "SETINDEXVAR";
// computes 1-acos(x)/Pi by a very simple, extremely slow (~70k gas) and imprecise method
// fixed256 acos_prepare_slow(fixed255 x);
@ -1014,12 +1014,12 @@ fun asin_slow_f255(x: int): int {
fun test_nrand(n: int): tuple {
var t: tuple = createEmptyTuple();
repeat (255) {
t~tuplePush(0);
t.tuplePush(0);
}
repeat (n) {
var x: int = fixed248_nrand();
var bucket: int = (abs(x) >> 243); // 255 buckets starting from x=0, each 1/32 wide
t~tset(bucket, t.tupleAt(bucket) + 1);
t.tset(bucket, t.tupleAt(bucket) + 1);
}
return t;
}
@ -1210,14 +1210,14 @@ fun main() {
// return atan_aux_f256(1); // atan(1/2^256)*2^261 = 32
//return fixed248_nrand();
// return test_nrand(100000);
var One2: int = stackMoveToTop(1 << 255);
var One2: int = 1 << 255;
// return asin_f255(One);
// return asin_f255(-2 * One ~/ -3);
var arg: int = mulDivRound(12, One2, 17); // 12/17
// return [ asin_slow_f255(arg), asin_f255(arg) ];
// return [ acos_slow_f255(arg), acos_f255(arg) ];
// return 4 * atan_f255(One ~/ 5) - atan_f255(One ~/ 239); // 4 * atan(1/5) - atan(1/239) = Pi/4 as fixed255
var One3: int = stackMoveToTop(1 << 248);
var One3: int = 1 << 248;
// return fixed248_atan(One) ~/ 5); // atan(1/5)
// return fixed248_acot(One ~/ 239); // atan(1/5)
}

View file

@ -17,11 +17,8 @@ fun bar(x: int, y: int): (int, int) {
}
return (x + 1, y);
}
fun bar2(x: int, y: int): (int,int) {
return bar(x, y);
}
fun main(x: int, y: int): (int, int) {
(x, y) = bar2(x, y);
(x, y) = bar(x, y);
return (x, y * 10);
}
/**

View file

@ -1,7 +1,7 @@
fun main(): int {
var c: cell = my_begin_cell().storeInt(demo_10, 32).my_end_cell();
var cs: slice = my_begin_parse(c);
var ten: int = cs~loadInt(32);
var ten: int = cs.loadInt(32);
return 1 + demo1(ten) + demo_var;
}

View file

@ -0,0 +1,22 @@
fun getBeginCell() {
return beginCell;
}
fun getBeginParse() {
return beginParse;
}
@method_id(101)
fun testVarApply1() {
var (_, f_end_cell) = (0, endCell);
var b: builder = (getBeginCell())().storeInt(1, 32);
b.storeInt(2, 32);
var s = (getBeginParse())(f_end_cell(b));
return (s.loadInt(32), s.loadInt(32));
}
fun main() {}
/**
@testcase | 101 | | 1 2
*/

View file

@ -1,6 +1,6 @@
@method_id(101)
fun test1(cs: slice) {
return cs~loadUint(8)+cs~loadUint(8)+cs~loadUint(8)+cs~loadUint(8);
return cs.loadUint(8)+cs.loadUint(8)+cs.loadUint(8)+cs.loadUint(8);
}
@method_id(102)
@ -12,15 +12,15 @@ fun test2(cs: slice) {
}
fun main(cs: slice) {
return (cs~loadUint(8), cs~loadUint(8), cs~loadUint(8), cs~loadUint(8));
return (cs.loadUint(8), cs.loadUint(8), cs.loadUint(8), cs.loadUint(8));
}
fun f(cs: slice) {
return (cs~loadUint(8), cs~loadUint(8), cs~loadUint(8), cs~loadUint(8),
cs~loadUint(8), cs~loadUint(8), cs~loadUint(8), cs~loadUint(8),
cs~loadUint(8), cs~loadUint(8), cs~loadUint(8), cs~loadUint(8),
cs~loadUint(8), cs~loadUint(8), cs~loadUint(8), cs~loadUint(8),
cs~loadUint(8), cs~loadUint(8), cs~loadUint(8), cs~loadUint(8));
return (cs.loadUint(8), cs.loadUint(8), cs.loadUint(8), cs.loadUint(8),
cs.loadUint(8), cs.loadUint(8), cs.loadUint(8), cs.loadUint(8),
cs.loadUint(8), cs.loadUint(8), cs.loadUint(8), cs.loadUint(8),
cs.loadUint(8), cs.loadUint(8), cs.loadUint(8), cs.loadUint(8),
cs.loadUint(8), cs.loadUint(8), cs.loadUint(8), cs.loadUint(8));
}

View file

@ -25,12 +25,8 @@ namespace tolk {
*
*/
TmpVar::TmpVar(var_idx_t _idx, bool _is_tmp_unnamed, TypeExpr* _type, SymDef* sym, SrcLocation loc)
: v_type(_type), idx(_idx), is_tmp_unnamed(_is_tmp_unnamed), coord(0), where(loc) {
if (sym) {
name = sym->sym_idx;
sym->value->idx = _idx;
}
TmpVar::TmpVar(var_idx_t _idx, TypeExpr* _type, sym_idx_t sym_idx, SrcLocation loc)
: v_type(_type), idx(_idx), sym_idx(sym_idx), coord(0), where(loc) {
if (!_type) {
v_type = TypeExpr::new_hole();
}
@ -59,8 +55,8 @@ void TmpVar::dump(std::ostream& os) const {
}
void TmpVar::show(std::ostream& os, int omit_idx) const {
if (!is_tmp_unnamed) {
os << G.symbols.get_name(name);
if (!is_unnamed()) {
os << G.symbols.get_name(sym_idx);
if (omit_idx >= 2) {
return;
}
@ -462,16 +458,13 @@ void CodeBlob::print(std::ostream& os, int flags) const {
os << "-------- END ---------\n\n";
}
var_idx_t CodeBlob::create_var(bool is_tmp_unnamed, TypeExpr* var_type, SymDef* sym, SrcLocation location) {
vars.emplace_back(var_cnt, is_tmp_unnamed, var_type, sym, location);
if (sym) {
sym->value->idx = var_cnt;
}
var_idx_t CodeBlob::create_var(TypeExpr* var_type, var_idx_t sym_idx, SrcLocation location) {
vars.emplace_back(var_cnt, var_type, sym_idx, location);
return var_cnt++;
}
bool CodeBlob::import_params(FormalArgList arg_list) {
if (var_cnt || in_var_cnt || op_cnt) {
if (var_cnt || in_var_cnt) {
return false;
}
std::vector<var_idx_t> list;
@ -480,7 +473,7 @@ bool CodeBlob::import_params(FormalArgList arg_list) {
SymDef* arg_sym;
SrcLocation arg_loc;
std::tie(arg_type, arg_sym, arg_loc) = par;
list.push_back(create_var(arg_sym == nullptr, arg_type, arg_sym, arg_loc));
list.push_back(create_var(arg_type, arg_sym ? arg_sym->sym_idx : 0, arg_loc));
}
emplace_back(loc, Op::_Import, list);
in_var_cnt = var_cnt;

View file

@ -46,10 +46,9 @@ int CodeBlob::split_vars(bool strict) {
if (k != 1) {
var.coord = ~((n << 8) + k);
for (int i = 0; i < k; i++) {
auto v = create_var(vars[j].is_tmp_unnamed, comp_types[i], 0, vars[j].where);
auto v = create_var(comp_types[i], vars[j].sym_idx, vars[j].where);
tolk_assert(v == n + i);
tolk_assert(vars[v].idx == v);
vars[v].name = vars[j].name;
vars[v].coord = ((int)j << 8) + i + 1;
}
n += k;

View file

@ -131,7 +131,8 @@ static AnyV maybe_replace_eq_null_with_isNull_call(V<ast_binary_operator> v) {
auto v_ident = createV<ast_identifier>(v->loc, "__isNull"); // built-in function
AnyV v_null = v->get_lhs()->type == ast_null_keyword ? v->get_rhs() : v->get_lhs();
AnyV v_isNull = createV<ast_function_call>(v->loc, v_ident, createV<ast_tensor>(v->loc, {v_null}));
AnyV v_arg = createV<ast_argument>(v->loc, v_null, false);
AnyV v_isNull = createV<ast_function_call>(v->loc, v_ident, createV<ast_argument_list>(v->loc, {v_arg}));
if (v->tok == tok_neq) {
v_isNull = createV<ast_unary_operator>(v->loc, "!", tok_logical_not, v_isNull);
}
@ -165,7 +166,7 @@ static TypeExpr* parse_type1(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
return TypeExpr::new_atomic(TypeExpr::_Builder);
case tok_continuation:
lex.next();
return TypeExpr::new_atomic(TypeExpr::_Cont);
return TypeExpr::new_atomic(TypeExpr::_Continutaion);
case tok_tuple:
lex.next();
return TypeExpr::new_atomic(TypeExpr::_Tuple);
@ -177,6 +178,8 @@ static TypeExpr* parse_type1(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
return TypeExpr::new_tensor({});
case tok_bool:
lex.error("bool type is not supported yet");
case tok_self:
lex.error("`self` type can be used only as a return type of a function (enforcing it to be chainable)");
case tok_identifier:
if (int idx = genericsT_list ? genericsT_list->lookup_idx(lex.cur_str()) : -1; idx != -1) {
lex.next();
@ -229,13 +232,27 @@ static TypeExpr* parse_type(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
AnyV parse_expr(Lexer& lex);
static AnyV parse_parameter(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
static AnyV parse_parameter(Lexer& lex, V<ast_genericsT_list> genericsT_list, bool is_first) {
SrcLocation loc = lex.cur_location();
// argument name (or underscore for an unnamed parameter)
// optional keyword `mutate` meaning that a function will mutate a passed argument (like passed by reference)
bool declared_as_mutate = false;
bool is_param_self = false;
if (lex.tok() == tok_mutate) {
lex.next();
declared_as_mutate = true;
}
// parameter name (or underscore for an unnamed parameter)
std::string_view param_name;
if (lex.tok() == tok_identifier) {
param_name = lex.cur_str();
} else if (lex.tok() == tok_self) {
if (!is_first) {
lex.error("`self` can only be the first parameter");
}
param_name = "self";
is_param_self = true;
} else if (lex.tok() != tok_underscore) {
lex.unexpected("parameter name");
}
@ -245,8 +262,14 @@ static AnyV parse_parameter(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
// parameter type after colon, also mandatory (even explicit ":auto")
lex.expect(tok_colon, "`: <parameter_type>`");
TypeExpr* param_type = parse_type(lex, genericsT_list);
if (declared_as_mutate && !param_type->has_fixed_width()) {
throw ParseError(loc, "`mutate` parameter must be strictly typed");
}
if (is_param_self && !param_type->has_fixed_width()) {
throw ParseError(loc, "`self` parameter must be strictly typed");
}
return createV<ast_parameter>(loc, v_ident, param_type);
return createV<ast_parameter>(loc, v_ident, param_type, declared_as_mutate);
}
static AnyV parse_global_var_declaration(Lexer& lex, const std::vector<V<ast_annotation>>& annotations) {
@ -301,21 +324,52 @@ static AnyV parse_constant_declaration(Lexer& lex, const std::vector<V<ast_annot
return createV<ast_constant_declaration>(loc, v_ident, declared_type, init_value);
}
static AnyV parse_parameter_list(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
// "parameters" are at function declaration: `fun f(param1: int, mutate param2: slice)`
static V<ast_parameter_list> parse_parameter_list(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
SrcLocation loc = lex.cur_location();
std::vector<AnyV> params;
lex.expect(tok_oppar, "parameter list");
if (lex.tok() != tok_clpar) {
params.push_back(parse_parameter(lex, genericsT_list));
params.push_back(parse_parameter(lex, genericsT_list, true));
while (lex.tok() == tok_comma) {
lex.next();
params.push_back(parse_parameter(lex, genericsT_list));
params.push_back(parse_parameter(lex, genericsT_list, false));
}
}
lex.expect(tok_clpar, "`)`");
return createV<ast_parameter_list>(loc, std::move(params));
}
// "arguments" are at function call: `f(arg1, mutate arg2)`
static AnyV parse_argument(Lexer& lex) {
SrcLocation loc = lex.cur_location();
// keyword `mutate` is necessary when a parameter is declared `mutate` (to make mutation obvious for the reader)
bool passed_as_mutate = false;
if (lex.tok() == tok_mutate) {
lex.next();
passed_as_mutate = true;
}
AnyV expr = parse_expr(lex);
return createV<ast_argument>(loc, expr, passed_as_mutate);
}
static V<ast_argument_list> parse_argument_list(Lexer& lex) {
SrcLocation loc = lex.cur_location();
std::vector<AnyV> args;
lex.expect(tok_oppar, "`(`");
if (lex.tok() != tok_clpar) {
args.push_back(parse_argument(lex));
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_argument(lex));
}
}
lex.expect(tok_clpar, "`)`");
return createV<ast_argument_list>(loc, std::move(args));
}
// parse (expr) / [expr] / identifier / number
static AnyV parse_expr100(Lexer& lex) {
SrcLocation loc = lex.cur_location();
@ -384,6 +438,10 @@ static AnyV parse_expr100(Lexer& lex) {
lex.next();
return createV<ast_null_keyword>(loc);
}
case tok_self: {
lex.next();
return createV<ast_self_keyword>(loc);
}
case tok_identifier: {
std::string_view str_val = lex.cur_str();
lex.next();
@ -400,48 +458,25 @@ static AnyV parse_expr100(Lexer& lex) {
}
}
// parse E(expr)
// parse E(args)
static AnyV parse_expr90(Lexer& lex) {
AnyV res = parse_expr100(lex);
if (lex.tok() == tok_oppar) {
lex.next();
SrcLocation loc = lex.cur_location();
std::vector<AnyV> args;
if (lex.tok() != tok_clpar) {
args.push_back(parse_expr(lex));
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_expr(lex));
}
}
lex.expect(tok_clpar, "`)`");
return createV<ast_function_call>(res->loc, res, createV<ast_tensor>(loc, std::move(args)));
return createV<ast_function_call>(res->loc, res, parse_argument_list(lex));
}
return res;
}
// parse E .method ~method E (left-to-right)
// parse E.method(...) (left-to-right)
static AnyV parse_expr80(Lexer& lex) {
AnyV lhs = parse_expr90(lex);
while (lex.tok() == tok_identifier && (lex.cur_str()[0] == '.' || lex.cur_str()[0] == '~')) {
while (lex.tok() == tok_dot) {
SrcLocation loc = lex.cur_location();
lex.next();
lex.check(tok_identifier, "method name");
std::string_view method_name = lex.cur_str();
lex.next();
SrcLocation loc = lex.cur_location();
std::vector<AnyV> args;
lex.expect(tok_oppar, "`(`");
if (lex.tok() != tok_clpar) {
args.push_back(parse_expr(lex));
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_expr(lex));
}
}
lex.expect(tok_clpar, "`)`");
lhs = createV<ast_dot_tilde_call>(lhs->loc, method_name, lhs, createV<ast_tensor>(loc, std::move(args)));
lhs = createV<ast_dot_method_call>(loc, method_name, lhs, parse_argument_list(lex));
}
return lhs;
}
@ -586,11 +621,11 @@ AnyV parse_expr(Lexer& lex) {
AnyV parse_statement(Lexer& lex);
static AnyV parse_var_declaration_lhs(Lexer& lex) {
static AnyV parse_var_declaration_lhs(Lexer& lex, bool is_immutable) {
SrcLocation loc = lex.cur_location();
if (lex.tok() == tok_oppar) {
lex.next();
AnyV first = parse_var_declaration_lhs(lex);
AnyV first = parse_var_declaration_lhs(lex, is_immutable);
if (lex.tok() == tok_clpar) {
lex.next();
return createV<ast_parenthesized_expr>(loc, first);
@ -598,17 +633,17 @@ static AnyV parse_var_declaration_lhs(Lexer& lex) {
std::vector<AnyV> args(1, first);
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_var_declaration_lhs(lex));
args.push_back(parse_var_declaration_lhs(lex, is_immutable));
}
lex.expect(tok_clpar, "`)`");
return createV<ast_tensor>(loc, std::move(args));
}
if (lex.tok() == tok_opbracket) {
lex.next();
std::vector<AnyV> args(1, parse_var_declaration_lhs(lex));
std::vector<AnyV> args(1, parse_var_declaration_lhs(lex, is_immutable));
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_var_declaration_lhs(lex));
args.push_back(parse_var_declaration_lhs(lex, is_immutable));
}
lex.expect(tok_clbracket, "`]`");
return createV<ast_tensor_square>(loc, std::move(args));
@ -625,7 +660,7 @@ static AnyV parse_var_declaration_lhs(Lexer& lex) {
lex.next();
marked_as_redef = true;
}
return createV<ast_local_var>(loc, v_ident, declared_type, marked_as_redef);
return createV<ast_local_var>(loc, v_ident, declared_type, is_immutable, marked_as_redef);
}
if (lex.tok() == tok_underscore) {
TypeExpr* declared_type = nullptr;
@ -634,21 +669,17 @@ static AnyV parse_var_declaration_lhs(Lexer& lex) {
lex.next();
declared_type = parse_type(lex, nullptr);
}
return createV<ast_local_var>(loc, createV<ast_underscore>(loc), declared_type, false);
return createV<ast_local_var>(loc, createV<ast_underscore>(loc), declared_type, true, false);
}
lex.unexpected("variable name");
}
static AnyV parse_local_vars_declaration(Lexer& lex) {
SrcLocation loc = lex.cur_location();
bool immutable = lex.tok() == tok_val;
bool is_immutable = lex.tok() == tok_val;
lex.next();
if (immutable) {
lex.error("immutable variables are not supported yet");
}
AnyV lhs = parse_var_declaration_lhs(lex);
AnyV lhs = parse_var_declaration_lhs(lex, is_immutable);
if (lex.tok() != tok_assign) {
lex.error("variables declaration must be followed by assignment: `var xxx = ...`");
}
@ -885,10 +916,10 @@ static AnyV parse_asm_func_body(Lexer& lex, V<ast_parameter_list> param_list) {
std::vector<int> arg_order, ret_order;
if (lex.tok() == tok_oppar) {
lex.next();
while (lex.tok() == tok_identifier || lex.tok() == tok_int_const) {
while (lex.tok() == tok_identifier || lex.tok() == tok_self) {
int arg_idx = param_list->lookup_idx(lex.cur_str());
if (arg_idx == -1) {
lex.unexpected("argument name");
lex.unexpected("parameter name");
}
arg_order.push_back(arg_idx);
lex.next();
@ -1006,17 +1037,44 @@ static AnyV parse_function_declaration(Lexer& lex, const std::vector<V<ast_annot
genericsT_list = parse_genericsT_list(lex)->as<ast_genericsT_list>();
}
V<ast_parameter_list> param_list = parse_parameter_list(lex, genericsT_list)->as<ast_parameter_list>();
V<ast_parameter_list> v_param_list = parse_parameter_list(lex, genericsT_list)->as<ast_parameter_list>();
bool accepts_self = !v_param_list->empty() && v_param_list->get_param(0)->get_identifier()->name == "self";
int n_mutate_params = v_param_list->get_mutate_params_count();
TypeExpr* ret_type = nullptr;
bool returns_self = false;
if (lex.tok() == tok_colon) { // : <ret_type> (if absent, it means "auto infer", not void)
lex.next();
ret_type = parse_type(lex, genericsT_list);
if (lex.tok() == tok_self) {
if (!accepts_self) {
lex.error("only a member function can return `self` (which accepts `self` first parameter)");
}
lex.next();
returns_self = true;
ret_type = TypeExpr::new_unit();
} else {
ret_type = parse_type(lex, genericsT_list);
}
}
if (is_entrypoint && (is_get_method || genericsT_list || !annotations.empty())) {
if (is_entrypoint && (is_get_method || genericsT_list || n_mutate_params || accepts_self)) {
throw ParseError(loc, "invalid declaration of a reserved function");
}
if (is_get_method && (genericsT_list || n_mutate_params || accepts_self)) {
throw ParseError(loc, "get methods can't have `mutate` and `self` params");
}
if (n_mutate_params) {
std::vector<TypeExpr*> ret_tensor_items;
ret_tensor_items.reserve(1 + n_mutate_params);
for (AnyV v_param : v_param_list->get_params()) {
if (v_param->as<ast_parameter>()->declared_as_mutate) {
ret_tensor_items.emplace_back(v_param->as<ast_parameter>()->param_type);
}
}
ret_tensor_items.emplace_back(ret_type ? ret_type : TypeExpr::new_hole());
ret_type = TypeExpr::new_tensor(std::move(ret_tensor_items));
}
AnyV v_body = nullptr;
@ -1030,17 +1088,19 @@ static AnyV parse_function_declaration(Lexer& lex, const std::vector<V<ast_annot
if (!ret_type) {
lex.error("asm function must specify return type");
}
v_body = parse_asm_func_body(lex, param_list);
v_body = parse_asm_func_body(lex, v_param_list);
} else {
lex.unexpected("{ function body }");
}
auto f_declaration = createV<ast_function_declaration>(loc, v_ident, param_list, v_body);
auto f_declaration = createV<ast_function_declaration>(loc, v_ident, v_param_list, v_body);
f_declaration->ret_type = ret_type ? ret_type : TypeExpr::new_hole();
f_declaration->is_entrypoint = is_entrypoint;
f_declaration->genericsT_list = genericsT_list;
f_declaration->marked_as_get_method = is_get_method;
f_declaration->marked_as_builtin = v_body->type == ast_empty;
f_declaration->accepts_self = accepts_self;
f_declaration->returns_self = returns_self;
for (auto v_annotation : annotations) {
switch (v_annotation->kind) {
@ -1054,7 +1114,7 @@ static AnyV parse_function_declaration(Lexer& lex, const std::vector<V<ast_annot
f_declaration->marked_as_pure = true;
break;
case AnnotationKind::method_id:
if (is_get_method || genericsT_list || is_entrypoint) {
if (is_get_method || genericsT_list || is_entrypoint || n_mutate_params || accepts_self) {
v_annotation->error("@method_id can be specified only for regular functions");
}
f_declaration->method_id = v_annotation->get_arg()->get_item(0)->as<ast_int_const>();

View file

@ -80,8 +80,8 @@ protected:
virtual AnyV replace(V<ast_bool_const> v) { return replace_children(v); }
virtual AnyV replace(V<ast_null_keyword> v) { return replace_children(v); }
virtual AnyV replace(V<ast_function_call> v) { return replace_children(v); }
virtual AnyV replace(V<ast_dot_method_call> v) { return replace_children(v); }
virtual AnyV replace(V<ast_underscore> v) { return replace_children(v); }
virtual AnyV replace(V<ast_dot_tilde_call> v) { return replace_children(v); }
virtual AnyV replace(V<ast_unary_operator> v) { return replace_children(v); }
virtual AnyV replace(V<ast_binary_operator> v) { return replace_children(v); }
virtual AnyV replace(V<ast_ternary_operator> v) { return replace_children(v); }
@ -109,9 +109,10 @@ protected:
case ast_string_const: return replace(v->as<ast_string_const>());
case ast_bool_const: return replace(v->as<ast_bool_const>());
case ast_null_keyword: return replace(v->as<ast_null_keyword>());
case ast_self_keyword: return replace(v->as<ast_self_keyword>());
case ast_function_call: return replace(v->as<ast_function_call>());
case ast_dot_method_call: return replace(v->as<ast_dot_method_call>());
case ast_underscore: return replace(v->as<ast_underscore>());
case ast_dot_tilde_call: return replace(v->as<ast_dot_tilde_call>());
case ast_unary_operator: return replace(v->as<ast_unary_operator>());
case ast_binary_operator: return replace(v->as<ast_binary_operator>());
case ast_ternary_operator: return replace(v->as<ast_ternary_operator>());

View file

@ -40,11 +40,14 @@ class ASTStringifier final : public ASTVisitor {
{ast_string_const, "ast_string_const"},
{ast_bool_const, "ast_bool_const"},
{ast_null_keyword, "ast_null_keyword"},
{ast_self_keyword, "ast_self_keyword"},
{ast_argument, "ast_argument"},
{ast_argument_list, "ast_argument_list"},
{ast_function_call, "ast_function_call"},
{ast_dot_method_call, "ast_dot_method_call"},
{ast_global_var_declaration, "ast_global_var_declaration"},
{ast_constant_declaration, "ast_constant_declaration"},
{ast_underscore, "ast_underscore"},
{ast_dot_tilde_call, "ast_dot_tilde_call"},
{ast_unary_operator, "ast_unary_operator"},
{ast_binary_operator, "ast_binary_operator"},
{ast_ternary_operator, "ast_ternary_operator"},
@ -125,12 +128,12 @@ class ASTStringifier final : public ASTVisitor {
}
return {};
}
case ast_dot_method_call:
return static_cast<std::string>(v->as<ast_dot_method_call>()->method_name);
case ast_global_var_declaration:
return static_cast<std::string>(v->as<ast_global_var_declaration>()->get_identifier()->name);
case ast_constant_declaration:
return static_cast<std::string>(v->as<ast_constant_declaration>()->get_identifier()->name);
case ast_dot_tilde_call:
return static_cast<std::string>(v->as<ast_dot_tilde_call>()->method_name);
case ast_unary_operator:
return static_cast<std::string>(v->as<ast_unary_operator>()->operator_name);
case ast_binary_operator:
@ -208,11 +211,14 @@ public:
case ast_string_const: return handle_vertex(v->as<ast_string_const>());
case ast_bool_const: return handle_vertex(v->as<ast_bool_const>());
case ast_null_keyword: return handle_vertex(v->as<ast_null_keyword>());
case ast_self_keyword: return handle_vertex(v->as<ast_self_keyword>());
case ast_argument: return handle_vertex(v->as<ast_argument>());
case ast_argument_list: return handle_vertex(v->as<ast_argument_list>());
case ast_function_call: return handle_vertex(v->as<ast_function_call>());
case ast_dot_method_call: return handle_vertex(v->as<ast_dot_method_call>());
case ast_global_var_declaration: return handle_vertex(v->as<ast_global_var_declaration>());
case ast_constant_declaration: return handle_vertex(v->as<ast_constant_declaration>());
case ast_underscore: return handle_vertex(v->as<ast_underscore>());
case ast_dot_tilde_call: return handle_vertex(v->as<ast_dot_tilde_call>());
case ast_unary_operator: return handle_vertex(v->as<ast_unary_operator>());
case ast_binary_operator: return handle_vertex(v->as<ast_binary_operator>());
case ast_ternary_operator: return handle_vertex(v->as<ast_ternary_operator>());

View file

@ -75,9 +75,10 @@ protected:
virtual void visit(V<ast_string_const> v) { return visit_children(v); }
virtual void visit(V<ast_bool_const> v) { return visit_children(v); }
virtual void visit(V<ast_null_keyword> v) { return visit_children(v); }
virtual void visit(V<ast_self_keyword> v) { return visit_children(v); }
virtual void visit(V<ast_function_call> v) { return visit_children(v); }
virtual void visit(V<ast_dot_method_call> v) { return visit_children(v); }
virtual void visit(V<ast_underscore> v) { return visit_children(v); }
virtual void visit(V<ast_dot_tilde_call> v) { return visit_children(v); }
virtual void visit(V<ast_unary_operator> v) { return visit_children(v); }
virtual void visit(V<ast_binary_operator> v) { return visit_children(v); }
virtual void visit(V<ast_ternary_operator> v) { return visit_children(v); }
@ -103,9 +104,10 @@ protected:
case ast_string_const: return visit(v->as<ast_string_const>());
case ast_bool_const: return visit(v->as<ast_bool_const>());
case ast_null_keyword: return visit(v->as<ast_null_keyword>());
case ast_self_keyword: return visit(v->as<ast_self_keyword>());
case ast_function_call: return visit(v->as<ast_function_call>());
case ast_dot_method_call: return visit(v->as<ast_dot_method_call>());
case ast_underscore: return visit(v->as<ast_underscore>());
case ast_dot_tilde_call: return visit(v->as<ast_dot_tilde_call>());
case ast_unary_operator: return visit(v->as<ast_unary_operator>());
case ast_binary_operator: return visit(v->as<ast_binary_operator>());
case ast_ternary_operator: return visit(v->as<ast_ternary_operator>());

View file

@ -86,6 +86,16 @@ int Vertex<ast_parameter_list>::lookup_idx(std::string_view param_name) const {
return -1;
}
int Vertex<ast_parameter_list>::get_mutate_params_count() const {
int n = 0;
for (AnyV param : children) {
if (param->as<ast_parameter>()->declared_as_mutate) {
n++;
}
}
return n;
}
void Vertex<ast_import_statement>::mutate_set_src_file(const SrcFile* file) const {
const_cast<Vertex*>(this)->file = file;
}

View file

@ -68,11 +68,14 @@ enum ASTNodeType {
ast_string_const,
ast_bool_const,
ast_null_keyword,
ast_self_keyword,
ast_argument,
ast_argument_list,
ast_function_call,
ast_dot_method_call,
ast_global_var_declaration,
ast_constant_declaration,
ast_underscore,
ast_dot_tilde_call,
ast_unary_operator,
ast_binary_operator,
ast_ternary_operator,
@ -285,13 +288,50 @@ struct Vertex<ast_null_keyword> final : ASTNodeLeaf {
};
template<>
struct Vertex<ast_function_call> final : ASTNodeBinary {
// even for f(1,2,3), f (lhs) is called with a single arg (tensor "(1,2,3)") (rhs)
AnyV get_called_f() const { return lhs; }
auto get_called_arg() const { return rhs->as<ast_tensor>(); }
struct Vertex<ast_self_keyword> final : ASTNodeLeaf {
explicit Vertex(SrcLocation loc)
: ASTNodeLeaf(ast_self_keyword, loc) {}
};
Vertex(SrcLocation loc, AnyV lhs_f, V<ast_tensor> arg)
: ASTNodeBinary(ast_function_call, loc, lhs_f, arg) {}
template<>
struct Vertex<ast_argument> final : ASTNodeUnary {
bool passed_as_mutate; // when called `f(mutate arg)`, not `f(arg)`
AnyV get_expr() const { return child; }
explicit Vertex(SrcLocation loc, AnyV expr, bool passed_as_mutate)
: ASTNodeUnary(ast_argument, loc, expr), passed_as_mutate(passed_as_mutate) {}
};
template<>
struct Vertex<ast_argument_list> final : ASTNodeVararg {
const std::vector<AnyV>& get_arguments() const { return children; }
auto get_arg(int i) const { return children.at(i)->as<ast_argument>(); }
explicit Vertex(SrcLocation loc, std::vector<AnyV> arguments)
: ASTNodeVararg(ast_argument_list, loc, std::move(arguments)) {}
};
template<>
struct Vertex<ast_function_call> final : ASTNodeBinary {
AnyV get_called_f() const { return lhs; }
auto get_arg_list() const { return rhs->as<ast_argument_list>(); }
int get_num_args() const { return rhs->as<ast_argument_list>()->size(); }
auto get_arg(int i) const { return rhs->as<ast_argument_list>()->get_arg(i); }
Vertex(SrcLocation loc, AnyV lhs_f, V<ast_argument_list> arguments)
: ASTNodeBinary(ast_function_call, loc, lhs_f, arguments) {}
};
template<>
struct Vertex<ast_dot_method_call> final : ASTNodeBinary {
std::string_view method_name;
AnyV get_obj() const { return lhs; }
auto get_arg_list() const { return rhs->as<ast_argument_list>(); }
Vertex(SrcLocation loc, std::string_view method_name, AnyV lhs, V<ast_argument_list> arguments)
: ASTNodeBinary(ast_dot_method_call, loc, lhs, arguments), method_name(method_name) {}
};
template<>
@ -321,17 +361,6 @@ struct Vertex<ast_underscore> final : ASTNodeLeaf {
: ASTNodeLeaf(ast_underscore, loc) {}
};
template<>
struct Vertex<ast_dot_tilde_call> final : ASTNodeBinary {
std::string_view method_name; // starts with . or ~
AnyV get_lhs() const { return lhs; }
auto get_arg() const { return rhs->as<ast_tensor>(); }
Vertex(SrcLocation loc, std::string_view method_name, AnyV lhs, V<ast_tensor> arg)
: ASTNodeBinary(ast_dot_tilde_call, loc, lhs, arg), method_name(method_name) {}
};
template<>
struct Vertex<ast_unary_operator> final : ASTNodeUnary {
std::string_view operator_name;
@ -475,11 +504,13 @@ struct Vertex<ast_genericsT_list> final : ASTNodeVararg {
template<>
struct Vertex<ast_parameter> final : ASTNodeUnary {
TypeExpr* param_type;
bool declared_as_mutate; // declared as `mutate param_name`
auto get_identifier() const { return child->as<ast_identifier>(); } // for underscore, its str_val is empty
auto get_identifier() const { return child->as<ast_identifier>(); } // for underscore, name is empty
bool is_underscore() const { return child->as<ast_identifier>()->name.empty(); }
Vertex(SrcLocation loc, V<ast_identifier> name_identifier, TypeExpr* param_type)
: ASTNodeUnary(ast_parameter, loc, name_identifier), param_type(param_type) {}
Vertex(SrcLocation loc, V<ast_identifier> name_identifier, TypeExpr* param_type, bool declared_as_mutate)
: ASTNodeUnary(ast_parameter, loc, name_identifier), param_type(param_type), declared_as_mutate(declared_as_mutate) {}
};
template<>
@ -491,6 +522,8 @@ struct Vertex<ast_parameter_list> final : ASTNodeVararg {
: ASTNodeVararg(ast_parameter_list, loc, std::move(params)) {}
int lookup_idx(std::string_view param_name) const;
int get_mutate_params_count() const;
bool has_mutate_params() const { return get_mutate_params_count() > 0; }
};
template<>
@ -519,12 +552,13 @@ struct Vertex<ast_annotation> final : ASTNodeUnary {
template<>
struct Vertex<ast_local_var> final : ASTNodeUnary {
TypeExpr* declared_type;
bool is_immutable; // declared via 'val', not 'var'
bool marked_as_redef; // var (existing_var redef, new_var: int) = ...
AnyV get_identifier() const { return child; } // ast_identifier / ast_underscore
Vertex(SrcLocation loc, AnyV name_identifier, TypeExpr* declared_type, bool marked_as_redef)
: ASTNodeUnary(ast_local_var, loc, name_identifier), declared_type(declared_type), marked_as_redef(marked_as_redef) {}
Vertex(SrcLocation loc, AnyV name_identifier, TypeExpr* declared_type, bool is_immutable, bool marked_as_redef)
: ASTNodeUnary(ast_local_var, loc, name_identifier), declared_type(declared_type), is_immutable(is_immutable), marked_as_redef(marked_as_redef) {}
};
template<>
@ -552,6 +586,8 @@ struct Vertex<ast_function_declaration> final : ASTNodeVararg {
bool marked_as_get_method = false;
bool marked_as_inline = false;
bool marked_as_inline_ref = false;
bool accepts_self = false;
bool returns_self = false;
V<ast_int_const> method_id = nullptr;
bool is_asm_function() const { return children.at(2)->type == ast_asm_body; }

View file

@ -29,44 +29,60 @@ using namespace std::literals::string_literals;
SymDef* define_builtin_func_impl(const std::string& name, SymValAsmFunc* func_val) {
sym_idx_t name_idx = G.symbols.lookup_add(name);
SymDef* def = define_global_symbol(name_idx);
if (!def) {
std::cerr << "fatal: global function `" << name << "` already defined" << std::endl;
std::exit(1);
}
func_val->flags |= SymValFunc::flagBuiltinFunction;
tolk_assert(!def->value);
def->value = func_val;
#ifdef TOLK_DEBUG
dynamic_cast<SymValAsmFunc*>(def->value)->name = name;
def->value->sym_name = name;
#endif
return def;
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, !impure});
// given func_type = `(slice, int) -> slice` and func flags, create SymDef for parameters
// currently (see at the bottom) parameters of built-in functions are unnamed:
// built-in functions are created using a resulting type
static std::vector<SymDef*> define_builtin_parameters(const TypeExpr* func_type, int func_flags) {
// `loadInt()`, `storeInt()`: they accept `self` and mutate it; no other options available in built-ins for now
bool is_mutate_self = func_flags & SymValFunc::flagHasMutateParams;
// func_type a map (params_type -> ret_type), probably surrounded by forall (internal representation of <T>)
TypeExpr* params_type = func_type->constr == TypeExpr::te_ForAll ? func_type->args[0]->args[0] : func_type->args[0];
std::vector<SymDef*> parameters;
if (params_type->constr == TypeExpr::te_Tensor) { // multiple parameters: it's a tensor
parameters.reserve(params_type->args.size());
for (int i = 0; i < static_cast<int>(params_type->args.size()); ++i) {
SymDef* sym_def = define_parameter(i, {});
SymValVariable* sym_val = new SymValVariable(i, params_type->args[i]);
if (i == 0 && is_mutate_self) {
sym_val->flags |= SymValVariable::flagMutateParameter;
}
sym_def->value = sym_val;
parameters.emplace_back(sym_def);
}
} else { // single parameter
SymDef* sym_def = define_parameter(0, {});
SymValVariable* sym_val = new SymValVariable(0, params_type);
if (is_mutate_self) {
sym_val->flags |= SymValVariable::flagMutateParameter;
}
sym_def->value = sym_val;
parameters.emplace_back(sym_def);
}
return parameters;
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const compile_func_t& func, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, !impure});
static SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, int flags) {
return define_builtin_func_impl(name, new SymValAsmFunc(define_builtin_parameters(func_type, flags), func_type, func, flags | SymValFunc::flagBuiltinFunction));
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const AsmOp& macro, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, make_simple_compile(macro), !impure});
static SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const AsmOp& macro, int flags) {
return define_builtin_func_impl(name, new SymValAsmFunc(define_builtin_parameters(func_type, flags), func_type, make_simple_compile(macro), flags | SymValFunc::flagBuiltinFunction));
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, arg_order, ret_order, !impure});
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const compile_func_t& func, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, arg_order, ret_order, !impure});
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const AsmOp& macro,
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order = {},
bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, !impure});
static SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, int flags,
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order) {
return define_builtin_func_impl(name, new SymValAsmFunc(define_builtin_parameters(func_type, flags), func_type, func, flags | SymValFunc::flagBuiltinFunction, arg_order, ret_order));
}
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in,
@ -963,7 +979,7 @@ AsmOp compile_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args, Src
}
}
AsmOp compile_throw_if_unless(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
AsmOp compile_throw_if_unless(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation) {
tolk_assert(res.empty() && args.size() == 3);
VarDescr &x = args[0], &y = args[1], &z = args[2];
if (!z.always_true() && !z.always_false()) {
@ -1007,10 +1023,10 @@ AsmOp compile_bool_const(std::vector<VarDescr>& res, std::vector<VarDescr>& args
return AsmOp::Const(val ? "TRUE" : "FALSE");
}
// (slice, int) load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
// (slice, int) load_uint(slice s, int len) asm( -> 1 0) "LDUX";
// int preload_int(slice s, int len) asm "PLDIX";
// int preload_uint(slice s, int len) asm "PLDUX";
// fun loadInt (mutate s: slice, len: int): int asm(s len -> 1 0) "LDIX";
// fun loadUint (mutate s: slice, len: int): int asm( -> 1 0) "LDUX";
// fun preloadInt (s: slice, len: int): int asm "PLDIX";
// fun preloadUint(s: slice, len: int): int asm "PLDUX";
AsmOp compile_fetch_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool fetch, bool sgnd) {
tolk_assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
auto &y = args[1], &r = res.back();
@ -1032,8 +1048,8 @@ AsmOp compile_fetch_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
return exec_op((fetch ? "LD"s : "PLD"s) + (sgnd ? "IX" : "UX"), 2, 1 + (unsigned)fetch);
}
// builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
// builder store_int(builder b, int x, int len) asm(x b len) "STIX";
// fun storeInt (mutate self: builder, x: int, len: int): self asm(x b len) "STIX";
// fun storeUint (mutate self: builder, x: int, len: int): self asm(x b len) "STUX";
AsmOp compile_store_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool sgnd) {
tolk_assert(args.size() == 3 && res.size() == 1);
auto& z = args[2];
@ -1044,6 +1060,8 @@ AsmOp compile_store_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
return exec_op("ST"s + (sgnd ? "IX" : "UX"), 3, 1);
}
// fun loadBits (mutate self: slice, len: int): self asm(s len -> 1 0) "LDSLICEX"
// fun preloadBits(self: slice, len: int): slice asm(s len -> 1 0) "PLDSLICEX"
AsmOp compile_fetch_slice(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool fetch) {
tolk_assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
auto& y = args[1];
@ -1058,7 +1076,7 @@ AsmOp compile_fetch_slice(std::vector<VarDescr>& res, std::vector<VarDescr>& arg
return exec_op(fetch ? "LDSLICEX" : "PLDSLICEX", 2, 1 + (unsigned)fetch);
}
// <type> <type>_at(tuple t, int index) asm "INDEXVAR";
// fun at<X>(t: tuple, index: int): X asm "INDEXVAR";
AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation) {
tolk_assert(args.size() == 2 && res.size() == 1);
auto& y = args[1];
@ -1079,102 +1097,146 @@ AsmOp compile_is_null(std::vector<VarDescr>& res, std::vector<VarDescr>& args, S
void define_builtins() {
using namespace std::placeholders;
auto Unit = TypeExpr::new_unit();
auto Int = TypeExpr::new_atomic(TypeExpr::_Int);
auto Cell = TypeExpr::new_atomic(TypeExpr::_Cell);
auto Slice = TypeExpr::new_atomic(TypeExpr::_Slice);
auto Builder = TypeExpr::new_atomic(TypeExpr::_Builder);
// auto Null = TypeExpr::new_atomic(TypeExpr::_Null);
auto Tuple = TypeExpr::new_atomic(TypeExpr::_Tuple);
auto Int2 = TypeExpr::new_tensor({Int, Int});
auto Int3 = TypeExpr::new_tensor({Int, Int, Int});
auto TupleInt = TypeExpr::new_tensor({Tuple, Int});
auto SliceInt = TypeExpr::new_tensor({Slice, Int});
auto X = TypeExpr::new_var(0);
auto Y = TypeExpr::new_var(1);
auto Z = TypeExpr::new_var(2);
auto XY = TypeExpr::new_tensor({X, Y});
auto arith_bin_op = TypeExpr::new_map(Int2, Int);
auto arith_un_op = TypeExpr::new_map(Int, Int);
auto impure_un_op = TypeExpr::new_map(Int, Unit);
auto fetch_int_op = TypeExpr::new_map(SliceInt, SliceInt);
auto prefetch_int_op = TypeExpr::new_map(SliceInt, Int);
auto store_int_op = TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), Builder);
auto store_int_method =
TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), TypeExpr::new_tensor({Builder, Unit}));
auto fetch_slice_op = TypeExpr::new_map(SliceInt, TypeExpr::new_tensor({Slice, Slice}));
auto prefetch_slice_op = TypeExpr::new_map(SliceInt, Slice);
//auto arith_null_op = TypeExpr::new_map(TypeExpr::new_unit(), Int);
auto throw_arg_op = TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({X, Int}), Unit));
// prevent unused vars warnings (there vars are created to acquire initial id of TypeExpr::value)
static_cast<void>(Z);
static_cast<void>(XY);
static_cast<void>(Cell);
TypeExpr* Unit = TypeExpr::new_unit();
TypeExpr* Int = TypeExpr::new_atomic(TypeExpr::_Int);
TypeExpr* Slice = TypeExpr::new_atomic(TypeExpr::_Slice);
TypeExpr* Builder = TypeExpr::new_atomic(TypeExpr::_Builder);
TypeExpr* Tuple = TypeExpr::new_atomic(TypeExpr::_Tuple);
TypeExpr* Int2 = TypeExpr::new_tensor({Int, Int});
TypeExpr* Int3 = TypeExpr::new_tensor({Int, Int, Int});
TypeExpr* TupleInt = TypeExpr::new_tensor({Tuple, Int});
TypeExpr* SliceInt = TypeExpr::new_tensor({Slice, Int});
TypeExpr* X = TypeExpr::new_var(0);
TypeExpr* arith_bin_op = TypeExpr::new_map(Int2, Int);
TypeExpr* arith_un_op = TypeExpr::new_map(Int, Int);
TypeExpr* impure_un_op = TypeExpr::new_map(Int, Unit);
TypeExpr* fetch_int_op_mutate = TypeExpr::new_map(SliceInt, SliceInt);
TypeExpr* prefetch_int_op = TypeExpr::new_map(SliceInt, Int);
TypeExpr* store_int_mutate = TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), TypeExpr::new_tensor({Builder, Unit}));
TypeExpr* fetch_slice_op_mutate = TypeExpr::new_map(SliceInt, TypeExpr::new_tensor({Slice, Slice}));
TypeExpr* prefetch_slice_op = TypeExpr::new_map(SliceInt, Slice);
TypeExpr* throw_arg_op = TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({X, Int}), Unit));
define_builtin_func("_+_", arith_bin_op, compile_add);
define_builtin_func("_-_", arith_bin_op, compile_sub);
define_builtin_func("-_", arith_un_op, compile_unary_minus);
define_builtin_func("+_", arith_un_op, compile_unary_plus);
define_builtin_func("_*_", arith_bin_op, compile_mul);
define_builtin_func("_/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1));
define_builtin_func("_~/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 0));
define_builtin_func("_^/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 1));
define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1));
define_builtin_func("_<<_", arith_bin_op, compile_lshift);
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1));
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 0));
define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 1));
define_builtin_func("!_", arith_un_op, compile_logical_not);
define_builtin_func("~_", arith_un_op, compile_bitwise_not);
define_builtin_func("_&_", arith_bin_op, compile_bitwise_and);
define_builtin_func("_|_", arith_bin_op, compile_bitwise_or);
define_builtin_func("_^_", arith_bin_op, compile_bitwise_xor);
define_builtin_func("^_+=_", arith_bin_op, compile_add);
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
define_builtin_func("^_*=_", arith_bin_op, compile_mul);
define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1));
define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1));
define_builtin_func("^_<<=_", arith_bin_op, compile_lshift);
define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1));
define_builtin_func("^_&=_", arith_bin_op, compile_bitwise_and);
define_builtin_func("^_|=_", arith_bin_op, compile_bitwise_or);
define_builtin_func("^_^=_", arith_bin_op, compile_bitwise_xor);
define_builtin_func("mulDivFloor", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, -1));
define_builtin_func("mulDivRound", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 0));
define_builtin_func("mulDivCeil", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 1));
define_builtin_func("mulDivMod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2));
define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2));
define_builtin_func("_!=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 5));
define_builtin_func("_<_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 4));
define_builtin_func("_>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 1));
define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6));
define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3));
define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7));
define_builtin_func("__true", TypeExpr::new_map(TypeExpr::new_unit(), Int), /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true));
define_builtin_func("__false", TypeExpr::new_map(TypeExpr::new_unit(), Int), /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false));
define_builtin_func("__null", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_unit(), X)), AsmOp::Const("PUSHNULL"));
define_builtin_func("__isNull", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null);
define_builtin_func("__throw", impure_un_op, compile_throw, true);
define_builtin_func("__throw_arg", throw_arg_op, compile_throw_arg, true);
define_builtin_func("__throw_if_unless", TypeExpr::new_map(Int3, Unit), std::bind(compile_throw_if_unless, _1, _2), true);
define_builtin_func("loadInt", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0});
define_builtin_func("loadUint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0});
define_builtin_func("loadBits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0});
define_builtin_func("preloadInt", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true));
define_builtin_func("preloadUint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false));
define_builtin_func("preloadBits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false));
define_builtin_func("storeInt", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
define_builtin_func("storeUint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
define_builtin_func("~storeInt", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
define_builtin_func("~storeUint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
define_builtin_func("tupleAt", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at);
define_builtin_func("_+_", arith_bin_op, compile_add,
SymValFunc::flagMarkedAsPure);
define_builtin_func("_-_", arith_bin_op, compile_sub,
SymValFunc::flagMarkedAsPure);
define_builtin_func("-_", arith_un_op, compile_unary_minus,
SymValFunc::flagMarkedAsPure);
define_builtin_func("+_", arith_un_op, compile_unary_plus,
SymValFunc::flagMarkedAsPure);
define_builtin_func("_*_", arith_bin_op, compile_mul,
SymValFunc::flagMarkedAsPure);
define_builtin_func("_/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_~/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 0),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_^/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_<<_", arith_bin_op, compile_lshift,
SymValFunc::flagMarkedAsPure);
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 0),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("!_", arith_un_op, compile_logical_not,
SymValFunc::flagMarkedAsPure);
define_builtin_func("~_", arith_un_op, compile_bitwise_not,
SymValFunc::flagMarkedAsPure);
define_builtin_func("_&_", arith_bin_op, compile_bitwise_and,
SymValFunc::flagMarkedAsPure);
define_builtin_func("_|_", arith_bin_op, compile_bitwise_or,
SymValFunc::flagMarkedAsPure);
define_builtin_func("_^_", arith_bin_op, compile_bitwise_xor,
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_+=_", arith_bin_op, compile_add,
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_-=_", arith_bin_op, compile_sub,
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_*=_", arith_bin_op, compile_mul,
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_<<=_", arith_bin_op, compile_lshift,
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_&=_", arith_bin_op, compile_bitwise_and,
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_|=_", arith_bin_op, compile_bitwise_or,
SymValFunc::flagMarkedAsPure);
define_builtin_func("^_^=_", arith_bin_op, compile_bitwise_xor,
SymValFunc::flagMarkedAsPure);
define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_!=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 5),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_<_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 4),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3),
SymValFunc::flagMarkedAsPure);
define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7),
SymValFunc::flagMarkedAsPure);
define_builtin_func("mulDivFloor", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, -1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("mulDivRound", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 0),
SymValFunc::flagMarkedAsPure);
define_builtin_func("mulDivCeil", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 1),
SymValFunc::flagMarkedAsPure);
define_builtin_func("mulDivMod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2),
SymValFunc::flagMarkedAsPure);
define_builtin_func("__true", TypeExpr::new_map(TypeExpr::new_unit(), Int), /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true),
SymValFunc::flagMarkedAsPure);
define_builtin_func("__false", TypeExpr::new_map(TypeExpr::new_unit(), Int), /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false),
SymValFunc::flagMarkedAsPure);
define_builtin_func("__null", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_unit(), X)), AsmOp::Const("PUSHNULL"),
SymValFunc::flagMarkedAsPure);
define_builtin_func("__isNull", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null,
SymValFunc::flagMarkedAsPure);
define_builtin_func("__throw", impure_un_op, compile_throw,
0);
define_builtin_func("__throw_arg", throw_arg_op, compile_throw_arg,
0);
define_builtin_func("__throw_if_unless", TypeExpr::new_map(Int3, Unit), compile_throw_if_unless,
0);
define_builtin_func("loadInt", fetch_int_op_mutate, std::bind(compile_fetch_int, _1, _2, true, true),
SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf, {}, {1, 0});
define_builtin_func("loadUint", fetch_int_op_mutate, std::bind(compile_fetch_int, _1, _2, true, false),
SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf, {}, {1, 0});
define_builtin_func("loadBits", fetch_slice_op_mutate, std::bind(compile_fetch_slice, _1, _2, true),
SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf, {}, {1, 0});
define_builtin_func("preloadInt", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true),
SymValFunc::flagMarkedAsPure | SymValFunc::flagAcceptsSelf);
define_builtin_func("preloadUint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false),
SymValFunc::flagMarkedAsPure | SymValFunc::flagAcceptsSelf);
define_builtin_func("preloadBits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false),
SymValFunc::flagMarkedAsPure | SymValFunc::flagAcceptsSelf);
define_builtin_func("storeInt", store_int_mutate, std::bind(compile_store_int, _1, _2, true),
SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf | SymValFunc::flagReturnsSelf, {1, 0, 2}, {});
define_builtin_func("storeUint", store_int_mutate, std::bind(compile_store_int, _1, _2, false),
SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf | SymValFunc::flagReturnsSelf, {1, 0, 2}, {});
define_builtin_func("tupleAt", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at,
SymValFunc::flagMarkedAsPure | SymValFunc::flagAcceptsSelf);
define_builtin_func("debugPrint", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Unit)),
AsmOp::Custom("s0 DUMP DROP", 1, 1), true);
AsmOp::Custom("s0 DUMP DROP", 1, 1),
0);
define_builtin_func("debugPrintString", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Unit)),
AsmOp::Custom("STRDUMP DROP", 1, 1), true);
AsmOp::Custom("STRDUMP DROP", 1, 1),
0);
define_builtin_func("debugDumpStack", TypeExpr::new_map(Unit, Unit),
AsmOp::Custom("DUMPSTK", 0, 0), true);
AsmOp::Custom("DUMPSTK", 0, 0),
0);
}
} // namespace tolk

View file

@ -41,21 +41,22 @@ Expr::Expr(ExprCls c, sym_idx_t name_idx, std::initializer_list<Expr*> _arglist)
}
}
bool Expr::deduce_type() {
void Expr::deduce_type() {
if (e_type) {
return true;
return;
}
switch (cls) {
case _Apply: {
if (!sym) {
return false;
return;
}
SymVal* sym_val = dynamic_cast<SymVal*>(sym->value);
SymValFunc* sym_val = dynamic_cast<SymValFunc*>(sym->value);
if (!sym_val || !sym_val->get_type()) {
return false;
return;
}
std::vector<TypeExpr*> arg_types;
for (const auto& arg : args) {
arg_types.reserve(args.size());
for (const Expr* arg : args) {
arg_types.push_back(arg->e_type);
}
TypeExpr* fun_type = TypeExpr::new_map(TypeExpr::new_tensor(arg_types), TypeExpr::new_hole());
@ -69,7 +70,7 @@ bool Expr::deduce_type() {
}
e_type = fun_type->args[1];
TypeExpr::remove_indirect(e_type);
return true;
return;
}
case _VarApply: {
tolk_assert(args.size() == 2);
@ -84,7 +85,27 @@ bool Expr::deduce_type() {
}
e_type = fun_type->args[1];
TypeExpr::remove_indirect(e_type);
return true;
return;
}
case _GrabMutatedVars: {
tolk_assert(args.size() == 2 && args[0]->cls == _Apply && sym);
SymValFunc* called_f = dynamic_cast<SymValFunc*>(sym->value);
tolk_assert(called_f->has_mutate_params());
TypeExpr* sym_type = called_f->get_type();
if (sym_type->constr == TypeExpr::te_ForAll) {
TypeExpr::remove_forall(sym_type);
}
tolk_assert(sym_type->args[1]->constr == TypeExpr::te_Tensor);
e_type = sym_type->args[1]->args[sym_type->args[1]->args.size() - 1];
TypeExpr::remove_indirect(e_type);
return;
}
case _ReturnSelf: {
tolk_assert(args.size() == 2 && sym);
Expr* this_arg = args[1];
e_type = this_arg->e_type;
TypeExpr::remove_indirect(e_type);
return;
}
case _Letop: {
tolk_assert(args.size() == 2);
@ -99,25 +120,7 @@ bool Expr::deduce_type() {
}
e_type = args[0]->e_type;
TypeExpr::remove_indirect(e_type);
return true;
}
case _LetFirst: {
tolk_assert(args.size() == 2);
TypeExpr* rhs_type = TypeExpr::new_tensor({args[0]->e_type, TypeExpr::new_hole()});
try {
// std::cerr << "in implicit assignment of a modifying method: " << rhs_type << " and " << args[1]->e_type << std::endl;
unify(rhs_type, args[1]->e_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "cannot implicitly assign an expression of type " << args[1]->e_type
<< " to a variable or pattern of type " << rhs_type << " in modifying method `" << G.symbols.get_name(val)
<< "` : " << ue;
throw ParseError(here, os.str());
}
e_type = rhs_type->args[1];
TypeExpr::remove_indirect(e_type);
// std::cerr << "result type is " << e_type << std::endl;
return true;
return;
}
case _CondExpr: {
tolk_assert(args.size() == 3);
@ -139,46 +142,46 @@ bool Expr::deduce_type() {
}
e_type = args[1]->e_type;
TypeExpr::remove_indirect(e_type);
return true;
return;
}
default:
throw Fatal("unexpected cls=" + std::to_string(cls) + " in Expr::deduce_type()");
}
return false;
}
int Expr::define_new_vars(CodeBlob& code) {
void Expr::define_new_vars(CodeBlob& code) {
switch (cls) {
case _Tensor:
case _MkTuple: {
int res = 0;
for (const auto& x : args) {
res += x->define_new_vars(code);
for (Expr* item : args) {
item->define_new_vars(code);
}
return res;
break;
}
case _Var:
if (val < 0) {
val = code.create_var(false, e_type, sym, here);
return 1;
val = code.create_var(e_type, sym->sym_idx, here);
sym->value->idx = val;
}
break;
case _Hole:
if (val < 0) {
val = code.create_var(true, e_type, nullptr, here);
val = code.create_tmp_var(e_type, here);
}
break;
default:
break;
}
return 0;
}
int Expr::predefine_vars() {
void Expr::predefine_vars() {
switch (cls) {
case _Tensor:
case _MkTuple: {
int res = 0;
for (const auto& x : args) {
res += x->predefine_vars();
for (Expr* item : args) {
item->predefine_vars();
}
return res;
break;
}
case _Var:
if (!sym) {
@ -188,12 +191,15 @@ int Expr::predefine_vars() {
if (!sym) {
throw ParseError{here, std::string{"redefined variable `"} + G.symbols.get_name(~val) + "`"};
}
sym->value = new SymVal{SymValKind::_Var, -1, e_type};
return 1;
sym->value = new SymValVariable(-1, e_type);
if (is_immutable()) {
dynamic_cast<SymValVariable*>(sym->value)->flags |= SymValVariable::flagImmutable;
}
}
break;
default:
break;
}
return 0;
}
var_idx_t Expr::new_tmp(CodeBlob& code) const {
@ -217,7 +223,7 @@ std::vector<var_idx_t> pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, Src
auto unpacked_type = rhs->e_type->args.at(0);
std::vector<var_idx_t> tmp{code.create_tmp_var(unpacked_type, rhs->here)};
code.emplace_back(lhs->here, Op::_UnTuple, tmp, std::move(right));
auto tvar = new Expr{Expr::_Var};
auto tvar = new Expr{Expr::_Var, lhs->here};
tvar->set_val(tmp[0]);
tvar->set_location(rhs->here);
tvar->e_type = unpacked_type;
@ -255,7 +261,7 @@ std::vector<var_idx_t> pre_compile_tensor(const std::vector<Expr *>& args, CodeB
res_lists[i] = args[i]->pre_compile(code, lval_globs);
for (size_t j = 0; j < res_lists[i].size(); ++j) {
TmpVar& var = code.vars.at(res_lists[i][j]);
if (!lval_globs && !var.is_tmp_unnamed) {
if (!lval_globs && !var.is_unnamed()) {
var.on_modification.push_back([&modified_vars, i, j, cur_ops = code.cur_ops, done = false](SrcLocation here) mutable {
if (!done) {
done = true;
@ -303,39 +309,39 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
}
case _Apply: {
tolk_assert(sym);
std::vector<var_idx_t> res;
SymDef* applied_sym = sym;
auto func = dynamic_cast<SymValFunc*>(applied_sym->value);
// replace `beginCell()` with `begin_cell()`
// todo it should be done at AST level, see comment above detect_if_function_just_wraps_another()
if (func && func->is_just_wrapper_for_another_f()) {
// todo currently, f is inlined only if anotherF is declared (and processed) before
if (!dynamic_cast<SymValCodeFunc*>(func)->code) { // if anotherF is processed after
func->flags |= SymValFunc::flagUsedAsNonCall;
res = pre_compile_tensor(args, code, lval_globs);
} else {
// body is { Op::_Import; Op::_Call; Op::_Return; }
const std::unique_ptr<Op>& op_call = dynamic_cast<SymValCodeFunc*>(func)->code->ops->next;
applied_sym = op_call->fun_ref;
// a function may call anotherF with shuffled arguments: f(x,y) { return anotherF(y,x) }
// then op_call looks like (_1,_0), so use op_call->right for correct positions in Op::_Call below
// it's correct, since every argument has width 1
std::vector<var_idx_t> res_inner = pre_compile_tensor(args, code, lval_globs);
res.reserve(res_inner.size());
for (var_idx_t right_idx : op_call->right) {
res.emplace_back(res_inner[right_idx]);
}
}
} else {
res = pre_compile_tensor(args, code, lval_globs);
}
std::vector<var_idx_t> res = pre_compile_tensor(args, code, lval_globs);;
auto rvect = new_tmp_vect(code);
auto& op = code.emplace_back(here, Op::_Call, rvect, res, applied_sym);
auto& op = code.emplace_back(here, Op::_Call, rvect, res, sym);
if (flags & _IsImpure) {
op.set_impure(code);
}
return rvect;
}
case _GrabMutatedVars: {
SymValFunc* func_val = dynamic_cast<SymValFunc*>(sym->value);
tolk_assert(func_val && func_val->has_mutate_params());
tolk_assert(args.size() == 2 && args[0]->cls == _Apply && args[1]->cls == _Tensor);
auto right = args[0]->pre_compile(code); // apply (returning function result and mutated)
std::vector<std::pair<SymDef*, var_idx_t>> local_globs;
if (!lval_globs) {
lval_globs = &local_globs;
}
auto left = args[1]->pre_compile(code, lval_globs); // mutated (lvalue)
auto rvect = new_tmp_vect(code);
left.push_back(rvect[0]);
for (var_idx_t v : left) {
code.on_var_modification(v, here);
}
code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
add_set_globs(code, local_globs, here);
return rvect;
}
case _ReturnSelf: {
tolk_assert(args.size() == 2 && sym);
Expr* this_arg = args[1];
auto right = args[0]->pre_compile(code);
return this_arg->pre_compile(code);
}
case _Var:
case _Hole:
if (val < 0) {
@ -372,7 +378,10 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
if (auto fun_ref = dynamic_cast<SymValFunc*>(sym->value)) {
fun_ref->flags |= SymValFunc::flagUsedAsNonCall;
if (!fun_ref->arg_order.empty() || !fun_ref->ret_order.empty()) {
throw ParseError(here, "Saving " + sym->name() + " into a variable will most likely lead to invalid usage, since it changes the order of variables on the stack");
throw ParseError(here, "saving `" + sym->name() + "` into a variable will most likely lead to invalid usage, since it changes the order of variables on the stack");
}
if (fun_ref->has_mutate_params()) {
throw ParseError(here, "saving `" + sym->name() + "` into a variable is impossible, since it has `mutate` parameters and thus can only be called directly");
}
}
auto rvect = new_tmp_vect(code);
@ -387,22 +396,6 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
case _Letop: {
return pre_compile_let(code, args.at(0), args.at(1), here);
}
case _LetFirst: {
auto rvect = new_tmp_vect(code);
auto right = args[1]->pre_compile(code);
std::vector<std::pair<SymDef*, var_idx_t>> local_globs;
if (!lval_globs) {
lval_globs = &local_globs;
}
auto left = args[0]->pre_compile(code, lval_globs);
left.push_back(rvect[0]);
for (var_idx_t v : left) {
code.on_var_modification(v, here);
}
code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
add_set_globs(code, local_globs, here);
return rvect;
}
case _MkTuple: {
auto left = new_tmp_vect(code);
auto right = args[0]->pre_compile(code);

View file

@ -323,8 +323,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
static TokenType maybe_keyword(std::string_view str) {
switch (str.size()) {
case 1:
if (str == "~") return tok_bitwise_not; // todo attention
if (str == "_") return tok_underscore; // todo attention
if (str == "_") return tok_underscore;
break;
case 2:
if (str == "do") return tok_do;
@ -347,6 +346,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
if (str == "void") return tok_void;
if (str == "bool") return tok_bool;
if (str == "auto") return tok_auto;
if (str == "self") return tok_self;
if (str == "tolk") return tok_tolk;
if (str == "type") return tok_type;
if (str == "enum") return tok_enum;
@ -368,6 +368,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
if (str == "assert") return tok_assert;
if (str == "import") return tok_import;
if (str == "global") return tok_global;
if (str == "mutate") return tok_mutate;
if (str == "repeat") return tok_repeat;
if (str == "struct") return tok_struct;
if (str == "export") return tok_export;
@ -394,8 +395,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
lex->skip_chars(1);
while (!lex->is_eof()) {
char c = lex->char_at();
// the pattern of valid identifier first symbol is provided in trie, here we test for identifier middle
bool allowed_in_identifier = std::isalnum(c) || c == '_' || c == '$' || c == '?' || c == '!' || c == '\'';
bool allowed_in_identifier = std::isalnum(c) || c == '_' || c == '$';
if (!allowed_in_identifier) {
break;
}
@ -438,28 +438,6 @@ struct ChunkIdentifierInBackticks final : ChunkLexerBase {
}
};
// Handle ~`some_method` and .`some_method` todo to be removed later
struct ChunkDotTildeAndBackticks final : ChunkLexerBase {
bool parse(Lexer* lex) const override {
const char* str_begin = lex->c_str();
lex->skip_chars(2);
while (!lex->is_eof() && lex->char_at() != '`' && lex->char_at() != '\n') {
lex->skip_chars(1);
}
if (lex->char_at() != '`') {
lex->error("unclosed backtick `");
}
std::string_view in_backticks(str_begin + 2, lex->c_str() - str_begin - 2);
std::string full = std::string(1, *str_begin) + static_cast<std::string>(in_backticks);
std::string* allocated = new std::string(full);
lex->skip_chars(1);
std::string_view str_val(allocated->c_str(), allocated->size());
lex->add_token(tok_identifier, str_val);
return true;
}
};
//
// ----------------------------------------------------------------------
// Here we define a grammar of Tolk.
@ -500,11 +478,8 @@ struct TolkLanguageGrammar {
trie.add_prefix("\n", singleton<ChunkSkipWhitespace>());
trie.add_pattern("[0-9]", singleton<ChunkNumber>());
// todo think of . ~
trie.add_pattern("[a-zA-Z_$.~]", singleton<ChunkIdentifierOrKeyword>());
trie.add_pattern("[a-zA-Z_$]", singleton<ChunkIdentifierOrKeyword>());
trie.add_prefix("`", singleton<ChunkIdentifierInBackticks>());
// todo to be removed after ~ becomes invalid and . becomes a separate token
trie.add_pattern("[.~]`", singleton<ChunkDotTildeAndBackticks>());
register_token("+", 1, tok_plus);
register_token("-", 1, tok_minus);
@ -528,6 +503,8 @@ struct TolkLanguageGrammar {
register_token("&", 1, tok_bitwise_and);
register_token("|", 1, tok_bitwise_or);
register_token("^", 1, tok_bitwise_xor);
register_token("~", 1, tok_bitwise_not);
register_token(".", 1, tok_dot);
register_token("==", 2, tok_eq);
register_token("!=", 2, tok_neq);
register_token("<=", 2, tok_leq);

View file

@ -38,6 +38,8 @@ enum TokenType {
tok_var,
tok_val,
tok_redef,
tok_mutate,
tok_self,
tok_annotation_at,
tok_colon,
@ -52,6 +54,7 @@ enum TokenType {
tok_null,
tok_identifier,
tok_dot,
tok_plus,
tok_minus,

View file

@ -37,7 +37,87 @@ static int calc_sym_idx(std::string_view sym_name) {
return G.symbols.lookup(sym_name);
}
void Expr::fire_error_rvalue_expected() const {
// generally, almost all vertices are rvalue, that's why code leading to "not rvalue"
// should be very strange, like `var x = _`
throw ParseError(here, "rvalue expected");
}
void Expr::fire_error_lvalue_expected(const std::string& details) const {
// "lvalue expected" is when a user modifies something unmodifiable
// example: `f() = 32`
// example: `loadUint(c.beginParse(), 32)` (since `loadUint()` mutates the first argument)
throw ParseError(here, "lvalue expected (" + details + ")");
}
void Expr::fire_error_modifying_immutable(const std::string& details) const {
// "modifying immutable variable" is when a user assigns to a variable declared `val`
// example: `immutable_val = 32`
// example: `(regular_var, immutable_val) = f()`
// for better error message, try to print out variable name if possible
std::string variable_name;
if (cls == _Var || cls == _Const) {
variable_name = sym->name();
} else if (cls == _Tensor || cls == _MkTuple) {
for (const Expr* arg : (cls == _Tensor ? args : args[0]->args)) {
if (arg->is_immutable() && (arg->cls == _Var || arg->cls == _Const)) {
variable_name = arg->sym->name();
break;
}
}
}
if (variable_name == "self") {
throw ParseError(here, "modifying `self` (" + details + "), which is immutable by default; probably, you want to declare `mutate self`");
} else if (!variable_name.empty()) {
throw ParseError(here, "modifying an immutable variable `" + variable_name + "` (" + details + ")");
} else {
throw ParseError(here, "modifying an immutable variable (" + details + ")");
}
}
GNU_ATTRIBUTE_COLD GNU_ATTRIBUTE_NORETURN
static void fire_error_invalid_mutate_arg_passed(SrcLocation loc, const SymDef* func_sym, const SymDef* param_sym, bool called_as_method, bool arg_passed_as_mutate, AnyV arg_expr) {
std::string func_name = func_sym->name();
std::string arg_str(arg_expr->type == ast_identifier ? arg_expr->as<ast_identifier>()->name : "obj");
const SymValFunc* func_val = dynamic_cast<const SymValFunc*>(func_sym->value);
const SymValVariable* param_val = dynamic_cast<const SymValVariable*>(param_sym->value);
// case: `loadInt(cs, 32)`; suggest: `cs.loadInt(32)`
if (param_val->is_mutate_parameter() && !arg_passed_as_mutate && !called_as_method && param_val->idx == 0 && func_val->does_accept_self()) {
throw ParseError(loc, "`" + func_name + "` is a mutating method; consider calling `" + arg_str + "." + func_name + "()`, not `" + func_name + "(" + arg_str + ")`");
}
// case: `cs.mutating_function()`; suggest: `mutating_function(mutate cs)` or make it a method
if (param_val->is_mutate_parameter() && called_as_method && param_val->idx == 0 && !func_val->does_accept_self()) {
throw ParseError(loc, "function `" + func_name + "` mutates parameter `" + param_sym->name() + "`; consider calling `" + func_name + "(mutate " + arg_str + ")`, not `" + arg_str + "." + func_name + "`(); alternatively, rename parameter to `self` to make it a method");
}
// case: `mutating_function(arg)`; suggest: `mutate arg`
if (param_val->is_mutate_parameter() && !arg_passed_as_mutate) {
throw ParseError(loc, "function `" + func_name + "` mutates parameter `" + param_sym->name() + "`; you need to specify `mutate` when passing an argument, like `mutate " + arg_str + "`");
}
// case: `usual_function(mutate arg)`
if (!param_val->is_mutate_parameter() && arg_passed_as_mutate) {
throw ParseError(loc, "incorrect `mutate`, since `" + func_name + "` does not mutate this parameter");
}
throw Fatal("unreachable");
}
namespace blk_fl {
enum { end = 1, ret = 2, empty = 4 };
typedef int val;
constexpr val init = end | empty;
void combine(val& x, const val y) {
x |= y & ret;
x &= y | ~(end | empty);
}
void combine_parallel(val& x, const val y) {
x &= y | ~(ret | empty);
x |= y & end;
}
} // namespace blk_fl
Expr* process_expr(AnyV v, CodeBlob& code);
blk_fl::val process_statement(AnyV v, CodeBlob& code);
static void check_global_func(SrcLocation loc, sym_idx_t func_name) {
SymDef* sym_def = lookup_symbol(func_name);
@ -46,22 +126,6 @@ static void check_global_func(SrcLocation loc, sym_idx_t func_name) {
}
}
static Expr* make_func_apply(Expr* fun, Expr* x) {
Expr* res{nullptr};
if (fun->cls == Expr::_GlobFunc) {
if (x->cls == Expr::_Tensor) {
res = new Expr{Expr::_Apply, fun->sym, x->args};
} else {
res = new Expr{Expr::_Apply, fun->sym, {x}};
}
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
} else {
res = new Expr{Expr::_VarApply, {fun, x}};
res->flags = Expr::_IsRvalue;
}
return res;
}
static void check_import_exists_when_using_sym(AnyV v_usage, const SymDef* used_sym) {
if (!v_usage->loc.is_symbol_from_same_or_builtin_file(used_sym->loc)) {
const SrcFile* declared_in = used_sym->loc.get_src_file();
@ -77,7 +141,7 @@ static void check_import_exists_when_using_sym(AnyV v_usage, const SymDef* used_
}
}
static Expr* create_new_local_variable(SrcLocation loc, std::string_view var_name, TypeExpr* var_type) {
static Expr* create_new_local_variable(SrcLocation loc, std::string_view var_name, TypeExpr* var_type, bool is_immutable) {
SymDef* sym = lookup_symbol(calc_sym_idx(var_name));
if (sym) { // creating a new variable, but something found in symtable
if (sym->level != G.scope_level) {
@ -89,7 +153,7 @@ static Expr* create_new_local_variable(SrcLocation loc, std::string_view var_nam
Expr* x = new Expr{Expr::_Var, loc};
x->val = ~calc_sym_idx(var_name);
x->e_type = var_type;
x->flags = Expr::_IsLvalue;
x->flags = Expr::_IsLvalue | (is_immutable ? Expr::_IsImmutable : 0);
return x;
}
@ -109,8 +173,13 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
t == tok_set_mod || t == tok_set_lshift || t == tok_set_rshift ||
t == tok_set_bitwise_and || t == tok_set_bitwise_or || t == tok_set_bitwise_xor) {
Expr* x = process_expr(v->get_lhs(), code);
x->chk_lvalue();
x->chk_rvalue();
if (!x->is_lvalue()) {
x->fire_error_lvalue_expected("left side of assignment");
}
if (x->is_immutable()) {
x->fire_error_modifying_immutable("left side of assignment");
}
sym_idx_t name = G.symbols.lookup_add("^_" + operator_name + "_");
Expr* y = process_expr(v->get_rhs(), code);
y->chk_rvalue();
@ -126,7 +195,12 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
}
if (t == tok_assign) {
Expr* x = process_expr(v->get_lhs(), code);
x->chk_lvalue();
if (!x->is_lvalue()) {
x->fire_error_lvalue_expected("left side of assignment");
}
if (x->is_immutable()) {
x->fire_error_modifying_immutable("left side of assignment");
}
Expr* y = process_expr(v->get_rhs(), code);
y->chk_rvalue();
x->predefine_vars();
@ -191,54 +265,6 @@ static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
return res;
}
static Expr* process_expr(V<ast_dot_tilde_call> v, CodeBlob& code) {
Expr* res = process_expr(v->get_lhs(), code);
bool modify = v->method_name[0] == '~';
Expr* obj = res;
if (modify) {
obj->chk_lvalue();
} else {
obj->chk_rvalue();
}
sym_idx_t name_idx = calc_sym_idx(v->method_name);
const SymDef* sym = lookup_symbol(name_idx);
if (!sym || !dynamic_cast<SymValFunc*>(sym->value)) {
sym_idx_t name1 = G.symbols.lookup(v->method_name.substr(1));
if (name1) {
const SymDef* sym1 = lookup_symbol(name1);
if (sym1 && dynamic_cast<SymValFunc*>(sym1->value)) {
name_idx = name1;
}
}
}
check_global_func(v->loc, name_idx);
sym = lookup_symbol(name_idx);
SymValFunc* val = sym ? dynamic_cast<SymValFunc*>(sym->value) : nullptr;
if (!val) {
v->error("undefined method call");
}
Expr* x = process_expr(v->get_arg(), code);
x->chk_rvalue();
if (x->cls == Expr::_Tensor) {
res = new Expr{Expr::_Apply, name_idx, {obj}};
res->args.insert(res->args.end(), x->args.begin(), x->args.end());
} else {
res = new Expr{Expr::_Apply, name_idx, {obj, x}};
}
res->here = v->loc;
res->flags = Expr::_IsRvalue | (val->is_marked_as_pure() ? 0 : Expr::_IsImpure);
res->deduce_type();
if (modify) {
Expr* tmp = res;
res = new Expr{Expr::_LetFirst, {obj->copy(), tmp}};
res->here = v->loc;
res->flags = tmp->flags;
res->set_val(name_idx);
res->deduce_type();
}
return res;
}
static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code) {
Expr* cond = process_expr(v->get_cond(), code);
cond->chk_rvalue();
@ -253,19 +279,194 @@ static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code) {
return res;
}
static Expr* process_expr(V<ast_function_call> v, CodeBlob& code) {
static Expr* process_function_arguments(SymDef* func_sym, V<ast_argument_list> v, Expr* lhs_of_dot_call, CodeBlob& code) {
SymValFunc* func_val = dynamic_cast<SymValFunc*>(func_sym->value);
int delta_self = lhs_of_dot_call ? 1 : 0;
int n_arguments = static_cast<int>(v->get_arguments().size()) + delta_self;
int n_parameters = static_cast<int>(func_val->parameters.size());
// Tolk doesn't have optional parameters currently, so just compare counts
if (n_parameters < n_arguments) {
v->error("too many arguments in call to `" + func_sym->name() + "`, expected " + std::to_string(n_parameters - delta_self) + ", have " + std::to_string(n_arguments - delta_self));
}
if (n_arguments < n_parameters) {
v->error("too few arguments in call to `" + func_sym->name() + "`, expected " + std::to_string(n_parameters - delta_self) + ", have " + std::to_string(n_arguments - delta_self));
}
std::vector<Expr*> apply_args;
apply_args.reserve(n_arguments);
if (lhs_of_dot_call) {
apply_args.push_back(lhs_of_dot_call);
}
for (int i = delta_self; i < n_arguments; ++i) {
auto v_arg = v->get_arg(i - delta_self);
if (SymDef* param_sym = func_val->parameters[i]) { // can be null (for underscore parameter)
SymValVariable* param_val = dynamic_cast<SymValVariable*>(param_sym->value);
if (param_val->is_mutate_parameter() != v_arg->passed_as_mutate) {
fire_error_invalid_mutate_arg_passed(v_arg->loc, func_sym, param_sym, false, v_arg->passed_as_mutate, v_arg->get_expr());
}
}
Expr* arg = process_expr(v_arg->get_expr(), code);
arg->chk_rvalue();
apply_args.push_back(arg);
}
Expr* apply = new Expr{Expr::_Apply, func_sym, std::move(apply_args)};
apply->flags = Expr::_IsRvalue | (!func_val->is_marked_as_pure() * Expr::_IsImpure);
apply->here = v->loc;
apply->deduce_type();
return apply;
}
static Expr* process_function_call(V<ast_function_call> v, CodeBlob& code) {
// special error for "null()" which is a FunC syntax
if (v->get_called_f()->type == ast_null_keyword) {
v->error("null is not a function: use `null`, not `null()`");
}
Expr* res = process_expr(v->get_called_f(), code);
Expr* x = process_expr(v->get_called_arg(), code);
x->chk_rvalue();
res = make_func_apply(res, x);
res->here = v->loc;
res->deduce_type();
return res;
// most likely it's a global function, but also may be `some_var(args)` or even `getF()(args)`
Expr* lhs = process_expr(v->get_called_f(), code);
if (lhs->cls != Expr::_GlobFunc) {
Expr* tensor_arg = new Expr(Expr::_Tensor, v->loc);
std::vector<TypeExpr*> type_list;
type_list.reserve(v->get_num_args());
for (int i = 0; i < v->get_num_args(); ++i) {
auto v_arg = v->get_arg(i);
if (v_arg->passed_as_mutate) {
v_arg->error("`mutate` used for non-mutate argument");
}
Expr* arg = process_expr(v_arg->get_expr(), code);
arg->chk_rvalue();
tensor_arg->pb_arg(arg);
type_list.push_back(arg->e_type);
}
tensor_arg->flags = Expr::_IsRvalue;
tensor_arg->e_type = TypeExpr::new_tensor(std::move(type_list));
Expr* var_apply = new Expr{Expr::_VarApply, {lhs, tensor_arg}};
var_apply->here = v->loc;
var_apply->flags = Expr::_IsRvalue;
var_apply->deduce_type();
return var_apply;
}
Expr* apply = process_function_arguments(lhs->sym, v->get_arg_list(), nullptr, code);
if (dynamic_cast<SymValFunc*>(apply->sym->value)->has_mutate_params()) {
const std::vector<Expr*>& args = apply->args;
SymValFunc* func_val = dynamic_cast<SymValFunc*>(apply->sym->value);
tolk_assert(func_val->parameters.size() == args.size());
Expr* grabbed_vars = new Expr(Expr::_Tensor, v->loc);
std::vector<TypeExpr*> type_list;
for (int i = 0; i < static_cast<int>(args.size()); ++i) {
SymDef* param_def = func_val->parameters[i];
if (param_def && dynamic_cast<SymValVariable*>(param_def->value)->is_mutate_parameter()) {
if (!args[i]->is_lvalue()) {
args[i]->fire_error_lvalue_expected("call a mutating function");
}
if (args[i]->is_immutable()) {
args[i]->fire_error_modifying_immutable("call a mutating function");
}
grabbed_vars->pb_arg(args[i]->copy());
type_list.emplace_back(args[i]->e_type);
}
}
grabbed_vars->flags = Expr::_IsRvalue;
Expr* grab_mutate = new Expr(Expr::_GrabMutatedVars, apply->sym, {apply, grabbed_vars});
grab_mutate->here = v->loc;
grab_mutate->flags = apply->flags;
grab_mutate->deduce_type();
return grab_mutate;
}
return apply;
}
static Expr* process_dot_method_call(V<ast_dot_method_call> v, CodeBlob& code) {
sym_idx_t name_idx = calc_sym_idx(v->method_name);
check_global_func(v->loc, name_idx);
SymDef* func_sym = lookup_symbol(name_idx);
SymValFunc* func_val = dynamic_cast<SymValFunc*>(func_sym->value);
tolk_assert(func_val != nullptr);
Expr* obj = process_expr(v->get_obj(), code);
obj->chk_rvalue();
if (func_val->parameters.empty()) {
v->error("`" + func_sym->name() + "` has no parameters and can not be called as method");
}
if (!func_val->does_accept_self() && func_val->parameters[0] && dynamic_cast<SymValVariable*>(func_val->parameters[0]->value)->is_mutate_parameter()) {
fire_error_invalid_mutate_arg_passed(v->loc, func_sym, func_val->parameters[0], true, false, v->get_obj());
}
Expr* apply = process_function_arguments(func_sym, v->get_arg_list(), obj, code);
Expr* obj_lval = apply->args[0];
if (!obj_lval->is_lvalue()) {
if (obj_lval->cls == Expr::_ReturnSelf) {
obj_lval = obj_lval->args[1];
} else {
Expr* tmp_var = create_new_underscore_variable(v->loc, obj_lval->e_type);
tmp_var->define_new_vars(code);
Expr* assign_to_tmp_var = new Expr(Expr::_Letop, {tmp_var, obj_lval});
assign_to_tmp_var->here = v->loc;
assign_to_tmp_var->flags = Expr::_IsRvalue;
assign_to_tmp_var->deduce_type();
apply->args[0] = assign_to_tmp_var;
obj_lval = tmp_var;
}
}
if (func_val->has_mutate_params()) {
tolk_assert(func_val->parameters.size() == apply->args.size());
Expr* grabbed_vars = new Expr(Expr::_Tensor, v->loc);
std::vector<TypeExpr*> type_list;
for (int i = 0; i < static_cast<int>(apply->args.size()); ++i) {
SymDef* param_sym = func_val->parameters[i];
if (param_sym && dynamic_cast<SymValVariable*>(param_sym->value)->is_mutate_parameter()) {
Expr* ith_arg = apply->args[i];
if (ith_arg->is_immutable()) {
ith_arg->fire_error_modifying_immutable("call a mutating method");
}
Expr* var_to_mutate = nullptr;
if (ith_arg->is_lvalue()) {
var_to_mutate = ith_arg->copy();
} else if (i == 0) {
var_to_mutate = obj_lval;
} else {
ith_arg->fire_error_lvalue_expected("call a mutating method");
}
tolk_assert(var_to_mutate->is_lvalue() && !var_to_mutate->is_immutable());
grabbed_vars->pb_arg(var_to_mutate);
type_list.emplace_back(var_to_mutate->e_type);
}
}
grabbed_vars->flags = Expr::_IsRvalue;
Expr* grab_mutate = new Expr(Expr::_GrabMutatedVars, func_sym, {apply, grabbed_vars});
grab_mutate->here = v->loc;
grab_mutate->flags = apply->flags;
grab_mutate->deduce_type();
apply = grab_mutate;
}
if (func_val->does_return_self()) {
Expr* self_arg = obj_lval;
tolk_assert(self_arg->is_lvalue());
Expr* return_self = new Expr(Expr::_ReturnSelf, func_sym, {apply, self_arg});
return_self->here = v->loc;
return_self->flags = Expr::_IsRvalue;
return_self->deduce_type();
apply = return_self;
}
return apply;
}
static Expr* process_expr(V<ast_tensor> v, CodeBlob& code) {
@ -285,7 +486,8 @@ static Expr* process_expr(V<ast_tensor> v, CodeBlob& code) {
for (int i = 1; i < v->size(); ++i) {
Expr* x = process_expr(v->get_item(i), code);
res->pb_arg(x);
f &= x->flags;
f &= (x->flags | Expr::_IsImmutable);
f |= (x->flags & Expr::_IsImmutable);
type_list.push_back(x->e_type);
}
res->here = v->loc;
@ -315,7 +517,8 @@ static Expr* process_expr(V<ast_tensor_square> v, CodeBlob& code) {
for (int i = 1; i < v->size(); ++i) {
Expr* x = process_expr(v->get_item(i), code);
res->pb_arg(x);
f &= x->flags;
f &= (x->flags | Expr::_IsImmutable);
f |= (x->flags & Expr::_IsImmutable);
type_list.push_back(x->e_type);
}
res->here = v->loc;
@ -419,21 +622,36 @@ static Expr* process_expr(V<ast_bool_const> v) {
return res;
}
static Expr* process_expr([[maybe_unused]] V<ast_null_keyword> v) {
static Expr* process_expr(V<ast_null_keyword> v) {
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__null"));
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
res->here = v->loc;
res->flags = Expr::_IsRvalue;
res->deduce_type();
return res;
}
static Expr* process_expr(V<ast_self_keyword> v, CodeBlob& code) {
if (!code.func_val->does_accept_self()) {
v->error("using `self` in a non-member function (it does not accept the first `self` parameter)");
}
SymDef* sym = lookup_symbol(calc_sym_idx("self"));
tolk_assert(sym);
SymValVariable* sym_val = dynamic_cast<SymValVariable*>(sym->value);
Expr* res = new Expr(Expr::_Var, v->loc);
res->sym = sym;
res->val = sym_val->idx;
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (sym_val->is_immutable() ? Expr::_IsImmutable : 0);
res->e_type = sym_val->get_type();
return res;
}
static Expr* process_identifier(V<ast_identifier> v) {
SymDef* sym = lookup_symbol(calc_sym_idx(v->name));
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
check_import_exists_when_using_sym(v, sym);
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
Expr* res = new Expr{Expr::_GlobVar, v->loc};
res->e_type = val->get_type();
res->e_type = sym->value->get_type();
res->sym = sym;
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImpure;
return res;
@ -441,19 +659,20 @@ static Expr* process_identifier(V<ast_identifier> v) {
if (sym && dynamic_cast<SymValConst*>(sym->value)) {
check_import_exists_when_using_sym(v, sym);
auto val = dynamic_cast<SymValConst*>(sym->value);
Expr* res = new Expr{Expr::_None, v->loc};
res->flags = Expr::_IsRvalue;
Expr* res = nullptr;
if (val->get_kind() == SymValConst::IntConst) {
res->cls = Expr::_Const;
res = new Expr{Expr::_Const, v->loc};
res->intval = val->get_int_value();
res->e_type = TypeExpr::new_atomic(tok_int);
res->e_type = TypeExpr::new_atomic(TypeExpr::_Int);
} else if (val->get_kind() == SymValConst::SliceConst) {
res->cls = Expr::_SliceConst;
res = new Expr{Expr::_SliceConst, v->loc};
res->strval = val->get_str_value();
res->e_type = TypeExpr::new_atomic(tok_slice);
res->e_type = TypeExpr::new_atomic(TypeExpr::_Slice);
} else {
v->error("invalid symbolic constant type");
}
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImmutable;
res->sym = sym;
return res;
}
if (sym && dynamic_cast<SymValFunc*>(sym->value)) {
@ -463,28 +682,26 @@ static Expr* process_identifier(V<ast_identifier> v) {
if (!sym) {
check_global_func(v->loc, calc_sym_idx(v->name));
sym = lookup_symbol(calc_sym_idx(v->name));
tolk_assert(sym);
}
res->sym = sym;
SymVal* val = nullptr;
bool impure = false;
if (sym) {
val = dynamic_cast<SymVal*>(sym->value);
}
if (!val) {
bool immutable = false;
if (const SymValFunc* func_val = dynamic_cast<SymValFunc*>(sym->value)) {
res->e_type = func_val->get_type();
res->cls = Expr::_GlobFunc;
impure = !func_val->is_marked_as_pure();
} else if (const SymValVariable* var_val = dynamic_cast<SymValVariable*>(sym->value)) {
tolk_assert(var_val->idx >= 0)
res->val = var_val->idx;
res->e_type = var_val->get_type();
immutable = var_val->is_immutable();
// std::cerr << "accessing variable " << lex.cur().str << " : " << res->e_type << std::endl;
} else {
v->error("undefined identifier '" + static_cast<std::string>(v->name) + "'");
}
if (val->kind == SymValKind::_Func) {
res->e_type = val->get_type();
res->cls = Expr::_GlobFunc;
impure = !dynamic_cast<SymValFunc*>(val)->is_marked_as_pure();
} else {
tolk_assert(val->idx >= 0);
res->val = val->idx;
res->e_type = val->get_type();
// std::cerr << "accessing variable " << lex.cur().str << " : " << res->e_type << std::endl;
}
// std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl;
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (impure ? Expr::_IsImpure : 0);
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (impure ? Expr::_IsImpure : 0) | (immutable ? Expr::_IsImmutable : 0);
res->deduce_type();
return res;
}
@ -495,12 +712,12 @@ Expr* process_expr(AnyV v, CodeBlob& code) {
return process_expr(v->as<ast_binary_operator>(), code);
case ast_unary_operator:
return process_expr(v->as<ast_unary_operator>(), code);
case ast_dot_tilde_call:
return process_expr(v->as<ast_dot_tilde_call>(), code);
case ast_ternary_operator:
return process_expr(v->as<ast_ternary_operator>(), code);
case ast_function_call:
return process_expr(v->as<ast_function_call>(), code);
return process_function_call(v->as<ast_function_call>(), code);
case ast_dot_method_call:
return process_dot_method_call(v->as<ast_dot_method_call>(), code);
case ast_parenthesized_expr:
return process_expr(v->as<ast_parenthesized_expr>()->get_expr(), code);
case ast_tensor:
@ -515,6 +732,8 @@ Expr* process_expr(AnyV v, CodeBlob& code) {
return process_expr(v->as<ast_bool_const>());
case ast_null_keyword:
return process_expr(v->as<ast_null_keyword>());
case ast_self_keyword:
return process_expr(v->as<ast_self_keyword>(), code);
case ast_identifier:
return process_identifier(v->as<ast_identifier>());
case ast_underscore:
@ -524,31 +743,22 @@ Expr* process_expr(AnyV v, CodeBlob& code) {
}
}
namespace blk_fl {
enum { end = 1, ret = 2, empty = 4 };
typedef int val;
constexpr val init = end | empty;
void combine(val& x, const val y) {
x |= y & ret;
x &= y | ~(end | empty);
}
void combine_parallel(val& x, const val y) {
x &= y | ~(ret | empty);
x |= y & end;
}
} // namespace blk_fl
static Expr* process_local_vars_lhs(AnyV v, CodeBlob& code) {
switch (v->type) {
case ast_local_var: {
if (v->as<ast_local_var>()->marked_as_redef) {
return process_identifier(v->as<ast_local_var>()->get_identifier()->as<ast_identifier>());
auto v_var = v->as<ast_local_var>();
if (v_var->marked_as_redef) {
Expr* redef_var = process_identifier(v_var->get_identifier()->as<ast_identifier>());
if (redef_var->is_immutable()) {
redef_var->fire_error_modifying_immutable("left side of assignment");
}
return redef_var;
}
TypeExpr* declared_type = v->as<ast_local_var>()->declared_type;
TypeExpr* var_type = v_var->declared_type ? v_var->declared_type : TypeExpr::new_hole();
if (auto v_ident = v->as<ast_local_var>()->get_identifier()->try_as<ast_identifier>()) {
return create_new_local_variable(v->loc, v_ident->name, declared_type ? declared_type : TypeExpr::new_hole());
return create_new_local_variable(v->loc, v_ident->name, var_type, v_var->is_immutable);
} else {
return create_new_underscore_variable(v->loc, declared_type ? declared_type : TypeExpr::new_hole());
return create_new_underscore_variable(v->loc, var_type);
}
}
case ast_parenthesized_expr:
@ -588,7 +798,6 @@ static Expr* process_local_vars_lhs(AnyV v, CodeBlob& code) {
static blk_fl::val process_vertex(V<ast_local_vars_declaration> v, CodeBlob& code) {
Expr* x = process_local_vars_lhs(v->get_lhs(), code);
x->chk_lvalue();
Expr* y = process_expr(v->get_assigned_val(), code);
y->chk_rvalue();
x->predefine_vars();
@ -602,11 +811,83 @@ static blk_fl::val process_vertex(V<ast_local_vars_declaration> v, CodeBlob& cod
return blk_fl::end;
}
static bool is_expr_valid_as_return_self(Expr* return_expr) {
// `return self`
if (return_expr->cls == Expr::_Var && return_expr->val == 0) {
return true;
}
if (return_expr->cls == Expr::_ReturnSelf) {
return is_expr_valid_as_return_self(return_expr->args[1]);
}
if (return_expr->cls == Expr::_CondExpr) {
return is_expr_valid_as_return_self(return_expr->args[1]) && is_expr_valid_as_return_self(return_expr->args[2]);
}
return false;
}
// for mutating functions, having `return expr`, transform it to `return (modify_var1, ..., expr)`
static Expr* wrap_return_value_with_mutate_params(SrcLocation loc, CodeBlob& code, Expr* return_expr) {
Expr* tmp_var;
if (return_expr->cls != Expr::_Var) {
// `return complex_expr` - extract this into temporary variable (eval it before return)
// this is mandatory if it assigns to one of modified vars
tmp_var = create_new_underscore_variable(loc, return_expr->e_type);
tmp_var->predefine_vars();
tmp_var->define_new_vars(code);
Expr* assign_to_tmp_var = new Expr(Expr::_Letop, {tmp_var, return_expr});
assign_to_tmp_var->here = loc;
assign_to_tmp_var->flags = tmp_var->flags | Expr::_IsRvalue;
assign_to_tmp_var->deduce_type();
assign_to_tmp_var->pre_compile(code);
} else {
tmp_var = return_expr;
}
Expr* ret_tensor = new Expr(Expr::_Tensor, loc);
std::vector<TypeExpr*> type_list;
for (SymDef* p_sym: code.func_val->parameters) {
if (p_sym && dynamic_cast<SymValVariable*>(p_sym->value)->is_mutate_parameter()) {
Expr* p_expr = new Expr{Expr::_Var, p_sym->loc};
p_expr->sym = p_sym;
p_expr->val = p_sym->value->idx;
p_expr->flags = Expr::_IsRvalue;
p_expr->e_type = p_sym->value->get_type();
ret_tensor->pb_arg(p_expr);
type_list.emplace_back(p_expr->e_type);
}
}
ret_tensor->pb_arg(tmp_var);
type_list.emplace_back(tmp_var->e_type);
ret_tensor->flags = Expr::_IsRvalue;
ret_tensor->e_type = TypeExpr::new_tensor(std::move(type_list));
return ret_tensor;
}
static blk_fl::val process_vertex(V<ast_return_statement> v, CodeBlob& code) {
Expr* expr = process_expr(v->get_return_value(), code);
if (code.func_val->does_return_self()) {
if (!is_expr_valid_as_return_self(expr)) {
v->error("invalid return from `self` function");
}
Expr* var_self = new Expr(Expr::_Var, v->loc);
var_self->flags = Expr::_IsRvalue | Expr::_IsLvalue;
var_self->e_type = code.func_val->parameters[0]->value->get_type();
Expr* assign_to_self = new Expr(Expr::_Letop, {var_self, expr});
assign_to_self->here = v->loc;
assign_to_self->flags = Expr::_IsRvalue;
assign_to_self->deduce_type();
assign_to_self->pre_compile(code);
Expr* empty_tensor = new Expr(Expr::_Tensor, {});
empty_tensor->here = v->loc;
empty_tensor->flags = Expr::_IsRvalue;
empty_tensor->e_type = TypeExpr::new_tensor({});
expr = empty_tensor;
}
if (code.func_val->has_mutate_params()) {
expr = wrap_return_value_with_mutate_params(v->loc, code, expr);
}
expr->chk_rvalue();
try {
// std::cerr << "in return: ";
unify(expr->e_type, code.ret_type);
} catch (UnifyError& ue) {
std::ostringstream os;
@ -619,22 +900,29 @@ static blk_fl::val process_vertex(V<ast_return_statement> v, CodeBlob& code) {
return blk_fl::ret;
}
static void append_implicit_ret_stmt(V<ast_sequence> v, CodeBlob& code) {
TypeExpr* ret_type = TypeExpr::new_unit();
static void append_implicit_ret_stmt(SrcLocation loc_end, CodeBlob& code) {
Expr* expr = new Expr{Expr::_Tensor, {}};
expr->flags = Expr::_IsRvalue;
expr->here = loc_end;
expr->e_type = TypeExpr::new_unit();
if (code.func_val->does_return_self()) {
throw ParseError(loc_end, "missing return; forgot `return self`?");
}
if (code.func_val->has_mutate_params()) {
expr = wrap_return_value_with_mutate_params(loc_end, code, expr);
}
try {
// std::cerr << "in implicit return: ";
unify(ret_type, code.ret_type);
unify(expr->e_type, code.ret_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "previous function return type " << code.ret_type
<< " cannot be unified with implicit end-of-block return type " << ret_type << ": " << ue;
throw ParseError(v->loc_end, os.str());
<< " cannot be unified with implicit end-of-block return type " << expr->e_type << ": " << ue;
throw ParseError(loc_end, os.str());
}
code.emplace_back(v->loc_end, Op::_Return);
std::vector<var_idx_t> tmp_vars = expr->pre_compile(code);
code.emplace_back(loc_end, Op::_Return, std::move(tmp_vars));
}
blk_fl::val process_statement(AnyV v, CodeBlob& code);
static blk_fl::val process_vertex(V<ast_sequence> v, CodeBlob& code, bool no_new_scope = false) {
if (!no_new_scope) {
open_scope(v->loc);
@ -792,7 +1080,7 @@ static blk_fl::val process_vertex(V<ast_assert_statement> v, CodeBlob& code) {
static Expr* process_catch_variable(AnyV catch_var, TypeExpr* var_type) {
if (auto v_ident = catch_var->try_as<ast_identifier>()) {
return create_new_local_variable(catch_var->loc, v_ident->name, var_type);
return create_new_local_variable(catch_var->loc, v_ident->name, var_type, true);
}
return create_new_underscore_variable(catch_var->loc, var_type);
}
@ -882,7 +1170,7 @@ blk_fl::val process_statement(AnyV v, CodeBlob& code) {
case ast_try_catch_statement:
return process_vertex(v->as<ast_try_catch_statement>(), code);
default: {
auto expr = process_expr(v, code);
Expr* expr = process_expr(v, code);
expr->chk_rvalue();
expr->pre_compile(code);
return blk_fl::end;
@ -890,18 +1178,16 @@ blk_fl::val process_statement(AnyV v, CodeBlob& code) {
}
}
static FormalArg process_vertex(V<ast_parameter> v, int fa_idx) {
if (v->get_identifier()->name.empty()) {
return std::make_tuple(v->param_type, (SymDef*)nullptr, v->loc);
static FormalArg process_vertex(V<ast_parameter> v, SymDef* param_sym) {
if (!param_sym) {
return std::make_tuple(v->param_type, nullptr, v->loc);
}
SymDef* new_sym_def = define_symbol(calc_sym_idx(v->get_identifier()->name), true, v->loc);
if (!new_sym_def) {
v->error("cannot define symbol");
if (!new_sym_def || new_sym_def->value) {
v->error("redefined parameter");
}
if (new_sym_def->value) {
v->error("redefined argument");
}
new_sym_def->value = new SymVal{SymValKind::_Param, fa_idx, v->param_type};
const SymValVariable* param_val = dynamic_cast<SymValVariable*>(param_sym->value);
new_sym_def->value = new SymValVariable(*param_val);
return std::make_tuple(v->param_type, new_sym_def, v->loc);
}
@ -911,13 +1197,13 @@ static void convert_function_body_to_CodeBlob(V<ast_function_declaration> v, V<a
tolk_assert(sym_val != nullptr);
open_scope(v->loc);
CodeBlob* blob = new CodeBlob{static_cast<std::string>(v->get_identifier()->name), v->loc, v->ret_type};
CodeBlob* blob = new CodeBlob{static_cast<std::string>(v->get_identifier()->name), v->loc, sym_val, v->ret_type};
if (v->marked_as_pure) {
blob->flags |= CodeBlob::_ForbidImpure;
}
FormalArgList legacy_arg_list;
for (int i = 0; i < v->get_num_params(); ++i) {
legacy_arg_list.emplace_back(process_vertex(v->get_param(i), i));
legacy_arg_list.emplace_back(process_vertex(v->get_param(i), sym_val->parameters[i]));
}
blob->import_params(std::move(legacy_arg_list));
@ -931,7 +1217,7 @@ static void convert_function_body_to_CodeBlob(V<ast_function_declaration> v, V<a
blk_fl::combine(res, process_statement(item, *blob));
}
if (res & blk_fl::end) {
append_implicit_ret_stmt(v_body, *blob);
append_implicit_ret_stmt(v_body->loc_end, *blob);
}
blob->close_blk(v_body->loc_end);

View file

@ -39,10 +39,10 @@ bool SymValCodeFunc::does_need_codegen() const {
if (flags & flagUsedAsNonCall) {
return true;
}
// when a function f() is just `return anotherF(...args)`, it doesn't need to be codegenerated at all,
// since all its usages are inlined
return !is_just_wrapper_for_another_f();
// in the future, we may want to implement a true AST inlining for `inline` functions also
// currently, there is no inlining, all functions are codegenerated
// (but actually, unused ones are later removed by Fift)
// in the future, we may want to implement a true AST inlining for "simple" functions
return true;
}
void SymValCodeFunc::set_code(CodeBlob* code) {

View file

@ -74,68 +74,6 @@ static td::RefInt256 calculate_method_id_by_func_name(std::string_view func_name
return td::make_refint((crc & 0xffff) | 0x10000);
}
static bool is_parameter_of_function(AnyV v_variable, V<ast_function_declaration> v_func) {
return v_variable->type == ast_identifier && v_func->get_param_list()->lookup_idx(v_variable->as<ast_identifier>()->name) != -1;
}
// if a function looks like `T f(...args) { return anotherF(...args); }`,
// set a bit to flags
// then, all calls to `f(...)` will be effectively replaced with `anotherF(...)`
// todo this function (and optimization) was done before implementing AST, but after AST and registering symbols in advance,
// its behavior became a bit wrong: if anotherF is declared before f, than it's detected here, but still not inlined,
// since inlining is done is legacy code, using Expr
// in the future, inlining should be done on AST level, but it's impossible until all names resolving (including scopes)
// is also done on AST level
// in the future, when working on AST level, inlining should become much more powerful
// (for instance, it should inline `return anotherF(constants)`, etc.)
static bool detect_if_function_just_wraps_another(V<ast_function_declaration> v) {
if (v->method_id || v->marked_as_get_method || v->marked_as_builtin || v->marked_as_inline_ref || v->is_entrypoint) {
return false;
}
for (int i = 0; i < v->get_num_params(); ++i) {
if (v->get_param(i)->param_type->get_width() != 1 || v->get_param(i)->param_type->has_unknown_inside()) {
return false; // avoid situations like `f(int a, (int,int) b)`, inlining will be cumbersome
}
}
auto v_body = v->get_body()->try_as<ast_sequence>();
if (!v_body || v_body->size() != 1 || v_body->get_item(0)->type != ast_return_statement) {
return false;
}
auto v_return = v_body->get_item(0)->as<ast_return_statement>();
auto v_anotherF = v_return->get_return_value()->try_as<ast_function_call>();
if (!v_anotherF) {
return false;
}
V<ast_tensor> called_arg = v_anotherF->get_called_arg();
if (v_anotherF->get_called_f()->type != ast_identifier) {
return false;
}
std::string_view called_name = v_anotherF->get_called_f()->try_as<ast_identifier>()->name;
std::string_view function_name = v->get_identifier()->name;
const std::vector<AnyV>& v_arg_items = called_arg->get_items();
std::set<std::string_view> used_args;
for (AnyV v_arg : v_arg_items) {
if (!is_parameter_of_function(v_arg, v)) {
return false;
}
used_args.emplace(v_arg->as<ast_identifier>()->name);
}
if (static_cast<int>(used_args.size()) != v->get_num_params() || used_args.size() != v_arg_items.size()) {
return false;
}
// ok, f_current is a wrapper
if (G.is_verbosity(2)) {
std::cerr << function_name << " -> " << called_name << std::endl;
}
return true;
}
static void calc_arg_ret_order_of_asm_function(V<ast_asm_body> v_body, V<ast_parameter_list> param_list, TypeExpr* ret_type,
std::vector<int>& arg_order, std::vector<int>& ret_order) {
int cnt = param_list->size();
@ -201,7 +139,7 @@ static void register_constant(V<ast_constant_declaration> v) {
// todo currently, constant value calculation is dirty and roughly: init_value is evaluated to fif code
// and waited to be a single expression
// although it works, of course it should be later rewritten using AST calculations, as well as lots of other parts
CodeBlob code("tmp", v->loc, nullptr);
CodeBlob code("tmp", v->loc, nullptr, nullptr);
Expr* x = process_expr(init_value, code);
if (!x->is_rvalue()) {
v->get_init_value()->error("expression is not strictly Rvalue");
@ -211,9 +149,9 @@ static void register_constant(V<ast_constant_declaration> v) {
}
SymValConst* sym_val = nullptr;
if (x->cls == Expr::_Const) { // Integer constant
sym_val = new SymValConst{static_cast<int>(G.all_constants.size()), x->intval};
sym_val = new SymValConst(static_cast<int>(G.all_constants.size()), x->intval);
} else if (x->cls == Expr::_SliceConst) { // Slice constant (string)
sym_val = new SymValConst{static_cast<int>(G.all_constants.size()), x->strval};
sym_val = new SymValConst(static_cast<int>(G.all_constants.size()), x->strval);
} else if (x->cls == Expr::_Apply) { // even "1 + 2" is Expr::_Apply (it applies `_+_`)
code.emplace_back(v->loc, Op::_Import, std::vector<var_idx_t>());
auto tmp_vars = x->pre_compile(code);
@ -241,14 +179,14 @@ static void register_constant(V<ast_constant_declaration> v) {
if (op.origin.is_null() || !op.origin->is_valid()) {
init_value->error("precompiled expression did not result in a valid integer constant");
}
sym_val = new SymValConst{static_cast<int>(G.all_constants.size()), op.origin};
sym_val = new SymValConst(static_cast<int>(G.all_constants.size()), op.origin);
} else {
init_value->error("integer or slice literal or constant expected");
}
sym_def->value = sym_val;
#ifdef TOLK_DEBUG
dynamic_cast<SymValConst*>(sym_def->value)->name = v->get_identifier()->name;
sym_def->value->sym_name = v->get_identifier()->name;
#endif
G.all_constants.push_back(sym_def);
}
@ -259,35 +197,68 @@ static void register_global_var(V<ast_global_var_declaration> v) {
fire_error_redefinition_of_symbol(v->get_identifier(), sym_def);
}
sym_def->value = new SymValGlobVar{static_cast<int>(G.all_global_vars.size()), v->declared_type};
sym_def->value = new SymValGlobVar(static_cast<int>(G.all_global_vars.size()), v->declared_type);
#ifdef TOLK_DEBUG
dynamic_cast<SymValGlobVar*>(sym_def->value)->name = v->get_identifier()->name;
sym_def->value->sym_name = v->get_identifier()->name;
#endif
G.all_global_vars.push_back(sym_def);
}
static SymDef* register_parameter(V<ast_parameter> v, int idx) {
if (v->is_underscore()) {
return nullptr;
}
SymDef* sym_def = define_parameter(calc_sym_idx(v->get_identifier()->name), v->loc);
if (sym_def->value) {
// todo always false now, how to detect similar parameter names? (remember about underscore)
v->error("redefined parameter");
}
SymValVariable* sym_val = new SymValVariable(idx, v->param_type);
if (v->declared_as_mutate) {
sym_val->flags |= SymValVariable::flagMutateParameter;
}
if (!v->declared_as_mutate && idx == 0 && v->get_identifier()->name == "self") {
sym_val->flags |= SymValVariable::flagImmutable;
}
sym_def->value = sym_val;
#ifdef TOLK_DEBUG
sym_def->value->sym_name = v->get_identifier()->name;
#endif
return sym_def;
}
static void register_function(V<ast_function_declaration> v) {
std::string_view func_name = v->get_identifier()->name;
// calculate TypeExpr of a function: it's a map (args -> ret), probably surrounded by forall
TypeExpr* func_type = nullptr;
if (int n_args = v->get_num_params()) {
std::vector<TypeExpr*> arg_types;
arg_types.reserve(n_args);
for (int idx = 0; idx < n_args; ++idx) {
arg_types.emplace_back(v->get_param(idx)->param_type);
// calculate TypeExpr of a function: it's a map (params -> ret), probably surrounded by forall
TypeExpr* params_tensor_type = nullptr;
int n_params = v->get_num_params();
int n_mutate_params = 0;
std::vector<SymDef*> parameters_syms;
if (n_params) {
std::vector<TypeExpr*> param_tensor_items;
param_tensor_items.reserve(n_params);
parameters_syms.reserve(n_params);
for (int i = 0; i < n_params; ++i) {
auto v_param = v->get_param(i);
n_mutate_params += static_cast<int>(v_param->declared_as_mutate);
param_tensor_items.emplace_back(v_param->param_type);
parameters_syms.emplace_back(register_parameter(v_param, i));
}
func_type = TypeExpr::new_map(TypeExpr::new_tensor(std::move(arg_types)), v->ret_type);
params_tensor_type = TypeExpr::new_tensor(std::move(param_tensor_items));
} else {
func_type = TypeExpr::new_map(TypeExpr::new_unit(), v->ret_type);
params_tensor_type = TypeExpr::new_unit();
}
TypeExpr* function_type = TypeExpr::new_map(params_tensor_type, v->ret_type);
if (v->genericsT_list) {
std::vector<TypeExpr*> type_vars;
type_vars.reserve(v->genericsT_list->size());
for (int idx = 0; idx < v->genericsT_list->size(); ++idx) {
type_vars.emplace_back(v->genericsT_list->get_item(idx)->created_type);
}
func_type = TypeExpr::new_forall(std::move(type_vars), func_type);
function_type = TypeExpr::new_forall(std::move(type_vars), function_type);
}
if (v->marked_as_builtin) {
const SymDef* builtin_func = lookup_symbol(G.symbols.lookup(func_name));
@ -297,7 +268,7 @@ static void register_function(V<ast_function_declaration> v) {
}
#ifdef TOLK_DEBUG
// in release, we don't need this check, since `builtin` is used only in stdlib, which is our responsibility
if (!func_val->sym_type->equals_to(func_type) || func_val->is_marked_as_pure() != v->marked_as_pure) {
if (!func_val->sym_type->equals_to(function_type) || func_val->is_marked_as_pure() != v->marked_as_pure) {
v->error("declaration for `builtin` function doesn't match an actual one");
}
#endif
@ -309,7 +280,7 @@ static void register_function(V<ast_function_declaration> v) {
fire_error_redefinition_of_symbol(v->get_identifier(), sym_def);
}
if (G.is_verbosity(1)) {
std::cerr << "fun " << func_name << " : " << func_type << std::endl;
std::cerr << "fun " << func_name << " : " << function_type << std::endl;
}
if (v->marked_as_pure && v->ret_type->get_width() == 0) {
v->error("a pure function should return something, otherwise it will be optimized out anyway");
@ -317,11 +288,11 @@ static void register_function(V<ast_function_declaration> v) {
SymValFunc* sym_val = nullptr;
if (const auto* v_seq = v->get_body()->try_as<ast_sequence>()) {
sym_val = new SymValCodeFunc{static_cast<int>(G.all_code_functions.size()), func_type, v->marked_as_pure};
sym_val = new SymValCodeFunc(std::move(parameters_syms), static_cast<int>(G.all_code_functions.size()), function_type);
} else if (const auto* v_asm = v->get_body()->try_as<ast_asm_body>()) {
std::vector<int> arg_order, ret_order;
calc_arg_ret_order_of_asm_function(v_asm, v->get_param_list(), v->ret_type, arg_order, ret_order);
sym_val = new SymValAsmFunc{func_type, std::move(arg_order), std::move(ret_order), v->marked_as_pure};
sym_val = new SymValAsmFunc(std::move(parameters_syms), function_type, std::move(arg_order), std::move(ret_order), 0);
} else {
v->error("Unexpected function body statement");
}
@ -341,6 +312,9 @@ static void register_function(V<ast_function_declaration> v) {
} else if (v->is_entrypoint) {
sym_val->method_id = calculate_method_id_for_entrypoint(func_name);
}
if (v->marked_as_pure) {
sym_val->flags |= SymValFunc::flagMarkedAsPure;
}
if (v->marked_as_inline) {
sym_val->flags |= SymValFunc::flagInline;
}
@ -353,13 +327,19 @@ static void register_function(V<ast_function_declaration> v) {
if (v->is_entrypoint) {
sym_val->flags |= SymValFunc::flagIsEntrypoint;
}
if (detect_if_function_just_wraps_another(v)) {
sym_val->flags |= SymValFunc::flagWrapsAnotherF;
if (n_mutate_params) {
sym_val->flags |= SymValFunc::flagHasMutateParams;
}
if (v->accepts_self) {
sym_val->flags |= SymValFunc::flagAcceptsSelf;
}
if (v->returns_self) {
sym_val->flags |= SymValFunc::flagReturnsSelf;
}
sym_def->value = sym_val;
#ifdef TOLK_DEBUG
dynamic_cast<SymValFunc*>(sym_def->value)->name = func_name;
sym_def->value->sym_name = func_name;
#endif
if (dynamic_cast<SymValCodeFunc*>(sym_val)) {
G.all_code_functions.push_back(sym_def);

View file

@ -126,6 +126,18 @@ SymDef* define_global_symbol(sym_idx_t name_idx, SrcLocation loc) {
return registered; // registered->value is nullptr; it means, it's just created
}
SymDef* define_parameter(sym_idx_t name_idx, SrcLocation loc) {
// note, that parameters (defined at function declaration) are not inserted into symtable
// their SymDef is registered to be inserted into SymValFunc::parameters
// (and later ->value is filled with SymValVariable)
SymDef* registered = new SymDef(0, name_idx, loc);
#ifdef TOLK_DEBUG
registered->sym_name = registered->name();
#endif
return registered;
}
SymDef* define_symbol(sym_idx_t name_idx, bool force_new, SrcLocation loc) {
if (!name_idx) {
return nullptr;

View file

@ -17,6 +17,7 @@
#pragma once
#include "src-file.h"
#include "type-expr.h"
#include <functional>
#include <memory>
@ -25,14 +26,23 @@ namespace tolk {
typedef int var_idx_t;
typedef int sym_idx_t;
enum class SymValKind { _Param, _Var, _Func, _Typename, _GlobVar, _Const };
enum class SymValKind { _Var, _Func, _GlobVar, _Const };
struct SymValBase {
SymValKind kind;
int idx;
SymValBase(SymValKind kind, int idx) : kind(kind), idx(idx) {
TypeExpr* sym_type;
#ifdef TOLK_DEBUG
std::string sym_name; // seeing symbol name in debugger makes it much easier to delve into Tolk sources
#endif
SymValBase(SymValKind kind, int idx, TypeExpr* sym_type) : kind(kind), idx(idx), sym_type(sym_type) {
}
virtual ~SymValBase() = default;
TypeExpr* get_type() const {
return sym_type;
}
};
@ -98,6 +108,7 @@ void close_scope();
SymDef* lookup_symbol(sym_idx_t idx);
SymDef* define_global_symbol(sym_idx_t name_idx, SrcLocation loc = {});
SymDef* define_parameter(sym_idx_t name_idx, SrcLocation loc);
SymDef* define_symbol(sym_idx_t name_idx, bool force_new, SrcLocation loc);
} // namespace tolk

View file

@ -69,13 +69,14 @@ using const_idx_t = int;
struct TmpVar {
TypeExpr* v_type;
var_idx_t idx;
bool is_tmp_unnamed;
sym_idx_t name;
sym_idx_t sym_idx;
int coord;
SrcLocation where;
std::vector<std::function<void(SrcLocation)>> on_modification;
TmpVar(var_idx_t _idx, bool _is_tmp_unnamed, TypeExpr* _type, SymDef* sym, SrcLocation loc);
TmpVar(var_idx_t _idx, TypeExpr* _type, sym_idx_t sym_idx, SrcLocation loc);
bool is_unnamed() const { return sym_idx == 0; }
void show(std::ostream& os, int omit_idx = 0) const;
void dump(std::ostream& os) const;
void set_location(SrcLocation loc);
@ -401,40 +402,56 @@ struct AsmOpList;
*
*/
struct SymVal : SymValBase {
TypeExpr* sym_type;
SymVal(SymValKind kind, int idx, TypeExpr* sym_type = nullptr)
: SymValBase(kind, idx), sym_type(sym_type) {
struct SymValVariable : SymValBase {
enum SymValFlag {
flagMutateParameter = 1, // parameter was declared with `mutate` keyword
flagImmutable = 2, // variable was declared via `val` (not `var`)
};
int flags{0};
~SymValVariable() override = default;
SymValVariable(int val, TypeExpr* sym_type)
: SymValBase(SymValKind::_Var, val, sym_type) {}
bool is_function_parameter() const {
return idx >= 0;
}
~SymVal() override = default;
TypeExpr* get_type() const {
return sym_type;
bool is_mutate_parameter() const {
return flags & flagMutateParameter;
}
bool is_local_var() const {
return idx == -1;
}
bool is_immutable() const {
return flags & flagImmutable;
}
};
struct SymValFunc : SymVal {
struct SymValFunc : SymValBase {
enum SymValFlag {
flagInline = 1, // marked `@inline`
flagInlineRef = 2, // marked `@inline_ref`
flagWrapsAnotherF = 4, // `fun thisF(...args) { return anotherF(...args); }` (calls to thisF will be inlined)
flagUsedAsNonCall = 8, // used not only as `f()`, but as a 1-st class function (assigned to var, pushed to tuple, etc.)
flagMarkedAsPure = 16, // declared as `pure`, can't call impure and access globals, unused invocations are optimized out
flagBuiltinFunction = 32, // was created via `define_builtin_func()`, not from source code
flagGetMethod = 64, // was declared via `get func(): T`, method_id is auto-assigned
flagIsEntrypoint = 128, // it's `main` / `onExternalMessage` / etc.
flagHasMutateParams = 256, // has parameters declared as `mutate`
flagAcceptsSelf = 512, // is a member function (has `self` first parameter)
flagReturnsSelf = 1024, // return type is `self` (returns the mutated 1st argument), calls can be chainable
};
td::RefInt256 method_id; // todo why int256? it's small
int flags{0};
std::vector<SymDef*> parameters; // [i]-th may be nullptr for underscore; if not, its val is SymValVariable
std::vector<int> arg_order, ret_order;
#ifdef TOLK_DEBUG
std::string name; // seeing function name in debugger makes it much easier to delve into Tolk sources
#endif
~SymValFunc() override = default;
SymValFunc(int val, TypeExpr* _ft, bool marked_as_pure)
: SymVal(SymValKind::_Func, val, _ft), flags(marked_as_pure ? flagMarkedAsPure : 0) {}
SymValFunc(int val, TypeExpr* _ft, std::initializer_list<int> _arg_order, std::initializer_list<int> _ret_order, bool marked_as_pure)
: SymVal(SymValKind::_Func, val, _ft), flags(marked_as_pure ? flagMarkedAsPure : 0), arg_order(_arg_order), ret_order(_ret_order) {
SymValFunc(std::vector<SymDef*> parameters, int val, TypeExpr* sym_type, int flags)
: SymValBase(SymValKind::_Func, val, sym_type), flags(flags), parameters(std::move(parameters)) {
}
SymValFunc(std::vector<SymDef*> parameters, int val, TypeExpr* sym_type, int flags, std::initializer_list<int> arg_order, std::initializer_list<int> ret_order)
: SymValBase(SymValKind::_Func, val, sym_type), flags(flags), parameters(std::move(parameters)), arg_order(arg_order), ret_order(ret_order) {
}
const std::vector<int>* get_arg_order() const {
@ -450,9 +467,6 @@ struct SymValFunc : SymVal {
bool is_inline_ref() const {
return flags & flagInlineRef;
}
bool is_just_wrapper_for_another_f() const {
return flags & flagWrapsAnotherF;
}
bool is_marked_as_pure() const {
return flags & flagMarkedAsPure;
}
@ -465,32 +479,35 @@ struct SymValFunc : SymVal {
bool is_entrypoint() const {
return flags & flagIsEntrypoint;
}
bool has_mutate_params() const {
return flags & flagHasMutateParams;
}
bool does_accept_self() const {
return flags & flagAcceptsSelf;
}
bool does_return_self() const {
return flags & flagReturnsSelf;
}
};
struct SymValCodeFunc : SymValFunc {
CodeBlob* code;
bool is_really_used{false}; // calculated via dfs; unused functions are not codegenerated
~SymValCodeFunc() override = default;
SymValCodeFunc(int val, TypeExpr* _ft, bool marked_as_pure) : SymValFunc(val, _ft, marked_as_pure), code(nullptr) {
SymValCodeFunc(std::vector<SymDef*> parameters, int val, TypeExpr* _ft)
: SymValFunc(std::move(parameters), val, _ft, 0), code(nullptr) {
}
bool does_need_codegen() const;
void set_code(CodeBlob* code);
};
struct SymValGlobVar : SymValBase {
TypeExpr* sym_type;
int out_idx{0};
bool is_really_used{false}; // calculated via dfs from used functions; unused globals are not codegenerated
#ifdef TOLK_DEBUG
std::string name; // seeing variable name in debugger makes it much easier to delve into Tolk sources
#endif
SymValGlobVar(int val, TypeExpr* gvtype, int oidx = 0)
: SymValBase(SymValKind::_GlobVar, val), sym_type(gvtype), out_idx(oidx) {
SymValGlobVar(int val, TypeExpr* gvtype)
: SymValBase(SymValKind::_GlobVar, val, gvtype) {
}
~SymValGlobVar() override = default;
TypeExpr* get_type() const {
return sym_type;
}
};
struct SymValConst : SymValBase {
@ -499,14 +516,12 @@ struct SymValConst : SymValBase {
td::RefInt256 intval;
std::string strval;
ConstKind kind;
#ifdef TOLK_DEBUG
std::string name; // seeing const name in debugger makes it much easier to delve into Tolk sources
#endif
SymValConst(int idx, td::RefInt256 value)
: SymValBase(SymValKind::_Const, idx), intval(value), kind(IntConst) {
: SymValBase(SymValKind::_Const, idx, TypeExpr::new_atomic(TypeExpr::_Int)), intval(std::move(value)), kind(IntConst) {
}
SymValConst(int idx, std::string value)
: SymValBase(SymValKind::_Const, idx), strval(value), kind(SliceConst) {
: SymValBase(SymValKind::_Const, idx, TypeExpr::new_atomic(TypeExpr::_Slice)), strval(std::move(value)), kind(SliceConst) {
}
~SymValConst() override = default;
td::RefInt256 get_int_value() const {
@ -529,9 +544,10 @@ struct SymValConst : SymValBase {
struct Expr {
enum ExprCls {
_None,
_Apply,
_VarApply,
_GrabMutatedVars,
_ReturnSelf,
_MkTuple,
_Tensor,
_Const,
@ -539,14 +555,13 @@ struct Expr {
_GlobFunc,
_GlobVar,
_Letop,
_LetFirst,
_Hole,
_CondExpr,
_SliceConst,
};
ExprCls cls;
int val{0};
enum { _IsRvalue = 2, _IsLvalue = 4, _IsImpure = 32 };
enum { _IsRvalue = 2, _IsLvalue = 4, _IsImmutable = 8, _IsImpure = 32 };
int flags{0};
SrcLocation here;
td::RefInt256 intval;
@ -554,8 +569,6 @@ struct Expr {
SymDef* sym{nullptr};
TypeExpr* e_type{nullptr};
std::vector<Expr*> args;
explicit Expr(ExprCls c = _None) : cls(c) {
}
Expr(ExprCls c, SrcLocation loc) : cls(c), here(loc) {
}
Expr(ExprCls c, std::vector<Expr*> _args) : cls(c), args(std::move(_args)) {
@ -585,33 +598,38 @@ struct Expr {
bool is_lvalue() const {
return flags & _IsLvalue;
}
bool is_immutable() const {
return flags & _IsImmutable;
}
bool is_mktuple() const {
return cls == _MkTuple;
}
void chk_rvalue() const {
if (!is_rvalue()) {
throw ParseError(here, "rvalue expected");
fire_error_rvalue_expected();
}
}
void chk_lvalue() const {
if (!is_lvalue()) {
throw ParseError(here, "lvalue expected");
}
}
bool deduce_type();
void deduce_type();
void set_location(SrcLocation loc) {
here = loc;
}
SrcLocation get_location() const {
return here;
}
int define_new_vars(CodeBlob& code);
int predefine_vars();
void define_new_vars(CodeBlob& code);
void predefine_vars();
std::vector<var_idx_t> pre_compile(CodeBlob& code, std::vector<std::pair<SymDef*, var_idx_t>>* lval_globs = nullptr) const;
var_idx_t new_tmp(CodeBlob& code) const;
std::vector<var_idx_t> new_tmp_vect(CodeBlob& code) const {
return {new_tmp(code)};
}
GNU_ATTRIBUTE_COLD GNU_ATTRIBUTE_NORETURN
void fire_error_rvalue_expected() const;
GNU_ATTRIBUTE_COLD GNU_ATTRIBUTE_NORETURN
void fire_error_lvalue_expected(const std::string& details) const;
GNU_ATTRIBUTE_COLD GNU_ATTRIBUTE_NORETURN
void fire_error_modifying_immutable(const std::string& details) const;
};
/*
@ -1324,24 +1342,17 @@ struct SymValAsmFunc : SymValFunc {
simple_compile_func_t simple_compile;
compile_func_t ext_compile;
~SymValAsmFunc() override = default;
SymValAsmFunc(TypeExpr* ft, std::vector<int>&& arg_order, std::vector<int>&& ret_order, bool marked_as_pure)
: SymValFunc(-1, ft, marked_as_pure) {
SymValAsmFunc(std::vector<SymDef*> parameters, TypeExpr* ft, std::vector<int>&& arg_order, std::vector<int>&& ret_order, int flags)
: SymValFunc(std::move(parameters), -1, ft, flags) {
this->arg_order = std::move(arg_order);
this->ret_order = std::move(ret_order);
}
SymValAsmFunc(TypeExpr* ft, simple_compile_func_t _compile, bool marked_as_pure)
: SymValFunc(-1, ft, marked_as_pure), simple_compile(std::move(_compile)) {
SymValAsmFunc(std::vector<SymDef*> parameters, TypeExpr* ft, simple_compile_func_t _compile, int flags)
: SymValFunc(std::move(parameters), -1, ft, flags), simple_compile(std::move(_compile)) {
}
SymValAsmFunc(TypeExpr* ft, compile_func_t _compile, bool marked_as_pure)
: SymValFunc(-1, ft, marked_as_pure), ext_compile(std::move(_compile)) {
}
SymValAsmFunc(TypeExpr* ft, simple_compile_func_t _compile, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool marked_as_pure = false)
: SymValFunc(-1, ft, arg_order, ret_order, marked_as_pure), simple_compile(std::move(_compile)) {
}
SymValAsmFunc(TypeExpr* ft, compile_func_t _compile, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool marked_as_pure = false)
: SymValFunc(-1, ft, arg_order, ret_order, marked_as_pure), ext_compile(std::move(_compile)) {
SymValAsmFunc(std::vector<SymDef*> parameters, TypeExpr* ft, simple_compile_func_t _compile, int flags,
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order)
: SymValFunc(std::move(parameters), -1, ft, flags, arg_order, ret_order), simple_compile(std::move(_compile)) {
}
void set_code(std::vector<AsmOp> code);
bool compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in, SrcLocation where) const;
@ -1349,29 +1360,32 @@ struct SymValAsmFunc : SymValFunc {
struct CodeBlob {
enum { _ForbidImpure = 4 };
int var_cnt, in_var_cnt, op_cnt;
int var_cnt, in_var_cnt;
TypeExpr* ret_type;
const SymValCodeFunc* func_val;
std::string name;
SrcLocation loc;
std::vector<TmpVar> vars;
std::unique_ptr<Op> ops;
std::unique_ptr<Op>* cur_ops;
std::vector<Op*> debug_ttt;
std::stack<std::unique_ptr<Op>*> cur_ops_stack;
int flags = 0;
bool require_callxargs = false;
CodeBlob(std::string name, SrcLocation loc, TypeExpr* ret)
: var_cnt(0), in_var_cnt(0), op_cnt(0), ret_type(ret), name(std::move(name)), loc(loc), cur_ops(&ops) {
CodeBlob(std::string name, SrcLocation loc, const SymValCodeFunc* func_val, TypeExpr* ret_type)
: var_cnt(0), in_var_cnt(0), ret_type(ret_type), func_val(func_val), name(std::move(name)), loc(loc), cur_ops(&ops) {
}
template <typename... Args>
Op& emplace_back(Args&&... args) {
Op& res = *(*cur_ops = std::make_unique<Op>(args...));
cur_ops = &(res.next);
debug_ttt.push_back(&res);
return res;
}
bool import_params(FormalArgList arg_list);
var_idx_t create_var(bool is_tmp_unnamed, TypeExpr* var_type, SymDef* sym, SrcLocation loc);
var_idx_t create_var(TypeExpr* var_type, var_idx_t sym_idx, SrcLocation loc);
var_idx_t create_tmp_var(TypeExpr* var_type, SrcLocation loc) {
return create_var(true, var_type, nullptr, loc);
return create_var(var_type, 0, loc);
}
int split_vars(bool strict = false);
bool compute_used_code_vars();

View file

@ -2,28 +2,20 @@
#include <vector>
#include <iostream>
#include "lexer.h"
namespace tolk {
struct TypeExpr {
enum Kind { te_Unknown, te_Var, te_Indirect, te_Atomic, te_Tensor, te_Tuple, te_Map, te_ForAll };
// todo not _
enum AtomicType {
_Int = tok_int,
_Cell = tok_cell,
_Slice = tok_slice,
_Builder = tok_builder,
_Cont = tok_continuation,
_Tuple = tok_tuple,
};
enum AtomicType { _Int, _Cell, _Slice, _Builder, _Continutaion, _Tuple };
Kind constr;
int value;
int minw, maxw;
static constexpr int w_inf = 1023;
std::vector<TypeExpr*> args;
bool was_forall_var = false;
TypeExpr(Kind _constr, int _val = 0) : constr(_constr), value(_val), minw(0), maxw(w_inf) {
explicit TypeExpr(Kind _constr, int _val = 0) : constr(_constr), value(_val), minw(0), maxw(w_inf) {
}
TypeExpr(Kind _constr, int _val, int width) : constr(_constr), value(_val), minw(width), maxw(width) {
}
@ -48,6 +40,7 @@ struct TypeExpr {
args.insert(args.end(), list.begin(), list.end());
compute_width();
}
bool is_atomic() const {
return constr == te_Atomic;
}
@ -127,9 +120,7 @@ struct TypeExpr {
static TypeExpr* new_forall(std::vector<TypeExpr*> list, TypeExpr* body) {
return new TypeExpr{te_ForAll, body, std::move(list)};
}
static TypeExpr* new_forall(std::initializer_list<TypeExpr*> list, TypeExpr* body) {
return new TypeExpr{te_ForAll, body, std::move(list)};
}
static bool remove_indirect(TypeExpr*& te, TypeExpr* forbidden = nullptr);
static std::vector<TypeExpr*> remove_forall(TypeExpr*& te);
static bool remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector<TypeExpr*>& new_vars);

View file

@ -264,7 +264,7 @@ std::ostream& TypeExpr::print(std::ostream& os, int lex_level) const {
return os << "slice";
case _Builder:
return os << "builder";
case _Cont:
case _Continutaion:
return os << "cont";
case _Tuple:
return os << "tuple";