1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 11:12:16 +00:00

[Tolk] Completely rework stdlib: multiple files and renaming

- split stdlib.tolk into multiple files (tolk-stdlib/ folder)
  (the "core" common.tolk is auto-imported, the rest are
  needed to be explicitly imported like "@stdlib/tvm-dicts.tolk")
- all functions were renamed to long and clear names
- new naming is camelCase
This commit is contained in:
tolk-vm 2024-10-31 11:16:19 +04:00
parent e2edadba92
commit 12ff28ac94
No known key found for this signature in database
GPG key ID: 7905DD7FE0324B12
48 changed files with 2966 additions and 2458 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,766 @@
// Standard library for Tolk (LGPL licence).
// It contains common functions that are available out of the box, the user doesn't have to import anything.
// More specific functions are required to be imported explicitly, like "@stdlib/tvm-dicts".
tolk 0.6
/**
Tuple manipulation primitives.
Elements of a tuple can be of arbitrary type.
Note that atomic type `tuple` can't be cast to composite tuple type (e.g. `[int, cell]`) and vise versa.
*/
/// Creates a tuple with zero elements.
@pure
fun createEmptyTuple(): tuple
asm "NIL";
/// Appends a value to tuple, resulting in `Tuple t' = (x1, ..., xn, value)`.
/// If its size exceeds 255, throws a type check exception.
@pure
fun tuplePush<X>(t: tuple, value: X): tuple
asm "TPUSH";
@pure
fun ~tuplePush<X>(t: tuple, value: X): (tuple, ())
asm "TPUSH";
/// Returns the first element of a non-empty tuple.
@pure
fun tupleFirst<X>(t: tuple): X
asm "FIRST";
/// Returns the [`index`]-th element of a tuple.
@pure
fun tupleAt<X>(t: tuple, index: int): X
builtin;
/// Returns the size of a tuple (elements count in it).
@pure
fun tupleSize(t: tuple): int
asm "TLEN";
/// Returns the last element of a non-empty tuple.
@pure
fun tupleLast(t: tuple): int
asm "LAST";
/**
Mathematical primitives.
*/
/// Computes the minimum of two integers.
@pure
fun min(x: int, y: int): int
asm "MIN";
/// Computes the maximum of two integers.
@pure
fun max(x: int, y: int): int
asm "MAX";
/// Sorts two integers.
@pure
fun minMax(x: int, y: int): (int, int)
asm "MINMAX";
/// Computes the absolute value of an integer.
@pure
fun abs(x: int): int
asm "ABS";
/// Returns the sign of an integer: `-1` if x < 0, `0` if x == 0, `1` if x > 0.
@pure
fun sign(x: int): int
asm "SGN";
/// Computes the quotient and remainder of [x] / [y]. Example: divMod(112,3) = (37,1)
@pure
fun divMod(x: int, y: int): (int, int)
asm "DIVMOD";
/// Computes the remainder and quotient of [x] / [y]. Example: modDiv(112,3) = (1,37)
@pure
fun modDiv(x: int, y: int): (int, int)
asm(-> 1 0) "DIVMOD";
/// Computes multiple-then-divide: floor([x] * [y] / [z]).
/// The intermediate result is stored in a 513-bit integer to prevent precision loss.
@pure
fun mulDivFloor(x: int, y: int, z: int): int
builtin;
/// Similar to `mulDivFloor`, but rounds the result: round([x] * [y] / [z]).
@pure
fun mulDivRound(x: int, y: int, z: int): int
builtin;
/// Similar to `mulDivFloor`, but ceils the result: ceil([x] * [y] / [z]).
@pure
fun mulDivCeil(x: int, y: int, z: int): int
builtin;
/// Computes the quotient and remainder of ([x] * [y] / [z]). Example: mulDivMod(112,3,10) = (33,6)
@pure
fun mulDivMod(x: int, y: int, z: int): (int, int)
builtin;
/**
Global getters of environment and contract state.
*/
const MASTERCHAIN = -1;
const BASECHAIN = 0;
/// Returns current Unix timestamp (in seconds).
@pure
fun now(): int
asm "NOW";
/// Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`.
/// If necessary, it can be parsed further using primitives such as [parseStandardAddress].
@pure
fun getMyAddress(): slice
asm "MYADDR";
/// Returns the balance (in nanotoncoins) of the smart contract at the start of Computation Phase.
/// Note that RAW primitives such as [sendMessage] do not update this field.
@pure
fun getMyOriginalBalance(): int
asm "BALANCE" "FIRST";
/// Same as [getMyOriginalBalance], but returns a tuple:
/// `int` — balance in nanotoncoins;
/// `cell` — a dictionary with 32-bit keys representing the balance of "extra currencies".
@pure
fun getMyOriginalBalanceWithExtraCurrencies(): [int, cell]
asm "BALANCE";
/// Returns the logical time of the current transaction.
@pure
fun getLogicalTime(): int
asm "LTIME";
/// Returns the starting logical time of the current block.
@pure
fun getCurrentBlockLogicalTime(): int
asm "BLOCKLT";
/// Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value.
@pure
fun getBlockchainConfigParam(x: int): cell
asm "CONFIGOPTPARAM";
/// Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later.
@pure
fun getContractData(): cell
asm "c4 PUSH";
/// Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive.
fun setContractData(c: cell): void
asm "c4 POP";
/// Retrieves code of smart-contract from c7
@pure
fun getContractCode(): cell
asm "MYCODE";
/// Creates an output action that would change this smart contract code to that given by cell [newCode].
/// Notice that this change will take effect only after the successful termination of the current run of the smart contract.
fun setContractCodePostponed(newCode: cell): void
asm "SETCODE";
/// Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”)
/// so that the current execution is considered “successful” with the saved values even if an exception
/// in Computation Phase is thrown later.
fun commitContractDataAndActions(): void
asm "COMMIT";
/**
Signature checks, hashing, cryptography.
*/
/// Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`.
/// Useful for signing and checking signatures of arbitrary entities represented by a tree of cells.
@pure
fun cellHash(c: cell): int
asm "HASHCU";
/// Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`.
/// The result is the same as if an ordinary cell containing only data and references from `s` had been created
/// and its hash computed by [cellHash].
@pure
fun sliceHash(s: slice): int
asm "HASHSU";
/// Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight,
/// throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`.
@pure
fun stringHash(s: slice): int
asm "SHA256U";
/// Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data)
/// using [publicKey] (also represented by a 256-bit unsigned integer).
/// The signature must contain at least 512 data bits; only the first 512 bits are used.
/// The result is `1` if the signature is valid, `0` otherwise.
/// Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`.
/// That is, if [hash] is computed as the hash of some data, these data are hashed twice,
/// the second hashing occurring inside `CHKSIGNS`.
@pure
fun isSignatureValid(hash: int, signature: slice, publicKey: int): int
asm "CHKSIGNU";
/// Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `publicKey`,
/// similarly to [isSignatureValid].
/// If the bit length of [data] is not divisible by eight, throws a cell underflow exception.
/// The verification of Ed25519 signatures is the standard one,
/// with sha256 used to reduce [data] to the 256-bit number that is actually signed.
@pure
fun isSliceSignatureValid(data: slice, signature: slice, publicKey: int): int
asm "CHKSIGNS";
/// Generates a new pseudo-random unsigned 256-bit integer x.
fun random(): int
asm "RANDU256";
/// Generates a new pseudo-random integer z in the range 0..range1 (or range..1, if range < 0).
/// More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed.
fun randomRange(range: int): int
asm "RAND";
/// Returns the current random seed as an unsigned 256-bit integer.
@pure
fun randomGetSeed(): int
asm "RANDSEED";
/// Sets the random seed to unsigned 256-bit seed.
fun randomSetSeed(seed: int): void
asm "SETRAND";
/// Initializes (mixes) random seed with unsigned 256-bit integer x.
fun randomizeBy(x: int): void
asm "ADDRAND";
/// Initializes random seed using current time. Don't forget to call this before calling `random`!
fun randomizeByLogicalTime(): void
asm "LTIME" "ADDRAND";
/**
Size computation primitives.
They may be useful for computing storage fees of user-provided data.
*/
/// Returns `(x, y, z, -1)` or `(null, null, null, 0)`.
/// Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z`
/// in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account
/// the identification of equal cells.
/// The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG,
/// with a hash table of visited cell hashes used to prevent visits of already-visited cells.
/// The total count of visited cells `x` cannot exceed non-negative [maxCells];
/// otherwise the computation is aborted before visiting the `(maxCells + 1)`-st cell and
/// a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`.
@pure
fun calculateCellSize(c: cell, maxCells: int): (int, int, int, int)
asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
/// Similar to [calculateCellSize], but accepting a `slice` [s] instead of a `cell`.
/// The returned value of `x` does not take into account the cell that contains the `slice` [s] itself;
/// however, the data bits and the cell references of [s] are accounted for in `y` and `z`.
@pure
fun calculateSliceSize(s: slice, maxCells: int): (int, int, int, int)
asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT";
/// A non-quiet version of [calculateCellSize] that throws a cell overflow exception (`8`) on failure.
fun calculateCellSizeStrict(c: cell, maxCells: int): (int, int, int)
asm "CDATASIZE";
/// A non-quiet version of [calculateSliceSize] that throws a cell overflow exception (`8`) on failure.
fun calculateSliceSizeStrict(s: slice, maxCells: int): (int, int, int)
asm "SDATASIZE";
/// Returns the depth of `cell` [c].
/// If [c] has no references, then return `0`;
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [c].
/// If [c] is a `null` instead of a cell, returns zero.
@pure
fun getCellDepth(c: cell): int
asm "CDEPTH";
/// Returns the depth of `slice` [s].
/// If [s] has no references, then returns `0`;
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [s].
@pure
fun getSliceDepth(s: slice): int
asm "SDEPTH";
/// Returns the depth of `builder` [b].
/// If no cell references are stored in [b], then returns 0;
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [b].
@pure
fun getBuilderDepth(b: builder): int
asm "BDEPTH";
/**
Debug primitives.
Only works for local TVM execution with debug level verbosity.
*/
/// Dump a variable [x] to the debug log.
fun debugPrint<X>(x: X): void
builtin;
/// Dump a string [x] to the debug log.
fun debugPrintString<X>(x: X): void
builtin;
/// Dumps the stack (at most the top 255 values) and shows the total stack depth.
fun debugDumpStack(): void
builtin;
/**
Slice primitives: parsing cells.
When you _load_ some data, you mutate the slice (shifting an internal pointer on the stack).
When you _preload_ some data, you just get the result without mutating the slice.
*/
/// Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell,
/// or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2)
/// which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards.
@pure
fun beginParse(c: cell): slice
asm "CTOS";
/// Checks if slice is empty. If not, throws an exception.
fun assertEndOfSlice(s: slice): void
asm "ENDS";
/// Loads the next reference from the slice.
@pure
fun loadRef(s: slice): (slice, cell)
asm( -> 1 0) "LDREF";
/// Preloads the next reference from the slice.
@pure
fun preloadRef(s: slice): cell
asm "PLDREF";
/// Loads a signed [len]-bit integer from a slice.
@pure
fun loadInt(s: slice, len: int): (slice, int)
builtin;
/// Loads an unsigned [len]-bit integer from a slice.
@pure
fun loadUint(s: slice, len: int): (slice, int)
builtin;
/// Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice `s''`.
@pure
fun loadBits(s: slice, len: int): (slice, slice)
builtin;
/// Preloads a signed [len]-bit integer from a slice.
@pure
fun preloadInt(s: slice, len: int): int
builtin;
/// Preloads an unsigned [len]-bit integer from a slice.
@pure
fun preloadUint(s: slice, len: int): int
builtin;
/// Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate slice.
@pure
fun preloadBits(s: slice, len: int): slice
builtin;
/// Loads serialized amount of Toncoins (any unsigned integer up to `2^120 - 1`).
@pure
fun loadCoins(s: slice): (slice, int)
asm( -> 1 0) "LDGRAMS";
/// Loads bool (-1 or 0) from a slice
@pure
fun loadBool(s: slice): (slice, int)
asm( -> 1 0) "1 LDI";
/// Shifts a slice pointer to [len] bits forward, mutating the slice.
@pure
fun skipBits(s: slice, len: int): slice
asm "SDSKIPFIRST"; // todo make mutating
@pure
fun ~skipBits(s: slice, len: int): (slice, ())
asm "SDSKIPFIRST";
/// Returns the first `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun getFirstBits(s: slice, len: int): slice
asm "SDCUTFIRST";
/// Returns all but the last `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun removeLastBits(s: slice, len: int): slice
asm "SDSKIPLAST"; // todo make mutating
@pure
fun ~removeLastBits(s: slice, len: int): (slice, ())
asm "SDSKIPLAST";
/// Returns the last `0 ≤ len ≤ 1023` bits of a slice.
@pure
fun getLastBits(s: slice, len: int): slice
asm "SDCUTLAST";
/// Loads a dictionary (TL HashMapE structure, represented as TVM cell) from a slice.
/// Returns `null` if `nothing` constructor is used.
@pure
fun loadDict(s: slice): (slice, cell)
asm( -> 1 0) "LDDICT";
/// Preloads a dictionary (cell) from a slice.
@pure
fun preloadDict(s: slice): cell
asm "PLDDICT";
/// Loads a dictionary as [loadDict], but returns only the remainder of the slice.
@pure
fun skipDict(s: slice): slice
asm "SKIPDICT"; // todo make mutating
@pure
fun ~skipDict(s: slice): (slice, ())
asm "SKIPDICT";
/// Loads (Maybe ^Cell) from a slice.
/// In other words, loads 1 bit: if it's true, loads the first ref, otherwise returns `null`.
@pure
fun loadMaybeRef(s: slice): (slice, cell)
asm( -> 1 0) "LDOPTREF";
/// Preloads (Maybe ^Cell) from a slice.
@pure
fun preloadMaybeRef(s: slice): cell
asm "PLDOPTREF";
/// Loads (Maybe ^Cell), but returns only the remainder of the slice.
@pure
fun ~skipMaybeRef(s: slice): (slice, ())
asm "SKIPOPTREF";
/**
Builder primitives: constructing cells.
When you _store_ some data, you mutate the builder (shifting an internal pointer on the stack).
All the primitives below first check whether there is enough space in the `builder`,
and only then check the range of the value being serialized.
*/
/// Creates a new empty builder.
@pure
fun beginCell(): builder
asm "NEWC";
/// Converts a builder into an ordinary `cell`.
@pure
fun endCell(b: builder): cell
asm "ENDC";
/// Stores a reference to a cell into a builder.
@pure
fun storeRef(b: builder, c: cell): builder
asm(c b) "STREF";
/// Stores a signed [len]-bit integer into a builder (`0 ≤ len ≤ 257`).
@pure
fun storeInt(b: builder, x: int, len: int): builder
builtin;
/// Stores an unsigned [len]-bit integer into a builder (`0 ≤ len ≤ 256`).
@pure
fun storeUint(b: builder, x: int, len: int): builder
builtin;
/// Stores a slice into a builder.
@pure
fun storeSlice(b: builder, s: slice): builder
asm "STSLICER";
/// Stores amount of Toncoins into a builder.
@pure
fun storeCoins(b: builder, x: int): builder
asm "STGRAMS";
/// Stores bool (-1 or 0) into a builder.
/// Attention: true value is `-1`, not 1! If you pass `1` here, TVM will throw an exception.
@pure
fun storeBool(b: builder, x: int): builder
asm(x b) "1 STI";
/// Stores dictionary (represented by TVM `cell` or `null`) into a builder.
/// In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise.
@pure
fun storeDict(b: builder, c: cell): builder
asm(c b) "STDICT";
/// Stores (Maybe ^Cell) into a builder.
/// In other words, if cell is `null`, store '0' bit; otherwise, store '1' and a ref to [c].
@pure
fun storeMaybeRef(b: builder, c: cell): builder
asm(c b) "STOPTREF";
/// Concatenates two builders.
@pure
fun storeBuilder(to: builder, from: builder): builder
asm "STBR";
@pure
fun storeAddressNone(b: builder): builder
asm "0 PUSHINT" "SWAP" "2 STU";
@pure
fun ~storeAddressNone(b: builder): (builder, ())
asm "b{00} STSLICECONST";
/**
Slice size primitives.
*/
/// Returns the number of references in a slice.
@pure
fun getRemainingRefsCount(s: slice): int
asm "SREFS";
/// Returns the number of data bits in a slice.
@pure
fun getRemainingBitsCount(s: slice): int
asm "SBITS";
/// Returns both the number of data bits and the number of references in a slice.
@pure
fun getRemainingBitsAndRefsCount(s: slice): (int, int)
asm "SBITREFS";
/// Checks whether a slice is empty (i.e., contains no bits of data and no cell references).
@pure
fun isEndOfSlice(s: slice): int
asm "SEMPTY";
/// Checks whether a slice has no bits of data.
@pure
fun isEndOfSliceBits(s: slice): int
asm "SDEMPTY";
/// Checks whether a slice has no references.
@pure
fun isEndOfSliceRefs(s: slice): int
asm "SREMPTY";
/// Checks whether data parts of two slices coinside.
@pure
fun isSliceBitsEqual(a: slice, b: slice): int
asm "SDEQ";
/// Returns the number of cell references already stored in a builder.
@pure
fun getBuilderRefsCount(b: builder): int
asm "BREFS";
/// Returns the number of data bits already stored in a builder.
@pure
fun getBuilderBitsCount(b: builder): int
asm "BBITS";
/**
Address manipulation primitives.
The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme:
```TL-B
addr_none$00 = MsgAddressExt;
addr_extern$01 len:(## 8) external_address:(bits len)
= MsgAddressExt;
anycast_info$_ depth:(#<= 30) { depth >= 1 }
rewrite_pfx:(bits depth) = Anycast;
addr_std$10 anycast:(Maybe Anycast)
workchain_id:int8 address:bits256 = MsgAddressInt;
addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
_ _:MsgAddressInt = MsgAddress;
_ _:MsgAddressExt = MsgAddress;
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddress dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt
created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed;
```
A deserialized `MsgAddress` is represented by a tuple `t` as follows:
- `addr_none` is represented by `t = (0)`,
i.e., a tuple containing exactly one integer equal to zero.
- `addr_extern` is represented by `t = (1, s)`,
where slice `s` contains the field `external_address`. In other words, `
t` is a pair (a tuple consisting of two entries), containing an integer equal to one and slice `s`.
- `addr_std` is represented by `t = (2, u, x, s)`,
where `u` is either a `null` (if `anycast` is absent) or a slice `s'` containing `rewrite_pfx` (if anycast is present).
Next, integer `x` is the `workchain_id`, and slice `s` contains the address.
- `addr_var` is represented by `t = (3, u, x, s)`,
where `u`, `x`, and `s` have the same meaning as for `addr_std`.
*/
/// Loads from slice [s] the only prefix that is a valid `MsgAddress`,
/// and returns both this prefix `s'` and the remainder `s''` of [s] as slices.
@pure
fun loadAddress(s: slice): (slice, slice)
asm( -> 1 0) "LDMSGADDR"; // todo make mutating
/// 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.
@pure
fun parseAddress(s: slice): tuple
asm "PARSEMSGADDR";
/// Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`),
/// applies rewriting from the anycast (if present) to the same-length prefix of the address,
/// and returns both the workchain and the 256-bit address as integers.
/// If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`,
/// throws a cell deserialization exception.
@pure
fun parseStandardAddress(s: slice): (int, int)
asm "REWRITESTDADDR";
/// Creates a slice representing TL addr_none$00 (two `0` bits).
@pure
fun createAddressNone(): slice
asm "b{00} PUSHSLICE";
/// Returns if a slice pointer contains an empty address (`-1` for true, `0` for false, as always).
/// In other words, a slice starts with two `0` bits (TL addr_none$00).
@pure
fun addressIsNone(s: slice): int
asm "2 PLDU" "0 EQINT";
/**
Reserving Toncoins on balance and its flags.
*/
/// mode = 0: Reserve exact amount of nanotoncoins
const RESERVE_MODE_EXACT_AMOUNT = 0;
/// +1: Actually reserves all but amount, meaning `currentContractBalance - amount`
const RESERVE_MODE_ALL_BUT_AMOUNT = 1;
/// +2: Actually set `min(amount, currentContractBalance)` (without this mode, if amount is greater, the action will fail)
const RESERVE_MODE_AT_MOST = 2;
/// +4: [amount] is increased by the _original_ balance of the current account (before the compute phase).
const RESERVE_MODE_INCREASE_BY_ORIGINAL_BALANCE = 4;
/// +8: Actually sets `amount = -amount` before performing any further actions.
const RESERVE_MODE_NEGATE_AMOUNT = 8;
/// +16: If this action fails, the transaction will be bounced.
const RESERVE_MODE_BOUNCE_ON_ACTION_FAIL = 16;
/// Creates an output action which would reserve Toncoins on balance.
/// For [reserveMode] consider constants above.
fun reserveToncoinsOnBalance(nanoTonCoins: int, reserveMode: int): void
asm "RAWRESERVE";
/// Similar to [reserveToncoinsOnBalance], but also accepts a dictionary extraAmount (represented by a cell or null)
/// with extra currencies. In this way currencies other than Toncoin can be reserved.
fun reserveExtraCurrenciesOnBalance(nanoTonCoins: int, extraAmount: cell, reserveMode: int): void
asm "RAWRESERVEX";
/**
Messages sending and parsing primitives.
Working with messages is low-level right now, but still, every contract should do that.
`Message` structure, its header and so on are specified in TL-B scheme, particularly:
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool ... = CommonMsgInfo;
*/
/// 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00
const BOUNCEABLE = 0x18;
/// 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00
const NON_BOUNCEABLE = 0x10;
/// Load msgFlags from incoming message body (4 bits).
@pure
fun loadMessageFlags(s: slice): (slice, int)
asm( -> 1 0) "4 LDU";
/// Having msgFlags (4 bits), check that a message is bounced.
/// Effectively, it's `msgFlags & 1` (the lowest bit present).
@pure
fun isMessageBounced(msgFlags: int): int
asm "1 PUSHINT" "AND";
/// Skip 0xFFFFFFFF prefix (when a message is bounced).
@pure
fun ~skipBouncedPrefix(s: slice): (slice, ())
asm "32 PUSHINT" "SDSKIPFIRST";
/// The guideline recommends to start the body of an internal message with uint32 `op` and uint64 `queryId`.
@pure
fun loadMessageOp(s: slice): (slice, int)
asm( -> 1 0) "32 LDU";
@pure
fun ~skipMessageOp(s: slice): (slice, ())
asm "32 PUSHINT" "SDSKIPFIRST";
@pure
fun storeMessageOp(b: builder, op: int): builder
asm(op b) "32 STU";
fun ~storeMessageOp(b: builder, op: int): (builder, ())
asm(op b) "32 STU";
/// The guideline recommends that uint64 `queryId` should follow uint32 `op`.
@pure
fun loadMessageQueryId(s: slice): (slice, int)
asm( -> 1 0) "64 LDU";
@pure
fun ~skipMessageQueryId(s: slice): (slice, ())
asm "64 PUSHINT" "SDSKIPFIRST";
@pure
fun storeMessageQueryId(b: builder, queryId: int): builder
asm(queryId b) "64 STU";
fun ~storeMessageQueryId(b: builder, queryId: int): (builder, ())
asm(queryId b) "64 STU";
/// SEND MODES - https://docs.ton.org/tvm.pdf page 137, SENDRAWMSG
/// mode = 0 is used for ordinary messages; the gas fees are deducted from the senging amount; action phaes should NOT be ignored.
const SEND_MODE_REGULAR = 0;
/// +1 means that the sender wants to pay transfer fees separately.
const SEND_MODE_PAY_FEES_SEPARATELY = 1;
/// +2 means that any errors arising while processing this message during the action phase should be ignored.
const SEND_MODE_IGNORE_ERRORS = 2;
/// in the case of action fail - bounce transaction. No effect if SEND_MODE_IGNORE_ERRORS (+2) is used. TVM UPGRADE 2023-07. https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages
const SEND_MODE_BOUNCE_ON_ACTION_FAIL = 16;
/// mode = 32 means that the current account must be destroyed if its resulting balance is zero.
const SEND_MODE_DESTROY = 32;
/// mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message.
const SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE = 64;
/// mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message).
const SEND_MODE_CARRY_ALL_BALANCE = 128;
/// do not create an action, only estimate fee. TVM UPGRADE 2023-07. https://docs.ton.org/learn/tvm-instructions/tvm-upgrade-2023-07#sending-messages
const SEND_MODE_ESTIMATE_FEE_ONLY = 1024;
/// Other modes affect the fee calculation as follows:
/// +64 substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account).
/// +128 substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account).
/// Sends a raw message — a correctly serialized TL object `Message X`.
/// For `mode`, see constants above (except SEND_MODE_ESTIMATE_FEE_ONLY).
/// This function is still available, but deprecated: consider using [sendMessage].
@deprecated
fun sendRawMessage(msg: cell, mode: int): void
asm "SENDRAWMSG";
/// Creates an output action and returns a fee for creating a message.
/// Mode has the same effect as in the case of SENDRAWMSG.
/// For mode including SEND_MODE_ESTIMATE_FEE_ONLY it just returns estimated fee without sending a message.
fun sendMessage(msg: cell, mode: int): int
asm "SENDMSG";

View file

@ -0,0 +1,63 @@
// A part of standard library for Tolk
tolk 0.6
/**
Gas and payment related primitives.
*/
/// Returns amount of gas (in gas units) consumed in current Computation Phase.
fun getGasConsumedAtTheMoment(): int
asm "GASCONSUMED";
/// This function is required to be called when you process an external message (from an outer world)
/// and "accept" it to blockchain.
/// Without calling this function, an external message would be discarded.
/// As an effect, the current smart contract agrees to buy some gas to finish the current transaction.
/// For more details, check [accept_message effects](https://ton.org/docs/#/smart-contracts/accept).
fun acceptExternalMessage(): void
asm "ACCEPT";
/// When processing an internal message, by default, the limit of gas consumption is determined by incoming message.
/// Functions [setGasLimit] and [setGasLimitToMaximum] allow you to change this behavior.
/// Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero,
/// decreasing the value of `gr` by `gc` in the process.
fun setGasLimitToMaximum(): void
asm "ACCEPT";
/// When processing an internal message, by default, the limit of gas consumption is determined by incoming message.
/// Functions [setGasLimit] and [setGasLimitToMaximum] allow you to change this behavior.
/// Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero.
/// If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`,
/// an (unhandled) out of gas exception is thrown before setting new gas limits.
fun setGasLimit(limit: int): void
asm "SETGASLIMIT";
/// Calculates fee (amount in nanotoncoins to be paid) for a transaction which consumed [gasUsed] gas units.
fun calculateGasFee(workchain: int, gasUsed: int): int
asm(gasUsed workchain) "GETGASFEE";
/// Same as [calculateGasFee], but without flat price (you have supposed to read https://docs.ton.org/develop/howto/fees-low-level)
fun calculateGasFeeWithoutFlatPrice(workchain: int, gasUsed: int): int
asm(gasUsed workchain) "GETGASFEESIMPLE";
/// Calculates amount of nanotoncoins you should pay for storing a contract of provided size for [seconds].
/// [bits] and [cells] represent contract state (code + data).
fun calculateStorageFee(workchain: int, seconds: int, bits: int, cells: int): int
asm(cells bits seconds workchain) "GETSTORAGEFEE";
/// Calculates amount of nanotoncoins you should pay to send a message of specified size.
fun calculateMessageFee(workchain: int, bits: int, cells: int): int
asm(cells bits workchain) "GETFORWARDFEE";
/// Same as [calculateMessageFee], but without lump price (you have supposed to read https://docs.ton.org/develop/howto/fees-low-level)
fun calculateMessageFeeWithoutLumpPrice(workchain: int, bits: int, cells: int): int
asm(cells bits workchain) "GETFORWARDFEESIMPLE";
/// Calculates fee that was paid by the sender of an incoming internal message.
fun calculateOriginalMessageFee(workchain: int, incomingFwdFee: int): int
asm(incomingFwdFee workchain) "GETORIGINALFWDFEE";
/// Returns the amount of nanotoncoins current contract debts for storage. ("due" and "debt" are synonyms)
/// If it has no debt, `0` is returned.
fun getMyStorageDuePayment(): int
asm "DUEPAYMENT";

View file

@ -0,0 +1,38 @@
// A part of standard library for Tolk
tolk 0.6
/**
Lisp-style lists are nested 2-elements tuples: `(1, (2, (3, null)))` represents list `[1, 2, 3]`.
Elements of a list can be of different types.
Empty list is conventionally represented as TVM `null` value.
*/
@pure
fun createEmptyList(): tuple
asm "PUSHNULL";
/// Adds an element to the beginning of lisp-style list.
/// Note, that it does not mutate the list: instead, it returns a new one (it's a lisp pattern).
@pure
fun listPrepend<X>(head: X, tail: tuple): tuple
asm "CONS";
/// Extracts the head and the tail of lisp-style list.
@pure
fun listSplit<X>(list: tuple): (X, tuple)
asm "UNCONS";
/// Extracts the tail and the head of lisp-style list.
@pure
fun ~listNext<X>(list: tuple): (tuple, X)
asm( -> 1 0) "UNCONS";
/// Returns the head of lisp-style list.
@pure
fun listGetHead<X>(list: tuple): X
asm "CAR";
/// Returns the tail of lisp-style list.
@pure
fun listGetTail(list: tuple): tuple
asm "CDR";

View file

@ -0,0 +1,447 @@
// A part of standard library for Tolk
tolk 0.6
/**
Dictionaries are represented as `cell` data type (cells can store anything, dicts in particular).
Currently, they have very low-level API very close to TVM internals.
Most of functions are duplicated for three common cases:
- iDict* - dicts with signed integer keys
- uDict* - dicts with unsigned integer keys
- sDict* - dicts with arbitrary slice keys
When accessing a dict element, you should not only provide a key, but provide keyLen,
since for optimization, for optimization, key length is not stored in the dictionary itself.
*/
/// Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL
@pure
fun createEmptyDict(): cell
asm "NEWDICT";
/// Checks whether a dictionary is empty.
@pure
fun dictIsEmpty(c: cell): int
asm "DICTEMPTY";
@pure
fun iDictGet(dict: cell, keyLen: int, key: int): (slice, int)
asm(key dict keyLen) "DICTIGET" "NULLSWAPIFNOT";
@pure
fun uDictGet(dict: cell, keyLen: int, key: int): (slice, int)
asm(key dict keyLen) "DICTUGET" "NULLSWAPIFNOT";
@pure
fun sDictGet(dict: cell, keyLen: int, key: slice): (slice, int)
asm(key dict keyLen) "DICTGET" "NULLSWAPIFNOT";
@pure
fun iDictSet(dict: cell, keyLen: int, key: int, value: slice): cell
asm(value key dict keyLen) "DICTISET";
@pure
fun ~iDictSet(dict: cell, keyLen: int, key: int, value: slice): (cell, ())
asm(value key dict keyLen) "DICTISET";
@pure
fun uDictSet(dict: cell, keyLen: int, key: int, value: slice): cell
asm(value key dict keyLen) "DICTUSET";
@pure
fun ~uDictSet(dict: cell, keyLen: int, key: int, value: slice): (cell, ())
asm(value key dict keyLen) "DICTUSET";
@pure
fun sDictSet(dict: cell, keyLen: int, key: slice, value: slice): cell
asm(value key dict keyLen) "DICTSET";
@pure
fun ~sDictSet(dict: cell, keyLen: int, key: slice, value: slice): (cell, ())
asm(value key dict keyLen) "DICTSET";
@pure
fun iDictSetRef(dict: cell, keyLen: int, key: int, value: cell): cell
asm(value key dict keyLen) "DICTISETREF";
@pure
fun ~iDictSetRef(dict: cell, keyLen: int, key: int, value: cell): (cell, ())
asm(value key dict keyLen) "DICTISETREF";
@pure
fun uDictSetRef(dict: cell, keyLen: int, key: int, value: cell): cell
asm(value key dict keyLen) "DICTUSETREF";
@pure
fun ~uDictSetRef(dict: cell, keyLen: int, key: int, value: cell): (cell, ())
asm(value key dict keyLen) "DICTUSETREF";
@pure
fun sDictSetRef(dict: cell, keyLen: int, key: slice, value: cell): cell
asm(value key dict keyLen) "DICTSETREF";
@pure
fun ~sDictSetRef(dict: cell, keyLen: int, key: slice, value: cell): (cell, ())
asm(value key dict keyLen) "DICTSETREF";
@pure
fun iDictSetIfNotExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTIADD";
@pure
fun ~iDictSetIfNotExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTIADD";
@pure
fun uDictSetIfNotExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTUADD";
@pure
fun ~uDictSetIfNotExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTUADD";
@pure
fun iDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTIREPLACE";
@pure
fun ~iDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTIREPLACE";
@pure
fun uDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTUREPLACE";
@pure
fun ~uDictSetIfExists(dict: cell, keyLen: int, key: int, value: slice): (cell, int)
asm(value key dict keyLen) "DICTUREPLACE";
@pure
fun iDictGetRef(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTIGETREF" "NULLSWAPIFNOT";
@pure
fun uDictGetRef(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTUGETREF" "NULLSWAPIFNOT";
@pure
fun sDictGetRef(dict: cell, keyLen: int, key: slice): (cell, int)
asm(key dict keyLen) "DICTGETREF" "NULLSWAPIFNOT";
@pure
fun iDictGetRefOrNull(dict: cell, keyLen: int, key: int): cell
asm(key dict keyLen) "DICTIGETOPTREF";
@pure
fun uDictGetRefOrNull(dict: cell, keyLen: int, key: int): cell
asm(key dict keyLen) "DICTUGETOPTREF";
@pure
fun sDictGetRefOrNull(dict: cell, keyLen: int, key: slice): cell
asm(key dict keyLen) "DICTGETOPTREF";
@pure
fun iDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTIDEL";
@pure
fun ~iDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTIDEL";
@pure
fun uDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTUDEL";
@pure
fun ~uDictDelete(dict: cell, keyLen: int, key: int): (cell, int)
asm(key dict keyLen) "DICTUDEL";
@pure
fun sDictDelete(dict: cell, keyLen: int, key: slice): (cell, int)
asm(key dict keyLen) "DICTDEL";
@pure
fun ~sDictDelete(dict: cell, keyLen: int, key: slice): (cell, int)
asm(key dict keyLen) "DICTDEL";
@pure
fun iDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, slice, int)
asm(value key dict keyLen) "DICTISETGET" "NULLSWAPIFNOT";
@pure
fun ~iDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, (slice, int))
asm(value key dict keyLen) "DICTISETGET" "NULLSWAPIFNOT";
@pure
fun uDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, slice, int)
asm(value key dict keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
@pure
fun ~uDictSetAndGet(dict: cell, keyLen: int, key: int, value: slice): (cell, (slice, int))
asm(value key dict keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
@pure
fun sDictSetAndGet(dict: cell, keyLen: int, key: slice, value: slice): (cell, slice, int)
asm(value key dict keyLen) "DICTSETGET" "NULLSWAPIFNOT";
@pure
fun ~sDictSetAndGet(dict: cell, keyLen: int, key: slice, value: slice): (cell, (slice, int))
asm(value key dict keyLen) "DICTSETGET" "NULLSWAPIFNOT";
@pure
fun iDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
asm(value key dict keyLen) "DICTISETGETOPTREF";
@pure
fun ~iDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
asm(value key dict keyLen) "DICTISETGETOPTREF";
@pure
fun uDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
asm(value key dict keyLen) "DICTUSETGETOPTREF";
@pure
fun ~uDictSetAndGetPreviousRefOrNull(dict: cell, keyLen: int, key: int, value: cell): (cell, cell)
asm(value key dict keyLen) "DICTUSETGETOPTREF";
@pure
fun iDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, slice, int)
asm(key dict keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
@pure
fun ~iDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, (slice, int))
asm(key dict keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
@pure
fun uDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, slice, int)
asm(key dict keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
@pure
fun ~uDictDeleteAndGet(dict: cell, keyLen: int, key: int): (cell, (slice, int))
asm(key dict keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
@pure
fun sDictDeleteAndGet(dict: cell, keyLen: int, key: slice): (cell, slice, int)
asm(key dict keyLen) "DICTDELGET" "NULLSWAPIFNOT";
@pure
fun ~sDictDeleteAndGet(dict: cell, keyLen: int, key: slice): (cell, (slice, int))
asm(key dict keyLen) "DICTDELGET" "NULLSWAPIFNOT";
@pure
fun iDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): cell
asm(value key dict keyLen) "DICTISETB";
@pure
fun ~iDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): (cell, ())
asm(value key dict keyLen) "DICTISETB";
@pure
fun uDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): cell
asm(value key dict keyLen) "DICTUSETB";
@pure
fun ~uDictSetBuilder(dict: cell, keyLen: int, key: int, value: builder): (cell, ())
asm(value key dict keyLen) "DICTUSETB";
@pure
fun sDictSetBuilder(dict: cell, keyLen: int, key: slice, value: builder): cell
asm(value key dict keyLen) "DICTSETB";
@pure
fun ~sDictSetBuilder(dict: cell, keyLen: int, key: slice, value: builder): (cell, ())
asm(value key dict keyLen) "DICTSETB";
@pure
fun iDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTIADDB";
@pure
fun ~iDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTIADDB";
@pure
fun uDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTUADDB";
@pure
fun ~uDictSetBuilderIfNotExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTUADDB";
@pure
fun iDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTIREPLACEB";
@pure
fun ~iDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTIREPLACEB";
@pure
fun uDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTUREPLACEB";
@pure
fun ~uDictSetBuilderIfExists(dict: cell, keyLen: int, key: int, value: builder): (cell, int)
asm(value key dict keyLen) "DICTUREPLACEB";
@pure
fun iDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
@pure
fun ~iDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
@pure
fun uDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
@pure
fun ~uDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
@pure
fun sDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, slice, slice, int)
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
@pure
fun ~sDictDeleteFirstAndGet(dict: cell, keyLen: int): (cell, (slice, slice, int))
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
@pure
fun iDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
@pure
fun ~iDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
@pure
fun uDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, int, slice, int)
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
@pure
fun ~uDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, (int, slice, int))
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
@pure
fun sDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, slice, slice, int)
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
@pure
fun ~sDictDeleteLastAndGet(dict: cell, keyLen: int): (cell, (slice, slice, int))
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
@pure
fun iDictGetFirst(dict: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
@pure
fun uDictGetFirst(dict: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
@pure
fun sDictGetFirst(dict: cell, keyLen: int): (slice, slice, int)
asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2";
@pure
fun iDictGetFirstAsRef(dict: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
@pure
fun uDictGetFirstAsRef(dict: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
@pure
fun sDictGetFirstAsRef(dict: cell, keyLen: int): (slice, cell, int)
asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2";
@pure
fun iDictGetLast(dict: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
@pure
fun uDictGetLast(dict: cell, keyLen: int): (int, slice, int)
asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
@pure
fun sDictGetLast(dict: cell, keyLen: int): (slice, slice, int)
asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2";
@pure
fun iDictGetLastAsRef(dict: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
@pure
fun uDictGetLastAsRef(dict: cell, keyLen: int): (int, cell, int)
asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
@pure
fun sDictGetLastAsRef(dict: cell, keyLen: int): (slice, cell, int)
asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2";
@pure
fun iDictGetNext(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
@pure
fun uDictGetNext(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
@pure
fun iDictGetNextOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
@pure
fun uDictGetNextOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
@pure
fun iDictGetPrev(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
@pure
fun uDictGetPrev(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
@pure
fun iDictGetPrevOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
@pure
fun uDictGetPrevOrEqual(dict: cell, keyLen: int, pivot: int): (int, slice, int)
asm(pivot dict keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
/**
Prefix dictionary primitives.
*/
@pure
fun prefixDictGet(dict: cell, keyLen: int, key: slice): (slice, slice, slice, int)
asm(key dict keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2";
@pure
fun prefixDictSet(dict: cell, keyLen: int, key: slice, value: slice): (cell, int)
asm(value key dict keyLen) "PFXDICTSET";
@pure
fun prefixDictDelete(dict: cell, keyLen: int, key: slice): (cell, int)
asm(key dict keyLen) "PFXDICTDEL";

View file

@ -0,0 +1,29 @@
// A part of standard library for Tolk
tolk 0.6
/// Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls.
/// The primitive returns the current value of `c3`.
@pure
fun getTvmRegisterC3(): continuation
asm "c3 PUSH";
/// Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time.
/// Note that after execution of this primitive the current code
/// (and the stack of recursive function calls) won't change,
/// but any other function call will use a function from the new code.
fun setTvmRegisterC3(c: continuation): void
asm "c3 POP";
/// Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist.
@pure
fun transformSliceToContinuation(s: slice): continuation
asm "BLESS";
/// Moves a variable or a value [x] to the top of the stack.
@pure
fun stackMoveToTop<X>(x: X): X
asm "NOP";
/// Mark a variable as used, such that the code which produced it won't be deleted even if it is not impure.
fun stackMoveToTopImpure<X>(x: X): void // todo needs to be deleted, check verified contracts
asm "DROP";

View file

@ -1,3 +1,5 @@
fun pair_first<X, Y>(p: [X, Y]): X asm "FIRST";
fun one(dummy: tuple) {
return 1;
}
@ -30,6 +32,29 @@ fun test88(x: int) {
}
}
@method_id(89)
fun test89(last: int) {
var t: tuple = createEmptyTuple();
t~tuplePush(1);
t~tuplePush(2);
t~tuplePush(3);
t~tuplePush(last);
return (t.tupleAt(0), t.tupleAt(t.tupleSize() - 1), t.tupleFirst(), t.tupleLast());
}
@method_id(93)
fun testStartBalanceCodegen1() {
var t = getMyOriginalBalanceWithExtraCurrencies();
var first = t.pair_first();
return first;
}
@method_id(94)
fun testStartBalanceCodegen2() {
var first = getMyOriginalBalance();
return first;
}
/**
method_id | in | out
@testcase | 0 | 101 15 | 100 1
@ -39,4 +64,24 @@ fun test88(x: int) {
@testcase | 0 | 100 10 | 100 0
@testcase | 88 | 5 | 234
@testcase | 88 | 50 | 0
@testcase | 89 | 4 | 1 4 1 4
@fif_codegen
"""
testStartBalanceCodegen1 PROC:<{
//
BALANCE // t
FIRST // first
}>
"""
@fif_codegen
"""
testStartBalanceCodegen2 PROC:<{
//
BALANCE
FIRST // first
}>
"""
*/

View file

@ -4,7 +4,7 @@ fun f(a: int, b: int, c: int, d: int, e: int, f: int): (int, int) {
return (Dx/D,Dy/D);
};;;;
fun mulDivR(x: int, y: int, z: int): int { return muldivr(x, y, z); }
fun mulDivR(x: int, y: int, z: int): int { return mulDivRound(x, y, z); }
fun calc_phi(): int {
var n = 1;

View file

@ -4,8 +4,8 @@ asm "NIL";
@pure
fun tpush2<X>(t: tuple, x: X): (tuple, ())
asm "TPUSH";
fun emptyTuple(): tuple { return empty_tuple2(); }
fun tuplePush<X>(t: tuple, value: X): (tuple, ()) { return tpush2(t, value); }
fun myEmptyTuple(): tuple { return empty_tuple2(); }
fun myTuplePush<X>(t: tuple, value: X): (tuple, ()) { return tpush2(t, value); }
@pure
fun asm_func_1(x: int, y: int, z: int): tuple
@ -31,7 +31,7 @@ fun asmFuncModify(a: tuple, b: int, c: int): (tuple, ()) { return asm_func_modif
global t: tuple;
fun foo(x: int): int {
t~tuplePush(x);
t~myTuplePush(x);
return x * 10;
}
@ -44,7 +44,7 @@ fun test_old_1(): (tuple, tuple) {
@method_id(12)
fun test_old_2(): (tuple, tuple) {
t = emptyTuple();
t = myEmptyTuple();
var t2: tuple = asm_func_2(foo(11), foo(22), foo(33));
return (t, t2);
}
@ -58,7 +58,7 @@ fun test_old_3(): (tuple, tuple) {
@method_id(14)
fun test_old_4(): (tuple, tuple) {
t = emptyTuple();
t = myEmptyTuple();
var t2: tuple = empty_tuple2();
// This actually computes left-to-right even without compute-asm-ltr
t2 = asm_func_4(foo(11), (foo(22), (foo(33), foo(44))), foo(55));

View file

@ -7,40 +7,45 @@
// 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 begin_cell(); }
fun myEndCell(b: builder): cell { return end_cell(b); }
fun myStoreRef(b: builder, c: cell): builder { return store_ref(b, c); }
fun myStoreUint(b: builder, i: int, bw: int): builder { return store_uint(b, i, bw); }
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 begin_parse(c); }
fun myBeginParse(c: cell): slice { return beginParse(c); }
@inline
@pure
fun mySkipBits(s: slice, len: int): slice { return skip_bits(s, len); }
fun mySkipBits(s: slice, len: int): slice { return skipBits(s, len); }
@inline
@pure
fun ~mySkipBits(s: slice, len: int): (slice, ()) { return ~skip_bits(s, len); }
fun ~mySkipBits(s: slice, len: int): (slice, ()) { return ~skipBits(s, len); }
@inline
@pure
fun ~myLoadUint(s: slice, len: int): (slice, int) { return load_uint(s, len); }
fun ~myLoadUint(s: slice, len: int): (slice, int) { return loadUint(s, len); }
fun myComputeDataSize(c: cell, maxCells: int): (int, int, int) { return compute_data_size(c, maxCells); }
fun myComputeDataSize(c: cell, maxCells: int): (int, int, int) { return calculateCellSizeStrict(c, maxCells); }
fun dict__new(): cell { return new_dict(); }
fun dict__iset(dict: cell, keyLen: int, index: int, value: slice): cell { return idict_set(dict, keyLen, index, value); }
fun ~dict__iset(dict: cell, keyLen: int, index: int, value: slice): (cell, ()) { return ~idict_set(dict, keyLen, index, value); }
fun dict__tryIGet(dict: cell, keyLen: int, index: int): (slice, int) { return idict_get?(dict, keyLen, index); }
fun dict__tryIGetMin(dict: cell, keyLen: int): (int, slice, int) { return idict_get_min?(dict, keyLen); }
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); }
fun myEmptyTuple(): tuple { return empty_tuple(); }
@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 tpush(t, value); }
fun ~myTuplePush<X>(t: tuple, value: X): (tuple, ()) { return ~tpush(t, value); }
fun myTupleAt<X>(t: tuple, index: int): X { return at(t, index); }
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

View file

@ -5,15 +5,15 @@
// But swapping arguments may sometimes lead to bytecode changes (see test2),
// both with compute-asm-ltr and without it.
fun myBeginCell(): builder { return begin_cell(); }
fun myEndCell(b: builder): cell { return end_cell(b); }
fun myStoreRef1(b: builder, c: cell): builder { return store_ref(b, c); }
fun myStoreRef2(c: cell, b: builder): builder { return store_ref(b, c); }
fun myStoreUint1(b: builder, x: int, bw: int): builder { return store_uint(b, x, bw); }
fun myStoreUint2(b: builder, bw: int, x: int): builder { return store_uint(b, x, bw); }
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 compute_data_size(c, maxCells); }
fun computeDataSize2(maxCells: int, c: cell): (int, int, int) { return compute_data_size(c, maxCells); }
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";
@ -48,24 +48,24 @@ fun test2(): (int, int, int) {
fun test3(): (int, int, int) {
var x: int = 1;
var y: int = 1;
var to_be_ref: cell = begin_cell().end_cell();
var in_c: builder = begin_cell().store_uint(123, 8);
in_c = store_ref(in_c, to_be_ref);
var (a, b, c) = compute_data_size(in_c.end_cell(), 10);
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 begin_cell(); }
fun beginCell1(): builder { return beginCell(); }
fun beginCell11(): builder { return beginCell1(); }
fun beginCell111(): builder { return beginCell11(); }
fun endCell1(b: builder): cell { return end_cell(b); }
fun endCell1(b: builder): cell { return endCell(b); }
fun endCell11(b: builder): cell { return endCell1(b); }
fun beginParse1(c: cell): slice { return begin_parse(c); }
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 store_int(b, x, bw); }
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); }

View file

@ -4,17 +4,17 @@
// (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 begin_cell(); }
fun myEndCell(b: builder): cell { return end_cell(b); }
fun myStoreRef(b: builder, c: cell): builder { return store_ref(b, c); }
fun myStoreUint3(i: int, bw: int, b: builder): builder { return store_uint(b, i, bw); }
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 compute_data_size(c, maxCells); }
fun computeDataSize2(maxCells: int, c: cell): (int, int, int) { return calculateCellSizeStrict(c, maxCells); }
fun myEmptyTuple(): tuple { return empty_tuple(); }
fun myTuplePush<X>(t: tuple, value: X): tuple { return tpush(t, value); }
fun ~myTuplePush<X>(t: tuple, value: X): (tuple, ()) { return ~tpush(t, value); }
fun tupleGetFirst<X>(t: tuple): X { return first(t); }
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
@ -32,7 +32,7 @@ fun test1(): (int, int, int) {
var t: tuple = myEmptyTuple();
t~myTuplePush(myStoreRef);
var refStorer = tupleGetFirst(t);
var refStorer = myTupleGetFirst(t);
var x: int = 1;
var y: int = 1;

View file

@ -1,7 +1,7 @@
// 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 store_uint(b, x, x); }
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; }
@ -17,11 +17,11 @@ fun postpone_elections(): int {
}
fun setAndGetData(ret: int): int {
var c: cell = begin_cell().store_uint(ret, 8).end_cell();
set_data(c);
var s: slice = get_data().begin_parse();
var c: cell = beginCell().storeUint(ret, 8).endCell();
setContractData(c);
var s: slice = getContractData().beginParse();
throwIf(101, 0);
return s~load_uint(8);
return s~loadUint(8);
}
fun setAndGetDataWrapper(ret: int): int {
@ -30,9 +30,9 @@ fun setAndGetDataWrapper(ret: int): int {
@method_id(101)
fun test1(): int {
var c: cell = begin_cell().myStoreUint(32, 10000000).end_cell();
var s: slice = c.begin_parse();
return s~load_uint(32);
var c: cell = beginCell().myStoreUint(32, 10000000).endCell();
var s: slice = c.beginParse();
return s~loadUint(32);
}
get fun test2(ret: int): int {
@ -47,13 +47,13 @@ fun test3(): int {
global t: tuple;
fun foo(x: int): int {
t~tpush(x);
t~tuplePush(x);
return x * 10;
}
@method_id(104)
fun test4(): (tuple, tuple) {
t = empty_tuple();
t = createEmptyTuple();
var t2: tuple = asmFunc4(foo(11), (foo(22), (foo(33), foo(44))), foo(55));
return (t, t2);
}

View file

@ -1,32 +1,32 @@
fun store_u32(b: builder, value: int): builder {
return b.store_uint(value, 32);
return b.storeUint(value, 32);
}
fun ~store_u32(b: builder, value: int): (builder, ()) {
return ~store_uint(b, value, 32);
return ~storeUint(b, value, 32);
}
fun load_u32(cs: slice): (slice, int) {
return cs.load_uint(32);
return cs.loadUint(32);
}
fun my_load_int(s: slice, len: int): (slice, int)
fun my_loadInt(s: slice, len: int): (slice, int)
asm(s len -> 1 0) "LDIX"; // top is "value slice"
fun my_store_int(b: builder, x: int, len: int): builder
fun my_storeInt(b: builder, x: int, len: int): builder
asm(x b len) "STIX";
fun ~my_store_int(b: builder, x: int, len: int): (builder, ())
fun ~my_storeInt(b: builder, x: int, len: int): (builder, ())
asm(x b len) "STIX";
@method_id(101)
fun test1(): [int,int,int,int,int] {
var b: builder = begin_cell().store_uint(1, 32);
b = b.store_uint(2, 32);
b~store_uint(3, 32);
var b: builder = beginCell().storeUint(1, 32);
b = b.storeUint(2, 32);
b~storeUint(3, 32);
b = b.store_u32(4);
b~store_u32(5);
var cs: slice = b.end_cell().begin_parse();
var (cs redef, one: int) = cs.load_uint(32);
var (two: int, three: int) = (cs~load_uint(32), cs~load_u32());
var cs: slice = b.endCell().beginParse();
var (cs redef, one: int) = cs.loadUint(32);
var (two: int, three: int) = (cs~loadUint(32), cs~load_u32());
var (cs redef, four: int) = cs.load_u32();
var five: int = cs~load_u32();
@ -35,82 +35,82 @@ fun test1(): [int,int,int,int,int] {
@method_id(102)
fun test2(): [int,int,int] {
var b: builder = begin_cell().my_store_int(1, 32);
b = b.my_store_int(2, 32);
b~my_store_int(3, 32);
var b: builder = beginCell().my_storeInt(1, 32);
b = b.my_storeInt(2, 32);
b~my_storeInt(3, 32);
var cs: slice = b.end_cell().begin_parse();
var (cs redef, one: int) = cs.my_load_int(32);
var (two: int, three: int) = (cs~my_load_int(32), cs~my_load_int(32));
var cs: slice = b.endCell().beginParse();
var (cs redef, one: int) = cs.my_loadInt(32);
var (two: int, three: int) = (cs~my_loadInt(32), cs~my_loadInt(32));
return [one,two,three];
}
@method_id(103)
fun test3(ret: int): int {
var (_, same: int) = begin_cell().store_uint(ret,32).end_cell().begin_parse().load_uint(32);
var (_, same: int) = beginCell().storeUint(ret,32).endCell().beginParse().loadUint(32);
return same;
}
@method_id(104)
fun test4(): [int,int] {
var b: builder = my_store_int(begin_cell(), 1, 32);
b = store_int(store_int(b, 2, 32), 3, 32);
var b: builder = my_storeInt(beginCell(), 1, 32);
b = storeInt(storeInt(b, 2, 32), 3, 32);
var cs: slice = b.end_cell().begin_parse();
var cs32: slice = cs.first_bits(32); // todo s.first_bits()~load_uint() doesn't work, 'lvalue expected'
var (one, _, three) = (cs32~load_int(32), cs~skip_bits(64), cs~load_u32());
var cs: slice = b.endCell().beginParse();
var cs32: slice = cs.getFirstBits(32); // todo s.first_bits()~loadUint() doesn't work, 'lvalue expected'
var (one, _, three) = (cs32~loadInt(32), cs~skipBits(64), cs~load_u32());
return [one,three];
}
@method_id(105)
fun test5(): [int,int] {
var cref: cell = end_cell(store_u32(begin_cell(), 105));
var c: cell = begin_cell().store_ref(cref).store_ref(cref).store_u32(1).end_cell();
var cref: cell = endCell(store_u32(beginCell(), 105));
var c: cell = beginCell().storeRef(cref).storeRef(cref).store_u32(1).endCell();
var cs: slice = begin_parse(c);
// todo I want cs~load_ref().begin_parse()~load_u32(), but 'lvalue expected'
var ref1 = cs~load_ref().begin_parse();
var ref2 = cs~load_ref().begin_parse();
var sto5x2: int = ref1~load_u32() + ref2~load_uint(32);
var cs: slice = beginParse(c);
// todo I want cs~loadRef().beginParse()~load_u32(), but 'lvalue expected'
var ref1 = cs~loadRef().beginParse();
var ref2 = cs~loadRef().beginParse();
var sto5x2: int = ref1~load_u32() + ref2~loadUint(32);
return [sto5x2, cs~load_u32()];
}
fun ~sumNumbersInSlice(s: slice): (slice, int) {
var result = 0;
while (!slice_data_empty?(s)) {
result += s~load_uint(32);
while (!s.isEndOfSliceBits()) {
result += s~loadUint(32);
}
return (s, result);
}
@method_id(106)
fun test6() {
var ref = begin_cell().store_int(100, 32).end_cell();
var s: slice = begin_cell().store_int(1, 32).store_int(2, 32).store_ref(ref).end_cell().begin_parse();
var result = (slice_bits(s), s~sumNumbersInSlice(), slice_bits(s), slice_empty?(s), slice_data_empty?(s), slice_refs_empty?(s));
var ref2: cell = s~load_ref();
var s2: slice = ref2.begin_parse();
s.end_parse();
return (result, s2~load_int(32), s2.slice_empty?());
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(107)
fun test7() {
var s: slice = begin_cell().store_int(1, 32).store_int(2, 32).store_int(3, 32).store_int(4, 32).store_int(5, 32).store_int(6, 32).store_int(7, 32).end_cell().begin_parse();
var size1 = slice_bits(s);
s~skip_bits(32);
var s1: slice = s.first_bits(64);
var n1 = s1~load_int(32);
var size2 = slice_bits(s);
s~load_int(32);
var size3 = slice_bits(s);
s~skip_last_bits(32);
var size4 = slice_bits(s);
var n2 = s~load_int(32);
var size5 = slice_bits(s);
var s: slice = beginCell().storeInt(1, 32).storeInt(2, 32).storeInt(3, 32).storeInt(4, 32).storeInt(5, 32).storeInt(6, 32).storeInt(7, 32).endCell().beginParse();
var size1 = getRemainingBitsCount(s);
s~skipBits(32);
var s1: slice = s.getFirstBits(64);
var n1 = s1~loadInt(32);
var size2 = getRemainingBitsCount(s);
s~loadInt(32);
var size3 = getRemainingBitsCount(s);
s~removeLastBits(32);
var size4 = getRemainingBitsCount(s);
var n2 = s~loadInt(32);
var size5 = getRemainingBitsCount(s);
return (n1, n2, size1, size2, size3, size4, size5);
}
@ -118,13 +118,13 @@ fun test7() {
fun test108() {
var (result1, result2) = (0, 0);
try {
begin_cell().store_ref(begin_cell().end_cell()).end_cell().begin_parse().end_parse();
beginCell().storeRef(beginCell().endCell()).endCell().beginParse().assertEndOfSlice();
result1 = 100;
} catch (code) {
result1 = code;
}
try {
begin_cell().end_cell().begin_parse().end_parse();
beginCell().endCell().beginParse().assertEndOfSlice();
result2 = 100;
} catch (code) {
result2 = code;
@ -134,18 +134,48 @@ fun test108() {
@method_id(109)
fun test109() {
var ref2 = begin_cell().store_int(1, 32).end_cell();
var ref1 = begin_cell().store_int(1, 32).store_ref(ref2).end_cell();
var c = begin_cell().store_int(444, 32).store_ref(ref1).store_ref(ref1).store_ref(ref1).store_ref(ref2).store_int(4, 32).end_cell();
var (n_cells1, n_bits1, n_refs1) = c.compute_data_size(10);
var s = c.begin_parse();
s~load_ref();
s~load_ref();
var n = s~load_int(32);
var (n_cells2, n_bits2, n_refs2) = s.slice_compute_data_size(10);
var ref2 = beginCell().storeInt(1, 32).endCell();
var ref1 = beginCell().storeInt(1, 32).storeRef(ref2).endCell();
var c = beginCell().storeInt(444, 32).storeRef(ref1).storeRef(ref1).storeRef(ref1).storeRef(ref2).storeInt(4, 32).endCell();
var (n_cells1, n_bits1, n_refs1) = c.calculateCellSizeStrict(10);
var s = c.beginParse();
s~loadRef();
s~loadRef();
var n = s~loadInt(32);
var (n_cells2, n_bits2, n_refs2) = s.calculateSliceSizeStrict(10);
return ([n_cells1, n_bits1, n_refs1], [n_cells2, n_bits2, n_refs2], n);
}
@method_id(110)
fun test110(x: int) {
var s = beginCell().storeBool(x < 0).storeBool(0).storeBool(x).endCell().beginParse();
return (s~loadBool(), s~loadBool(), s~loadBool());
}
@method_id(111)
fun test111() {
var s = beginCell().storeMessageOp(123).storeMessageQueryId(456)
.storeAddressNone().storeAddressNone()
.storeUint(0, 32)
.storeUint(123, 32).storeUint(456, 64).storeUint(789, 64)
.endCell().beginParse();
var op1 = s~loadUint(32);
var q1 = s~loadUint(64);
if (s.addressIsNone()) {
s~skipBits(2);
}
if (s~loadBool() == 0) {
assert(s~loadBool() == 0) throw 444;
s~skipBits(32);
}
var op2 = s~loadMessageOp();
var q2 = s~loadMessageQueryId();
s~skipBits(64);
s.assertEndOfSlice();
assert(isMessageBounced(0x001)) throw 444;
return (op1, q1, op2, q2);
}
fun main(): int {
return 0;
}
@ -160,4 +190,7 @@ fun main(): int {
@testcase | 107 | | 2 3 224 192 160 128 96
@testcase | 108 | | 9 100
@testcase | 109 | | [ 3 128 5 ] [ 2 96 3 ] 444
@testcase | 110 | -1 | -1 0 -1
@testcase | 110 | 0 | 0 0 0
@testcase | 111 | | 123 456 123 456
*/

View file

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

View file

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

View file

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

View file

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

View file

@ -4,6 +4,8 @@
/**
@compilation_should_fail
@stderr invalid-import.tolk:2:7: error: Failed to import: cannot find file
On Linux/Mac, `realpath()` returns an error, and the error message is `cannot find file`
On Windows, it fails after, on reading, with a message "cannot open file"
@stderr invalid-import.tolk:2:7: error: Failed to import: cannot
@stderr import "unexisting.tolk";
*/

View file

@ -0,0 +1,9 @@
import "@stdlib/tvm-dicts"
import "imports/use-dicts-err.tolk"
/**
@compilation_should_fail
@stderr imports/use-dicts-err.tolk:2:22
@stderr Using a non-imported symbol `createEmptyDict`
@stderr Forgot to import "@stdlib/tvm-dicts"?
*/

View file

@ -2,7 +2,7 @@ global g: int;
@pure
fun f_pure(): builder {
var b: builder = begin_cell();
var b: builder = beginCell();
g = g + 1;
return b;
}

View file

@ -1,12 +1,12 @@
@pure
fun validate_input(input: cell): (int, int) {
var (x, y, z, correct) = compute_data_size?(input, 10);
var (x, y, z, correct) = calculateCellSize(input, 10);
assert(correct) throw 102;
}
@pure
fun someF(): int {
var c: cell = begin_cell().end_cell();
var c: cell = beginCell().endCell();
validate_input(c);
return 0;
}

View file

@ -1,7 +1,7 @@
global moddiv: int;
global mulDivMod: int;
/**
@compilation_should_fail
@stderr global moddiv: int;
@stderr global mulDivMod: int;
@stderr redefinition of built-in symbol
*/

View file

@ -4,11 +4,11 @@ fun main(x: int): int {
} else {
var y: slice = "20";
}
~dump(y);
debugPrint(y);
}
/**
@compilation_should_fail
@stderr ~dump(y);
@stderr debugPrint(y);
@stderr undefined symbol `y`
*/

View file

@ -1,3 +1,5 @@
import "imports/use-dicts.tolk"
fun simpleAllConst() {
return (!0, !!0 & !false, !!!0, !1, !!1, !-1, !!-1, (!5 == 0) == !0, !0 == true);
}
@ -43,29 +45,10 @@ fun someSum(upto: int) {
return x;
}
fun lookupIdxByValue(idict32: cell, value: int) {
var cur_key = -1;
do {
var (cur_key redef, cs: slice, found: int) = idict32.idict_get_next?(32, cur_key);
// todo one-line condition (via &) doesn't work, since right side is calculated immediately
if (found) {
if (cs~load_int(32) == value) {
return cur_key;
}
}
} while (found);
return -1;
}
@method_id(104)
fun testDict(last: int) {
// prepare dict: [3 => 30, 4 => 40, 5 => 50]
var dict: cell = new_dict();
dict~idict_set_builder(32, 3, begin_cell().store_int(30, 32));
dict~idict_set_builder(32, 4, begin_cell().store_int(40, 32));
dict~idict_set_builder(32, 5, begin_cell().store_int(!last ? 100 : last, 32));
// prepare dict: [3 => 30, 4 => 40, 5 => x]
var dict = prepareDict_3_30_4_40_5_x(!last ? 100 : last);
return (lookupIdxByValue(dict, 30), lookupIdxByValue(dict, last), lookupIdxByValue(dict, 100));
}

View file

@ -50,14 +50,14 @@ fun add3(a: int, b: int, c: int) { return a+b+c; }
}
fun `load:u32`(cs: slice): (slice, int) {
return cs.load_uint(32);
return cs.loadUint(32);
}
@method_id(116) fun `call_~_via_backticks`():[int,int,int,int] {
var b:builder = begin_cell().store_uint(1, 32).store_uint(2, 32).store_uint(3, 32).store_uint(4, 32);
var `cs`:slice = b.end_cell().begin_parse();
var (`cs` redef,one:int) = `cs`.`load_uint`(32);
var (two:int,three:int) = (`cs`~`load_uint`(32), cs~`load:u32`());
var b:builder = beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32).storeUint(4, 32);
var `cs`:slice = b.endCell().beginParse();
var (`cs` redef,one:int) = `cs`.`loadUint`(32);
var (two:int,three:int) = (`cs`~`loadUint`(32), cs~`load:u32`());
var (cs redef,four:int) = cs.`load:u32`();
return [one,two,three,four];
}

View file

@ -1,18 +1,19 @@
import "../../crypto/smartcont/stdlib.tolk"
import "@stdlib/lisp-lists"
@method_id(101)
fun test1() {
var numbers: tuple = null;
numbers = cons(1, numbers);
numbers = cons(2, numbers);
numbers = cons(3, numbers);
numbers = cons(4, numbers);
var (h, numbers redef) = uncons(numbers);
h += car(numbers);
var numbers: tuple = createEmptyList();
numbers = listPrepend(1, numbers);
numbers = listPrepend(2, numbers);
numbers = listPrepend(3, numbers);
numbers = listPrepend(4, numbers);
var (h, numbers redef) = listSplit(numbers);
h += listGetHead(numbers);
var t = empty_tuple();
var t = createEmptyTuple();
do {
var num = numbers~list_next();
t~tpush(num);
var num = numbers~listNext();
t~tuplePush(num);
} while (numbers != null);
return (h, numbers == null, t);
@ -52,7 +53,7 @@ fun getUntypedNull() {
@method_id(104)
fun test4() {
var (_, (_, untyped)) = (3, (empty_tuple, null));
var (_, (_, untyped)) = (3, (createEmptyTuple, null));
if (true) {
return untyped;
}
@ -62,7 +63,7 @@ fun test4() {
@method_id(105)
fun test5() {
var n = getUntypedNull();
return !(null == n) ? n~load_int(32) : 100;
return !(null == n) ? n~loadInt(32) : 100;
}
@method_id(106)
@ -72,9 +73,9 @@ fun test6(x: int) {
@method_id(107)
fun test7() {
var b = begin_cell().store_maybe_ref(null);
var s = b.end_cell().begin_parse();
var c = s~load_maybe_ref();
var b = beginCell().storeMaybeRef(null);
var s = b.endCell().beginParse();
var c = s~loadMaybeRef();
return (null == c) * 10 + (b != null);
}

View file

@ -11,16 +11,16 @@ fun f_pure2(): int {
@pure
fun get_contract_data(): (int, int) {
var c: cell = get_data();
var cs: slice = c.begin_parse();
cs~load_bits(32);
var value: int = cs~load_uint(16);
var c: cell = getContractData();
var cs: slice = c.beginParse();
cs~loadBits(32);
var value: int = cs~loadUint(16);
return (1, value);
}
fun save_contract_data(value: int) {
var b: builder = begin_cell().store_int(1, 32).store_uint(value, 16);
set_data(b.end_cell());
var b: builder = beginCell().storeInt(1, 32).storeUint(value, 16);
setContractData(b.endCell());
}
@pure

View file

@ -43,10 +43,10 @@ fun main() {
var i_mini: int = string_minihash();
var i_maxi: int = string_maxihash();
var i_crc: int = string_crc32();
assert(sdeq(s_ascii, newc().store_uint(0x737472696E67, 12 * 4).endcs())) throw 101;
assert(sdeq(s_raw, newc().store_uint(0xABCDEF, 6 * 4).endcs())) throw 102;
assert(sdeq(s_addr, newc().store_uint(4, 3).store_int(-1, 8)
.store_uint(0x3333333333333333333333333333333333333333333333333333333333333333, 256).endcs()), 103);
assert(sdeq(s_ascii, newc().storeUint(0x737472696E67, 12 * 4).endcs())) throw 101;
assert(sdeq(s_raw, newc().storeUint(0xABCDEF, 6 * 4).endcs())) throw 102;
assert(sdeq(s_addr, newc().storeUint(4, 3).storeInt(-1, 8)
.storeUint(0x3333333333333333333333333333333333333333333333333333333333333333, 256).endcs()), 103);
assert(i_hex == 0x4142434445464748494A4B4C4D4E4F505152535455565758595A303132333435) throw 104;
assert(i_mini == 0x7a62e8a8) throw 105;
assert(i_maxi == 0x7a62e8a8ebac41bd6de16c65e7be363bc2d2cbc6a0873778dead4795c13db979) throw 106;

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
fun main(): int {
var c: cell = my_begin_cell().store_int(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 ten: int = cs~load_int(32);
var ten: int = cs~loadInt(32);
return 1 + demo1(ten) + demo_var;
}

View file

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

View file

@ -27,6 +27,7 @@ const TOLKFIFTLIB_MODULE = getenv('TOLKFIFTLIB_MODULE')
const TOLKFIFTLIB_WASM = getenv('TOLKFIFTLIB_WASM')
const FIFT_EXECUTABLE = getenv('FIFT_EXECUTABLE')
const FIFT_LIBS_FOLDER = getenv('FIFTPATH') // this env is needed for fift to work properly
const STDLIB_FOLDER = __dirname + '/../crypto/smartcont/tolk-stdlib'
const TMP_DIR = os.tmpdir()
class CmdLineOptions {
@ -475,25 +476,33 @@ function copyToCStringPtr(mod, str, ptr) {
return allocated;
}
/** @return {string} */
function copyFromCString(mod, ptr) {
return mod.UTF8ToString(ptr);
}
/** @return {{status: string, message: string, fiftCode: string, codeBoc: string, codeHashHex: string}} */
function compileFile(mod, filename, experimentalOptions) {
// see tolk-wasm.cpp: typedef void (*CStyleReadFileCallback)(int, char const*, char**, char**)
// see tolk-wasm.cpp: typedef void (*WasmFsReadCallback)(int, char const*, char**, char**)
const callbackPtr = mod.addFunction((kind, dataPtr, destContents, destError) => {
if (kind === 0) { // realpath
try {
const relativeFilename = copyFromCString(mod, dataPtr)
copyToCStringPtr(mod, fs.realpathSync(relativeFilename), destContents);
let relative = copyFromCString(mod, dataPtr)
if (relative.startsWith('@stdlib/')) {
// import "@stdlib/filename" or import "@stdlib/filename.tolk"
relative = STDLIB_FOLDER + '/' + relative.substring(7)
if (!relative.endsWith('.tolk')) {
relative += '.tolk'
}
}
copyToCStringPtr(mod, fs.realpathSync(relative), destContents);
} catch (err) {
copyToCStringPtr(mod, 'cannot find file', destError);
}
} else if (kind === 1) { // read file
try {
const filename = copyFromCString(mod, dataPtr) // already normalized (as returned above)
copyToCStringPtr(mod, fs.readFileSync(filename).toString('utf-8'), destContents);
const absolute = copyFromCString(mod, dataPtr) // already normalized (as returned above)
copyToCStringPtr(mod, fs.readFileSync(absolute).toString('utf-8'), destContents);
} catch (err) {
copyToCStringPtr(mod, err.message || err.toString(), destError);
}
@ -506,7 +515,6 @@ function compileFile(mod, filename, experimentalOptions) {
optimizationLevel: 2,
withStackComments: true,
experimentalOptions: experimentalOptions || undefined,
stdlibLocation: __dirname + '/../crypto/smartcont/stdlib.tolk',
entrypointFileName: filename
};

View file

@ -35,12 +35,6 @@ if (${TOLK_DEBUG}) # -DTOLK_DEBUG=1 in CMake options => #define TOLK_DEBUG (for
target_compile_definitions(tolk PRIVATE TOLK_DEBUG=1)
endif()
if (NOT USE_EMSCRIPTEN)
get_filename_component(STDLIB_TOLK_IF_BUILD_FROM_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto/smartcont/stdlib.tolk" REALPATH)
target_compile_definitions(tolk PRIVATE STDLIB_TOLK_IF_BUILD_FROM_SOURCES="${STDLIB_TOLK_IF_BUILD_FROM_SOURCES}")
endif()
if (USE_EMSCRIPTEN)
add_executable(tolkfiftlib tolk-wasm.cpp ${TOLK_SOURCE})
target_include_directories(tolkfiftlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)

View file

@ -1121,10 +1121,6 @@ void define_builtins() {
define_builtin_func("_~/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 0));
define_builtin_func("_^/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 1));
define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1));
define_builtin_func("divmod", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2));
define_builtin_func("~divmod", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2));
define_builtin_func("moddiv", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2), {}, {1, 0});
define_builtin_func("~moddiv", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2), {}, {1, 0});
define_builtin_func("_<<_", arith_bin_op, compile_lshift);
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1));
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 0));
@ -1144,10 +1140,10 @@ void define_builtins() {
define_builtin_func("^_&=_", arith_bin_op, compile_bitwise_and);
define_builtin_func("^_|=_", arith_bin_op, compile_bitwise_or);
define_builtin_func("^_^=_", arith_bin_op, compile_bitwise_xor);
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, -1));
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 0));
define_builtin_func("muldivc", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 1));
define_builtin_func("muldivmod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2));
define_builtin_func("mulDivFloor", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, -1));
define_builtin_func("mulDivRound", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 0));
define_builtin_func("mulDivCeil", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 1));
define_builtin_func("mulDivMod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2));
define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2));
define_builtin_func("_!=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 5));
define_builtin_func("_<_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 4));
@ -1162,24 +1158,23 @@ void define_builtins() {
define_builtin_func("__throw", impure_un_op, compile_throw, true);
define_builtin_func("__throw_arg", throw_arg_op, compile_throw_arg, true);
define_builtin_func("__throw_if_unless", TypeExpr::new_map(Int3, Unit), std::bind(compile_throw_if_unless, _1, _2), true);
define_builtin_func("load_int", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0});
define_builtin_func("load_uint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0});
define_builtin_func("preload_int", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true));
define_builtin_func("preload_uint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false));
define_builtin_func("store_int", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
define_builtin_func("store_uint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
define_builtin_func("~store_int", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
define_builtin_func("~store_uint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
define_builtin_func("load_bits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0});
define_builtin_func("preload_bits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false));
define_builtin_func("at", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at);
define_builtin_func("touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, X)), AsmOp::Nop());
define_builtin_func("~touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
AsmOp::Nop());
define_builtin_func("~dump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
AsmOp::Custom("s0 DUMP", 1, 1), true);
define_builtin_func("~strdump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
AsmOp::Custom("STRDUMP", 1, 1), true);
define_builtin_func("loadInt", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0});
define_builtin_func("loadUint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0});
define_builtin_func("loadBits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0});
define_builtin_func("preloadInt", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true));
define_builtin_func("preloadUint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false));
define_builtin_func("preloadBits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false));
define_builtin_func("storeInt", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
define_builtin_func("storeUint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
define_builtin_func("~storeInt", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2});
define_builtin_func("~storeUint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2});
define_builtin_func("tupleAt", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at);
define_builtin_func("debugPrint", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Unit)),
AsmOp::Custom("s0 DUMP DROP", 1, 1), true);
define_builtin_func("debugPrintString", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Unit)),
AsmOp::Custom("STRDUMP DROP", 1, 1), true);
define_builtin_func("debugDumpStack", TypeExpr::new_map(Unit, Unit),
AsmOp::Custom("DUMPSTK", 0, 0), true);
}
} // namespace tolk

View file

@ -52,10 +52,9 @@ struct CompilerSettings {
int optimization_level = 2;
bool stack_layout_comments = true;
std::string entrypoint_filename;
std::string output_filename;
std::string boc_output_filename;
std::string stdlib_filename;
std::string stdlib_folder; // a path to tolk-stdlib/; files imported via @stdlib/xxx are there
FsReadCallback read_callback;
@ -82,8 +81,6 @@ struct CompilerState {
std::vector<SymDef*> all_code_functions, all_global_vars, all_get_methods, all_constants;
AllRegisteredSrcFiles all_src_files;
std::string generated_from;
bool is_verbosity(int gt_eq) const { return settings.verbosity >= gt_eq; }
};

View file

@ -979,12 +979,6 @@ void pipeline_convert_ast_to_legacy_Expr_Op(const AllSrcFiles& all_src_files) {
for (const SrcFile* file : all_src_files) {
tolk_assert(file->ast);
if (!file->is_stdlib_file()) {
// file->ast->debug_print();
G.generated_from += file->rel_filename;
G.generated_from += ", ";
}
for (AnyV v : file->ast->as<ast_tolk_file>()->get_toplevel_declarations()) {
if (auto v_func = v->try_as<ast_function_declaration>()) {
if (v_func->is_asm_function()) {

View file

@ -42,10 +42,11 @@ AllSrcFiles pipeline_discover_and_parse_sources(const std::string& stdlib_filena
for (AnyV v_toplevel : file->ast->as<ast_tolk_file>()->get_toplevel_declarations()) {
if (auto v_import = v_toplevel->try_as<ast_import_statement>()) {
size_t pos = file->rel_filename.rfind('/');
std::string rel_filename = pos == std::string::npos
? v_import->get_file_name()
: file->rel_filename.substr(0, pos + 1) + v_import->get_file_name();
std::string imported_str = v_import->get_file_name();
size_t cur_slash_pos = file->rel_filename.rfind('/');
std::string rel_filename = cur_slash_pos == std::string::npos || imported_str[0] == '@'
? std::move(imported_str)
: file->rel_filename.substr(0, cur_slash_pos + 1) + imported_str;
SrcFile* imported = G.all_src_files.locate_and_register_source_file(rel_filename, v_import->loc);
file->imports.push_back(SrcFile::ImportStatement{imported});

View file

@ -132,9 +132,20 @@ static void generate_output_func(SymDef* func_sym) {
}
}
void pipeline_generate_fif_output_to_std_cout() {
void pipeline_generate_fif_output_to_std_cout(const AllSrcFiles& all_src_files) {
std::cout << "\"Asm.fif\" include\n";
std::cout << "// automatically generated from " << G.generated_from << std::endl;
std::cout << "// automatically generated from ";
bool need_comma = false;
for (const SrcFile* file : all_src_files) {
if (!file->is_stdlib_file()) {
if (need_comma) {
std::cout << ", ";
}
std::cout << file->rel_filename;
need_comma = true;
}
}
std::cout << std::endl;
std::cout << "PROGRAM{\n";
bool has_main_procedure = false;

View file

@ -296,7 +296,7 @@ static void register_function(V<ast_function_declaration> v) {
v->error("`builtin` used for non-builtin function");
}
#ifdef TOLK_DEBUG
// in release, we don't need this check, since `builtin` is used only in stdlib.tolk, 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) {
v->error("declaration for `builtin` function doesn't match an actual one");
}

View file

@ -36,6 +36,6 @@ void pipeline_register_global_symbols(const AllSrcFiles&);
void pipeline_convert_ast_to_legacy_Expr_Op(const AllSrcFiles&);
void pipeline_find_unused_symbols();
void pipeline_generate_fif_output_to_std_cout();
void pipeline_generate_fif_output_to_std_cout(const AllSrcFiles&);
} // namespace tolk

View file

@ -87,6 +87,11 @@ AllSrcFiles AllRegisteredSrcFiles::get_all_files() const {
return src_files_immutable;
}
bool SrcFile::is_stdlib_file() const {
std::string_view rel(rel_filename);
return rel.size() > 10 && rel.substr(0, 8) == "@stdlib/"; // common.tolk, tvm-dicts.tolk, etc
}
bool SrcFile::is_offset_valid(int offset) const {
return offset >= 0 && offset < static_cast<int>(text.size());
}

View file

@ -51,7 +51,7 @@ struct SrcFile {
SrcFile(const SrcFile& other) = delete;
SrcFile &operator=(const SrcFile&) = delete;
bool is_stdlib_file() const { return file_id == 0; /* stdlib always exists, has no imports and parsed the first */ }
bool is_stdlib_file() const;
bool is_offset_valid(int offset) const;
SrcPosition convert_offset(int offset) const;

View file

@ -29,9 +29,14 @@
#include "td/utils/port/path.h"
#include <getopt.h>
#include <fstream>
#include <utility>
#include <sys/stat.h>
#include <filesystem>
#ifdef TD_DARWIN
#include <mach-o/dyld.h>
#elif TD_WINDOWS
#include <windows.h>
#else // linux
#include <unistd.h>
#endif
#include "git.h"
using namespace tolk;
@ -50,42 +55,89 @@ void usage(const char* progname) {
std::exit(2);
}
static bool stdlib_file_exists(std::filesystem::path& stdlib_tolk) {
static bool stdlib_folder_exists(const char* stdlib_folder) {
struct stat f_stat;
stdlib_tolk = stdlib_tolk.lexically_normal();
int res = stat(stdlib_tolk.c_str(), &f_stat);
return res == 0 && S_ISREG(f_stat.st_mode);
int res = stat(stdlib_folder, &f_stat);
return res == 0 && (f_stat.st_mode & S_IFMT) == S_IFDIR;
}
static std::string auto_discover_stdlib_location(const char* argv0) {
// first, the user can specify env var that points directly to stdlib (useful for non-standard compiler locations)
if (const char* env_var = getenv("TOLK_STDLIB")) {
return env_var;
// getting current executable path is a complicated and not cross-platform task
// for instance, we can't just use argv[0] or even filesystem::canonical
// https://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe/1024937
static bool get_current_executable_filename(std::string& out) {
#ifdef TD_DARWIN
char name_buf[1024];
unsigned int size = 1024;
if (0 == _NSGetExecutablePath(name_buf, &size)) { // may contain ../, so normalize it
char *exe_path = realpath(name_buf, nullptr);
if (exe_path != nullptr) {
out = exe_path;
return true;
}
}
#elif TD_WINDOWS
char exe_path[1024];
if (GetModuleFileNameA(nullptr, exe_path, 1024)) {
out = exe_path;
std::replace(out.begin(), out.end(), '\\', '/'); // modern Windows correctly deals with / separator
return true;
}
#else // linux
char exe_path[1024];
ssize_t res = readlink("/proc/self/exe", exe_path, 1024 - 1);
if (res >= 0) {
exe_path[res] = 0;
out = exe_path;
return true;
}
#endif
return false;
}
// simple join "/some/folder/" (guaranteed to end with /) and "../relative/path"
static std::string join_path(std::string dir, const char* relative) {
while (relative[0] == '.' && relative[1] == '.' && relative[2] == '/') {
size_t slash_pos = dir.find_last_of('/', dir.size() - 2); // last symbol is slash, find before it
if (slash_pos != std::string::npos) {
dir = dir.substr(0, slash_pos + 1);
}
relative += 3;
}
return dir + relative;
}
static std::string auto_discover_stdlib_folder() {
// if the user launches tolk compiler from a package installed (e.g. /usr/bin/tolk),
// locate stdlib in /usr/share/ton/smartcont (this folder exists on package installation)
// (note, that paths are not absolute, they are relative to the launched binary)
// consider https://github.com/ton-blockchain/packages for actual paths
std::filesystem::path executable_dir = std::filesystem::canonical(argv0).remove_filename();
std::string executable_filename;
if (!get_current_executable_filename(executable_filename)) {
return {};
}
// extract dirname to concatenate with relative paths (separator / is ok even for windows)
size_t slash_pos = executable_filename.find_last_of('/');
std::string executable_dir = executable_filename.substr(0, slash_pos + 1);
#ifdef TD_DARWIN
auto def_location = executable_dir / "../share/ton/ton/smartcont/stdlib.tolk";
std::string def_location = join_path(executable_dir, "../share/ton/ton/smartcont/tolk-stdlib");
#elif TD_WINDOWS
auto def_location = executable_dir / "smartcont/stdlib.tolk";
std::string def_location = join_path(executable_dir, "smartcont/tolk-stdlib");
#else // linux
auto def_location = executable_dir / "../share/ton/smartcont/stdlib.tolk";
std::string def_location = join_path(executable_dir, "../share/ton/smartcont/tolk-stdlib");
#endif
if (stdlib_file_exists(def_location)) {
if (stdlib_folder_exists(def_location.c_str())) {
return def_location;
}
// so, the binary is not from a system package
// maybe it's just built from sources? e.g. ~/ton/cmake-build-debug/tolk/tolk
// then, check the ~/ton/crypto/smartcont folder
auto near_when_built_from_sources = executable_dir / "../../crypto/smartcont/stdlib.tolk";
if (stdlib_file_exists(near_when_built_from_sources)) {
std::string near_when_built_from_sources = join_path(executable_dir, "../../crypto/smartcont/tolk-stdlib");
if (stdlib_folder_exists(near_when_built_from_sources.c_str())) {
return near_when_built_from_sources;
}
@ -95,10 +147,31 @@ static std::string auto_discover_stdlib_location(const char* argv0) {
td::Result<std::string> fs_read_callback(CompilerSettings::FsReadCallbackKind kind, const char* query) {
switch (kind) {
case CompilerSettings::FsReadCallbackKind::Realpath: {
td::Result<std::string> res_realpath;
if (query[0] == '@' && strlen(query) > 8 && !strncmp(query, "@stdlib/", 8)) {
// import "@stdlib/filename" or import "@stdlib/filename.tolk"
std::string path = G.settings.stdlib_folder + static_cast<std::string>(query + 7);
if (strncmp(path.c_str() + path.size() - 5, ".tolk", 5) != 0) {
path += ".tolk";
}
res_realpath = td::realpath(td::CSlice(path.c_str()));
} else {
// import "relative/to/cwd/path.tolk"
res_realpath = td::realpath(td::CSlice(query));
}
if (res_realpath.is_error()) {
// note, that for non-existing files, `realpath()` on Linux/Mac returns an error,
// whereas on Windows, it returns okay, but fails after, on reading, with a message "cannot open file"
return td::Status::Error(std::string{"cannot find file "} + query);
}
return res_realpath;
}
case CompilerSettings::FsReadCallbackKind::ReadFile: {
struct stat f_stat;
int res = stat(query, &f_stat);
if (res != 0 || !S_ISREG(f_stat.st_mode)) {
int res = stat(query, &f_stat); // query here is already resolved realpath
if (res != 0 || (f_stat.st_mode & S_IFMT) != S_IFREG) {
return td::Status::Error(std::string{"cannot open file "} + query);
}
@ -110,15 +183,8 @@ td::Result<std::string> fs_read_callback(CompilerSettings::FsReadCallbackKind ki
fclose(f);
return std::move(str);
}
case CompilerSettings::FsReadCallbackKind::Realpath: {
td::Result<std::string> res_realpath = td::realpath(td::CSlice(query));
if (res_realpath.is_error()) {
return td::Status::Error(std::string{"cannot find file "} + query);
}
return res_realpath;
}
default: {
return td::Status::Error("Unknown query kind");
return td::Status::Error("unknown query kind");
}
}
}
@ -185,16 +251,26 @@ int main(int argc, char* const argv[]) {
return 2;
}
// locate stdlib.tolk based on env or default system paths
G.settings.stdlib_filename = auto_discover_stdlib_location(argv[0]);
if (G.settings.stdlib_filename.empty()) {
std::cerr << "Failed to discover stdlib.tolk.\n"
// locate tolk-stdlib/ based on env or default system paths
if (const char* env_var = getenv("TOLK_STDLIB")) {
std::string stdlib_filename = static_cast<std::string>(env_var) + "/common.tolk";
td::Result<std::string> res = td::realpath(td::CSlice(stdlib_filename.c_str()));
if (res.is_error()) {
std::cerr << "Environment variable TOLK_STDLIB is invalid: " << res.move_as_error().message().c_str() << std::endl;
return 2;
}
G.settings.stdlib_folder = env_var;
} else {
G.settings.stdlib_folder = auto_discover_stdlib_folder();
}
if (G.settings.stdlib_folder.empty()) {
std::cerr << "Failed to discover Tolk stdlib.\n"
"Probably, you have a non-standard Tolk installation.\n"
"Please, provide env variable TOLK_STDLIB referencing to it.\n";
"Please, provide env variable TOLK_STDLIB referencing to tolk-stdlib/ folder.\n";
return 2;
}
if (G.is_verbosity(2)) {
std::cerr << "stdlib located at " << G.settings.stdlib_filename << std::endl;
std::cerr << "stdlib folder: " << G.settings.stdlib_folder << std::endl;
}
if (optind != argc - 1) {
@ -202,8 +278,8 @@ int main(int argc, char* const argv[]) {
return 2;
}
G.settings.entrypoint_filename = argv[optind];
G.settings.read_callback = fs_read_callback;
return tolk_proceed(G.settings.entrypoint_filename);
int exit_code = tolk_proceed(argv[optind]);
return exit_code;
}

View file

@ -34,21 +34,18 @@
using namespace tolk;
td::Result<std::string> compile_internal(char *config_json) {
static td::Result<std::string> compile_internal(char *config_json) {
TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json)))
td::JsonObject& config = input_json.get_object();
TRY_RESULT(opt_level, td::get_json_object_int_field(config, "optimizationLevel", true, 2));
TRY_RESULT(stdlib_tolk, td::get_json_object_string_field(config, "stdlibLocation", false));
TRY_RESULT(stack_comments, td::get_json_object_bool_field(config, "withStackComments", true, false));
TRY_RESULT(entrypoint_filename, td::get_json_object_string_field(config, "entrypointFileName", false));
TRY_RESULT(experimental_options, td::get_json_object_string_field(config, "experimentalOptions", true));
G.settings.verbosity = 0;
G.settings.optimization_level = std::max(0, opt_level);
G.settings.stdlib_filename = stdlib_tolk;
G.settings.stack_layout_comments = stack_comments;
G.settings.entrypoint_filename = entrypoint_filename;
if (!experimental_options.empty()) {
G.settings.parse_experimental_options_cmd_arg(experimental_options.c_str());
}
@ -56,8 +53,8 @@ td::Result<std::string> compile_internal(char *config_json) {
std::ostringstream outs, errs;
std::cout.rdbuf(outs.rdbuf());
std::cerr.rdbuf(errs.rdbuf());
int tolk_res = tolk::tolk_proceed(entrypoint_filename);
if (tolk_res != 0) {
int exit_code = tolk_proceed(entrypoint_filename);
if (exit_code != 0) {
return td::Status::Error("Tolk compilation error: " + errs.str());
}
@ -78,32 +75,29 @@ td::Result<std::string> compile_internal(char *config_json) {
/// Callback used to retrieve file contents from a "not file system". See tolk-js for implementation.
/// The callback must fill either destContents or destError.
/// The implementor must use malloc() for them and use free() after tolk_compile returns.
typedef void (*CStyleReadFileCallback)(int kind, char const* data, char** destContents, char** destError);
typedef void (*WasmFsReadCallback)(int kind, char const* data, char** destContents, char** destError);
CompilerSettings::FsReadCallback wrapReadCallback(CStyleReadFileCallback _readCallback)
{
CompilerSettings::FsReadCallback readCallback;
if (_readCallback) {
readCallback = [=](CompilerSettings::FsReadCallbackKind kind, char const* data) -> td::Result<std::string> {
char* destContents = nullptr;
char* destError = nullptr;
static CompilerSettings::FsReadCallback wrap_wasm_read_callback(WasmFsReadCallback _readCallback) {
return [_readCallback](CompilerSettings::FsReadCallbackKind kind, char const* data) -> td::Result<std::string> {
char* destContents = nullptr;
char* destError = nullptr;
if (_readCallback) {
_readCallback(static_cast<int>(kind), data, &destContents, &destError);
if (!destContents && !destError) {
return td::Status::Error("Callback not supported");
}
if (destContents) {
return destContents;
}
}
if (destContents) {
return destContents;
}
if (destError) {
return td::Status::Error(std::string(destError));
};
}
return readCallback;
}
return td::Status::Error("Invalid callback from wasm");
};
}
extern "C" {
const char* version() {
auto version_json = td::JsonBuilder();
td::JsonBuilder version_json = td::JsonBuilder();
auto obj = version_json.enter_object();
obj("tolkVersion", TOLK_VERSION);
obj("tolkFiftLibCommitHash", GitMetadata::CommitSHA1());
@ -112,23 +106,22 @@ const char* version() {
return strdup(version_json.string_builder().as_cslice().c_str());
}
const char *tolk_compile(char *config_json, CStyleReadFileCallback callback) {
G.settings.read_callback = wrapReadCallback(callback);
const char *tolk_compile(char *config_json, WasmFsReadCallback callback) {
G.settings.read_callback = wrap_wasm_read_callback(callback);
td::Result<std::string> res = compile_internal(config_json);
if (res.is_error()) {
auto result = res.move_as_error();
auto error_res = td::JsonBuilder();
auto error_o = error_res.enter_object();
error_o("status", "error");
error_o("message", result.message().str());
error_o.leave();
td::JsonBuilder error_res = td::JsonBuilder();
auto obj = error_res.enter_object();
obj("status", "error");
obj("message", res.move_as_error().message().str());
obj.leave();
return strdup(error_res.string_builder().as_cslice().c_str());
}
auto res_string = res.move_as_ok();
std::string res_string = res.move_as_ok();
return strdup(res_string.c_str());
}
}
} // extern "C"

View file

@ -48,21 +48,16 @@ int tolk_proceed(const std::string &entrypoint_filename) {
define_builtins();
lexer_init();
// on any error, an exception is thrown, and the message is printed out below
// (currently, only a single error can be printed)
try {
if (G.settings.stdlib_filename.empty()) {
throw Fatal("stdlib filename not specified");
}
// on any error, an exception is thrown, and the message is printed out below
// (currently, only a single error can be printed)
AllSrcFiles all_files = pipeline_discover_and_parse_sources(G.settings.stdlib_filename, entrypoint_filename);
AllSrcFiles all_files = pipeline_discover_and_parse_sources("@stdlib/common.tolk", entrypoint_filename);
pipeline_register_global_symbols(all_files);
pipeline_convert_ast_to_legacy_Expr_Op(all_files);
pipeline_find_unused_symbols();
pipeline_generate_fif_output_to_std_cout();
pipeline_generate_fif_output_to_std_cout(all_files);
return 0;
} catch (Fatal& fatal) {