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:
parent
12ff28ac94
commit
d9dba320cc
85 changed files with 2710 additions and 1965 deletions
|
@ -17,11 +17,7 @@ fun createEmptyTuple(): tuple
|
||||||
/// Appends a value to tuple, resulting in `Tuple t' = (x1, ..., xn, value)`.
|
/// Appends a value to tuple, resulting in `Tuple t' = (x1, ..., xn, value)`.
|
||||||
/// If its size exceeds 255, throws a type check exception.
|
/// If its size exceeds 255, throws a type check exception.
|
||||||
@pure
|
@pure
|
||||||
fun tuplePush<X>(t: tuple, value: X): tuple
|
fun tuplePush<X>(mutate self: tuple, value: X): void
|
||||||
asm "TPUSH";
|
|
||||||
|
|
||||||
@pure
|
|
||||||
fun ~tuplePush<X>(t: tuple, value: X): (tuple, ())
|
|
||||||
asm "TPUSH";
|
asm "TPUSH";
|
||||||
|
|
||||||
/// Returns the first element of a non-empty tuple.
|
/// Returns the first element of a non-empty tuple.
|
||||||
|
@ -336,118 +332,109 @@ fun beginParse(c: cell): slice
|
||||||
asm "CTOS";
|
asm "CTOS";
|
||||||
|
|
||||||
/// Checks if slice is empty. If not, throws an exception.
|
/// Checks if slice is empty. If not, throws an exception.
|
||||||
fun assertEndOfSlice(s: slice): void
|
fun assertEndOfSlice(self: slice): void
|
||||||
asm "ENDS";
|
asm "ENDS";
|
||||||
|
|
||||||
/// Loads the next reference from the slice.
|
/// Loads the next reference from the slice.
|
||||||
@pure
|
@pure
|
||||||
fun loadRef(s: slice): (slice, cell)
|
fun loadRef(mutate self: slice): cell
|
||||||
asm( -> 1 0) "LDREF";
|
asm( -> 1 0) "LDREF";
|
||||||
|
|
||||||
/// Preloads the next reference from the slice.
|
/// Preloads the next reference from the slice.
|
||||||
@pure
|
@pure
|
||||||
fun preloadRef(s: slice): cell
|
fun preloadRef(self: slice): cell
|
||||||
asm "PLDREF";
|
asm "PLDREF";
|
||||||
|
|
||||||
/// Loads a signed [len]-bit integer from a slice.
|
/// Loads a signed [len]-bit integer from a slice.
|
||||||
@pure
|
@pure
|
||||||
fun loadInt(s: slice, len: int): (slice, int)
|
fun loadInt(mutate self: slice, len: int): int
|
||||||
builtin;
|
builtin;
|
||||||
|
|
||||||
/// Loads an unsigned [len]-bit integer from a slice.
|
/// Loads an unsigned [len]-bit integer from a slice.
|
||||||
@pure
|
@pure
|
||||||
fun loadUint(s: slice, len: int): (slice, int)
|
fun loadUint(mutate self: slice, len: int): int
|
||||||
builtin;
|
builtin;
|
||||||
|
|
||||||
/// Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice `s''`.
|
/// Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice `s''`.
|
||||||
@pure
|
@pure
|
||||||
fun loadBits(s: slice, len: int): (slice, slice)
|
fun loadBits(mutate self: slice, len: int): slice
|
||||||
builtin;
|
builtin;
|
||||||
|
|
||||||
/// Preloads a signed [len]-bit integer from a slice.
|
/// Preloads a signed [len]-bit integer from a slice.
|
||||||
@pure
|
@pure
|
||||||
fun preloadInt(s: slice, len: int): int
|
fun preloadInt(self: slice, len: int): int
|
||||||
builtin;
|
builtin;
|
||||||
|
|
||||||
/// Preloads an unsigned [len]-bit integer from a slice.
|
/// Preloads an unsigned [len]-bit integer from a slice.
|
||||||
@pure
|
@pure
|
||||||
fun preloadUint(s: slice, len: int): int
|
fun preloadUint(self: slice, len: int): int
|
||||||
builtin;
|
builtin;
|
||||||
|
|
||||||
/// Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice.
|
/// Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice.
|
||||||
@pure
|
@pure
|
||||||
fun preloadBits(s: slice, len: int): slice
|
fun preloadBits(self: slice, len: int): slice
|
||||||
builtin;
|
builtin;
|
||||||
|
|
||||||
/// Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`).
|
/// Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`).
|
||||||
@pure
|
@pure
|
||||||
fun loadCoins(s: slice): (slice, int)
|
fun loadCoins(mutate self: slice): int
|
||||||
asm( -> 1 0) "LDGRAMS";
|
asm( -> 1 0) "LDGRAMS";
|
||||||
|
|
||||||
/// Loads bool (-1 or 0) from a slice
|
/// Loads bool (-1 or 0) from a slice
|
||||||
@pure
|
@pure
|
||||||
fun loadBool(s: slice): (slice, int)
|
fun loadBool(mutate self: slice): int
|
||||||
asm( -> 1 0) "1 LDI";
|
asm( -> 1 0) "1 LDI";
|
||||||
|
|
||||||
/// Shifts a slice pointer to [len] bits forward, mutating the slice.
|
/// Shifts a slice pointer to [len] bits forward, mutating the slice.
|
||||||
@pure
|
@pure
|
||||||
fun skipBits(s: slice, len: int): slice
|
fun skipBits(mutate self: slice, len: int): self
|
||||||
asm "SDSKIPFIRST"; // todo make mutating
|
|
||||||
@pure
|
|
||||||
fun ~skipBits(s: slice, len: int): (slice, ())
|
|
||||||
asm "SDSKIPFIRST";
|
asm "SDSKIPFIRST";
|
||||||
|
|
||||||
/// Returns the first `0 ≤ len ≤ 1023` bits of a slice.
|
/// Returns the first `0 ≤ len ≤ 1023` bits of a slice.
|
||||||
@pure
|
@pure
|
||||||
fun getFirstBits(s: slice, len: int): slice
|
fun getFirstBits(self: slice, len: int): slice
|
||||||
asm "SDCUTFIRST";
|
asm "SDCUTFIRST";
|
||||||
|
|
||||||
/// Returns all but the last `0 ≤ len ≤ 1023` bits of a slice.
|
/// Returns all but the last `0 ≤ len ≤ 1023` bits of a slice.
|
||||||
@pure
|
@pure
|
||||||
fun removeLastBits(s: slice, len: int): slice
|
fun removeLastBits(mutate self: slice, len: int): self
|
||||||
asm "SDSKIPLAST"; // todo make mutating
|
|
||||||
@pure
|
|
||||||
fun ~removeLastBits(s: slice, len: int): (slice, ())
|
|
||||||
asm "SDSKIPLAST";
|
asm "SDSKIPLAST";
|
||||||
|
|
||||||
/// Returns the last `0 ≤ len ≤ 1023` bits of a slice.
|
/// Returns the last `0 ≤ len ≤ 1023` bits of a slice.
|
||||||
@pure
|
@pure
|
||||||
fun getLastBits(s: slice, len: int): slice
|
fun getLastBits(self: slice, len: int): slice
|
||||||
asm "SDCUTLAST";
|
asm "SDCUTLAST";
|
||||||
|
|
||||||
/// Loads a dictionary (TL HashMapE structure, represented as TVM cell) from a slice.
|
/// Loads a dictionary (TL HashMapE structure, represented as TVM cell) from a slice.
|
||||||
/// Returns `null` if `nothing` constructor is used.
|
/// Returns `null` if `nothing` constructor is used.
|
||||||
@pure
|
@pure
|
||||||
fun loadDict(s: slice): (slice, cell)
|
fun loadDict(mutate self: slice): cell
|
||||||
asm( -> 1 0) "LDDICT";
|
asm( -> 1 0) "LDDICT";
|
||||||
|
|
||||||
/// Preloads a dictionary (cell) from a slice.
|
/// Preloads a dictionary (cell) from a slice.
|
||||||
@pure
|
@pure
|
||||||
fun preloadDict(s: slice): cell
|
fun preloadDict(self: slice): cell
|
||||||
asm "PLDDICT";
|
asm "PLDDICT";
|
||||||
|
|
||||||
/// Loads a dictionary as [loadDict], but returns only the remainder of the slice.
|
/// Loads a dictionary as [loadDict], but returns only the remainder of the slice.
|
||||||
@pure
|
@pure
|
||||||
fun skipDict(s: slice): slice
|
fun skipDict(mutate self: slice): self
|
||||||
asm "SKIPDICT"; // todo make mutating
|
|
||||||
@pure
|
|
||||||
fun ~skipDict(s: slice): (slice, ())
|
|
||||||
asm "SKIPDICT";
|
asm "SKIPDICT";
|
||||||
|
|
||||||
/// Loads (Maybe ^Cell) from a slice.
|
/// Loads (Maybe ^Cell) from a slice.
|
||||||
/// In other words, loads 1 bit: if it's true, loads the first ref, otherwise returns `null`.
|
/// In other words, loads 1 bit: if it's true, loads the first ref, otherwise returns `null`.
|
||||||
@pure
|
@pure
|
||||||
fun loadMaybeRef(s: slice): (slice, cell)
|
fun loadMaybeRef(mutate self: slice): cell
|
||||||
asm( -> 1 0) "LDOPTREF";
|
asm( -> 1 0) "LDOPTREF";
|
||||||
|
|
||||||
/// Preloads (Maybe ^Cell) from a slice.
|
/// Preloads (Maybe ^Cell) from a slice.
|
||||||
@pure
|
@pure
|
||||||
fun preloadMaybeRef(s: slice): cell
|
fun preloadMaybeRef(self: slice): cell
|
||||||
asm "PLDOPTREF";
|
asm "PLDOPTREF";
|
||||||
|
|
||||||
/// Loads (Maybe ^Cell), but returns only the remainder of the slice.
|
/// Loads (Maybe ^Cell), but returns only the remainder of the slice.
|
||||||
@pure
|
@pure
|
||||||
fun ~skipMaybeRef(s: slice): (slice, ())
|
fun skipMaybeRef(mutate self: slice): self
|
||||||
asm "SKIPOPTREF";
|
asm "SKIPOPTREF";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -464,62 +451,60 @@ fun beginCell(): builder
|
||||||
|
|
||||||
/// Converts a builder into an ordinary `cell`.
|
/// Converts a builder into an ordinary `cell`.
|
||||||
@pure
|
@pure
|
||||||
fun endCell(b: builder): cell
|
fun endCell(self: builder): cell
|
||||||
asm "ENDC";
|
asm "ENDC";
|
||||||
|
|
||||||
/// Stores a reference to a cell into a builder.
|
/// Stores a reference to a cell into a builder.
|
||||||
@pure
|
@pure
|
||||||
fun storeRef(b: builder, c: cell): builder
|
fun storeRef(mutate self: builder, c: cell): self
|
||||||
asm(c b) "STREF";
|
asm(c self) "STREF";
|
||||||
|
|
||||||
/// Stores a signed [len]-bit integer into a builder (`0 ≤ len ≤ 257`).
|
/// Stores a signed [len]-bit integer into a builder (`0 ≤ len ≤ 257`).
|
||||||
@pure
|
@pure
|
||||||
fun storeInt(b: builder, x: int, len: int): builder
|
fun storeInt(mutate self: builder, x: int, len: int): self
|
||||||
builtin;
|
builtin;
|
||||||
|
|
||||||
/// Stores an unsigned [len]-bit integer into a builder (`0 ≤ len ≤ 256`).
|
/// Stores an unsigned [len]-bit integer into a builder (`0 ≤ len ≤ 256`).
|
||||||
@pure
|
@pure
|
||||||
fun storeUint(b: builder, x: int, len: int): builder
|
fun storeUint(mutate self: builder, x: int, len: int): self
|
||||||
builtin;
|
builtin;
|
||||||
|
|
||||||
/// Stores a slice into a builder.
|
/// Stores a slice into a builder.
|
||||||
@pure
|
@pure
|
||||||
fun storeSlice(b: builder, s: slice): builder
|
fun storeSlice(mutate self: builder, s: slice): self
|
||||||
asm "STSLICER";
|
asm "STSLICER";
|
||||||
|
|
||||||
/// Stores amount of Toncoins into a builder.
|
/// Stores amount of Toncoins into a builder.
|
||||||
@pure
|
@pure
|
||||||
fun storeCoins(b: builder, x: int): builder
|
fun storeCoins(mutate self: builder, x: int): self
|
||||||
asm "STGRAMS";
|
asm "STGRAMS";
|
||||||
|
|
||||||
/// Stores bool (-1 or 0) into a builder.
|
/// 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.
|
/// Attention: true value is `-1`, not 1! If you pass `1` here, TVM will throw an exception.
|
||||||
@pure
|
@pure
|
||||||
fun storeBool(b: builder, x: int): builder
|
fun storeBool(mutate self: builder, x: int): self
|
||||||
asm(x b) "1 STI";
|
asm(x self) "1 STI";
|
||||||
|
|
||||||
/// Stores dictionary (represented by TVM `cell` or `null`) into a builder.
|
/// 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.
|
/// In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise.
|
||||||
@pure
|
@pure
|
||||||
fun storeDict(b: builder, c: cell): builder
|
fun storeDict(mutate self: builder, c: cell): self
|
||||||
asm(c b) "STDICT";
|
asm(c self) "STDICT";
|
||||||
|
|
||||||
/// Stores (Maybe ^Cell) into a builder.
|
/// Stores (Maybe ^Cell) into a builder.
|
||||||
/// In other words, if cell is `null`, store '0' bit; otherwise, store '1' and a ref to [c].
|
/// In other words, if cell is `null`, store '0' bit; otherwise, store '1' and a ref to [c].
|
||||||
@pure
|
@pure
|
||||||
fun storeMaybeRef(b: builder, c: cell): builder
|
fun storeMaybeRef(mutate self: builder, c: cell): self
|
||||||
asm(c b) "STOPTREF";
|
asm(c self) "STOPTREF";
|
||||||
|
|
||||||
/// Concatenates two builders.
|
/// Concatenates two builders.
|
||||||
@pure
|
@pure
|
||||||
fun storeBuilder(to: builder, from: builder): builder
|
fun storeBuilder(mutate self: builder, from: builder): self
|
||||||
asm "STBR";
|
asm "STBR";
|
||||||
|
|
||||||
|
/// Stores a slice representing TL addr_none$00 (two `0` bits).
|
||||||
@pure
|
@pure
|
||||||
fun storeAddressNone(b: builder): builder
|
fun storeAddressNone(mutate self: builder): self
|
||||||
asm "0 PUSHINT" "SWAP" "2 STU";
|
|
||||||
@pure
|
|
||||||
fun ~storeAddressNone(b: builder): (builder, ())
|
|
||||||
asm "b{00} STSLICECONST";
|
asm "b{00} STSLICECONST";
|
||||||
|
|
||||||
|
|
||||||
|
@ -529,47 +514,47 @@ fun ~storeAddressNone(b: builder): (builder, ())
|
||||||
|
|
||||||
/// Returns the number of references in a slice.
|
/// Returns the number of references in a slice.
|
||||||
@pure
|
@pure
|
||||||
fun getRemainingRefsCount(s: slice): int
|
fun getRemainingRefsCount(self: slice): int
|
||||||
asm "SREFS";
|
asm "SREFS";
|
||||||
|
|
||||||
/// Returns the number of data bits in a slice.
|
/// Returns the number of data bits in a slice.
|
||||||
@pure
|
@pure
|
||||||
fun getRemainingBitsCount(s: slice): int
|
fun getRemainingBitsCount(self: slice): int
|
||||||
asm "SBITS";
|
asm "SBITS";
|
||||||
|
|
||||||
/// Returns both the number of data bits and the number of references in a slice.
|
/// Returns both the number of data bits and the number of references in a slice.
|
||||||
@pure
|
@pure
|
||||||
fun getRemainingBitsAndRefsCount(s: slice): (int, int)
|
fun getRemainingBitsAndRefsCount(self: slice): (int, int)
|
||||||
asm "SBITREFS";
|
asm "SBITREFS";
|
||||||
|
|
||||||
/// Checks whether a slice is empty (i.e., contains no bits of data and no cell references).
|
/// Checks whether a slice is empty (i.e., contains no bits of data and no cell references).
|
||||||
@pure
|
@pure
|
||||||
fun isEndOfSlice(s: slice): int
|
fun isEndOfSlice(self: slice): int
|
||||||
asm "SEMPTY";
|
asm "SEMPTY";
|
||||||
|
|
||||||
/// Checks whether a slice has no bits of data.
|
/// Checks whether a slice has no bits of data.
|
||||||
@pure
|
@pure
|
||||||
fun isEndOfSliceBits(s: slice): int
|
fun isEndOfSliceBits(self: slice): int
|
||||||
asm "SDEMPTY";
|
asm "SDEMPTY";
|
||||||
|
|
||||||
/// Checks whether a slice has no references.
|
/// Checks whether a slice has no references.
|
||||||
@pure
|
@pure
|
||||||
fun isEndOfSliceRefs(s: slice): int
|
fun isEndOfSliceRefs(self: slice): int
|
||||||
asm "SREMPTY";
|
asm "SREMPTY";
|
||||||
|
|
||||||
/// Checks whether data parts of two slices coinside.
|
/// Checks whether data parts of two slices coinside.
|
||||||
@pure
|
@pure
|
||||||
fun isSliceBitsEqual(a: slice, b: slice): int
|
fun isSliceBitsEqual(self: slice, b: slice): int
|
||||||
asm "SDEQ";
|
asm "SDEQ";
|
||||||
|
|
||||||
/// Returns the number of cell references already stored in a builder.
|
/// Returns the number of cell references already stored in a builder.
|
||||||
@pure
|
@pure
|
||||||
fun getBuilderRefsCount(b: builder): int
|
fun getBuilderRefsCount(self: builder): int
|
||||||
asm "BREFS";
|
asm "BREFS";
|
||||||
|
|
||||||
/// Returns the number of data bits already stored in a builder.
|
/// Returns the number of data bits already stored in a builder.
|
||||||
@pure
|
@pure
|
||||||
fun getBuilderBitsCount(b: builder): int
|
fun getBuilderBitsCount(self: builder): int
|
||||||
asm "BBITS";
|
asm "BBITS";
|
||||||
|
|
||||||
|
|
||||||
|
@ -613,8 +598,8 @@ fun getBuilderBitsCount(b: builder): int
|
||||||
/// Loads from slice [s] the only prefix that is a valid `MsgAddress`,
|
/// 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.
|
/// and returns both this prefix `s'` and the remainder `s''` of [s] as slices.
|
||||||
@pure
|
@pure
|
||||||
fun loadAddress(s: slice): (slice, slice)
|
fun loadAddress(mutate self: slice): slice
|
||||||
asm( -> 1 0) "LDMSGADDR"; // todo make mutating
|
asm( -> 1 0) "LDMSGADDR";
|
||||||
|
|
||||||
/// Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`.
|
/// 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.
|
/// 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).
|
/// Load msgFlags from incoming message body (4 bits).
|
||||||
@pure
|
@pure
|
||||||
fun loadMessageFlags(s: slice): (slice, int)
|
fun loadMessageFlags(mutate self: slice): int
|
||||||
asm( -> 1 0) "4 LDU";
|
asm( -> 1 0) "4 LDU";
|
||||||
|
|
||||||
/// Having msgFlags (4 bits), check that a message is bounced.
|
/// 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).
|
/// Skip 0xFFFFFFFF prefix (when a message is bounced).
|
||||||
@pure
|
@pure
|
||||||
fun ~skipBouncedPrefix(s: slice): (slice, ())
|
fun skipBouncedPrefix(mutate self: slice): self
|
||||||
asm "32 PUSHINT" "SDSKIPFIRST";
|
asm "32 PUSHINT" "SDSKIPFIRST";
|
||||||
|
|
||||||
/// The guideline recommends to start the body of an internal message with uint32 `op` and uint64 `queryId`.
|
/// The guideline recommends to start the body of an internal message with uint32 `op` and uint64 `queryId`.
|
||||||
@pure
|
@pure
|
||||||
fun loadMessageOp(s: slice): (slice, int)
|
fun loadMessageOp(mutate self: slice): int
|
||||||
asm( -> 1 0) "32 LDU";
|
asm( -> 1 0) "32 LDU";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~skipMessageOp(s: slice): (slice, ())
|
fun skipMessageOp(mutate self: slice): self
|
||||||
asm "32 PUSHINT" "SDSKIPFIRST";
|
asm "32 PUSHINT" "SDSKIPFIRST";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun storeMessageOp(b: builder, op: int): builder
|
fun storeMessageOp(mutate self: builder, op: int): self
|
||||||
asm(op b) "32 STU";
|
asm(op self) "32 STU";
|
||||||
fun ~storeMessageOp(b: builder, op: int): (builder, ())
|
|
||||||
asm(op b) "32 STU";
|
|
||||||
|
|
||||||
/// The guideline recommends that uint64 `queryId` should follow uint32 `op`.
|
/// The guideline recommends that uint64 `queryId` should follow uint32 `op`.
|
||||||
@pure
|
@pure
|
||||||
fun loadMessageQueryId(s: slice): (slice, int)
|
fun loadMessageQueryId(mutate self: slice): int
|
||||||
asm( -> 1 0) "64 LDU";
|
asm( -> 1 0) "64 LDU";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~skipMessageQueryId(s: slice): (slice, ())
|
fun skipMessageQueryId(mutate self: slice): self
|
||||||
asm "64 PUSHINT" "SDSKIPFIRST";
|
asm "64 PUSHINT" "SDSKIPFIRST";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun storeMessageQueryId(b: builder, queryId: int): builder
|
fun storeMessageQueryId(mutate self: builder, queryId: int): self
|
||||||
asm(queryId b) "64 STU";
|
asm(queryId self) "64 STU";
|
||||||
fun ~storeMessageQueryId(b: builder, queryId: int): (builder, ())
|
|
||||||
asm(queryId b) "64 STU";
|
|
||||||
|
|
||||||
/// SEND MODES - https://docs.ton.org/tvm.pdf page 137, SENDRAWMSG
|
/// SEND MODES - https://docs.ton.org/tvm.pdf page 137, SENDRAWMSG
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ fun listSplit<X>(list: tuple): (X, tuple)
|
||||||
|
|
||||||
/// Extracts the tail and the head of lisp-style list.
|
/// Extracts the tail and the head of lisp-style list.
|
||||||
@pure
|
@pure
|
||||||
fun ~listNext<X>(list: tuple): (tuple, X)
|
fun listNext<X>(mutate self: tuple): X
|
||||||
asm( -> 1 0) "UNCONS";
|
asm( -> 1 0) "UNCONS";
|
||||||
|
|
||||||
/// Returns the head of lisp-style list.
|
/// Returns the head of lisp-style list.
|
||||||
|
|
|
@ -19,415 +19,279 @@ fun createEmptyDict(): cell
|
||||||
|
|
||||||
/// Checks whether a dictionary is empty.
|
/// Checks whether a dictionary is empty.
|
||||||
@pure
|
@pure
|
||||||
fun dictIsEmpty(c: cell): int
|
fun dictIsEmpty(self: cell): int
|
||||||
asm "DICTEMPTY";
|
asm "DICTEMPTY";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictGet(dict: cell, keyLen: int, key: int): (slice, int)
|
fun iDictGet(self: cell, keyLen: int, key: int): (slice, int)
|
||||||
asm(key dict keyLen) "DICTIGET" "NULLSWAPIFNOT";
|
asm(key self keyLen) "DICTIGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictGet(dict: cell, keyLen: int, key: int): (slice, int)
|
fun uDictGet(self: cell, keyLen: int, key: int): (slice, int)
|
||||||
asm(key dict keyLen) "DICTUGET" "NULLSWAPIFNOT";
|
asm(key self keyLen) "DICTUGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun sDictGet(dict: cell, keyLen: int, key: slice): (slice, int)
|
fun sDictGet(self: cell, keyLen: int, key: slice): (slice, int)
|
||||||
asm(key dict keyLen) "DICTGET" "NULLSWAPIFNOT";
|
asm(key self keyLen) "DICTGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictSet(dict: cell, keyLen: int, key: int, value: slice): cell
|
fun iDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
|
||||||
asm(value key dict keyLen) "DICTISET";
|
asm(value key self keyLen) "DICTISET";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictSet(dict: cell, keyLen: int, key: int, value: slice): (cell, ())
|
fun uDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
|
||||||
asm(value key dict keyLen) "DICTISET";
|
asm(value key self keyLen) "DICTUSET";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictSet(dict: cell, keyLen: int, key: int, value: slice): cell
|
fun sDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): void
|
||||||
asm(value key dict keyLen) "DICTUSET";
|
asm(value key self keyLen) "DICTSET";
|
||||||
|
|
||||||
@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";
|
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
|
fun iDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
|
||||||
asm(value key dict keyLen) "DICTIREPLACE";
|
asm(value key self keyLen) "DICTISETREF";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
|
fun uDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
|
||||||
asm(value key dict keyLen) "DICTIREPLACE";
|
asm(value key self keyLen) "DICTUSETREF";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
|
fun sDictSetRef(mutate self: cell, keyLen: int, key: slice, value: cell): void
|
||||||
asm(value key dict keyLen) "DICTUREPLACE";
|
asm(value key self keyLen) "DICTSETREF";
|
||||||
|
|
||||||
@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";
|
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
|
fun iDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int
|
||||||
asm(key dict keyLen) "DICTIDEL";
|
asm(value key self keyLen) "DICTIADD";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
|
fun uDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int
|
||||||
asm(key dict keyLen) "DICTIDEL";
|
asm(value key self keyLen) "DICTUADD";
|
||||||
|
|
||||||
@pure
|
|
||||||
fun uDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
|
|
||||||
asm(key dict keyLen) "DICTUDEL";
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~uDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
|
fun iDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int
|
||||||
asm(key dict keyLen) "DICTUDEL";
|
asm(value key self keyLen) "DICTIREPLACE";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun sDictDelete(dict: cell, keyLen: int, key: slice): (cell, int)
|
fun uDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int
|
||||||
asm(key dict keyLen) "DICTDEL";
|
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
|
@pure
|
||||||
fun ~iDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, (slice, int))
|
fun iDictGetRef(self: cell, keyLen: int, key: int): (cell, int)
|
||||||
asm(value key dict keyLen) "DICTISETGET" "NULLSWAPIFNOT";
|
asm(key self keyLen) "DICTIGETREF" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, slice, int)
|
fun uDictGetRef(self: cell, keyLen: int, key: int): (cell, int)
|
||||||
asm(value key dict keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
|
asm(key self keyLen) "DICTUGETREF" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~uDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, (slice, int))
|
fun sDictGetRef(self: cell, keyLen: int, key: slice): (cell, int)
|
||||||
asm(value key dict keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
|
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
|
@pure
|
||||||
fun ~sDictSetAndGet(dict: cell, keyLen: int, key: slice, value: slice): (cell, (slice, int))
|
fun iDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
|
||||||
asm(value key dict keyLen) "DICTSETGET" "NULLSWAPIFNOT";
|
asm(key self keyLen) "DICTIGETOPTREF";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
|
fun uDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
|
||||||
asm(value key dict keyLen) "DICTISETGETOPTREF";
|
asm(key self keyLen) "DICTUGETOPTREF";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
|
fun sDictGetRefOrNull(self: cell, keyLen: int, key: slice): cell
|
||||||
asm(value key dict keyLen) "DICTISETGETOPTREF";
|
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
|
@pure
|
||||||
fun ~uDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
|
fun iDictDelete(mutate self: cell, keyLen: int, key: int): int
|
||||||
asm(value key dict keyLen) "DICTUSETGETOPTREF";
|
asm(key self keyLen) "DICTIDEL";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, slice, int)
|
fun uDictDelete(mutate self: cell, keyLen: int, key: int): int
|
||||||
asm(key dict keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
|
asm(key self keyLen) "DICTUDEL";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, (slice, int))
|
fun sDictDelete(mutate self: cell, keyLen: int, key: slice): int
|
||||||
asm(key dict keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
|
asm(key self keyLen) "DICTDEL";
|
||||||
|
|
||||||
@pure
|
|
||||||
fun uDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, slice, int)
|
|
||||||
asm(key dict keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~uDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, (slice, int))
|
fun iDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int)
|
||||||
asm(key dict keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
|
asm(value key self keyLen) "DICTISETGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun sDictDeleteAndGet(dict: cell, keyLen: int, key: slice): (cell, slice, int)
|
fun uDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int)
|
||||||
asm(key dict keyLen) "DICTDELGET" "NULLSWAPIFNOT";
|
asm(value key self keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~sDictDeleteAndGet(dict: cell, keyLen: int, key: slice): (cell, (slice, int))
|
fun sDictSetAndGet(mutate self: cell, keyLen: int, key: slice, value: slice): (slice, int)
|
||||||
asm(key dict keyLen) "DICTDELGET" "NULLSWAPIFNOT";
|
asm(value key self keyLen) "DICTSETGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): cell
|
fun iDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
|
||||||
asm(value key dict keyLen) "DICTISETB";
|
asm(value key self keyLen) "DICTISETGETOPTREF";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): (cell, ())
|
fun uDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
|
||||||
asm(value key dict keyLen) "DICTISETB";
|
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
|
@pure
|
||||||
fun ~uDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): (cell, ())
|
fun iDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int)
|
||||||
asm(value key dict keyLen) "DICTUSETB";
|
asm(key self keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun sDictSetBuilder(dict: cell, keyLen: int, key: slice, value: builder): cell
|
fun uDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int)
|
||||||
asm(value key dict keyLen) "DICTSETB";
|
asm(key self keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~sDictSetBuilder(dict: cell, keyLen: int, key: slice, value: builder): (cell, ())
|
fun sDictDeleteAndGet(mutate self: cell, keyLen: int, key: slice): (slice, int)
|
||||||
asm(value key dict keyLen) "DICTSETB";
|
asm(key self keyLen) "DICTDELGET" "NULLSWAPIFNOT";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
|
fun iDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
|
||||||
asm(value key dict keyLen) "DICTIADDB";
|
asm(value key self keyLen) "DICTISETB";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
|
fun uDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
|
||||||
asm(value key dict keyLen) "DICTIADDB";
|
asm(value key self keyLen) "DICTUSETB";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
|
fun sDictSetBuilder(mutate self: cell, keyLen: int, key: slice, value: builder): void
|
||||||
asm(value key dict keyLen) "DICTUADDB";
|
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
|
@pure
|
||||||
fun iDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
|
fun iDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int
|
||||||
asm(value key dict keyLen) "DICTIREPLACEB";
|
asm(value key self keyLen) "DICTIADDB";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
|
fun uDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int
|
||||||
asm(value key dict keyLen) "DICTIREPLACEB";
|
asm(value key self keyLen) "DICTUADDB";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
|
fun iDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int
|
||||||
asm(value key dict keyLen) "DICTUREPLACEB";
|
asm(value key self keyLen) "DICTIREPLACEB";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~uDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
|
fun uDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int
|
||||||
asm(value key dict keyLen) "DICTUREPLACEB";
|
asm(value key self keyLen) "DICTUREPLACEB";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@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";
|
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
|
fun uDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int)
|
||||||
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
|
|
||||||
|
|
||||||
@pure
|
|
||||||
fun uDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
|
|
||||||
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
|
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~uDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
|
fun sDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (slice, 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))
|
|
||||||
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
|
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@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";
|
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~iDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
|
fun uDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int)
|
||||||
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
|
|
||||||
|
|
||||||
@pure
|
|
||||||
fun uDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
|
|
||||||
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
|
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~uDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
|
fun sDictDeleteLastAndGet(mutate self: cell, keyLen: int): (slice, 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))
|
|
||||||
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
|
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@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";
|
asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictGetNext(dict: cell, keyLen: int, pivot: int): (int, slice, int)
|
fun iDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||||
asm(pivot dict keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
|
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictGetNext(dict: cell, keyLen: int, pivot: int): (int, slice, int)
|
fun uDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||||
asm(pivot dict keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
|
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictGetNextOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
|
fun iDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||||
asm(pivot dict keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
|
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictGetNextOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
|
fun uDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||||
asm(pivot dict keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
|
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictGetPrev(dict: cell, keyLen: int, pivot: int): (int, slice, int)
|
fun iDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||||
asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
|
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictGetPrev(dict: cell, keyLen: int, pivot: int): (int, slice, int)
|
fun uDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||||
asm(pivot dict keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
|
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun iDictGetPrevOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
|
fun iDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||||
asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
|
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun uDictGetPrevOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
|
fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int)
|
||||||
asm(pivot dict keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
|
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
|
@pure
|
||||||
fun prefixDictGet(dict: cell, keyLen: int, key: slice): (slice, slice, slice, int)
|
fun prefixDictGet(self: cell, keyLen: int, key: slice): (slice, slice, slice, int)
|
||||||
asm(key dict keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2";
|
asm(key self keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun prefixDictSet(dict: cell, keyLen: int, key: slice, value: slice): (cell, int)
|
fun prefixDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): int
|
||||||
asm(value key dict keyLen) "PFXDICTSET";
|
asm(value key self keyLen) "PFXDICTSET";
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun prefixDictDelete(dict: cell, keyLen: int, key: slice): (cell, int)
|
fun prefixDictDelete(mutate self: cell, keyLen: int, key: slice): int
|
||||||
asm(key dict keyLen) "PFXDICTDEL";
|
asm(key self keyLen) "PFXDICTDEL";
|
||||||
|
|
|
@ -21,9 +21,5 @@ fun transformSliceToContinuation(s: slice): continuation
|
||||||
|
|
||||||
/// Moves a variable or a value [x] to the top of the stack.
|
/// Moves a variable or a value [x] to the top of the stack.
|
||||||
@pure
|
@pure
|
||||||
fun stackMoveToTop<X>(x: X): X
|
fun stackMoveToTop<X>(mutate self: X): void
|
||||||
asm "NOP";
|
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";
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import "@stdlib/tvm-lowlevel"
|
||||||
|
|
||||||
fun pair_first<X, Y>(p: [X, Y]): X asm "FIRST";
|
fun pair_first<X, Y>(p: [X, Y]): X asm "FIRST";
|
||||||
|
|
||||||
fun one(dummy: tuple) {
|
fun one(dummy: tuple) {
|
||||||
|
@ -35,13 +37,34 @@ fun test88(x: int) {
|
||||||
@method_id(89)
|
@method_id(89)
|
||||||
fun test89(last: int) {
|
fun test89(last: int) {
|
||||||
var t: tuple = createEmptyTuple();
|
var t: tuple = createEmptyTuple();
|
||||||
t~tuplePush(1);
|
t.tuplePush(1);
|
||||||
t~tuplePush(2);
|
t.tuplePush(2);
|
||||||
t~tuplePush(3);
|
t.tuplePush(3);
|
||||||
t~tuplePush(last);
|
t.tuplePush(last);
|
||||||
return (t.tupleAt(0), t.tupleAt(t.tupleSize() - 1), t.tupleFirst(), t.tupleLast());
|
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)
|
@method_id(93)
|
||||||
fun testStartBalanceCodegen1() {
|
fun testStartBalanceCodegen1() {
|
||||||
var t = getMyOriginalBalanceWithExtraCurrencies();
|
var t = getMyOriginalBalanceWithExtraCurrencies();
|
||||||
|
@ -65,7 +88,27 @@ fun testStartBalanceCodegen2() {
|
||||||
@testcase | 88 | 5 | 234
|
@testcase | 88 | 5 | 234
|
||||||
@testcase | 88 | 50 | 0
|
@testcase | 88 | 50 | 0
|
||||||
@testcase | 89 | 4 | 1 4 1 4
|
@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
|
@fif_codegen
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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);
|
return (Dx/D,Dy/D);
|
||||||
};;;;
|
};;;;
|
||||||
|
|
||||||
fun mulDivR(x: int, y: int, z: int): int { return mulDivRound(x, y, z); }
|
|
||||||
|
|
||||||
fun calc_phi(): int {
|
fun calc_phi(): int {
|
||||||
var n = 1;
|
var n = 1;
|
||||||
repeat (70) { n*=10; };
|
repeat (70) { n*=10; };
|
||||||
|
@ -14,7 +12,7 @@ fun calc_phi(): int {
|
||||||
do {
|
do {
|
||||||
(p,q)=(q,p+q);
|
(p,q)=(q,p+q);
|
||||||
} while (q <= n); //;;
|
} while (q <= n); //;;
|
||||||
return mulDivR(p, n, q);
|
return mulDivRound(p, n, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calc_sqrt2(): int {
|
fun calc_sqrt2(): int {
|
||||||
|
@ -26,7 +24,7 @@ fun calc_sqrt2(): int {
|
||||||
var t = p + q;
|
var t = p + q;
|
||||||
(p, q) = (q, t + q);
|
(p, q) = (q, t + q);
|
||||||
} while (q <= n);
|
} while (q <= n);
|
||||||
return mulDivR(p, n, q);
|
return mulDivRound(p, n, q);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calc_root(m: auto): auto {
|
fun calc_root(m: auto): auto {
|
||||||
|
@ -63,18 +61,14 @@ fun ataninv(base: int, q: int): int { // computes base*atan(1/q)
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun arctanInv(base: int, q: int): int { return ataninv(base, q); }
|
|
||||||
|
|
||||||
fun calc_pi(): int {
|
fun calc_pi(): int {
|
||||||
var base: int = 64;
|
var base: int = 64;
|
||||||
repeat (70) { base *= 10; }
|
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 {
|
fun main(): int {
|
||||||
return calcPi();
|
return calc_pi();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
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
|
method_id | in | out
|
||||||
@testcase | 0 | 1 1 1 -1 10 6 | 8 2
|
@testcase | 0 | 1 1 1 -1 10 6 | 8 2
|
||||||
@testcase | 0 | 817 -31 624 -241 132272 272276 | 132 -788
|
@testcase | 0 | 817 -31 624 -241 132272 272276 | 132 -788
|
||||||
@testcase | 0 | -886 562 498 -212 -36452 -68958 | -505 -861
|
@testcase | 0 | -886 562 498 -212 -36452 -68958 | -505 -861
|
||||||
@testcase | 0 | 448 -433 -444 792 150012 -356232 | -218 -572
|
@testcase | 0 | 448 -433 -444 792 150012 -356232 | -218 -572
|
||||||
@testcase | 0 | -40 -821 433 -734 -721629 -741724 | -206 889
|
@testcase | 0 | -40 -821 433 -734 -721629 -741724 | -206 889
|
||||||
@testcase | 0 | -261 -98 -494 868 -166153 733738 | 263 995
|
@testcase | 0 | -261 -98 -494 868 -166153 733738 | 263 995
|
||||||
|
@testcase | 101 | 112 3 | [ 37 1 1 37 33 6 ]
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,35 +4,32 @@ fun unsafe_tuple<X>(x: X): tuple
|
||||||
fun inc(x: int, y: int): (int, int) {
|
fun inc(x: int, y: int): (int, int) {
|
||||||
return (x + y, y * 10);
|
return (x + y, y * 10);
|
||||||
}
|
}
|
||||||
fun ~inc(x: int, y: int): (int, int) {
|
fun `~inc`(mutate self: int, y: int): int {
|
||||||
(x, y) = inc(x, y);
|
val (newX, newY) = inc(self, y);
|
||||||
return (x, y);
|
self = newX;
|
||||||
}
|
return newY;
|
||||||
|
|
||||||
fun ~incWrap(x: int, y: int): (int, int) {
|
|
||||||
return ~inc(x, y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(11)
|
@method_id(11)
|
||||||
fun test_return(x: int): (int, int, int, int, int, int, int) {
|
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)
|
@method_id(12)
|
||||||
fun test_assign(x: int): (int, int, int, int, int, int, int) {
|
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);
|
return (x1, x2, x3, x4, x5, x6, x7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(13)
|
@method_id(13)
|
||||||
fun test_tuple(x: int): tuple {
|
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;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(14)
|
@method_id(14)
|
||||||
fun test_tuple_assign(x: int): (int, int, int, int, int, int, int) {
|
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);
|
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)
|
@method_id(15)
|
||||||
fun test_call_1(x: int): (int, int, int, int, int, int, int) {
|
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) {
|
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)
|
@method_id(16)
|
||||||
fun test_call_2(x: int): (int, int, int, int, int, int, int) {
|
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)
|
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)
|
@method_id(17)
|
||||||
fun test_call_asm_old(x: int): (int, int, int, int, int, int, int) {
|
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)
|
@method_id(18)
|
||||||
fun test_call_asm_new(x: int): (int, int, int, int, int, int, int) {
|
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;
|
global xx: int;
|
||||||
@method_id(19)
|
@method_id(19)
|
||||||
fun test_global(x: int): (int, int, int, int, int, int, int) {
|
fun test_global(x: int): (int, int, int, int, int, int, int) {
|
||||||
xx = x;
|
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)
|
@method_id(20)
|
||||||
fun test_if_else(x: int): (int, int, int, int, int) {
|
fun test_if_else(x: int): (int, int, int, int, int) {
|
||||||
if (x > 10) {
|
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 {
|
} else {
|
||||||
xx = 9;
|
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 | 80 | 80 89 1 8 8
|
||||||
@testcase | 20 | 9 | 9 -40 -10 -1 13
|
@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
|
@code_hash 97139400653362069936987769894397430077752335662822462908581556703209313861576
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
fun empty_tuple2(): tuple
|
fun empty_tuple2(): tuple
|
||||||
asm "NIL";
|
asm "NIL";
|
||||||
@pure
|
@pure
|
||||||
fun tpush2<X>(t: tuple, x: X): (tuple, ())
|
fun tpush2<X>(mutate self: tuple, x: X): void
|
||||||
asm "TPUSH";
|
asm "TPUSH";
|
||||||
fun myEmptyTuple(): tuple { return empty_tuple2(); }
|
|
||||||
fun myTuplePush<X>(t: tuple, value: X): (tuple, ()) { return tpush2(t, value); }
|
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun asm_func_1(x: int, y: int, z: int): tuple
|
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
|
fun asm_func_4(a: int, b: (int, (int, int)), c: int): tuple
|
||||||
asm (b a c -> 0) "5 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
|
@pure
|
||||||
fun asm_func_modify(a: tuple, b: int, c: int): (tuple, ())
|
fun asm_func_modify(mutate self: tuple, b: int, c: int): void
|
||||||
asm (c b a -> 0) "SWAP TPUSH SWAP TPUSH";
|
asm (c b self) "SWAP TPUSH SWAP TPUSH";
|
||||||
fun asmFuncModify(a: tuple, b: int, c: int): (tuple, ()) { return asm_func_modify(a, b, c); }
|
|
||||||
|
|
||||||
global t: tuple;
|
global t: tuple;
|
||||||
|
|
||||||
fun foo(x: int): int {
|
fun foo(x: int): int {
|
||||||
t~myTuplePush(x);
|
t.tpush2(x);
|
||||||
return x * 10;
|
return x * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(11)
|
@method_id(11)
|
||||||
fun test_old_1(): (tuple, tuple) {
|
fun test_old_1(): (tuple, tuple) {
|
||||||
t = empty_tuple2();
|
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);
|
return (t, t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(12)
|
@method_id(12)
|
||||||
fun test_old_2(): (tuple, tuple) {
|
fun test_old_2(): (tuple, tuple) {
|
||||||
t = myEmptyTuple();
|
t = empty_tuple2();
|
||||||
var t2: tuple = asm_func_2(foo(11), foo(22), foo(33));
|
var t2: tuple = asm_func_2(foo(11), foo(22), foo(33));
|
||||||
return (t, t2);
|
return (t, t2);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +52,7 @@ fun test_old_3(): (tuple, tuple) {
|
||||||
|
|
||||||
@method_id(14)
|
@method_id(14)
|
||||||
fun test_old_4(): (tuple, tuple) {
|
fun test_old_4(): (tuple, tuple) {
|
||||||
t = myEmptyTuple();
|
t = empty_tuple2();
|
||||||
var t2: tuple = empty_tuple2();
|
var t2: tuple = empty_tuple2();
|
||||||
// This actually computes left-to-right even without compute-asm-ltr
|
// 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));
|
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) {
|
fun test_old_modify(): (tuple, tuple) {
|
||||||
t = empty_tuple2();
|
t = empty_tuple2();
|
||||||
var t2: tuple = empty_tuple2();
|
var t2: tuple = empty_tuple2();
|
||||||
t2~asmFuncModify(foo(22), foo(33));
|
t2.asm_func_modify(foo(22), foo(33));
|
||||||
return (t, t2);
|
return (t, t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(16)
|
@method_id(16)
|
||||||
fun test_old_dot(): (tuple, tuple) {
|
fun test_old_dot(): (tuple, tuple) {
|
||||||
t = empty_tuple2();
|
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);
|
return (t, t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(21)
|
@method_id(21)
|
||||||
fun test_new_1(): (tuple, tuple) {
|
fun test_new_1(): (tuple, tuple) {
|
||||||
t = empty_tuple2();
|
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);
|
return (t, t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +106,7 @@ fun test_new_4(): (tuple, tuple) {
|
||||||
fun test_new_modify(): (tuple, tuple) {
|
fun test_new_modify(): (tuple, tuple) {
|
||||||
t = empty_tuple2();
|
t = empty_tuple2();
|
||||||
var t2: tuple = 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);
|
return (t, t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
*/
|
|
|
@ -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
|
|
||||||
*/
|
|
|
@ -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
|
|
||||||
*/
|
|
|
@ -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
|
|
||||||
*/
|
|
|
@ -1,121 +1,136 @@
|
||||||
fun store_u32(b: builder, value: int): builder {
|
fun store_u32(mutate self: builder, value: int): self {
|
||||||
return b.storeUint(value, 32);
|
return self.storeUint(value, 32);
|
||||||
}
|
|
||||||
fun ~store_u32(b: builder, value: int): (builder, ()) {
|
|
||||||
return ~storeUint(b, value, 32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load_u32(cs: slice): (slice, int) {
|
fun load_u32(mutate self: slice): int {
|
||||||
return cs.loadUint(32);
|
return self.loadUint(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun my_loadInt(s: slice, len: int): (slice, int)
|
fun myLoadInt(mutate self: slice, len: int): int
|
||||||
asm(s len -> 1 0) "LDIX"; // top is "value slice"
|
asm(-> 1 0) "LDIX";
|
||||||
fun my_storeInt(b: builder, x: int, len: int): builder
|
fun myStoreInt(mutate self: builder, x: int, len: int): self
|
||||||
asm(x b len) "STIX";
|
asm(x self len) "STIX";
|
||||||
fun ~my_storeInt(b: builder, x: int, len: int): (builder, ())
|
|
||||||
asm(x b len) "STIX";
|
|
||||||
|
|
||||||
@method_id(101)
|
@method_id(101)
|
||||||
fun test1(): [int,int,int,int,int] {
|
fun test1(): [int,int,int,int,int] {
|
||||||
var b: builder = beginCell().storeUint(1, 32);
|
var b: builder = beginCell().storeUint(1, 32);
|
||||||
b = b.storeUint(2, 32);
|
b = b.storeUint(2, 32);
|
||||||
b~storeUint(3, 32);
|
b.storeUint(3, 32);
|
||||||
b = b.store_u32(4);
|
b = b.store_u32(4);
|
||||||
b~store_u32(5);
|
b.store_u32(5);
|
||||||
|
|
||||||
var cs: slice = b.endCell().beginParse();
|
var cs: slice = b.endCell().beginParse();
|
||||||
var (cs redef, one: int) = cs.loadUint(32);
|
var one: int = cs.loadUint(32);
|
||||||
var (two: int, three: int) = (cs~loadUint(32), cs~load_u32());
|
var (two: int, three: int) = (cs.loadUint(32), cs.load_u32());
|
||||||
var (cs redef, four: int) = cs.load_u32();
|
var four: int = cs.load_u32();
|
||||||
var five: int = cs~load_u32();
|
var five: int = cs.load_u32();
|
||||||
|
|
||||||
return [one,two,three,four,five];
|
return [one,two,three,four,five];
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(102)
|
@method_id(102)
|
||||||
fun test2(): [int,int,int] {
|
fun test2(): [int,int,int] {
|
||||||
var b: builder = beginCell().my_storeInt(1, 32);
|
var b: builder = beginCell().myStoreInt(1, 32);
|
||||||
b = b.my_storeInt(2, 32);
|
b = b.myStoreInt(2, 32);
|
||||||
b~my_storeInt(3, 32);
|
b.myStoreInt(3, 32);
|
||||||
|
|
||||||
var cs: slice = b.endCell().beginParse();
|
var cs: slice = b.endCell().beginParse();
|
||||||
var (cs redef, one: int) = cs.my_loadInt(32);
|
var one: int = cs.myLoadInt(32);
|
||||||
var (two: int, three: int) = (cs~my_loadInt(32), cs~my_loadInt(32));
|
var (two: int, three: int) = (cs.myLoadInt(32), cs.myLoadInt(32));
|
||||||
|
|
||||||
return [one,two,three];
|
return [one,two,three];
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(103)
|
@method_id(103)
|
||||||
fun test3(ret: int): int {
|
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;
|
return same;
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(104)
|
@method_id(104)
|
||||||
fun test4(): [int,int] {
|
fun test4(): [int,int] {
|
||||||
var b: builder = my_storeInt(beginCell(), 1, 32);
|
var b: builder = beginCell().myStoreInt(1, 32);
|
||||||
b = storeInt(storeInt(b, 2, 32), 3, 32);
|
b = b.storeInt(2, 32).storeInt(3, 32);
|
||||||
|
|
||||||
var cs: slice = b.endCell().beginParse();
|
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) = (cs.getFirstBits(32).loadUint(32), cs.skipBits(64), cs.load_u32());
|
||||||
var (one, _, three) = (cs32~loadInt(32), cs~skipBits(64), cs~load_u32());
|
|
||||||
|
|
||||||
return [one,three];
|
return [one,three];
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(105)
|
@method_id(105)
|
||||||
fun test5(): [int,int] {
|
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 c: cell = beginCell().storeRef(cref).storeRef(cref).store_u32(1).endCell();
|
||||||
|
|
||||||
var cs: slice = beginParse(c);
|
var cs: slice = beginParse(c);
|
||||||
// todo I want cs~loadRef().beginParse()~load_u32(), but 'lvalue expected'
|
var sto5x2: int = cs.loadRef().beginParse().load_u32() + cs.loadRef().beginParse().loadUint(32);
|
||||||
var ref1 = cs~loadRef().beginParse();
|
return [sto5x2, cs.load_u32()];
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(106)
|
@method_id(106)
|
||||||
fun test6() {
|
fun test6() {
|
||||||
var ref = beginCell().storeInt(100, 32).endCell();
|
return beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32);
|
||||||
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(107)
|
@method_id(107)
|
||||||
fun test7() {
|
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 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);
|
var size1 = getRemainingBitsCount(s);
|
||||||
s~skipBits(32);
|
s.skipBits(32);
|
||||||
var s1: slice = s.getFirstBits(64);
|
var s1: slice = s.getFirstBits(64);
|
||||||
var n1 = s1~loadInt(32);
|
var n1 = s1.loadInt(32);
|
||||||
var size2 = getRemainingBitsCount(s);
|
var size2 = getRemainingBitsCount(s);
|
||||||
s~loadInt(32);
|
s.loadInt(32);
|
||||||
var size3 = getRemainingBitsCount(s);
|
var size3 = getRemainingBitsCount(s);
|
||||||
s~removeLastBits(32);
|
s.removeLastBits(32);
|
||||||
var size4 = getRemainingBitsCount(s);
|
var size4 = getRemainingBitsCount(s);
|
||||||
var n2 = s~loadInt(32);
|
var n2 = s.loadInt(32);
|
||||||
var size5 = getRemainingBitsCount(s);
|
var size5 = getRemainingBitsCount(s);
|
||||||
return (n1, n2, size1, size2, size3, size4, size5);
|
return (n1, n2, size1, size2, size3, size4, size5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(108)
|
@method_id(112)
|
||||||
fun test108() {
|
fun test12() {
|
||||||
var (result1, result2) = (0, 0);
|
var (result1, result2) = (0, 0);
|
||||||
try {
|
try {
|
||||||
beginCell().storeRef(beginCell().endCell()).endCell().beginParse().assertEndOfSlice();
|
beginCell().storeRef(beginCell().endCell()).endCell().beginParse().assertEndOfSlice();
|
||||||
|
@ -132,45 +147,45 @@ fun test108() {
|
||||||
return (result1, result2);
|
return (result1, result2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(109)
|
@method_id(113)
|
||||||
fun test109() {
|
fun test13() {
|
||||||
var ref2 = beginCell().storeInt(1, 32).endCell();
|
var ref2 = beginCell().storeInt(1, 32).endCell();
|
||||||
var ref1 = beginCell().storeInt(1, 32).storeRef(ref2).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 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 (n_cells1, n_bits1, n_refs1) = c.calculateCellSizeStrict(10);
|
||||||
var s = c.beginParse();
|
var s = c.beginParse();
|
||||||
s~loadRef();
|
s.loadRef();
|
||||||
s~loadRef();
|
s.loadRef();
|
||||||
var n = s~loadInt(32);
|
var n = s.loadInt(32);
|
||||||
var (n_cells2, n_bits2, n_refs2) = s.calculateSliceSizeStrict(10);
|
var (n_cells2, n_bits2, n_refs2) = s.calculateSliceSizeStrict(10);
|
||||||
return ([n_cells1, n_bits1, n_refs1], [n_cells2, n_bits2, n_refs2], n);
|
return ([n_cells1, n_bits1, n_refs1], [n_cells2, n_bits2, n_refs2], n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(110)
|
@method_id(114)
|
||||||
fun test110(x: int) {
|
fun test110(x: int) {
|
||||||
var s = beginCell().storeBool(x < 0).storeBool(0).storeBool(x).endCell().beginParse();
|
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() {
|
fun test111() {
|
||||||
var s = beginCell().storeMessageOp(123).storeMessageQueryId(456)
|
var s = beginCell().storeMessageOp(123).storeMessageQueryId(456)
|
||||||
.storeAddressNone().storeAddressNone()
|
.storeAddressNone().storeAddressNone()
|
||||||
.storeUint(0, 32)
|
.storeUint(0, 32)
|
||||||
.storeUint(123, 32).storeUint(456, 64).storeUint(789, 64)
|
.storeUint(123, 32).storeUint(456, 64).storeUint(789, 64)
|
||||||
.endCell().beginParse();
|
.endCell().beginParse();
|
||||||
var op1 = s~loadUint(32);
|
var op1 = s.loadUint(32);
|
||||||
var q1 = s~loadUint(64);
|
var q1 = s.loadUint(64);
|
||||||
if (s.addressIsNone()) {
|
if (s.addressIsNone()) {
|
||||||
s~skipBits(2);
|
s.skipBits(2);
|
||||||
}
|
}
|
||||||
if (s~loadBool() == 0) {
|
if (s.loadBool() == 0) {
|
||||||
assert(s~loadBool() == 0) throw 444;
|
assert(s.loadBool() == 0) throw 444;
|
||||||
s~skipBits(32);
|
s.skipBouncedPrefix();
|
||||||
}
|
}
|
||||||
var op2 = s~loadMessageOp();
|
var op2 = s.loadMessageOp();
|
||||||
var q2 = s~loadMessageQueryId();
|
var q2 = s.loadMessageQueryId();
|
||||||
s~skipBits(64);
|
s.skipBits(64);
|
||||||
s.assertEndOfSlice();
|
s.assertEndOfSlice();
|
||||||
assert(isMessageBounced(0x001)) throw 444;
|
assert(isMessageBounced(0x001)) throw 444;
|
||||||
return (op1, q1, op2, q2);
|
return (op1, q1, op2, q2);
|
||||||
|
@ -186,11 +201,31 @@ fun main(): int {
|
||||||
@testcase | 103 | 103 | 103
|
@testcase | 103 | 103 | 103
|
||||||
@testcase | 104 | | [ 1 3 ]
|
@testcase | 104 | | [ 1 3 ]
|
||||||
@testcase | 105 | | [ 210 1 ]
|
@testcase | 105 | | [ 210 1 ]
|
||||||
@testcase | 106 | | 64 3 0 0 -1 0 100 -1
|
@testcase | 107 | | 72 40 72
|
||||||
@testcase | 107 | | 2 3 224 192 160 128 96
|
@testcase | 108 | | 0 40 32
|
||||||
@testcase | 108 | | 9 100
|
@testcase | 110 | | 64 3 0 0 -1 0 100 -1
|
||||||
@testcase | 109 | | [ 3 128 5 ] [ 2 96 3 ] 444
|
@testcase | 111 | | 2 3 224 192 160 128 96
|
||||||
@testcase | 110 | -1 | -1 0 -1
|
@testcase | 112 | | 9 100
|
||||||
@testcase | 110 | 0 | 0 0 0
|
@testcase | 113 | | [ 3 128 5 ] [ 2 96 3 ] 444
|
||||||
@testcase | 111 | | 123 456 123 456
|
@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
|
||||||
|
}>
|
||||||
|
"""
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -43,9 +43,6 @@ asm "SDEQ";
|
||||||
fun stslicer(b: builder, s: slice): builder
|
fun stslicer(b: builder, s: slice): builder
|
||||||
asm "STSLICER";
|
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() {
|
fun main() {
|
||||||
var i1: int = iget1();
|
var i1: int = iget1();
|
||||||
var i2: int = iget2();
|
var i2: int = iget2();
|
||||||
|
@ -59,8 +56,8 @@ fun main() {
|
||||||
var s2: slice = sget2();
|
var s2: slice = sget2();
|
||||||
var s3: slice = newc().stslicer(str1).stslicer(str2r).endcs();
|
var s3: slice = newc().stslicer(str1).stslicer(str2r).endcs();
|
||||||
|
|
||||||
assert(sdeq(s1, newc().myStoreUint(str1int, 12 * nibbles).endcs())) throw int111;
|
assert(sdeq(s1, newc().storeUint(str1int, 12 * nibbles).endcs())) throw int111;
|
||||||
assert(sdeq(s2, newc().storeUint(str2int, 6 * nibbles).endSlice())) throw 112;
|
assert(sdeq(s2, newc().storeUint(str2int, 6 * nibbles).endcs())) throw 112;
|
||||||
assert(sdeq(s3, newc().storeUint(0x636f6e737431AABBCC, 18 * nibbles).endcs())) throw 113;
|
assert(sdeq(s3, newc().storeUint(0x636f6e737431AABBCC, 18 * nibbles).endcs())) throw 113;
|
||||||
|
|
||||||
var i4: int = iget240();
|
var i4: int = iget240();
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import "@stdlib/tvm-dicts"
|
import "@stdlib/tvm-dicts"
|
||||||
|
|
||||||
fun ~addIntToIDict(iDict: cell, key: int, number: int): (cell, ()) {
|
fun addIntToIDict(mutate self: cell, key: int, number: int): void {
|
||||||
iDict~iDictSetBuilder(32, key, beginCell().storeInt(number, 32));
|
return self.iDictSetBuilder(32, key, beginCell().storeInt(number, 32));
|
||||||
return (iDict, ());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateDictLen(d: cell) {
|
fun calculateDictLen(d: cell) {
|
||||||
|
@ -15,40 +14,40 @@ fun calculateDictLen(d: cell) {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ~loadTwoDigitNumberFromSlice(s: slice): (slice, int) {
|
fun loadTwoDigitNumberFromSlice(mutate self: slice): int {
|
||||||
var n1 = s~loadInt(8);
|
var n1 = self.loadInt(8);
|
||||||
var n2 = s~loadInt(8);
|
var n2 = self.loadInt(8);
|
||||||
return (s, (n1 - 48) * 10 + (n2 - 48));
|
return (n1 - 48) * 10 + (n2 - 48);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@method_id(101)
|
@method_id(101)
|
||||||
fun test101(getK1: int, getK2: int, getK3: int) {
|
fun test101(getK1: int, getK2: int, getK3: int) {
|
||||||
var dict = createEmptyDict();
|
var dict = createEmptyDict();
|
||||||
dict~uDictSetBuilder(32, 1, beginCell().storeUint(1, 32));
|
dict.uDictSetBuilder(32, 1, beginCell().storeUint(1, 32));
|
||||||
var (old1: slice, found1) = dict~uDictSetAndGet(32, getK1, beginCell().storeUint(2, 32).endCell().beginParse());
|
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 (old2: slice, found2) = dict.uDictSetAndGet(32, getK2, beginCell().storeUint(3, 32).endCell().beginParse());
|
||||||
var (cur3: slice, found3) = dict.uDictGet(32, getK3);
|
var (cur3: slice, found3) = dict.uDictGet(32, getK3);
|
||||||
return (
|
return (
|
||||||
found1 ? old1~loadUint(32) : -1,
|
found1 ? old1.loadUint(32) : -1,
|
||||||
found2 ? old2~loadUint(32) : -1,
|
found2 ? old2.loadUint(32) : -1,
|
||||||
found3 ? cur3~loadUint(32) : -1
|
found3 ? cur3.loadUint(32) : -1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(102)
|
@method_id(102)
|
||||||
fun test102() {
|
fun test102() {
|
||||||
var dict = createEmptyDict();
|
var dict = createEmptyDict();
|
||||||
dict~addIntToIDict(2, 102);
|
dict.addIntToIDict(2, 102);
|
||||||
dict~addIntToIDict(1, 101);
|
dict.addIntToIDict(1, 101);
|
||||||
dict~addIntToIDict(4, 104);
|
dict.addIntToIDict(4, 104);
|
||||||
dict~addIntToIDict(3, 103);
|
dict.addIntToIDict(3, 103);
|
||||||
var deleted = createEmptyTuple();
|
var deleted = createEmptyTuple();
|
||||||
var shouldBreak = false;
|
var shouldBreak = false;
|
||||||
while (!shouldBreak) {
|
while (!shouldBreak) {
|
||||||
var (kDel, kVal, wasDel) = dict~iDictDeleteLastAndGet(32);
|
var (kDel, kVal, wasDel) = dict.iDictDeleteLastAndGet(32);
|
||||||
if (wasDel) {
|
if (wasDel) {
|
||||||
deleted~tuplePush([kDel, kVal~loadInt(32)]);
|
deleted.tuplePush([kDel, kVal.loadInt(32)]);
|
||||||
} else {
|
} else {
|
||||||
shouldBreak = true;
|
shouldBreak = true;
|
||||||
}
|
}
|
||||||
|
@ -59,38 +58,38 @@ fun test102() {
|
||||||
@method_id(103)
|
@method_id(103)
|
||||||
fun test103() {
|
fun test103() {
|
||||||
var dict = createEmptyDict();
|
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);
|
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);
|
var len2 = calculateDictLen(dict);
|
||||||
dict~uDictSetBuilder(32, 3,beginCell().storeInt(1, 32));
|
dict.uDictSetBuilder(32, 3,beginCell().storeInt(1, 32));
|
||||||
dict~uDictSetBuilderIfExists(32, 3,beginCell().storeInt(1, 32));
|
dict.uDictSetBuilderIfExists(32, 3,beginCell().storeInt(1, 32));
|
||||||
var len3 = calculateDictLen(dict);
|
var len3 = calculateDictLen(dict);
|
||||||
var (delK1, _, _) = dict~uDictDeleteFirstAndGet(32);
|
var (delK1, _, _) = dict.uDictDeleteFirstAndGet(32);
|
||||||
var (delK2, _, _) = dict~uDictDeleteFirstAndGet(32);
|
var (delK2, _, _) = dict.uDictDeleteFirstAndGet(32);
|
||||||
var (delK3, _, _) = dict~uDictDeleteFirstAndGet(32);
|
var (delK3, _, _) = dict.uDictDeleteFirstAndGet(32);
|
||||||
return (len1, len2, len3, delK1, delK2, delK3);
|
return (len1, len2, len3, delK1, delK2, delK3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(104)
|
@method_id(104)
|
||||||
fun test104() {
|
fun test104() {
|
||||||
var dict = createEmptyDict();
|
var dict = createEmptyDict();
|
||||||
dict~sDictSetBuilder(32, "7800", beginCell().storeUint(5 + 48, 8).storeUint(6 + 48, 8));
|
dict.sDictSetBuilder(32, "7800", beginCell().storeUint(5 + 48, 8).storeUint(6 + 48, 8));
|
||||||
dict~sDictSet(32, "key1", "12");
|
dict.sDictSet(32, "key1", "12");
|
||||||
var (old1, _) = dict~sDictSetAndGet(32, "key1", "34");
|
var (old1, _) = dict.sDictSetAndGet(32, "key1", "34");
|
||||||
var (old2, _) = dict~sDictDeleteAndGet(32, "key1");
|
var (old2, _) = dict.sDictDeleteAndGet(32, "key1");
|
||||||
var (restK, restV, _) = dict.sDictGetFirst(32);
|
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 (restK.isSliceBitsEqual(restK1)) throw 123;
|
||||||
assert (restV.isSliceBitsEqual(restV1)) throw 123;
|
assert (restV.isSliceBitsEqual(restV1)) throw 123;
|
||||||
return (
|
return (
|
||||||
old1~loadTwoDigitNumberFromSlice(),
|
old1.loadTwoDigitNumberFromSlice(),
|
||||||
old2~loadTwoDigitNumberFromSlice(),
|
old2.loadTwoDigitNumberFromSlice(),
|
||||||
restV~loadTwoDigitNumberFromSlice(),
|
restV.loadTwoDigitNumberFromSlice(),
|
||||||
restK~loadTwoDigitNumberFromSlice(),
|
restK.loadTwoDigitNumberFromSlice(),
|
||||||
restK~loadTwoDigitNumberFromSlice()
|
restK.loadTwoDigitNumberFromSlice()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
|
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
|
||||||
var dict: cell = createEmptyDict();
|
var dict: cell = createEmptyDict();
|
||||||
dict~idict_set_builder(32, 3, begin_cell().store_int(30, 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, 4, begin_cell().store_int(40, 32));
|
||||||
dict~idict_set_builder(32, 5, begin_cell().store_int(valueAt5, 32));
|
dict.idict_set_builder(32, 5, begin_cell().store_int(valueAt5, 32));
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
fun lookupIdxByValue(idict32: cell, value: int): int {
|
fun lookupIdxByValue(idict32: cell, value: int): int {
|
||||||
var cur_key = -1;
|
var cur_key = -1;
|
||||||
do {
|
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
|
// one-line condition (via &) doesn't work, since right side is calculated immediately
|
||||||
if (found) {
|
if (found) {
|
||||||
if (cs~load_int(32) == value) {
|
if (cs.loadInt(32) == value) {
|
||||||
return cur_key;
|
return cur_key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ import "@stdlib/tvm-dicts"
|
||||||
|
|
||||||
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
|
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
|
||||||
var dict: cell = createEmptyDict();
|
var dict: cell = createEmptyDict();
|
||||||
dict~iDictSetBuilder(32, 3, beginCell().storeInt(30, 32));
|
dict.iDictSetBuilder(32, 3, beginCell().storeInt(30, 32));
|
||||||
dict~iDictSetBuilder(32, 4, beginCell().storeInt(40, 32));
|
dict.iDictSetBuilder(32, 4, beginCell().storeInt(40, 32));
|
||||||
dict~iDictSetBuilder(32, 5, beginCell().storeInt(valueAt5, 32));
|
dict.iDictSetBuilder(32, 5, beginCell().storeInt(valueAt5, 32));
|
||||||
return dict;
|
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);
|
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
|
// one-line condition (via &) doesn't work, since right side is calculated immediately
|
||||||
if (found) {
|
if (found) {
|
||||||
if (cs~loadInt(32) == value) {
|
if (cs.loadInt(32) == value) {
|
||||||
return cur_key;
|
return cur_key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
9
tolk-tester/tests/invalid-call-1.tolk
Normal file
9
tolk-tester/tests/invalid-call-1.tolk
Normal 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
|
||||||
|
*/
|
14
tolk-tester/tests/invalid-call-2.tolk
Normal file
14
tolk-tester/tests/invalid-call-2.tolk
Normal 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
|
||||||
|
*/
|
12
tolk-tester/tests/invalid-call-3.tolk
Normal file
12
tolk-tester/tests/invalid-call-3.tolk
Normal 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
|
||||||
|
*/
|
13
tolk-tester/tests/invalid-call-4.tolk
Normal file
13
tolk-tester/tests/invalid-call-4.tolk
Normal 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
|
||||||
|
*/
|
13
tolk-tester/tests/invalid-call-5.tolk
Normal file
13
tolk-tester/tests/invalid-call-5.tolk
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
fun inc(x: int) {
|
||||||
|
return x + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
return inc(_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@compilation_should_fail
|
||||||
|
@stderr rvalue expected
|
||||||
|
@stderr inc(_)
|
||||||
|
*/
|
12
tolk-tester/tests/invalid-call-6.tolk
Normal file
12
tolk-tester/tests/invalid-call-6.tolk
Normal 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
|
||||||
|
*/
|
14
tolk-tester/tests/invalid-call-7.tolk
Normal file
14
tolk-tester/tests/invalid-call-7.tolk
Normal 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()
|
||||||
|
*/
|
8
tolk-tester/tests/invalid-call-8.tolk
Normal file
8
tolk-tester/tests/invalid-call-8.tolk
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fun main() {
|
||||||
|
var incoming_ton: int = get_incoming_value().3();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@compilation_should_fail
|
||||||
|
@stderr expected method name, got `3`
|
||||||
|
*/
|
|
@ -1,5 +1,5 @@
|
||||||
fun main(): int {
|
fun main(): int {
|
||||||
;; this is not a comment
|
;; here is not a comment
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
fun main() {
|
get seqno(self: int) {
|
||||||
val imm = 10;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@compilation_should_fail
|
@compilation_should_fail
|
||||||
@stderr immutable variables are not supported yet
|
@stderr get methods can't have `mutate` and `self` params
|
||||||
*/
|
*/
|
||||||
|
|
11
tolk-tester/tests/invalid-mutate-1.tolk
Normal file
11
tolk-tester/tests/invalid-mutate-1.tolk
Normal 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`
|
||||||
|
*/
|
16
tolk-tester/tests/invalid-mutate-10.tolk
Normal file
16
tolk-tester/tests/invalid-mutate-10.tolk
Normal 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
|
||||||
|
*/
|
8
tolk-tester/tests/invalid-mutate-11.tolk
Normal file
8
tolk-tester/tests/invalid-mutate-11.tolk
Normal 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
|
||||||
|
*/
|
14
tolk-tester/tests/invalid-mutate-12.tolk
Normal file
14
tolk-tester/tests/invalid-mutate-12.tolk
Normal 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
|
||||||
|
*/
|
8
tolk-tester/tests/invalid-mutate-13.tolk
Normal file
8
tolk-tester/tests/invalid-mutate-13.tolk
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fun onInternalMessage(mutate in_msg_body: slice) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@compilation_should_fail
|
||||||
|
@stderr invalid declaration of a reserved function
|
||||||
|
*/
|
8
tolk-tester/tests/invalid-mutate-14.tolk
Normal file
8
tolk-tester/tests/invalid-mutate-14.tolk
Normal 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)`
|
||||||
|
*/
|
12
tolk-tester/tests/invalid-mutate-15.tolk
Normal file
12
tolk-tester/tests/invalid-mutate-15.tolk
Normal 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
|
||||||
|
*/
|
10
tolk-tester/tests/invalid-mutate-2.tolk
Normal file
10
tolk-tester/tests/invalid-mutate-2.tolk
Normal 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`
|
||||||
|
*/
|
11
tolk-tester/tests/invalid-mutate-3.tolk
Normal file
11
tolk-tester/tests/invalid-mutate-3.tolk
Normal 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`
|
||||||
|
*/
|
14
tolk-tester/tests/invalid-mutate-4.tolk
Normal file
14
tolk-tester/tests/invalid-mutate-4.tolk
Normal 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`
|
||||||
|
*/
|
14
tolk-tester/tests/invalid-mutate-5.tolk
Normal file
14
tolk-tester/tests/invalid-mutate-5.tolk
Normal 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
|
||||||
|
*/
|
16
tolk-tester/tests/invalid-mutate-6.tolk
Normal file
16
tolk-tester/tests/invalid-mutate-6.tolk
Normal 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)
|
||||||
|
*/
|
15
tolk-tester/tests/invalid-mutate-7.tolk
Normal file
15
tolk-tester/tests/invalid-mutate-7.tolk
Normal 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)
|
||||||
|
*/
|
10
tolk-tester/tests/invalid-mutate-8.tolk
Normal file
10
tolk-tester/tests/invalid-mutate-8.tolk
Normal 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)
|
||||||
|
*/
|
9
tolk-tester/tests/invalid-mutate-9.tolk
Normal file
9
tolk-tester/tests/invalid-mutate-9.tolk
Normal 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`
|
||||||
|
*/
|
8
tolk-tester/tests/invalid-self-1.tolk
Normal file
8
tolk-tester/tests/invalid-self-1.tolk
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fun cantReturnFromSelf(mutate self: int): self {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@compilation_should_fail
|
||||||
|
@stderr invalid return from `self` function
|
||||||
|
*/
|
8
tolk-tester/tests/invalid-self-2.tolk
Normal file
8
tolk-tester/tests/invalid-self-2.tolk
Normal 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)
|
||||||
|
*/
|
10
tolk-tester/tests/invalid-self-3.tolk
Normal file
10
tolk-tester/tests/invalid-self-3.tolk
Normal 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
|
||||||
|
*/
|
9
tolk-tester/tests/invalid-self-4.tolk
Normal file
9
tolk-tester/tests/invalid-self-4.tolk
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fun cantReturnNothingFromSelf(mutate self: int): self {
|
||||||
|
self = self + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@compilation_should_fail
|
||||||
|
@stderr missing return; forgot `return self`?
|
||||||
|
@stderr }
|
||||||
|
*/
|
15
tolk-tester/tests/invalid-self-5.tolk
Normal file
15
tolk-tester/tests/invalid-self-5.tolk
Normal 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
|
||||||
|
*/
|
8
tolk-tester/tests/invalid-self-6.tolk
Normal file
8
tolk-tester/tests/invalid-self-6.tolk
Normal 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
|
||||||
|
*/
|
8
tolk-tester/tests/invalid-self-7.tolk
Normal file
8
tolk-tester/tests/invalid-self-7.tolk
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fun increment(x: int): int {
|
||||||
|
return self + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@compilation_should_fail
|
||||||
|
@stderr using `self` in a non-member function
|
||||||
|
*/
|
19
tolk-tester/tests/invalid-typing-3.tolk
Normal file
19
tolk-tester/tests/invalid-typing-3.tolk
Normal 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
|
||||||
|
*/
|
14
tolk-tester/tests/invalid-typing-4.tolk
Normal file
14
tolk-tester/tests/invalid-typing-4.tolk
Normal 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
|
||||||
|
*/
|
14
tolk-tester/tests/invalid-typing-5.tolk
Normal file
14
tolk-tester/tests/invalid-typing-5.tolk
Normal 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
|
||||||
|
*/
|
27
tolk-tester/tests/known-bugs.tolk
Normal file
27
tolk-tester/tests/known-bugs.tolk
Normal 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
|
||||||
|
*/
|
337
tolk-tester/tests/mutate-methods.tolk
Normal file
337
tolk-tester/tests/mutate-methods.tolk
Normal 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
|
||||||
|
}>
|
||||||
|
"""
|
||||||
|
|
||||||
|
*/
|
|
@ -30,13 +30,11 @@ global `some()var`:int;
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(113)fun`unary+bitwise-constant`():[int,int,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]{
|
@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; }
|
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)];
|
return [add3(fst2,snd2,trd2),add3(fst1,snd1,trd1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
fun `load:u32`(cs: slice): (slice, int) {
|
fun `load:u32`(mutate self: slice): int {
|
||||||
return cs.loadUint(32);
|
return self.loadUint(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(116) fun `call_~_via_backticks`():[int,int,int,int] {
|
@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 b:builder = beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32).storeUint(4, 32);
|
||||||
var `cs`:slice = b.endCell().beginParse();
|
var `cs`:slice = b.endCell().beginParse();
|
||||||
var (`cs` redef,one:int) = `cs`.`loadUint`(32);
|
val one:int=`cs`.`loadUint`(32);
|
||||||
var (two:int,three:int) = (`cs`~`loadUint`(32), cs~`load:u32`());
|
val (two:int,three:int) = (`cs`.`loadUint`(32), cs.`load:u32`());
|
||||||
var (cs redef,four:int) = cs.`load:u32`();
|
val four:int = cs.`load:u32`();
|
||||||
return [one,two,three,four];
|
return [one,two,three,four];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ fun test1() {
|
||||||
|
|
||||||
var t = createEmptyTuple();
|
var t = createEmptyTuple();
|
||||||
do {
|
do {
|
||||||
var num = numbers~listNext();
|
var num = numbers.listNext();
|
||||||
t~tuplePush(num);
|
t.tuplePush(num);
|
||||||
} while (numbers != null);
|
} while (numbers != null);
|
||||||
|
|
||||||
return (h, numbers == null, t);
|
return (h, numbers == null, t);
|
||||||
|
@ -63,7 +63,7 @@ fun test4() {
|
||||||
@method_id(105)
|
@method_id(105)
|
||||||
fun test5() {
|
fun test5() {
|
||||||
var n = getUntypedNull();
|
var n = getUntypedNull();
|
||||||
return !(null == n) ? n~loadInt(32) : 100;
|
return !(null == n) ? n.loadInt(32) : 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@method_id(106)
|
@method_id(106)
|
||||||
|
@ -75,7 +75,7 @@ fun test6(x: int) {
|
||||||
fun test7() {
|
fun test7() {
|
||||||
var b = beginCell().storeMaybeRef(null);
|
var b = beginCell().storeMaybeRef(null);
|
||||||
var s = b.endCell().beginParse();
|
var s = b.endCell().beginParse();
|
||||||
var c = s~loadMaybeRef();
|
var c = s.loadMaybeRef();
|
||||||
return (null == c) * 10 + (b != null);
|
return (null == c) * 10 + (b != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,14 +145,14 @@ fun main() {
|
||||||
"""
|
"""
|
||||||
test7 PROC:<{
|
test7 PROC:<{
|
||||||
...
|
...
|
||||||
LDOPTREF // b _17 _16
|
LDOPTREF // b _20 _19
|
||||||
DROP // b c
|
DROP // b c
|
||||||
ISNULL // b _10
|
ISNULL // b _13
|
||||||
10 MULCONST // b _12
|
10 MULCONST // b _15
|
||||||
SWAP // _12 b
|
SWAP // _15 b
|
||||||
ISNULL // _12 _13
|
ISNULL // _15 _16
|
||||||
0 EQINT // _12 _14
|
0 EQINT // _15 _17
|
||||||
ADD // _15
|
ADD // _18
|
||||||
}>
|
}>
|
||||||
"""
|
"""
|
||||||
*/
|
*/
|
||||||
|
|
113
tolk-tester/tests/parse-address.tolk
Normal file
113
tolk-tester/tests/parse-address.tolk
Normal 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
|
||||||
|
*/
|
|
@ -13,8 +13,8 @@ fun f_pure2(): int {
|
||||||
fun get_contract_data(): (int, int) {
|
fun get_contract_data(): (int, int) {
|
||||||
var c: cell = getContractData();
|
var c: cell = getContractData();
|
||||||
var cs: slice = c.beginParse();
|
var cs: slice = c.beginParse();
|
||||||
cs~loadBits(32);
|
cs.loadBits(32);
|
||||||
var value: int = cs~loadUint(16);
|
var value: int = cs.loadUint(16);
|
||||||
return (1, value);
|
return (1, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
213
tolk-tester/tests/self-keyword.tolk
Normal file
213
tolk-tester/tests/self-keyword.tolk
Normal 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
|
||||||
|
}>
|
||||||
|
"""
|
||||||
|
*/
|
|
@ -243,7 +243,7 @@ fun tanh_f258(x: int, n: int): int {
|
||||||
repeat (n) {
|
repeat (n) {
|
||||||
a = (c -= Two) + mulDivRound(x2, 1 << 239, a); // a := 2k+1+x^2/a as fixed250, k=n+1,n,...,2
|
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
|
// 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);
|
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
|
// (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 x2: int = mulDivRound(x, x, 1 << 255); // x^2 as fixed261
|
||||||
var Two: int = (1 << 251); // 2. as fixed250
|
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;
|
var c = a;
|
||||||
repeat (17) {
|
repeat (17) {
|
||||||
a = (c -= Two) + mulDivRound(x2, 1 << 239, a); // a := 2k+1+x^2/a as fixed250, k=n+1,n,...,2
|
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))
|
// 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
|
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))
|
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 {
|
fun tan_f260_inlined(x: int): int {
|
||||||
var x2: int = mulrshiftr256(x, x); // x^2 as fixed264
|
var x2: int = mulrshiftr256(x, x); // x^2 as fixed264
|
||||||
var Two: int = (1 << 251); // 2. as fixed250
|
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;
|
var c = a;
|
||||||
repeat (14) {
|
repeat (14) {
|
||||||
a = (c -= Two) - mulDivRound(x2, 1 << 236, a); // a := 2k+1-x^2/a as fixed250, k=n+1,n,...,2
|
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
|
// 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);
|
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 {
|
fun tan_f258_inlined(x: int): int {
|
||||||
var x2: int = mulrshiftr256(x, x); // x^2 as fixed260
|
var x2: int = mulrshiftr256(x, x); // x^2 as fixed260
|
||||||
var Two: int = (1 << 251); // 2. as fixed250
|
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;
|
var c = a;
|
||||||
repeat (18) {
|
repeat (18) {
|
||||||
a = (c -= Two) - mulDivRound(x2, 1 << 240, a); // a := 2k+1-x^2/a as fixed250, k=n+1,n,...,2
|
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
|
// 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);
|
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) {
|
fun log_aux_f257(x: int): (int, int) {
|
||||||
var s: int = log2_floor_p1(x);
|
var s: int = log2_floor_p1(x);
|
||||||
x <<= 256 - s;
|
x <<= 256 - s;
|
||||||
var t: int = stackMoveToTop(-1 << 256);
|
var t: int = -1 << 256;
|
||||||
if ((x >> 249) <= 90) {
|
if ((x >> 249) <= 90) {
|
||||||
// t~stackMoveToTop();
|
|
||||||
t >>= 1;
|
t >>= 1;
|
||||||
s -= 1;
|
s -= 1;
|
||||||
}
|
}
|
||||||
|
@ -593,7 +592,7 @@ fun pow33b(m: int): int {
|
||||||
fun log_auxx_f260(x: int): (int, int, int) {
|
fun log_auxx_f260(x: int): (int, int, int) {
|
||||||
var s: int = log2_floor_p1(x) - 1;
|
var s: int = log2_floor_p1(x) - 1;
|
||||||
x <<= 255 - s; // rescale to 1 <= x < 2 as fixed255
|
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 x1: int = (x - t) >> 1;
|
||||||
var q: int = mulDivRound(x1, 65, x1 + t) + 11; // crude approximation to round(log(x)/log(33/32))
|
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
|
// 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 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
|
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
|
// 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 (pa, pb) = (33226912, 5232641); // (32+I)^5
|
||||||
var (qh, ql) = divMod(q, 5);
|
var (qh, ql) = divMod(q, 5);
|
||||||
var (a, b) = (1 << (5 * (51 - q)), 0); // (1/32^q, 0) as fixed255
|
var (a, b) = (1 << (5 * (51 - q)), 0); // (1/32^q, 0) as fixed255
|
||||||
repeat (ql) {
|
repeat (ql) {
|
||||||
// a+b*I *= 32+I
|
// 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) {
|
repeat (qh) {
|
||||||
// a+b*I *= (32+I)^5 = pa + pb*I
|
// a+b*I *= (32+I)^5 = pa + pb*I
|
||||||
|
@ -807,7 +807,7 @@ fun atan_auxx_f256(x: int): (int, int) {
|
||||||
@inline_ref
|
@inline_ref
|
||||||
fun atan_f255(x: int): int {
|
fun atan_f255(x: int): int {
|
||||||
var s: int = (x ~>> 256);
|
var s: int = (x ~>> 256);
|
||||||
stackMoveToTop(x);
|
x.stackMoveToTop();
|
||||||
if (s) {
|
if (s) {
|
||||||
x = lshift256divr(-1 << 255, x); // x:=-1/x as fixed256
|
x = lshift256divr(-1 << 255, x); // x:=-1/x as fixed256
|
||||||
} else {
|
} else {
|
||||||
|
@ -880,7 +880,7 @@ fun fixed248_acos(x: int): int {
|
||||||
@inline_ref
|
@inline_ref
|
||||||
fun fixed248_atan(x: int): int {
|
fun fixed248_atan(x: int): int {
|
||||||
var s: int = (x ~>> 249);
|
var s: int = (x ~>> 249);
|
||||||
stackMoveToTop(x);
|
x.stackMoveToTop();
|
||||||
if (s) {
|
if (s) {
|
||||||
s = sign(s);
|
s = sign(s);
|
||||||
x = lshift256divr(-1 << 248, x); // x:=-1/x as fixed256
|
x = lshift256divr(-1 << 248, x); // x:=-1/x as fixed256
|
||||||
|
@ -897,7 +897,7 @@ fun fixed248_atan(x: int): int {
|
||||||
@inline_ref
|
@inline_ref
|
||||||
fun fixed248_acot(x: int): int {
|
fun fixed248_acot(x: int): int {
|
||||||
var s: int = (x ~>> 249);
|
var s: int = (x ~>> 249);
|
||||||
stackMoveToTop(x);
|
x.stackMoveToTop();
|
||||||
if (s) {
|
if (s) {
|
||||||
x = lshift256divr(-1 << 248, x); // x:=-1/x as fixed256
|
x = lshift256divr(-1 << 248, x); // x:=-1/x as fixed256
|
||||||
s = 0;
|
s = 0;
|
||||||
|
@ -918,7 +918,7 @@ fun fixed248_acot(x: int): int {
|
||||||
/// fixed252 nrand();
|
/// fixed252 nrand();
|
||||||
@inline_ref
|
@inline_ref
|
||||||
fun nrand_f252(): int {
|
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
|
// 4/sqrt(e*Pi) = 1.369 loop iterations on average
|
||||||
do {
|
do {
|
||||||
var (u, v) = (random() / 16 + 1, mulDivRound(random() - (1 << 255), 7027, 1 << 16)); // fixed252; 7027=ceil(sqrt(8/e)*2^12)
|
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();
|
/// fixed252 nrand_fast();
|
||||||
@inline_ref
|
@inline_ref
|
||||||
fun nrand_fast_f252(): int {
|
fun nrand_fast_f252(): int {
|
||||||
var t: int = stackMoveToTop(-3) << 253; // -6. as fixed252
|
var t: int = -3 << 253; // -6. as fixed252
|
||||||
repeat (12) {
|
repeat (12) {
|
||||||
t += random() / 16; // add together 12 uniformly random numbers
|
t += random() / 16; // add together 12 uniformly random numbers
|
||||||
}
|
}
|
||||||
|
@ -979,8 +979,8 @@ fun fixed248_nrand_fast(): int {
|
||||||
}
|
}
|
||||||
|
|
||||||
@pure
|
@pure
|
||||||
fun ~tset<X>(t: tuple, idx: int, value: X): (tuple, ())
|
fun tset<X>(mutate self: tuple, idx: int, value: X): void
|
||||||
asm(t value idx) "SETINDEXVAR";
|
asm(self value idx) "SETINDEXVAR";
|
||||||
|
|
||||||
// computes 1-acos(x)/Pi by a very simple, extremely slow (~70k gas) and imprecise method
|
// computes 1-acos(x)/Pi by a very simple, extremely slow (~70k gas) and imprecise method
|
||||||
// fixed256 acos_prepare_slow(fixed255 x);
|
// fixed256 acos_prepare_slow(fixed255 x);
|
||||||
|
@ -1014,12 +1014,12 @@ fun asin_slow_f255(x: int): int {
|
||||||
fun test_nrand(n: int): tuple {
|
fun test_nrand(n: int): tuple {
|
||||||
var t: tuple = createEmptyTuple();
|
var t: tuple = createEmptyTuple();
|
||||||
repeat (255) {
|
repeat (255) {
|
||||||
t~tuplePush(0);
|
t.tuplePush(0);
|
||||||
}
|
}
|
||||||
repeat (n) {
|
repeat (n) {
|
||||||
var x: int = fixed248_nrand();
|
var x: int = fixed248_nrand();
|
||||||
var bucket: int = (abs(x) >> 243); // 255 buckets starting from x=0, each 1/32 wide
|
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;
|
return t;
|
||||||
}
|
}
|
||||||
|
@ -1210,14 +1210,14 @@ fun main() {
|
||||||
// return atan_aux_f256(1); // atan(1/2^256)*2^261 = 32
|
// return atan_aux_f256(1); // atan(1/2^256)*2^261 = 32
|
||||||
//return fixed248_nrand();
|
//return fixed248_nrand();
|
||||||
// return test_nrand(100000);
|
// return test_nrand(100000);
|
||||||
var One2: int = stackMoveToTop(1 << 255);
|
var One2: int = 1 << 255;
|
||||||
// return asin_f255(One);
|
// return asin_f255(One);
|
||||||
// return asin_f255(-2 * One ~/ -3);
|
// return asin_f255(-2 * One ~/ -3);
|
||||||
var arg: int = mulDivRound(12, One2, 17); // 12/17
|
var arg: int = mulDivRound(12, One2, 17); // 12/17
|
||||||
// return [ asin_slow_f255(arg), asin_f255(arg) ];
|
// return [ asin_slow_f255(arg), asin_f255(arg) ];
|
||||||
// return [ acos_slow_f255(arg), acos_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
|
// 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_atan(One) ~/ 5); // atan(1/5)
|
||||||
// return fixed248_acot(One ~/ 239); // atan(1/5)
|
// return fixed248_acot(One ~/ 239); // atan(1/5)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,8 @@ fun bar(x: int, y: int): (int, int) {
|
||||||
}
|
}
|
||||||
return (x + 1, y);
|
return (x + 1, y);
|
||||||
}
|
}
|
||||||
fun bar2(x: int, y: int): (int,int) {
|
|
||||||
return bar(x, y);
|
|
||||||
}
|
|
||||||
fun main(x: int, y: int): (int, int) {
|
fun main(x: int, y: int): (int, int) {
|
||||||
(x, y) = bar2(x, y);
|
(x, y) = bar(x, y);
|
||||||
return (x, y * 10);
|
return (x, y * 10);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
fun main(): int {
|
fun main(): int {
|
||||||
var c: cell = my_begin_cell().storeInt(demo_10, 32).my_end_cell();
|
var c: cell = my_begin_cell().storeInt(demo_10, 32).my_end_cell();
|
||||||
var cs: slice = my_begin_parse(c);
|
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;
|
return 1 + demo1(ten) + demo_var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
tolk-tester/tests/var-apply.tolk
Normal file
22
tolk-tester/tests/var-apply.tolk
Normal 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
|
||||||
|
*/
|
|
@ -1,6 +1,6 @@
|
||||||
@method_id(101)
|
@method_id(101)
|
||||||
fun test1(cs: slice) {
|
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)
|
@method_id(102)
|
||||||
|
@ -12,15 +12,15 @@ fun test2(cs: slice) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main(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) {
|
fun f(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),
|
||||||
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),
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,8 @@ namespace tolk {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
TmpVar::TmpVar(var_idx_t _idx, bool _is_tmp_unnamed, TypeExpr* _type, SymDef* sym, SrcLocation loc)
|
TmpVar::TmpVar(var_idx_t _idx, TypeExpr* _type, sym_idx_t sym_idx, SrcLocation loc)
|
||||||
: v_type(_type), idx(_idx), is_tmp_unnamed(_is_tmp_unnamed), coord(0), where(loc) {
|
: v_type(_type), idx(_idx), sym_idx(sym_idx), coord(0), where(loc) {
|
||||||
if (sym) {
|
|
||||||
name = sym->sym_idx;
|
|
||||||
sym->value->idx = _idx;
|
|
||||||
}
|
|
||||||
if (!_type) {
|
if (!_type) {
|
||||||
v_type = TypeExpr::new_hole();
|
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 {
|
void TmpVar::show(std::ostream& os, int omit_idx) const {
|
||||||
if (!is_tmp_unnamed) {
|
if (!is_unnamed()) {
|
||||||
os << G.symbols.get_name(name);
|
os << G.symbols.get_name(sym_idx);
|
||||||
if (omit_idx >= 2) {
|
if (omit_idx >= 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -462,16 +458,13 @@ void CodeBlob::print(std::ostream& os, int flags) const {
|
||||||
os << "-------- END ---------\n\n";
|
os << "-------- END ---------\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
var_idx_t CodeBlob::create_var(bool is_tmp_unnamed, TypeExpr* var_type, SymDef* sym, SrcLocation location) {
|
var_idx_t CodeBlob::create_var(TypeExpr* var_type, var_idx_t sym_idx, SrcLocation location) {
|
||||||
vars.emplace_back(var_cnt, is_tmp_unnamed, var_type, sym, location);
|
vars.emplace_back(var_cnt, var_type, sym_idx, location);
|
||||||
if (sym) {
|
|
||||||
sym->value->idx = var_cnt;
|
|
||||||
}
|
|
||||||
return var_cnt++;
|
return var_cnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CodeBlob::import_params(FormalArgList arg_list) {
|
bool CodeBlob::import_params(FormalArgList arg_list) {
|
||||||
if (var_cnt || in_var_cnt || op_cnt) {
|
if (var_cnt || in_var_cnt) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::vector<var_idx_t> list;
|
std::vector<var_idx_t> list;
|
||||||
|
@ -480,7 +473,7 @@ bool CodeBlob::import_params(FormalArgList arg_list) {
|
||||||
SymDef* arg_sym;
|
SymDef* arg_sym;
|
||||||
SrcLocation arg_loc;
|
SrcLocation arg_loc;
|
||||||
std::tie(arg_type, arg_sym, arg_loc) = par;
|
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);
|
emplace_back(loc, Op::_Import, list);
|
||||||
in_var_cnt = var_cnt;
|
in_var_cnt = var_cnt;
|
||||||
|
|
|
@ -46,10 +46,9 @@ int CodeBlob::split_vars(bool strict) {
|
||||||
if (k != 1) {
|
if (k != 1) {
|
||||||
var.coord = ~((n << 8) + k);
|
var.coord = ~((n << 8) + k);
|
||||||
for (int i = 0; i < k; i++) {
|
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(v == n + i);
|
||||||
tolk_assert(vars[v].idx == v);
|
tolk_assert(vars[v].idx == v);
|
||||||
vars[v].name = vars[j].name;
|
|
||||||
vars[v].coord = ((int)j << 8) + i + 1;
|
vars[v].coord = ((int)j << 8) + i + 1;
|
||||||
}
|
}
|
||||||
n += k;
|
n += k;
|
||||||
|
|
|
@ -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
|
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_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) {
|
if (v->tok == tok_neq) {
|
||||||
v_isNull = createV<ast_unary_operator>(v->loc, "!", tok_logical_not, v_isNull);
|
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);
|
return TypeExpr::new_atomic(TypeExpr::_Builder);
|
||||||
case tok_continuation:
|
case tok_continuation:
|
||||||
lex.next();
|
lex.next();
|
||||||
return TypeExpr::new_atomic(TypeExpr::_Cont);
|
return TypeExpr::new_atomic(TypeExpr::_Continutaion);
|
||||||
case tok_tuple:
|
case tok_tuple:
|
||||||
lex.next();
|
lex.next();
|
||||||
return TypeExpr::new_atomic(TypeExpr::_Tuple);
|
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({});
|
return TypeExpr::new_tensor({});
|
||||||
case tok_bool:
|
case tok_bool:
|
||||||
lex.error("bool type is not supported yet");
|
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:
|
case tok_identifier:
|
||||||
if (int idx = genericsT_list ? genericsT_list->lookup_idx(lex.cur_str()) : -1; idx != -1) {
|
if (int idx = genericsT_list ? genericsT_list->lookup_idx(lex.cur_str()) : -1; idx != -1) {
|
||||||
lex.next();
|
lex.next();
|
||||||
|
@ -229,13 +232,27 @@ static TypeExpr* parse_type(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
|
||||||
|
|
||||||
AnyV parse_expr(Lexer& lex);
|
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();
|
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;
|
std::string_view param_name;
|
||||||
if (lex.tok() == tok_identifier) {
|
if (lex.tok() == tok_identifier) {
|
||||||
param_name = lex.cur_str();
|
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) {
|
} else if (lex.tok() != tok_underscore) {
|
||||||
lex.unexpected("parameter name");
|
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")
|
// parameter type after colon, also mandatory (even explicit ":auto")
|
||||||
lex.expect(tok_colon, "`: <parameter_type>`");
|
lex.expect(tok_colon, "`: <parameter_type>`");
|
||||||
TypeExpr* param_type = parse_type(lex, genericsT_list);
|
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) {
|
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);
|
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();
|
SrcLocation loc = lex.cur_location();
|
||||||
std::vector<AnyV> params;
|
std::vector<AnyV> params;
|
||||||
lex.expect(tok_oppar, "parameter list");
|
lex.expect(tok_oppar, "parameter list");
|
||||||
if (lex.tok() != tok_clpar) {
|
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) {
|
while (lex.tok() == tok_comma) {
|
||||||
lex.next();
|
lex.next();
|
||||||
params.push_back(parse_parameter(lex, genericsT_list));
|
params.push_back(parse_parameter(lex, genericsT_list, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lex.expect(tok_clpar, "`)`");
|
lex.expect(tok_clpar, "`)`");
|
||||||
return createV<ast_parameter_list>(loc, std::move(params));
|
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
|
// parse (expr) / [expr] / identifier / number
|
||||||
static AnyV parse_expr100(Lexer& lex) {
|
static AnyV parse_expr100(Lexer& lex) {
|
||||||
SrcLocation loc = lex.cur_location();
|
SrcLocation loc = lex.cur_location();
|
||||||
|
@ -384,6 +438,10 @@ static AnyV parse_expr100(Lexer& lex) {
|
||||||
lex.next();
|
lex.next();
|
||||||
return createV<ast_null_keyword>(loc);
|
return createV<ast_null_keyword>(loc);
|
||||||
}
|
}
|
||||||
|
case tok_self: {
|
||||||
|
lex.next();
|
||||||
|
return createV<ast_self_keyword>(loc);
|
||||||
|
}
|
||||||
case tok_identifier: {
|
case tok_identifier: {
|
||||||
std::string_view str_val = lex.cur_str();
|
std::string_view str_val = lex.cur_str();
|
||||||
lex.next();
|
lex.next();
|
||||||
|
@ -400,48 +458,25 @@ static AnyV parse_expr100(Lexer& lex) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse E(expr)
|
// parse E(args)
|
||||||
static AnyV parse_expr90(Lexer& lex) {
|
static AnyV parse_expr90(Lexer& lex) {
|
||||||
AnyV res = parse_expr100(lex);
|
AnyV res = parse_expr100(lex);
|
||||||
if (lex.tok() == tok_oppar) {
|
if (lex.tok() == tok_oppar) {
|
||||||
lex.next();
|
return createV<ast_function_call>(res->loc, res, parse_argument_list(lex));
|
||||||
|
|
||||||
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 res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse E .method ~method E (left-to-right)
|
// parse E.method(...) (left-to-right)
|
||||||
static AnyV parse_expr80(Lexer& lex) {
|
static AnyV parse_expr80(Lexer& lex) {
|
||||||
AnyV lhs = parse_expr90(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();
|
std::string_view method_name = lex.cur_str();
|
||||||
lex.next();
|
lex.next();
|
||||||
|
lhs = createV<ast_dot_method_call>(loc, method_name, lhs, parse_argument_list(lex));
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
@ -586,11 +621,11 @@ AnyV parse_expr(Lexer& lex) {
|
||||||
|
|
||||||
AnyV parse_statement(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();
|
SrcLocation loc = lex.cur_location();
|
||||||
if (lex.tok() == tok_oppar) {
|
if (lex.tok() == tok_oppar) {
|
||||||
lex.next();
|
lex.next();
|
||||||
AnyV first = parse_var_declaration_lhs(lex);
|
AnyV first = parse_var_declaration_lhs(lex, is_immutable);
|
||||||
if (lex.tok() == tok_clpar) {
|
if (lex.tok() == tok_clpar) {
|
||||||
lex.next();
|
lex.next();
|
||||||
return createV<ast_parenthesized_expr>(loc, first);
|
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);
|
std::vector<AnyV> args(1, first);
|
||||||
while (lex.tok() == tok_comma) {
|
while (lex.tok() == tok_comma) {
|
||||||
lex.next();
|
lex.next();
|
||||||
args.push_back(parse_var_declaration_lhs(lex));
|
args.push_back(parse_var_declaration_lhs(lex, is_immutable));
|
||||||
}
|
}
|
||||||
lex.expect(tok_clpar, "`)`");
|
lex.expect(tok_clpar, "`)`");
|
||||||
return createV<ast_tensor>(loc, std::move(args));
|
return createV<ast_tensor>(loc, std::move(args));
|
||||||
}
|
}
|
||||||
if (lex.tok() == tok_opbracket) {
|
if (lex.tok() == tok_opbracket) {
|
||||||
lex.next();
|
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) {
|
while (lex.tok() == tok_comma) {
|
||||||
lex.next();
|
lex.next();
|
||||||
args.push_back(parse_var_declaration_lhs(lex));
|
args.push_back(parse_var_declaration_lhs(lex, is_immutable));
|
||||||
}
|
}
|
||||||
lex.expect(tok_clbracket, "`]`");
|
lex.expect(tok_clbracket, "`]`");
|
||||||
return createV<ast_tensor_square>(loc, std::move(args));
|
return createV<ast_tensor_square>(loc, std::move(args));
|
||||||
|
@ -625,7 +660,7 @@ static AnyV parse_var_declaration_lhs(Lexer& lex) {
|
||||||
lex.next();
|
lex.next();
|
||||||
marked_as_redef = true;
|
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) {
|
if (lex.tok() == tok_underscore) {
|
||||||
TypeExpr* declared_type = nullptr;
|
TypeExpr* declared_type = nullptr;
|
||||||
|
@ -634,21 +669,17 @@ static AnyV parse_var_declaration_lhs(Lexer& lex) {
|
||||||
lex.next();
|
lex.next();
|
||||||
declared_type = parse_type(lex, nullptr);
|
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");
|
lex.unexpected("variable name");
|
||||||
}
|
}
|
||||||
|
|
||||||
static AnyV parse_local_vars_declaration(Lexer& lex) {
|
static AnyV parse_local_vars_declaration(Lexer& lex) {
|
||||||
SrcLocation loc = lex.cur_location();
|
SrcLocation loc = lex.cur_location();
|
||||||
bool immutable = lex.tok() == tok_val;
|
bool is_immutable = lex.tok() == tok_val;
|
||||||
lex.next();
|
lex.next();
|
||||||
|
|
||||||
if (immutable) {
|
AnyV lhs = parse_var_declaration_lhs(lex, is_immutable);
|
||||||
lex.error("immutable variables are not supported yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
AnyV lhs = parse_var_declaration_lhs(lex);
|
|
||||||
if (lex.tok() != tok_assign) {
|
if (lex.tok() != tok_assign) {
|
||||||
lex.error("variables declaration must be followed by assignment: `var xxx = ...`");
|
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;
|
std::vector<int> arg_order, ret_order;
|
||||||
if (lex.tok() == tok_oppar) {
|
if (lex.tok() == tok_oppar) {
|
||||||
lex.next();
|
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());
|
int arg_idx = param_list->lookup_idx(lex.cur_str());
|
||||||
if (arg_idx == -1) {
|
if (arg_idx == -1) {
|
||||||
lex.unexpected("argument name");
|
lex.unexpected("parameter name");
|
||||||
}
|
}
|
||||||
arg_order.push_back(arg_idx);
|
arg_order.push_back(arg_idx);
|
||||||
lex.next();
|
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>();
|
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;
|
TypeExpr* ret_type = nullptr;
|
||||||
|
bool returns_self = false;
|
||||||
if (lex.tok() == tok_colon) { // : <ret_type> (if absent, it means "auto infer", not void)
|
if (lex.tok() == tok_colon) { // : <ret_type> (if absent, it means "auto infer", not void)
|
||||||
lex.next();
|
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");
|
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;
|
AnyV v_body = nullptr;
|
||||||
|
|
||||||
|
@ -1030,17 +1088,19 @@ static AnyV parse_function_declaration(Lexer& lex, const std::vector<V<ast_annot
|
||||||
if (!ret_type) {
|
if (!ret_type) {
|
||||||
lex.error("asm function must specify return 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 {
|
} else {
|
||||||
lex.unexpected("{ function body }");
|
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->ret_type = ret_type ? ret_type : TypeExpr::new_hole();
|
||||||
f_declaration->is_entrypoint = is_entrypoint;
|
f_declaration->is_entrypoint = is_entrypoint;
|
||||||
f_declaration->genericsT_list = genericsT_list;
|
f_declaration->genericsT_list = genericsT_list;
|
||||||
f_declaration->marked_as_get_method = is_get_method;
|
f_declaration->marked_as_get_method = is_get_method;
|
||||||
f_declaration->marked_as_builtin = v_body->type == ast_empty;
|
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) {
|
for (auto v_annotation : annotations) {
|
||||||
switch (v_annotation->kind) {
|
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;
|
f_declaration->marked_as_pure = true;
|
||||||
break;
|
break;
|
||||||
case AnnotationKind::method_id:
|
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");
|
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>();
|
f_declaration->method_id = v_annotation->get_arg()->get_item(0)->as<ast_int_const>();
|
||||||
|
|
|
@ -80,8 +80,8 @@ protected:
|
||||||
virtual AnyV replace(V<ast_bool_const> v) { return replace_children(v); }
|
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_null_keyword> v) { return replace_children(v); }
|
||||||
virtual AnyV replace(V<ast_function_call> 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_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_unary_operator> v) { return replace_children(v); }
|
||||||
virtual AnyV replace(V<ast_binary_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); }
|
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_string_const: return replace(v->as<ast_string_const>());
|
||||||
case ast_bool_const: return replace(v->as<ast_bool_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_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_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_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_unary_operator: return replace(v->as<ast_unary_operator>());
|
||||||
case ast_binary_operator: return replace(v->as<ast_binary_operator>());
|
case ast_binary_operator: return replace(v->as<ast_binary_operator>());
|
||||||
case ast_ternary_operator: return replace(v->as<ast_ternary_operator>());
|
case ast_ternary_operator: return replace(v->as<ast_ternary_operator>());
|
||||||
|
|
|
@ -40,11 +40,14 @@ class ASTStringifier final : public ASTVisitor {
|
||||||
{ast_string_const, "ast_string_const"},
|
{ast_string_const, "ast_string_const"},
|
||||||
{ast_bool_const, "ast_bool_const"},
|
{ast_bool_const, "ast_bool_const"},
|
||||||
{ast_null_keyword, "ast_null_keyword"},
|
{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_function_call, "ast_function_call"},
|
||||||
|
{ast_dot_method_call, "ast_dot_method_call"},
|
||||||
{ast_global_var_declaration, "ast_global_var_declaration"},
|
{ast_global_var_declaration, "ast_global_var_declaration"},
|
||||||
{ast_constant_declaration, "ast_constant_declaration"},
|
{ast_constant_declaration, "ast_constant_declaration"},
|
||||||
{ast_underscore, "ast_underscore"},
|
{ast_underscore, "ast_underscore"},
|
||||||
{ast_dot_tilde_call, "ast_dot_tilde_call"},
|
|
||||||
{ast_unary_operator, "ast_unary_operator"},
|
{ast_unary_operator, "ast_unary_operator"},
|
||||||
{ast_binary_operator, "ast_binary_operator"},
|
{ast_binary_operator, "ast_binary_operator"},
|
||||||
{ast_ternary_operator, "ast_ternary_operator"},
|
{ast_ternary_operator, "ast_ternary_operator"},
|
||||||
|
@ -125,12 +128,12 @@ class ASTStringifier final : public ASTVisitor {
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
case ast_dot_method_call:
|
||||||
|
return static_cast<std::string>(v->as<ast_dot_method_call>()->method_name);
|
||||||
case ast_global_var_declaration:
|
case ast_global_var_declaration:
|
||||||
return static_cast<std::string>(v->as<ast_global_var_declaration>()->get_identifier()->name);
|
return static_cast<std::string>(v->as<ast_global_var_declaration>()->get_identifier()->name);
|
||||||
case ast_constant_declaration:
|
case ast_constant_declaration:
|
||||||
return static_cast<std::string>(v->as<ast_constant_declaration>()->get_identifier()->name);
|
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:
|
case ast_unary_operator:
|
||||||
return static_cast<std::string>(v->as<ast_unary_operator>()->operator_name);
|
return static_cast<std::string>(v->as<ast_unary_operator>()->operator_name);
|
||||||
case ast_binary_operator:
|
case ast_binary_operator:
|
||||||
|
@ -208,11 +211,14 @@ public:
|
||||||
case ast_string_const: return handle_vertex(v->as<ast_string_const>());
|
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_bool_const: return handle_vertex(v->as<ast_bool_const>());
|
||||||
case ast_null_keyword: return handle_vertex(v->as<ast_null_keyword>());
|
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_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_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_constant_declaration: return handle_vertex(v->as<ast_constant_declaration>());
|
||||||
case ast_underscore: return handle_vertex(v->as<ast_underscore>());
|
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_unary_operator: return handle_vertex(v->as<ast_unary_operator>());
|
||||||
case ast_binary_operator: return handle_vertex(v->as<ast_binary_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>());
|
case ast_ternary_operator: return handle_vertex(v->as<ast_ternary_operator>());
|
||||||
|
|
|
@ -75,9 +75,10 @@ protected:
|
||||||
virtual void visit(V<ast_string_const> v) { return visit_children(v); }
|
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_bool_const> v) { return visit_children(v); }
|
||||||
virtual void visit(V<ast_null_keyword> 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_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_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_unary_operator> v) { return visit_children(v); }
|
||||||
virtual void visit(V<ast_binary_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); }
|
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_string_const: return visit(v->as<ast_string_const>());
|
||||||
case ast_bool_const: return visit(v->as<ast_bool_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_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_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_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_unary_operator: return visit(v->as<ast_unary_operator>());
|
||||||
case ast_binary_operator: return visit(v->as<ast_binary_operator>());
|
case ast_binary_operator: return visit(v->as<ast_binary_operator>());
|
||||||
case ast_ternary_operator: return visit(v->as<ast_ternary_operator>());
|
case ast_ternary_operator: return visit(v->as<ast_ternary_operator>());
|
||||||
|
|
10
tolk/ast.cpp
10
tolk/ast.cpp
|
@ -86,6 +86,16 @@ int Vertex<ast_parameter_list>::lookup_idx(std::string_view param_name) const {
|
||||||
return -1;
|
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 {
|
void Vertex<ast_import_statement>::mutate_set_src_file(const SrcFile* file) const {
|
||||||
const_cast<Vertex*>(this)->file = file;
|
const_cast<Vertex*>(this)->file = file;
|
||||||
}
|
}
|
||||||
|
|
82
tolk/ast.h
82
tolk/ast.h
|
@ -68,11 +68,14 @@ enum ASTNodeType {
|
||||||
ast_string_const,
|
ast_string_const,
|
||||||
ast_bool_const,
|
ast_bool_const,
|
||||||
ast_null_keyword,
|
ast_null_keyword,
|
||||||
|
ast_self_keyword,
|
||||||
|
ast_argument,
|
||||||
|
ast_argument_list,
|
||||||
ast_function_call,
|
ast_function_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_unary_operator,
|
ast_unary_operator,
|
||||||
ast_binary_operator,
|
ast_binary_operator,
|
||||||
ast_ternary_operator,
|
ast_ternary_operator,
|
||||||
|
@ -285,13 +288,50 @@ struct Vertex<ast_null_keyword> final : ASTNodeLeaf {
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct Vertex<ast_function_call> final : ASTNodeBinary {
|
struct Vertex<ast_self_keyword> final : ASTNodeLeaf {
|
||||||
// even for f(1,2,3), f (lhs) is called with a single arg (tensor "(1,2,3)") (rhs)
|
explicit Vertex(SrcLocation loc)
|
||||||
AnyV get_called_f() const { return lhs; }
|
: ASTNodeLeaf(ast_self_keyword, loc) {}
|
||||||
auto get_called_arg() const { return rhs->as<ast_tensor>(); }
|
};
|
||||||
|
|
||||||
Vertex(SrcLocation loc, AnyV lhs_f, V<ast_tensor> arg)
|
template<>
|
||||||
: ASTNodeBinary(ast_function_call, loc, lhs_f, arg) {}
|
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<>
|
template<>
|
||||||
|
@ -321,17 +361,6 @@ struct Vertex<ast_underscore> final : ASTNodeLeaf {
|
||||||
: ASTNodeLeaf(ast_underscore, loc) {}
|
: 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<>
|
template<>
|
||||||
struct Vertex<ast_unary_operator> final : ASTNodeUnary {
|
struct Vertex<ast_unary_operator> final : ASTNodeUnary {
|
||||||
std::string_view operator_name;
|
std::string_view operator_name;
|
||||||
|
@ -475,11 +504,13 @@ struct Vertex<ast_genericsT_list> final : ASTNodeVararg {
|
||||||
template<>
|
template<>
|
||||||
struct Vertex<ast_parameter> final : ASTNodeUnary {
|
struct Vertex<ast_parameter> final : ASTNodeUnary {
|
||||||
TypeExpr* param_type;
|
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)
|
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) {}
|
: ASTNodeUnary(ast_parameter, loc, name_identifier), param_type(param_type), declared_as_mutate(declared_as_mutate) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -491,6 +522,8 @@ struct Vertex<ast_parameter_list> final : ASTNodeVararg {
|
||||||
: ASTNodeVararg(ast_parameter_list, loc, std::move(params)) {}
|
: ASTNodeVararg(ast_parameter_list, loc, std::move(params)) {}
|
||||||
|
|
||||||
int lookup_idx(std::string_view param_name) const;
|
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<>
|
template<>
|
||||||
|
@ -519,12 +552,13 @@ struct Vertex<ast_annotation> final : ASTNodeUnary {
|
||||||
template<>
|
template<>
|
||||||
struct Vertex<ast_local_var> final : ASTNodeUnary {
|
struct Vertex<ast_local_var> final : ASTNodeUnary {
|
||||||
TypeExpr* declared_type;
|
TypeExpr* declared_type;
|
||||||
|
bool is_immutable; // declared via 'val', not 'var'
|
||||||
bool marked_as_redef; // var (existing_var redef, new_var: int) = ...
|
bool marked_as_redef; // var (existing_var redef, new_var: int) = ...
|
||||||
|
|
||||||
AnyV get_identifier() const { return child; } // ast_identifier / ast_underscore
|
AnyV get_identifier() const { return child; } // ast_identifier / ast_underscore
|
||||||
|
|
||||||
Vertex(SrcLocation loc, AnyV name_identifier, TypeExpr* declared_type, bool 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), marked_as_redef(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<>
|
template<>
|
||||||
|
@ -552,6 +586,8 @@ struct Vertex<ast_function_declaration> final : ASTNodeVararg {
|
||||||
bool marked_as_get_method = false;
|
bool marked_as_get_method = false;
|
||||||
bool marked_as_inline = false;
|
bool marked_as_inline = false;
|
||||||
bool marked_as_inline_ref = false;
|
bool marked_as_inline_ref = false;
|
||||||
|
bool accepts_self = false;
|
||||||
|
bool returns_self = false;
|
||||||
V<ast_int_const> method_id = nullptr;
|
V<ast_int_const> method_id = nullptr;
|
||||||
|
|
||||||
bool is_asm_function() const { return children.at(2)->type == ast_asm_body; }
|
bool is_asm_function() const { return children.at(2)->type == ast_asm_body; }
|
||||||
|
|
|
@ -29,44 +29,60 @@ using namespace std::literals::string_literals;
|
||||||
SymDef* define_builtin_func_impl(const std::string& name, SymValAsmFunc* func_val) {
|
SymDef* define_builtin_func_impl(const std::string& name, SymValAsmFunc* func_val) {
|
||||||
sym_idx_t name_idx = G.symbols.lookup_add(name);
|
sym_idx_t name_idx = G.symbols.lookup_add(name);
|
||||||
SymDef* def = define_global_symbol(name_idx);
|
SymDef* def = define_global_symbol(name_idx);
|
||||||
if (!def) {
|
tolk_assert(!def->value);
|
||||||
std::cerr << "fatal: global function `" << name << "` already defined" << std::endl;
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
func_val->flags |= SymValFunc::flagBuiltinFunction;
|
|
||||||
def->value = func_val;
|
def->value = func_val;
|
||||||
#ifdef TOLK_DEBUG
|
#ifdef TOLK_DEBUG
|
||||||
dynamic_cast<SymValAsmFunc*>(def->value)->name = name;
|
def->value->sym_name = name;
|
||||||
#endif
|
#endif
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, bool impure = false) {
|
// given func_type = `(slice, int) -> slice` and func flags, create SymDef for parameters
|
||||||
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, !impure});
|
// 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) {
|
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{func_type, func, !impure});
|
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) {
|
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{func_type, make_simple_compile(macro), !impure});
|
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,
|
static SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, int flags,
|
||||||
std::initializer_list<int> ret_order = {}, bool impure = false) {
|
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order) {
|
||||||
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, arg_order, ret_order, !impure});
|
return define_builtin_func_impl(name, new SymValAsmFunc(define_builtin_parameters(func_type, flags), func_type, func, flags | SymValFunc::flagBuiltinFunction, arg_order, ret_order));
|
||||||
}
|
|
||||||
|
|
||||||
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});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in,
|
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);
|
tolk_assert(res.empty() && args.size() == 3);
|
||||||
VarDescr &x = args[0], &y = args[1], &z = args[2];
|
VarDescr &x = args[0], &y = args[1], &z = args[2];
|
||||||
if (!z.always_true() && !z.always_false()) {
|
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");
|
return AsmOp::Const(val ? "TRUE" : "FALSE");
|
||||||
}
|
}
|
||||||
|
|
||||||
// (slice, int) load_int(slice s, int len) asm(s len -> 1 0) "LDIX";
|
// fun loadInt (mutate s: slice, len: int): int asm(s len -> 1 0) "LDIX";
|
||||||
// (slice, int) load_uint(slice s, int len) asm( -> 1 0) "LDUX";
|
// fun loadUint (mutate s: slice, len: int): int asm( -> 1 0) "LDUX";
|
||||||
// int preload_int(slice s, int len) asm "PLDIX";
|
// fun preloadInt (s: slice, len: int): int asm "PLDIX";
|
||||||
// int preload_uint(slice s, int len) asm "PLDUX";
|
// 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) {
|
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);
|
tolk_assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
|
||||||
auto &y = args[1], &r = res.back();
|
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);
|
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";
|
// fun storeInt (mutate self: builder, x: int, len: int): self asm(x b len) "STIX";
|
||||||
// builder store_int(builder b, int x, int len) 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) {
|
AsmOp compile_store_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool sgnd) {
|
||||||
tolk_assert(args.size() == 3 && res.size() == 1);
|
tolk_assert(args.size() == 3 && res.size() == 1);
|
||||||
auto& z = args[2];
|
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);
|
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) {
|
AsmOp compile_fetch_slice(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool fetch) {
|
||||||
tolk_assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
|
tolk_assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
|
||||||
auto& y = args[1];
|
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);
|
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) {
|
AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation) {
|
||||||
tolk_assert(args.size() == 2 && res.size() == 1);
|
tolk_assert(args.size() == 2 && res.size() == 1);
|
||||||
auto& y = args[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() {
|
void define_builtins() {
|
||||||
using namespace std::placeholders;
|
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)
|
TypeExpr* Unit = TypeExpr::new_unit();
|
||||||
static_cast<void>(Z);
|
TypeExpr* Int = TypeExpr::new_atomic(TypeExpr::_Int);
|
||||||
static_cast<void>(XY);
|
TypeExpr* Slice = TypeExpr::new_atomic(TypeExpr::_Slice);
|
||||||
static_cast<void>(Cell);
|
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_add,
|
||||||
define_builtin_func("_-_", arith_bin_op, compile_sub);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("-_", arith_un_op, compile_unary_minus);
|
define_builtin_func("_-_", arith_bin_op, compile_sub,
|
||||||
define_builtin_func("+_", arith_un_op, compile_unary_plus);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("_*_", arith_bin_op, compile_mul);
|
define_builtin_func("-_", arith_un_op, compile_unary_minus,
|
||||||
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));
|
define_builtin_func("+_", arith_un_op, compile_unary_plus,
|
||||||
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));
|
define_builtin_func("_*_", arith_bin_op, compile_mul,
|
||||||
define_builtin_func("_<<_", arith_bin_op, compile_lshift);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1));
|
define_builtin_func("_/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1),
|
||||||
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));
|
define_builtin_func("_~/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 0),
|
||||||
define_builtin_func("!_", arith_un_op, compile_logical_not);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("~_", arith_un_op, compile_bitwise_not);
|
define_builtin_func("_^/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 1),
|
||||||
define_builtin_func("_&_", arith_bin_op, compile_bitwise_and);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("_|_", arith_bin_op, compile_bitwise_or);
|
define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1),
|
||||||
define_builtin_func("_^_", arith_bin_op, compile_bitwise_xor);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("^_+=_", arith_bin_op, compile_add);
|
define_builtin_func("_<<_", arith_bin_op, compile_lshift,
|
||||||
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("^_*=_", arith_bin_op, compile_mul);
|
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1),
|
||||||
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));
|
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 0),
|
||||||
define_builtin_func("^_<<=_", arith_bin_op, compile_lshift);
|
SymValFunc::flagMarkedAsPure);
|
||||||
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, 1),
|
||||||
define_builtin_func("^_&=_", arith_bin_op, compile_bitwise_and);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("^_|=_", arith_bin_op, compile_bitwise_or);
|
define_builtin_func("!_", arith_un_op, compile_logical_not,
|
||||||
define_builtin_func("^_^=_", arith_bin_op, compile_bitwise_xor);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("mulDivFloor", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, -1));
|
define_builtin_func("~_", arith_un_op, compile_bitwise_not,
|
||||||
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));
|
define_builtin_func("_&_", arith_bin_op, compile_bitwise_and,
|
||||||
define_builtin_func("mulDivMod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2));
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2));
|
define_builtin_func("_|_", arith_bin_op, compile_bitwise_or,
|
||||||
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));
|
define_builtin_func("_^_", arith_bin_op, compile_bitwise_xor,
|
||||||
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));
|
define_builtin_func("^_+=_", arith_bin_op, compile_add,
|
||||||
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));
|
define_builtin_func("^_-=_", arith_bin_op, compile_sub,
|
||||||
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));
|
define_builtin_func("^_*=_", arith_bin_op, compile_mul,
|
||||||
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);
|
define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1),
|
||||||
define_builtin_func("__throw", impure_un_op, compile_throw, true);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("__throw_arg", throw_arg_op, compile_throw_arg, true);
|
define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1),
|
||||||
define_builtin_func("__throw_if_unless", TypeExpr::new_map(Int3, Unit), std::bind(compile_throw_if_unless, _1, _2), true);
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("loadInt", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0});
|
define_builtin_func("^_<<=_", arith_bin_op, compile_lshift,
|
||||||
define_builtin_func("loadUint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0});
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("loadBits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0});
|
define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1),
|
||||||
define_builtin_func("preloadInt", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true));
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("preloadUint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false));
|
define_builtin_func("^_&=_", arith_bin_op, compile_bitwise_and,
|
||||||
define_builtin_func("preloadBits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false));
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("storeInt", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
|
define_builtin_func("^_|=_", arith_bin_op, compile_bitwise_or,
|
||||||
define_builtin_func("storeUint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("~storeInt", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
|
define_builtin_func("^_^=_", arith_bin_op, compile_bitwise_xor,
|
||||||
define_builtin_func("~storeUint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
|
SymValFunc::flagMarkedAsPure);
|
||||||
define_builtin_func("tupleAt", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at);
|
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)),
|
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)),
|
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),
|
define_builtin_func("debugDumpStack", TypeExpr::new_map(Unit, Unit),
|
||||||
AsmOp::Custom("DUMPSTK", 0, 0), true);
|
AsmOp::Custom("DUMPSTK", 0, 0),
|
||||||
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tolk
|
} // namespace tolk
|
||||||
|
|
|
@ -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) {
|
if (e_type) {
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
switch (cls) {
|
switch (cls) {
|
||||||
case _Apply: {
|
case _Apply: {
|
||||||
if (!sym) {
|
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()) {
|
if (!sym_val || !sym_val->get_type()) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
std::vector<TypeExpr*> arg_types;
|
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);
|
arg_types.push_back(arg->e_type);
|
||||||
}
|
}
|
||||||
TypeExpr* fun_type = TypeExpr::new_map(TypeExpr::new_tensor(arg_types), TypeExpr::new_hole());
|
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];
|
e_type = fun_type->args[1];
|
||||||
TypeExpr::remove_indirect(e_type);
|
TypeExpr::remove_indirect(e_type);
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
case _VarApply: {
|
case _VarApply: {
|
||||||
tolk_assert(args.size() == 2);
|
tolk_assert(args.size() == 2);
|
||||||
|
@ -84,7 +85,27 @@ bool Expr::deduce_type() {
|
||||||
}
|
}
|
||||||
e_type = fun_type->args[1];
|
e_type = fun_type->args[1];
|
||||||
TypeExpr::remove_indirect(e_type);
|
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: {
|
case _Letop: {
|
||||||
tolk_assert(args.size() == 2);
|
tolk_assert(args.size() == 2);
|
||||||
|
@ -99,25 +120,7 @@ bool Expr::deduce_type() {
|
||||||
}
|
}
|
||||||
e_type = args[0]->e_type;
|
e_type = args[0]->e_type;
|
||||||
TypeExpr::remove_indirect(e_type);
|
TypeExpr::remove_indirect(e_type);
|
||||||
return true;
|
return;
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
case _CondExpr: {
|
case _CondExpr: {
|
||||||
tolk_assert(args.size() == 3);
|
tolk_assert(args.size() == 3);
|
||||||
|
@ -139,46 +142,46 @@ bool Expr::deduce_type() {
|
||||||
}
|
}
|
||||||
e_type = args[1]->e_type;
|
e_type = args[1]->e_type;
|
||||||
TypeExpr::remove_indirect(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) {
|
switch (cls) {
|
||||||
case _Tensor:
|
case _Tensor:
|
||||||
case _MkTuple: {
|
case _MkTuple: {
|
||||||
int res = 0;
|
for (Expr* item : args) {
|
||||||
for (const auto& x : args) {
|
item->define_new_vars(code);
|
||||||
res += x->define_new_vars(code);
|
|
||||||
}
|
}
|
||||||
return res;
|
break;
|
||||||
}
|
}
|
||||||
case _Var:
|
case _Var:
|
||||||
if (val < 0) {
|
if (val < 0) {
|
||||||
val = code.create_var(false, e_type, sym, here);
|
val = code.create_var(e_type, sym->sym_idx, here);
|
||||||
return 1;
|
sym->value->idx = val;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case _Hole:
|
case _Hole:
|
||||||
if (val < 0) {
|
if (val < 0) {
|
||||||
val = code.create_var(true, e_type, nullptr, here);
|
val = code.create_tmp_var(e_type, here);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Expr::predefine_vars() {
|
void Expr::predefine_vars() {
|
||||||
switch (cls) {
|
switch (cls) {
|
||||||
case _Tensor:
|
case _Tensor:
|
||||||
case _MkTuple: {
|
case _MkTuple: {
|
||||||
int res = 0;
|
for (Expr* item : args) {
|
||||||
for (const auto& x : args) {
|
item->predefine_vars();
|
||||||
res += x->predefine_vars();
|
|
||||||
}
|
}
|
||||||
return res;
|
break;
|
||||||
}
|
}
|
||||||
case _Var:
|
case _Var:
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
|
@ -188,12 +191,15 @@ int Expr::predefine_vars() {
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
throw ParseError{here, std::string{"redefined variable `"} + G.symbols.get_name(~val) + "`"};
|
throw ParseError{here, std::string{"redefined variable `"} + G.symbols.get_name(~val) + "`"};
|
||||||
}
|
}
|
||||||
sym->value = new SymVal{SymValKind::_Var, -1, e_type};
|
sym->value = new SymValVariable(-1, e_type);
|
||||||
return 1;
|
if (is_immutable()) {
|
||||||
|
dynamic_cast<SymValVariable*>(sym->value)->flags |= SymValVariable::flagImmutable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var_idx_t Expr::new_tmp(CodeBlob& code) const {
|
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);
|
auto unpacked_type = rhs->e_type->args.at(0);
|
||||||
std::vector<var_idx_t> tmp{code.create_tmp_var(unpacked_type, rhs->here)};
|
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));
|
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_val(tmp[0]);
|
||||||
tvar->set_location(rhs->here);
|
tvar->set_location(rhs->here);
|
||||||
tvar->e_type = unpacked_type;
|
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);
|
res_lists[i] = args[i]->pre_compile(code, lval_globs);
|
||||||
for (size_t j = 0; j < res_lists[i].size(); ++j) {
|
for (size_t j = 0; j < res_lists[i].size(); ++j) {
|
||||||
TmpVar& var = code.vars.at(res_lists[i][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 {
|
var.on_modification.push_back([&modified_vars, i, j, cur_ops = code.cur_ops, done = false](SrcLocation here) mutable {
|
||||||
if (!done) {
|
if (!done) {
|
||||||
done = true;
|
done = true;
|
||||||
|
@ -303,39 +309,39 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
|
||||||
}
|
}
|
||||||
case _Apply: {
|
case _Apply: {
|
||||||
tolk_assert(sym);
|
tolk_assert(sym);
|
||||||
std::vector<var_idx_t> res;
|
std::vector<var_idx_t> res = pre_compile_tensor(args, code, lval_globs);;
|
||||||
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);
|
|
||||||
}
|
|
||||||
auto rvect = new_tmp_vect(code);
|
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) {
|
if (flags & _IsImpure) {
|
||||||
op.set_impure(code);
|
op.set_impure(code);
|
||||||
}
|
}
|
||||||
return rvect;
|
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 _Var:
|
||||||
case _Hole:
|
case _Hole:
|
||||||
if (val < 0) {
|
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)) {
|
if (auto fun_ref = dynamic_cast<SymValFunc*>(sym->value)) {
|
||||||
fun_ref->flags |= SymValFunc::flagUsedAsNonCall;
|
fun_ref->flags |= SymValFunc::flagUsedAsNonCall;
|
||||||
if (!fun_ref->arg_order.empty() || !fun_ref->ret_order.empty()) {
|
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);
|
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: {
|
case _Letop: {
|
||||||
return pre_compile_let(code, args.at(0), args.at(1), here);
|
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: {
|
case _MkTuple: {
|
||||||
auto left = new_tmp_vect(code);
|
auto left = new_tmp_vect(code);
|
||||||
auto right = args[0]->pre_compile(code);
|
auto right = args[0]->pre_compile(code);
|
||||||
|
|
|
@ -323,8 +323,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
|
||||||
static TokenType maybe_keyword(std::string_view str) {
|
static TokenType maybe_keyword(std::string_view str) {
|
||||||
switch (str.size()) {
|
switch (str.size()) {
|
||||||
case 1:
|
case 1:
|
||||||
if (str == "~") return tok_bitwise_not; // todo attention
|
if (str == "_") return tok_underscore;
|
||||||
if (str == "_") return tok_underscore; // todo attention
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (str == "do") return tok_do;
|
if (str == "do") return tok_do;
|
||||||
|
@ -347,6 +346,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
|
||||||
if (str == "void") return tok_void;
|
if (str == "void") return tok_void;
|
||||||
if (str == "bool") return tok_bool;
|
if (str == "bool") return tok_bool;
|
||||||
if (str == "auto") return tok_auto;
|
if (str == "auto") return tok_auto;
|
||||||
|
if (str == "self") return tok_self;
|
||||||
if (str == "tolk") return tok_tolk;
|
if (str == "tolk") return tok_tolk;
|
||||||
if (str == "type") return tok_type;
|
if (str == "type") return tok_type;
|
||||||
if (str == "enum") return tok_enum;
|
if (str == "enum") return tok_enum;
|
||||||
|
@ -368,6 +368,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
|
||||||
if (str == "assert") return tok_assert;
|
if (str == "assert") return tok_assert;
|
||||||
if (str == "import") return tok_import;
|
if (str == "import") return tok_import;
|
||||||
if (str == "global") return tok_global;
|
if (str == "global") return tok_global;
|
||||||
|
if (str == "mutate") return tok_mutate;
|
||||||
if (str == "repeat") return tok_repeat;
|
if (str == "repeat") return tok_repeat;
|
||||||
if (str == "struct") return tok_struct;
|
if (str == "struct") return tok_struct;
|
||||||
if (str == "export") return tok_export;
|
if (str == "export") return tok_export;
|
||||||
|
@ -394,8 +395,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
|
||||||
lex->skip_chars(1);
|
lex->skip_chars(1);
|
||||||
while (!lex->is_eof()) {
|
while (!lex->is_eof()) {
|
||||||
char c = lex->char_at();
|
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 == '$';
|
||||||
bool allowed_in_identifier = std::isalnum(c) || c == '_' || c == '$' || c == '?' || c == '!' || c == '\'';
|
|
||||||
if (!allowed_in_identifier) {
|
if (!allowed_in_identifier) {
|
||||||
break;
|
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.
|
// Here we define a grammar of Tolk.
|
||||||
|
@ -500,11 +478,8 @@ struct TolkLanguageGrammar {
|
||||||
trie.add_prefix("\n", singleton<ChunkSkipWhitespace>());
|
trie.add_prefix("\n", singleton<ChunkSkipWhitespace>());
|
||||||
|
|
||||||
trie.add_pattern("[0-9]", singleton<ChunkNumber>());
|
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>());
|
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_plus);
|
||||||
register_token("-", 1, tok_minus);
|
register_token("-", 1, tok_minus);
|
||||||
|
@ -528,6 +503,8 @@ struct TolkLanguageGrammar {
|
||||||
register_token("&", 1, tok_bitwise_and);
|
register_token("&", 1, tok_bitwise_and);
|
||||||
register_token("|", 1, tok_bitwise_or);
|
register_token("|", 1, tok_bitwise_or);
|
||||||
register_token("^", 1, tok_bitwise_xor);
|
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_eq);
|
||||||
register_token("!=", 2, tok_neq);
|
register_token("!=", 2, tok_neq);
|
||||||
register_token("<=", 2, tok_leq);
|
register_token("<=", 2, tok_leq);
|
||||||
|
|
|
@ -38,6 +38,8 @@ enum TokenType {
|
||||||
tok_var,
|
tok_var,
|
||||||
tok_val,
|
tok_val,
|
||||||
tok_redef,
|
tok_redef,
|
||||||
|
tok_mutate,
|
||||||
|
tok_self,
|
||||||
|
|
||||||
tok_annotation_at,
|
tok_annotation_at,
|
||||||
tok_colon,
|
tok_colon,
|
||||||
|
@ -52,6 +54,7 @@ enum TokenType {
|
||||||
tok_null,
|
tok_null,
|
||||||
|
|
||||||
tok_identifier,
|
tok_identifier,
|
||||||
|
tok_dot,
|
||||||
|
|
||||||
tok_plus,
|
tok_plus,
|
||||||
tok_minus,
|
tok_minus,
|
||||||
|
|
|
@ -37,7 +37,87 @@ static int calc_sym_idx(std::string_view sym_name) {
|
||||||
return G.symbols.lookup(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);
|
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) {
|
static void check_global_func(SrcLocation loc, sym_idx_t func_name) {
|
||||||
SymDef* sym_def = lookup_symbol(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) {
|
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)) {
|
if (!v_usage->loc.is_symbol_from_same_or_builtin_file(used_sym->loc)) {
|
||||||
const SrcFile* declared_in = used_sym->loc.get_src_file();
|
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));
|
SymDef* sym = lookup_symbol(calc_sym_idx(var_name));
|
||||||
if (sym) { // creating a new variable, but something found in symtable
|
if (sym) { // creating a new variable, but something found in symtable
|
||||||
if (sym->level != G.scope_level) {
|
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};
|
Expr* x = new Expr{Expr::_Var, loc};
|
||||||
x->val = ~calc_sym_idx(var_name);
|
x->val = ~calc_sym_idx(var_name);
|
||||||
x->e_type = var_type;
|
x->e_type = var_type;
|
||||||
x->flags = Expr::_IsLvalue;
|
x->flags = Expr::_IsLvalue | (is_immutable ? Expr::_IsImmutable : 0);
|
||||||
return x;
|
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_mod || t == tok_set_lshift || t == tok_set_rshift ||
|
||||||
t == tok_set_bitwise_and || t == tok_set_bitwise_or || t == tok_set_bitwise_xor) {
|
t == tok_set_bitwise_and || t == tok_set_bitwise_or || t == tok_set_bitwise_xor) {
|
||||||
Expr* x = process_expr(v->get_lhs(), code);
|
Expr* x = process_expr(v->get_lhs(), code);
|
||||||
x->chk_lvalue();
|
|
||||||
x->chk_rvalue();
|
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 + "_");
|
sym_idx_t name = G.symbols.lookup_add("^_" + operator_name + "_");
|
||||||
Expr* y = process_expr(v->get_rhs(), code);
|
Expr* y = process_expr(v->get_rhs(), code);
|
||||||
y->chk_rvalue();
|
y->chk_rvalue();
|
||||||
|
@ -126,7 +195,12 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
|
||||||
}
|
}
|
||||||
if (t == tok_assign) {
|
if (t == tok_assign) {
|
||||||
Expr* x = process_expr(v->get_lhs(), code);
|
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);
|
Expr* y = process_expr(v->get_rhs(), code);
|
||||||
y->chk_rvalue();
|
y->chk_rvalue();
|
||||||
x->predefine_vars();
|
x->predefine_vars();
|
||||||
|
@ -191,54 +265,6 @@ static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
|
||||||
return res;
|
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) {
|
static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code) {
|
||||||
Expr* cond = process_expr(v->get_cond(), code);
|
Expr* cond = process_expr(v->get_cond(), code);
|
||||||
cond->chk_rvalue();
|
cond->chk_rvalue();
|
||||||
|
@ -253,19 +279,194 @@ static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code) {
|
||||||
return res;
|
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
|
// special error for "null()" which is a FunC syntax
|
||||||
if (v->get_called_f()->type == ast_null_keyword) {
|
if (v->get_called_f()->type == ast_null_keyword) {
|
||||||
v->error("null is not a function: use `null`, not `null()`");
|
v->error("null is not a function: use `null`, not `null()`");
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr* res = process_expr(v->get_called_f(), code);
|
// most likely it's a global function, but also may be `some_var(args)` or even `getF()(args)`
|
||||||
Expr* x = process_expr(v->get_called_arg(), code);
|
Expr* lhs = process_expr(v->get_called_f(), code);
|
||||||
x->chk_rvalue();
|
if (lhs->cls != Expr::_GlobFunc) {
|
||||||
res = make_func_apply(res, x);
|
Expr* tensor_arg = new Expr(Expr::_Tensor, v->loc);
|
||||||
res->here = v->loc;
|
std::vector<TypeExpr*> type_list;
|
||||||
res->deduce_type();
|
type_list.reserve(v->get_num_args());
|
||||||
return res;
|
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) {
|
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) {
|
for (int i = 1; i < v->size(); ++i) {
|
||||||
Expr* x = process_expr(v->get_item(i), code);
|
Expr* x = process_expr(v->get_item(i), code);
|
||||||
res->pb_arg(x);
|
res->pb_arg(x);
|
||||||
f &= x->flags;
|
f &= (x->flags | Expr::_IsImmutable);
|
||||||
|
f |= (x->flags & Expr::_IsImmutable);
|
||||||
type_list.push_back(x->e_type);
|
type_list.push_back(x->e_type);
|
||||||
}
|
}
|
||||||
res->here = v->loc;
|
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) {
|
for (int i = 1; i < v->size(); ++i) {
|
||||||
Expr* x = process_expr(v->get_item(i), code);
|
Expr* x = process_expr(v->get_item(i), code);
|
||||||
res->pb_arg(x);
|
res->pb_arg(x);
|
||||||
f &= x->flags;
|
f &= (x->flags | Expr::_IsImmutable);
|
||||||
|
f |= (x->flags & Expr::_IsImmutable);
|
||||||
type_list.push_back(x->e_type);
|
type_list.push_back(x->e_type);
|
||||||
}
|
}
|
||||||
res->here = v->loc;
|
res->here = v->loc;
|
||||||
|
@ -419,21 +622,36 @@ static Expr* process_expr(V<ast_bool_const> v) {
|
||||||
return res;
|
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"));
|
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__null"));
|
||||||
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
|
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
|
||||||
|
res->here = v->loc;
|
||||||
res->flags = Expr::_IsRvalue;
|
res->flags = Expr::_IsRvalue;
|
||||||
res->deduce_type();
|
res->deduce_type();
|
||||||
return res;
|
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) {
|
static Expr* process_identifier(V<ast_identifier> v) {
|
||||||
SymDef* sym = lookup_symbol(calc_sym_idx(v->name));
|
SymDef* sym = lookup_symbol(calc_sym_idx(v->name));
|
||||||
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
|
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
|
||||||
check_import_exists_when_using_sym(v, sym);
|
check_import_exists_when_using_sym(v, sym);
|
||||||
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
|
|
||||||
Expr* res = new Expr{Expr::_GlobVar, v->loc};
|
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->sym = sym;
|
||||||
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImpure;
|
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImpure;
|
||||||
return res;
|
return res;
|
||||||
|
@ -441,19 +659,20 @@ static Expr* process_identifier(V<ast_identifier> v) {
|
||||||
if (sym && dynamic_cast<SymValConst*>(sym->value)) {
|
if (sym && dynamic_cast<SymValConst*>(sym->value)) {
|
||||||
check_import_exists_when_using_sym(v, sym);
|
check_import_exists_when_using_sym(v, sym);
|
||||||
auto val = dynamic_cast<SymValConst*>(sym->value);
|
auto val = dynamic_cast<SymValConst*>(sym->value);
|
||||||
Expr* res = new Expr{Expr::_None, v->loc};
|
Expr* res = nullptr;
|
||||||
res->flags = Expr::_IsRvalue;
|
|
||||||
if (val->get_kind() == SymValConst::IntConst) {
|
if (val->get_kind() == SymValConst::IntConst) {
|
||||||
res->cls = Expr::_Const;
|
res = new Expr{Expr::_Const, v->loc};
|
||||||
res->intval = val->get_int_value();
|
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) {
|
} 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->strval = val->get_str_value();
|
||||||
res->e_type = TypeExpr::new_atomic(tok_slice);
|
res->e_type = TypeExpr::new_atomic(TypeExpr::_Slice);
|
||||||
} else {
|
} else {
|
||||||
v->error("invalid symbolic constant type");
|
v->error("invalid symbolic constant type");
|
||||||
}
|
}
|
||||||
|
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImmutable;
|
||||||
|
res->sym = sym;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (sym && dynamic_cast<SymValFunc*>(sym->value)) {
|
if (sym && dynamic_cast<SymValFunc*>(sym->value)) {
|
||||||
|
@ -463,28 +682,26 @@ static Expr* process_identifier(V<ast_identifier> v) {
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
check_global_func(v->loc, calc_sym_idx(v->name));
|
check_global_func(v->loc, calc_sym_idx(v->name));
|
||||||
sym = lookup_symbol(calc_sym_idx(v->name));
|
sym = lookup_symbol(calc_sym_idx(v->name));
|
||||||
|
tolk_assert(sym);
|
||||||
}
|
}
|
||||||
res->sym = sym;
|
res->sym = sym;
|
||||||
SymVal* val = nullptr;
|
|
||||||
bool impure = false;
|
bool impure = false;
|
||||||
if (sym) {
|
bool immutable = false;
|
||||||
val = dynamic_cast<SymVal*>(sym->value);
|
if (const SymValFunc* func_val = dynamic_cast<SymValFunc*>(sym->value)) {
|
||||||
}
|
res->e_type = func_val->get_type();
|
||||||
if (!val) {
|
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) + "'");
|
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;
|
// 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();
|
res->deduce_type();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -495,12 +712,12 @@ Expr* process_expr(AnyV v, CodeBlob& code) {
|
||||||
return process_expr(v->as<ast_binary_operator>(), code);
|
return process_expr(v->as<ast_binary_operator>(), code);
|
||||||
case ast_unary_operator:
|
case ast_unary_operator:
|
||||||
return process_expr(v->as<ast_unary_operator>(), code);
|
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:
|
case ast_ternary_operator:
|
||||||
return process_expr(v->as<ast_ternary_operator>(), code);
|
return process_expr(v->as<ast_ternary_operator>(), code);
|
||||||
case ast_function_call:
|
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:
|
case ast_parenthesized_expr:
|
||||||
return process_expr(v->as<ast_parenthesized_expr>()->get_expr(), code);
|
return process_expr(v->as<ast_parenthesized_expr>()->get_expr(), code);
|
||||||
case ast_tensor:
|
case ast_tensor:
|
||||||
|
@ -515,6 +732,8 @@ Expr* process_expr(AnyV v, CodeBlob& code) {
|
||||||
return process_expr(v->as<ast_bool_const>());
|
return process_expr(v->as<ast_bool_const>());
|
||||||
case ast_null_keyword:
|
case ast_null_keyword:
|
||||||
return process_expr(v->as<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:
|
case ast_identifier:
|
||||||
return process_identifier(v->as<ast_identifier>());
|
return process_identifier(v->as<ast_identifier>());
|
||||||
case ast_underscore:
|
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) {
|
static Expr* process_local_vars_lhs(AnyV v, CodeBlob& code) {
|
||||||
switch (v->type) {
|
switch (v->type) {
|
||||||
case ast_local_var: {
|
case ast_local_var: {
|
||||||
if (v->as<ast_local_var>()->marked_as_redef) {
|
auto v_var = v->as<ast_local_var>();
|
||||||
return process_identifier(v->as<ast_local_var>()->get_identifier()->as<ast_identifier>());
|
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>()) {
|
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 {
|
} 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:
|
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) {
|
static blk_fl::val process_vertex(V<ast_local_vars_declaration> v, CodeBlob& code) {
|
||||||
Expr* x = process_local_vars_lhs(v->get_lhs(), code);
|
Expr* x = process_local_vars_lhs(v->get_lhs(), code);
|
||||||
x->chk_lvalue();
|
|
||||||
Expr* y = process_expr(v->get_assigned_val(), code);
|
Expr* y = process_expr(v->get_assigned_val(), code);
|
||||||
y->chk_rvalue();
|
y->chk_rvalue();
|
||||||
x->predefine_vars();
|
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;
|
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) {
|
static blk_fl::val process_vertex(V<ast_return_statement> v, CodeBlob& code) {
|
||||||
Expr* expr = process_expr(v->get_return_value(), 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();
|
expr->chk_rvalue();
|
||||||
try {
|
try {
|
||||||
// std::cerr << "in return: ";
|
|
||||||
unify(expr->e_type, code.ret_type);
|
unify(expr->e_type, code.ret_type);
|
||||||
} catch (UnifyError& ue) {
|
} catch (UnifyError& ue) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
|
@ -619,22 +900,29 @@ static blk_fl::val process_vertex(V<ast_return_statement> v, CodeBlob& code) {
|
||||||
return blk_fl::ret;
|
return blk_fl::ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void append_implicit_ret_stmt(V<ast_sequence> v, CodeBlob& code) {
|
static void append_implicit_ret_stmt(SrcLocation loc_end, CodeBlob& code) {
|
||||||
TypeExpr* ret_type = TypeExpr::new_unit();
|
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 {
|
try {
|
||||||
// std::cerr << "in implicit return: ";
|
unify(expr->e_type, code.ret_type);
|
||||||
unify(ret_type, code.ret_type);
|
|
||||||
} catch (UnifyError& ue) {
|
} catch (UnifyError& ue) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << "previous function return type " << code.ret_type
|
os << "previous function return type " << code.ret_type
|
||||||
<< " cannot be unified with implicit end-of-block return type " << ret_type << ": " << ue;
|
<< " cannot be unified with implicit end-of-block return type " << expr->e_type << ": " << ue;
|
||||||
throw ParseError(v->loc_end, os.str());
|
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) {
|
static blk_fl::val process_vertex(V<ast_sequence> v, CodeBlob& code, bool no_new_scope = false) {
|
||||||
if (!no_new_scope) {
|
if (!no_new_scope) {
|
||||||
open_scope(v->loc);
|
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) {
|
static Expr* process_catch_variable(AnyV catch_var, TypeExpr* var_type) {
|
||||||
if (auto v_ident = catch_var->try_as<ast_identifier>()) {
|
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);
|
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:
|
case ast_try_catch_statement:
|
||||||
return process_vertex(v->as<ast_try_catch_statement>(), code);
|
return process_vertex(v->as<ast_try_catch_statement>(), code);
|
||||||
default: {
|
default: {
|
||||||
auto expr = process_expr(v, code);
|
Expr* expr = process_expr(v, code);
|
||||||
expr->chk_rvalue();
|
expr->chk_rvalue();
|
||||||
expr->pre_compile(code);
|
expr->pre_compile(code);
|
||||||
return blk_fl::end;
|
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) {
|
static FormalArg process_vertex(V<ast_parameter> v, SymDef* param_sym) {
|
||||||
if (v->get_identifier()->name.empty()) {
|
if (!param_sym) {
|
||||||
return std::make_tuple(v->param_type, (SymDef*)nullptr, v->loc);
|
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);
|
SymDef* new_sym_def = define_symbol(calc_sym_idx(v->get_identifier()->name), true, v->loc);
|
||||||
if (!new_sym_def) {
|
if (!new_sym_def || new_sym_def->value) {
|
||||||
v->error("cannot define symbol");
|
v->error("redefined parameter");
|
||||||
}
|
}
|
||||||
if (new_sym_def->value) {
|
const SymValVariable* param_val = dynamic_cast<SymValVariable*>(param_sym->value);
|
||||||
v->error("redefined argument");
|
new_sym_def->value = new SymValVariable(*param_val);
|
||||||
}
|
|
||||||
new_sym_def->value = new SymVal{SymValKind::_Param, fa_idx, v->param_type};
|
|
||||||
return std::make_tuple(v->param_type, new_sym_def, v->loc);
|
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);
|
tolk_assert(sym_val != nullptr);
|
||||||
|
|
||||||
open_scope(v->loc);
|
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) {
|
if (v->marked_as_pure) {
|
||||||
blob->flags |= CodeBlob::_ForbidImpure;
|
blob->flags |= CodeBlob::_ForbidImpure;
|
||||||
}
|
}
|
||||||
FormalArgList legacy_arg_list;
|
FormalArgList legacy_arg_list;
|
||||||
for (int i = 0; i < v->get_num_params(); ++i) {
|
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));
|
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));
|
blk_fl::combine(res, process_statement(item, *blob));
|
||||||
}
|
}
|
||||||
if (res & blk_fl::end) {
|
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);
|
blob->close_blk(v_body->loc_end);
|
||||||
|
|
|
@ -39,10 +39,10 @@ bool SymValCodeFunc::does_need_codegen() const {
|
||||||
if (flags & flagUsedAsNonCall) {
|
if (flags & flagUsedAsNonCall) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// when a function f() is just `return anotherF(...args)`, it doesn't need to be codegenerated at all,
|
// currently, there is no inlining, all functions are codegenerated
|
||||||
// since all its usages are inlined
|
// (but actually, unused ones are later removed by Fift)
|
||||||
return !is_just_wrapper_for_another_f();
|
// in the future, we may want to implement a true AST inlining for "simple" functions
|
||||||
// in the future, we may want to implement a true AST inlining for `inline` functions also
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymValCodeFunc::set_code(CodeBlob* code) {
|
void SymValCodeFunc::set_code(CodeBlob* code) {
|
||||||
|
|
|
@ -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);
|
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,
|
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) {
|
std::vector<int>& arg_order, std::vector<int>& ret_order) {
|
||||||
int cnt = param_list->size();
|
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
|
// todo currently, constant value calculation is dirty and roughly: init_value is evaluated to fif code
|
||||||
// and waited to be a single expression
|
// 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
|
// 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);
|
Expr* x = process_expr(init_value, code);
|
||||||
if (!x->is_rvalue()) {
|
if (!x->is_rvalue()) {
|
||||||
v->get_init_value()->error("expression is not strictly 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;
|
SymValConst* sym_val = nullptr;
|
||||||
if (x->cls == Expr::_Const) { // Integer constant
|
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)
|
} 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 `_+_`)
|
} 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>());
|
code.emplace_back(v->loc, Op::_Import, std::vector<var_idx_t>());
|
||||||
auto tmp_vars = x->pre_compile(code);
|
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()) {
|
if (op.origin.is_null() || !op.origin->is_valid()) {
|
||||||
init_value->error("precompiled expression did not result in a valid integer constant");
|
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 {
|
} else {
|
||||||
init_value->error("integer or slice literal or constant expected");
|
init_value->error("integer or slice literal or constant expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
sym_def->value = sym_val;
|
sym_def->value = sym_val;
|
||||||
#ifdef TOLK_DEBUG
|
#ifdef TOLK_DEBUG
|
||||||
dynamic_cast<SymValConst*>(sym_def->value)->name = v->get_identifier()->name;
|
sym_def->value->sym_name = v->get_identifier()->name;
|
||||||
#endif
|
#endif
|
||||||
G.all_constants.push_back(sym_def);
|
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);
|
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
|
#ifdef TOLK_DEBUG
|
||||||
dynamic_cast<SymValGlobVar*>(sym_def->value)->name = v->get_identifier()->name;
|
sym_def->value->sym_name = v->get_identifier()->name;
|
||||||
#endif
|
#endif
|
||||||
G.all_global_vars.push_back(sym_def);
|
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) {
|
static void register_function(V<ast_function_declaration> v) {
|
||||||
std::string_view func_name = v->get_identifier()->name;
|
std::string_view func_name = v->get_identifier()->name;
|
||||||
|
|
||||||
// calculate TypeExpr of a function: it's a map (args -> ret), probably surrounded by forall
|
// calculate TypeExpr of a function: it's a map (params -> ret), probably surrounded by forall
|
||||||
TypeExpr* func_type = nullptr;
|
TypeExpr* params_tensor_type = nullptr;
|
||||||
if (int n_args = v->get_num_params()) {
|
int n_params = v->get_num_params();
|
||||||
std::vector<TypeExpr*> arg_types;
|
int n_mutate_params = 0;
|
||||||
arg_types.reserve(n_args);
|
std::vector<SymDef*> parameters_syms;
|
||||||
for (int idx = 0; idx < n_args; ++idx) {
|
if (n_params) {
|
||||||
arg_types.emplace_back(v->get_param(idx)->param_type);
|
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 {
|
} 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) {
|
if (v->genericsT_list) {
|
||||||
std::vector<TypeExpr*> type_vars;
|
std::vector<TypeExpr*> type_vars;
|
||||||
type_vars.reserve(v->genericsT_list->size());
|
type_vars.reserve(v->genericsT_list->size());
|
||||||
for (int idx = 0; idx < v->genericsT_list->size(); ++idx) {
|
for (int idx = 0; idx < v->genericsT_list->size(); ++idx) {
|
||||||
type_vars.emplace_back(v->genericsT_list->get_item(idx)->created_type);
|
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) {
|
if (v->marked_as_builtin) {
|
||||||
const SymDef* builtin_func = lookup_symbol(G.symbols.lookup(func_name));
|
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
|
#ifdef TOLK_DEBUG
|
||||||
// in release, we don't need this check, since `builtin` is used only in stdlib, which is our responsibility
|
// 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");
|
v->error("declaration for `builtin` function doesn't match an actual one");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -309,7 +280,7 @@ static void register_function(V<ast_function_declaration> v) {
|
||||||
fire_error_redefinition_of_symbol(v->get_identifier(), sym_def);
|
fire_error_redefinition_of_symbol(v->get_identifier(), sym_def);
|
||||||
}
|
}
|
||||||
if (G.is_verbosity(1)) {
|
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) {
|
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");
|
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;
|
SymValFunc* sym_val = nullptr;
|
||||||
if (const auto* v_seq = v->get_body()->try_as<ast_sequence>()) {
|
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>()) {
|
} else if (const auto* v_asm = v->get_body()->try_as<ast_asm_body>()) {
|
||||||
std::vector<int> arg_order, ret_order;
|
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);
|
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 {
|
} else {
|
||||||
v->error("Unexpected function body statement");
|
v->error("Unexpected function body statement");
|
||||||
}
|
}
|
||||||
|
@ -341,6 +312,9 @@ static void register_function(V<ast_function_declaration> v) {
|
||||||
} else if (v->is_entrypoint) {
|
} else if (v->is_entrypoint) {
|
||||||
sym_val->method_id = calculate_method_id_for_entrypoint(func_name);
|
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) {
|
if (v->marked_as_inline) {
|
||||||
sym_val->flags |= SymValFunc::flagInline;
|
sym_val->flags |= SymValFunc::flagInline;
|
||||||
}
|
}
|
||||||
|
@ -353,13 +327,19 @@ static void register_function(V<ast_function_declaration> v) {
|
||||||
if (v->is_entrypoint) {
|
if (v->is_entrypoint) {
|
||||||
sym_val->flags |= SymValFunc::flagIsEntrypoint;
|
sym_val->flags |= SymValFunc::flagIsEntrypoint;
|
||||||
}
|
}
|
||||||
if (detect_if_function_just_wraps_another(v)) {
|
if (n_mutate_params) {
|
||||||
sym_val->flags |= SymValFunc::flagWrapsAnotherF;
|
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;
|
sym_def->value = sym_val;
|
||||||
#ifdef TOLK_DEBUG
|
#ifdef TOLK_DEBUG
|
||||||
dynamic_cast<SymValFunc*>(sym_def->value)->name = func_name;
|
sym_def->value->sym_name = func_name;
|
||||||
#endif
|
#endif
|
||||||
if (dynamic_cast<SymValCodeFunc*>(sym_val)) {
|
if (dynamic_cast<SymValCodeFunc*>(sym_val)) {
|
||||||
G.all_code_functions.push_back(sym_def);
|
G.all_code_functions.push_back(sym_def);
|
||||||
|
|
|
@ -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
|
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) {
|
SymDef* define_symbol(sym_idx_t name_idx, bool force_new, SrcLocation loc) {
|
||||||
if (!name_idx) {
|
if (!name_idx) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "src-file.h"
|
#include "src-file.h"
|
||||||
|
#include "type-expr.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -25,14 +26,23 @@ namespace tolk {
|
||||||
typedef int var_idx_t;
|
typedef int var_idx_t;
|
||||||
typedef int sym_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 {
|
struct SymValBase {
|
||||||
SymValKind kind;
|
SymValKind kind;
|
||||||
int idx;
|
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;
|
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* lookup_symbol(sym_idx_t idx);
|
||||||
|
|
||||||
SymDef* define_global_symbol(sym_idx_t name_idx, SrcLocation loc = {});
|
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);
|
SymDef* define_symbol(sym_idx_t name_idx, bool force_new, SrcLocation loc);
|
||||||
|
|
||||||
} // namespace tolk
|
} // namespace tolk
|
||||||
|
|
156
tolk/tolk.h
156
tolk/tolk.h
|
@ -69,13 +69,14 @@ using const_idx_t = int;
|
||||||
struct TmpVar {
|
struct TmpVar {
|
||||||
TypeExpr* v_type;
|
TypeExpr* v_type;
|
||||||
var_idx_t idx;
|
var_idx_t idx;
|
||||||
bool is_tmp_unnamed;
|
sym_idx_t sym_idx;
|
||||||
sym_idx_t name;
|
|
||||||
int coord;
|
int coord;
|
||||||
SrcLocation where;
|
SrcLocation where;
|
||||||
std::vector<std::function<void(SrcLocation)>> on_modification;
|
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 show(std::ostream& os, int omit_idx = 0) const;
|
||||||
void dump(std::ostream& os) const;
|
void dump(std::ostream& os) const;
|
||||||
void set_location(SrcLocation loc);
|
void set_location(SrcLocation loc);
|
||||||
|
@ -401,40 +402,56 @@ struct AsmOpList;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct SymVal : SymValBase {
|
struct SymValVariable : SymValBase {
|
||||||
TypeExpr* sym_type;
|
enum SymValFlag {
|
||||||
SymVal(SymValKind kind, int idx, TypeExpr* sym_type = nullptr)
|
flagMutateParameter = 1, // parameter was declared with `mutate` keyword
|
||||||
: SymValBase(kind, idx), sym_type(sym_type) {
|
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;
|
bool is_mutate_parameter() const {
|
||||||
TypeExpr* get_type() const {
|
return flags & flagMutateParameter;
|
||||||
return sym_type;
|
}
|
||||||
|
bool is_local_var() const {
|
||||||
|
return idx == -1;
|
||||||
|
}
|
||||||
|
bool is_immutable() const {
|
||||||
|
return flags & flagImmutable;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SymValFunc : SymVal {
|
struct SymValFunc : SymValBase {
|
||||||
enum SymValFlag {
|
enum SymValFlag {
|
||||||
flagInline = 1, // marked `@inline`
|
flagInline = 1, // marked `@inline`
|
||||||
flagInlineRef = 2, // marked `@inline_ref`
|
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.)
|
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
|
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
|
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
|
flagGetMethod = 64, // was declared via `get func(): T`, method_id is auto-assigned
|
||||||
flagIsEntrypoint = 128, // it's `main` / `onExternalMessage` / etc.
|
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
|
td::RefInt256 method_id; // todo why int256? it's small
|
||||||
int flags{0};
|
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;
|
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() override = default;
|
||||||
SymValFunc(int val, TypeExpr* _ft, bool marked_as_pure)
|
SymValFunc(std::vector<SymDef*> parameters, int val, TypeExpr* sym_type, int flags)
|
||||||
: SymVal(SymValKind::_Func, val, _ft), flags(marked_as_pure ? flagMarkedAsPure : 0) {}
|
: SymValBase(SymValKind::_Func, val, sym_type), flags(flags), parameters(std::move(parameters)) {
|
||||||
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, 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 {
|
const std::vector<int>* get_arg_order() const {
|
||||||
|
@ -450,9 +467,6 @@ struct SymValFunc : SymVal {
|
||||||
bool is_inline_ref() const {
|
bool is_inline_ref() const {
|
||||||
return flags & flagInlineRef;
|
return flags & flagInlineRef;
|
||||||
}
|
}
|
||||||
bool is_just_wrapper_for_another_f() const {
|
|
||||||
return flags & flagWrapsAnotherF;
|
|
||||||
}
|
|
||||||
bool is_marked_as_pure() const {
|
bool is_marked_as_pure() const {
|
||||||
return flags & flagMarkedAsPure;
|
return flags & flagMarkedAsPure;
|
||||||
}
|
}
|
||||||
|
@ -465,32 +479,35 @@ struct SymValFunc : SymVal {
|
||||||
bool is_entrypoint() const {
|
bool is_entrypoint() const {
|
||||||
return flags & flagIsEntrypoint;
|
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 {
|
struct SymValCodeFunc : SymValFunc {
|
||||||
CodeBlob* code;
|
CodeBlob* code;
|
||||||
bool is_really_used{false}; // calculated via dfs; unused functions are not codegenerated
|
bool is_really_used{false}; // calculated via dfs; unused functions are not codegenerated
|
||||||
~SymValCodeFunc() override = default;
|
~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;
|
bool does_need_codegen() const;
|
||||||
void set_code(CodeBlob* code);
|
void set_code(CodeBlob* code);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SymValGlobVar : SymValBase {
|
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
|
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
|
SymValGlobVar(int val, TypeExpr* gvtype)
|
||||||
#endif
|
: SymValBase(SymValKind::_GlobVar, val, gvtype) {
|
||||||
SymValGlobVar(int val, TypeExpr* gvtype, int oidx = 0)
|
|
||||||
: SymValBase(SymValKind::_GlobVar, val), sym_type(gvtype), out_idx(oidx) {
|
|
||||||
}
|
}
|
||||||
~SymValGlobVar() override = default;
|
~SymValGlobVar() override = default;
|
||||||
TypeExpr* get_type() const {
|
|
||||||
return sym_type;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SymValConst : SymValBase {
|
struct SymValConst : SymValBase {
|
||||||
|
@ -499,14 +516,12 @@ struct SymValConst : SymValBase {
|
||||||
td::RefInt256 intval;
|
td::RefInt256 intval;
|
||||||
std::string strval;
|
std::string strval;
|
||||||
ConstKind kind;
|
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)
|
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)
|
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;
|
~SymValConst() override = default;
|
||||||
td::RefInt256 get_int_value() const {
|
td::RefInt256 get_int_value() const {
|
||||||
|
@ -529,9 +544,10 @@ struct SymValConst : SymValBase {
|
||||||
|
|
||||||
struct Expr {
|
struct Expr {
|
||||||
enum ExprCls {
|
enum ExprCls {
|
||||||
_None,
|
|
||||||
_Apply,
|
_Apply,
|
||||||
_VarApply,
|
_VarApply,
|
||||||
|
_GrabMutatedVars,
|
||||||
|
_ReturnSelf,
|
||||||
_MkTuple,
|
_MkTuple,
|
||||||
_Tensor,
|
_Tensor,
|
||||||
_Const,
|
_Const,
|
||||||
|
@ -539,14 +555,13 @@ struct Expr {
|
||||||
_GlobFunc,
|
_GlobFunc,
|
||||||
_GlobVar,
|
_GlobVar,
|
||||||
_Letop,
|
_Letop,
|
||||||
_LetFirst,
|
|
||||||
_Hole,
|
_Hole,
|
||||||
_CondExpr,
|
_CondExpr,
|
||||||
_SliceConst,
|
_SliceConst,
|
||||||
};
|
};
|
||||||
ExprCls cls;
|
ExprCls cls;
|
||||||
int val{0};
|
int val{0};
|
||||||
enum { _IsRvalue = 2, _IsLvalue = 4, _IsImpure = 32 };
|
enum { _IsRvalue = 2, _IsLvalue = 4, _IsImmutable = 8, _IsImpure = 32 };
|
||||||
int flags{0};
|
int flags{0};
|
||||||
SrcLocation here;
|
SrcLocation here;
|
||||||
td::RefInt256 intval;
|
td::RefInt256 intval;
|
||||||
|
@ -554,8 +569,6 @@ struct Expr {
|
||||||
SymDef* sym{nullptr};
|
SymDef* sym{nullptr};
|
||||||
TypeExpr* e_type{nullptr};
|
TypeExpr* e_type{nullptr};
|
||||||
std::vector<Expr*> args;
|
std::vector<Expr*> args;
|
||||||
explicit Expr(ExprCls c = _None) : cls(c) {
|
|
||||||
}
|
|
||||||
Expr(ExprCls c, SrcLocation loc) : cls(c), here(loc) {
|
Expr(ExprCls c, SrcLocation loc) : cls(c), here(loc) {
|
||||||
}
|
}
|
||||||
Expr(ExprCls c, std::vector<Expr*> _args) : cls(c), args(std::move(_args)) {
|
Expr(ExprCls c, std::vector<Expr*> _args) : cls(c), args(std::move(_args)) {
|
||||||
|
@ -585,33 +598,38 @@ struct Expr {
|
||||||
bool is_lvalue() const {
|
bool is_lvalue() const {
|
||||||
return flags & _IsLvalue;
|
return flags & _IsLvalue;
|
||||||
}
|
}
|
||||||
|
bool is_immutable() const {
|
||||||
|
return flags & _IsImmutable;
|
||||||
|
}
|
||||||
bool is_mktuple() const {
|
bool is_mktuple() const {
|
||||||
return cls == _MkTuple;
|
return cls == _MkTuple;
|
||||||
}
|
}
|
||||||
void chk_rvalue() const {
|
void chk_rvalue() const {
|
||||||
if (!is_rvalue()) {
|
if (!is_rvalue()) {
|
||||||
throw ParseError(here, "rvalue expected");
|
fire_error_rvalue_expected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void chk_lvalue() const {
|
void deduce_type();
|
||||||
if (!is_lvalue()) {
|
|
||||||
throw ParseError(here, "lvalue expected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool deduce_type();
|
|
||||||
void set_location(SrcLocation loc) {
|
void set_location(SrcLocation loc) {
|
||||||
here = loc;
|
here = loc;
|
||||||
}
|
}
|
||||||
SrcLocation get_location() const {
|
SrcLocation get_location() const {
|
||||||
return here;
|
return here;
|
||||||
}
|
}
|
||||||
int define_new_vars(CodeBlob& code);
|
void define_new_vars(CodeBlob& code);
|
||||||
int predefine_vars();
|
void predefine_vars();
|
||||||
std::vector<var_idx_t> pre_compile(CodeBlob& code, std::vector<std::pair<SymDef*, var_idx_t>>* lval_globs = nullptr) const;
|
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;
|
var_idx_t new_tmp(CodeBlob& code) const;
|
||||||
std::vector<var_idx_t> new_tmp_vect(CodeBlob& code) const {
|
std::vector<var_idx_t> new_tmp_vect(CodeBlob& code) const {
|
||||||
return {new_tmp(code)};
|
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;
|
simple_compile_func_t simple_compile;
|
||||||
compile_func_t ext_compile;
|
compile_func_t ext_compile;
|
||||||
~SymValAsmFunc() override = default;
|
~SymValAsmFunc() override = default;
|
||||||
SymValAsmFunc(TypeExpr* ft, std::vector<int>&& arg_order, std::vector<int>&& ret_order, bool marked_as_pure)
|
SymValAsmFunc(std::vector<SymDef*> parameters, TypeExpr* ft, std::vector<int>&& arg_order, std::vector<int>&& ret_order, int flags)
|
||||||
: SymValFunc(-1, ft, marked_as_pure) {
|
: SymValFunc(std::move(parameters), -1, ft, flags) {
|
||||||
this->arg_order = std::move(arg_order);
|
this->arg_order = std::move(arg_order);
|
||||||
this->ret_order = std::move(ret_order);
|
this->ret_order = std::move(ret_order);
|
||||||
}
|
}
|
||||||
SymValAsmFunc(TypeExpr* ft, simple_compile_func_t _compile, bool marked_as_pure)
|
SymValAsmFunc(std::vector<SymDef*> parameters, TypeExpr* ft, simple_compile_func_t _compile, int flags)
|
||||||
: SymValFunc(-1, ft, marked_as_pure), simple_compile(std::move(_compile)) {
|
: SymValFunc(std::move(parameters), -1, ft, flags), simple_compile(std::move(_compile)) {
|
||||||
}
|
}
|
||||||
SymValAsmFunc(TypeExpr* ft, compile_func_t _compile, bool marked_as_pure)
|
SymValAsmFunc(std::vector<SymDef*> parameters, TypeExpr* ft, simple_compile_func_t _compile, int flags,
|
||||||
: SymValFunc(-1, ft, marked_as_pure), ext_compile(std::move(_compile)) {
|
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)) {
|
||||||
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)) {
|
|
||||||
}
|
}
|
||||||
void set_code(std::vector<AsmOp> code);
|
void set_code(std::vector<AsmOp> code);
|
||||||
bool compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in, SrcLocation where) const;
|
bool compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in, SrcLocation where) const;
|
||||||
|
@ -1349,29 +1360,32 @@ struct SymValAsmFunc : SymValFunc {
|
||||||
|
|
||||||
struct CodeBlob {
|
struct CodeBlob {
|
||||||
enum { _ForbidImpure = 4 };
|
enum { _ForbidImpure = 4 };
|
||||||
int var_cnt, in_var_cnt, op_cnt;
|
int var_cnt, in_var_cnt;
|
||||||
TypeExpr* ret_type;
|
TypeExpr* ret_type;
|
||||||
|
const SymValCodeFunc* func_val;
|
||||||
std::string name;
|
std::string name;
|
||||||
SrcLocation loc;
|
SrcLocation loc;
|
||||||
std::vector<TmpVar> vars;
|
std::vector<TmpVar> vars;
|
||||||
std::unique_ptr<Op> ops;
|
std::unique_ptr<Op> ops;
|
||||||
std::unique_ptr<Op>* cur_ops;
|
std::unique_ptr<Op>* cur_ops;
|
||||||
|
std::vector<Op*> debug_ttt;
|
||||||
std::stack<std::unique_ptr<Op>*> cur_ops_stack;
|
std::stack<std::unique_ptr<Op>*> cur_ops_stack;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
bool require_callxargs = false;
|
bool require_callxargs = false;
|
||||||
CodeBlob(std::string name, SrcLocation loc, TypeExpr* ret)
|
CodeBlob(std::string name, SrcLocation loc, const SymValCodeFunc* func_val, TypeExpr* ret_type)
|
||||||
: var_cnt(0), in_var_cnt(0), op_cnt(0), ret_type(ret), name(std::move(name)), loc(loc), cur_ops(&ops) {
|
: 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>
|
template <typename... Args>
|
||||||
Op& emplace_back(Args&&... args) {
|
Op& emplace_back(Args&&... args) {
|
||||||
Op& res = *(*cur_ops = std::make_unique<Op>(args...));
|
Op& res = *(*cur_ops = std::make_unique<Op>(args...));
|
||||||
cur_ops = &(res.next);
|
cur_ops = &(res.next);
|
||||||
|
debug_ttt.push_back(&res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
bool import_params(FormalArgList arg_list);
|
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) {
|
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);
|
int split_vars(bool strict = false);
|
||||||
bool compute_used_code_vars();
|
bool compute_used_code_vars();
|
||||||
|
|
|
@ -2,28 +2,20 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "lexer.h"
|
|
||||||
|
|
||||||
namespace tolk {
|
namespace tolk {
|
||||||
|
|
||||||
struct TypeExpr {
|
struct TypeExpr {
|
||||||
enum Kind { te_Unknown, te_Var, te_Indirect, te_Atomic, te_Tensor, te_Tuple, te_Map, te_ForAll };
|
enum Kind { te_Unknown, te_Var, te_Indirect, te_Atomic, te_Tensor, te_Tuple, te_Map, te_ForAll };
|
||||||
// todo not _
|
enum AtomicType { _Int, _Cell, _Slice, _Builder, _Continutaion, _Tuple };
|
||||||
enum AtomicType {
|
|
||||||
_Int = tok_int,
|
|
||||||
_Cell = tok_cell,
|
|
||||||
_Slice = tok_slice,
|
|
||||||
_Builder = tok_builder,
|
|
||||||
_Cont = tok_continuation,
|
|
||||||
_Tuple = tok_tuple,
|
|
||||||
};
|
|
||||||
Kind constr;
|
Kind constr;
|
||||||
int value;
|
int value;
|
||||||
int minw, maxw;
|
int minw, maxw;
|
||||||
static constexpr int w_inf = 1023;
|
static constexpr int w_inf = 1023;
|
||||||
std::vector<TypeExpr*> args;
|
std::vector<TypeExpr*> args;
|
||||||
bool was_forall_var = false;
|
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) {
|
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());
|
args.insert(args.end(), list.begin(), list.end());
|
||||||
compute_width();
|
compute_width();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_atomic() const {
|
bool is_atomic() const {
|
||||||
return constr == te_Atomic;
|
return constr == te_Atomic;
|
||||||
}
|
}
|
||||||
|
@ -127,9 +120,7 @@ struct TypeExpr {
|
||||||
static TypeExpr* new_forall(std::vector<TypeExpr*> list, TypeExpr* body) {
|
static TypeExpr* new_forall(std::vector<TypeExpr*> list, TypeExpr* body) {
|
||||||
return new TypeExpr{te_ForAll, body, std::move(list)};
|
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 bool remove_indirect(TypeExpr*& te, TypeExpr* forbidden = nullptr);
|
||||||
static std::vector<TypeExpr*> remove_forall(TypeExpr*& te);
|
static std::vector<TypeExpr*> remove_forall(TypeExpr*& te);
|
||||||
static bool remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector<TypeExpr*>& new_vars);
|
static bool remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector<TypeExpr*>& new_vars);
|
||||||
|
|
|
@ -264,7 +264,7 @@ std::ostream& TypeExpr::print(std::ostream& os, int lex_level) const {
|
||||||
return os << "slice";
|
return os << "slice";
|
||||||
case _Builder:
|
case _Builder:
|
||||||
return os << "builder";
|
return os << "builder";
|
||||||
case _Cont:
|
case _Continutaion:
|
||||||
return os << "cont";
|
return os << "cont";
|
||||||
case _Tuple:
|
case _Tuple:
|
||||||
return os << "tuple";
|
return os << "tuple";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue