mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	Merge pull request #1477 from ton-blockchain/tolk-v0.7
Tolk v0.7: overhaul compiler internals and the type system; `bool` type
This commit is contained in:
		
						commit
						1b7c46f496
					
				
					 137 changed files with 8936 additions and 4485 deletions
				
			
		|  | @ -1,7 +1,7 @@ | |||
| // 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 | ||||
| tolk 0.7 | ||||
| 
 | ||||
| /** | ||||
|   Tuple manipulation primitives. | ||||
|  | @ -17,17 +17,17 @@ fun createEmptyTuple(): tuple | |||
| /// Appends a value to tuple, resulting in `Tuple t' = (x1, ..., xn, value)`. | ||||
| /// If its size exceeds 255, throws a type check exception. | ||||
| @pure | ||||
| fun tuplePush<X>(mutate self: tuple, value: X): void | ||||
| fun tuplePush<T>(mutate self: tuple, value: T): void | ||||
|     asm "TPUSH"; | ||||
| 
 | ||||
| /// Returns the first element of a non-empty tuple. | ||||
| @pure | ||||
| fun tupleFirst<X>(t: tuple): X | ||||
| fun tupleFirst<T>(t: tuple): T | ||||
|     asm "FIRST"; | ||||
| 
 | ||||
| /// Returns the [`index`]-th element of a tuple. | ||||
| @pure | ||||
| fun tupleAt<X>(t: tuple, index: int): X | ||||
| fun tupleAt<T>(t: tuple, index: int): T | ||||
|     builtin; | ||||
| 
 | ||||
| /// Returns the size of a tuple (elements count in it). | ||||
|  | @ -37,7 +37,7 @@ fun tupleSize(t: tuple): int | |||
| 
 | ||||
| /// Returns the last element of a non-empty tuple. | ||||
| @pure | ||||
| fun tupleLast(t: tuple): int | ||||
| fun tupleLast<T>(t: tuple): T | ||||
|     asm "LAST"; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -205,7 +205,7 @@ fun stringHash(s: slice): int | |||
| /// 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 | ||||
| fun isSignatureValid(hash: int, signature: slice, publicKey: int): bool | ||||
|     asm "CHKSIGNU"; | ||||
| 
 | ||||
| /// Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `publicKey`, | ||||
|  | @ -214,7 +214,7 @@ fun isSignatureValid(hash: int, signature: slice, publicKey: int): int | |||
| /// 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 | ||||
| fun isSliceSignatureValid(data: slice, signature: slice, publicKey: int): bool | ||||
|     asm "CHKSIGNS"; | ||||
| 
 | ||||
| /// Generates a new pseudo-random unsigned 256-bit integer x. | ||||
|  | @ -259,14 +259,14 @@ fun randomizeByLogicalTime(): void | |||
| /// 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) | ||||
| fun calculateCellSize(c: cell, maxCells: int): (int, int, int, bool) | ||||
|     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) | ||||
| fun calculateSliceSize(s: slice, maxCells: int): (int, int, int, bool) | ||||
|     asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; | ||||
| 
 | ||||
| /// A non-quiet version of [calculateCellSize] that throws a cell overflow exception (`8`) on failure. | ||||
|  | @ -306,11 +306,11 @@ fun getBuilderDepth(b: builder): int | |||
|  */ | ||||
| 
 | ||||
| /// Dump a variable [x] to the debug log. | ||||
| fun debugPrint<X>(x: X): void | ||||
| fun debugPrint<T>(x: T): void | ||||
|     builtin; | ||||
| 
 | ||||
| /// Dump a string [x] to the debug log. | ||||
| fun debugPrintString<X>(x: X): void | ||||
| fun debugPrintString<T>(x: T): void | ||||
|     builtin; | ||||
| 
 | ||||
| /// Dumps the stack (at most the top 255 values) and shows the total stack depth. | ||||
|  | @ -382,7 +382,7 @@ fun loadCoins(mutate self: slice): int | |||
| 
 | ||||
| /// Loads bool (-1 or 0) from a slice | ||||
| @pure | ||||
| fun loadBool(mutate self: slice): int | ||||
| fun loadBool(mutate self: slice): bool | ||||
|     asm( -> 1 0) "1 LDI"; | ||||
| 
 | ||||
| /// Shifts a slice pointer to [len] bits forward, mutating the slice. | ||||
|  | @ -482,7 +482,7 @@ fun storeCoins(mutate self: builder, x: int): self | |||
| /// 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(mutate self: builder, x: int): self | ||||
| fun storeBool(mutate self: builder, x: bool): self | ||||
|     asm(x self) "1 STI"; | ||||
| 
 | ||||
| /// Stores dictionary (represented by TVM `cell` or `null`) into a builder. | ||||
|  | @ -529,22 +529,22 @@ fun getRemainingBitsAndRefsCount(self: slice): (int, int) | |||
| 
 | ||||
| /// Checks whether a slice is empty (i.e., contains no bits of data and no cell references). | ||||
| @pure | ||||
| fun isEndOfSlice(self: slice): int | ||||
| fun isEndOfSlice(self: slice): bool | ||||
|     asm "SEMPTY"; | ||||
| 
 | ||||
| /// Checks whether a slice has no bits of data. | ||||
| @pure | ||||
| fun isEndOfSliceBits(self: slice): int | ||||
| fun isEndOfSliceBits(self: slice): bool | ||||
|     asm "SDEMPTY"; | ||||
| 
 | ||||
| /// Checks whether a slice has no references. | ||||
| @pure | ||||
| fun isEndOfSliceRefs(self: slice): int | ||||
| fun isEndOfSliceRefs(self: slice): bool | ||||
|     asm "SREMPTY"; | ||||
| 
 | ||||
| /// Checks whether data parts of two slices coinside. | ||||
| @pure | ||||
| fun isSliceBitsEqual(self: slice, b: slice): int | ||||
| fun isSliceBitsEqual(self: slice, b: slice): bool | ||||
|     asm "SDEQ"; | ||||
| 
 | ||||
| /// Returns the number of cell references already stored in a builder. | ||||
|  | @ -621,10 +621,10 @@ fun parseStandardAddress(s: slice): (int, int) | |||
| fun createAddressNone(): slice | ||||
|     asm "b{00} PUSHSLICE"; | ||||
| 
 | ||||
| /// Returns if a slice pointer contains an empty address (`-1` for true, `0` for false, as always). | ||||
| /// Returns if a slice pointer contains an empty address. | ||||
| /// In other words, a slice starts with two `0` bits (TL addr_none$00). | ||||
| @pure | ||||
| fun addressIsNone(s: slice): int | ||||
| fun addressIsNone(s: slice): bool | ||||
|     asm "2 PLDU" "0 EQINT"; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -677,8 +677,8 @@ fun loadMessageFlags(mutate self: slice): int | |||
| /// 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"; | ||||
| fun isMessageBounced(msgFlags: int): bool | ||||
|     asm "2 PUSHINT" "MODR"; | ||||
| 
 | ||||
| /// Skip 0xFFFFFFFF prefix (when a message is bounced). | ||||
| @pure | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // A part of standard library for Tolk | ||||
| tolk 0.6 | ||||
| tolk 0.7 | ||||
| 
 | ||||
| /** | ||||
|   Gas and payment related primitives. | ||||
|  | @ -61,3 +61,9 @@ fun calculateOriginalMessageFee(workchain: int, incomingFwdFee: int): int | |||
| /// If it has no debt, `0` is returned. | ||||
| fun getMyStorageDuePayment(): int | ||||
|     asm "DUEPAYMENT"; | ||||
| 
 | ||||
| /// Returns the amount of nanotoncoins charged for storage. | ||||
| /// (during storage phase preceeding to current computation phase) | ||||
| @pure | ||||
| fun getMyStoragePaidPayment(): int | ||||
|     asm "STORAGEFEES"; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // A part of standard library for Tolk | ||||
| tolk 0.6 | ||||
| tolk 0.7 | ||||
| 
 | ||||
| /** | ||||
|     Lisp-style lists are nested 2-elements tuples: `(1, (2, (3, null)))` represents list `[1, 2, 3]`. | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // A part of standard library for Tolk | ||||
| tolk 0.6 | ||||
| tolk 0.7 | ||||
| 
 | ||||
| /** | ||||
|     Dictionaries are represented as `cell` data type (cells can store anything, dicts in particular). | ||||
|  | @ -19,20 +19,20 @@ fun createEmptyDict(): cell | |||
| 
 | ||||
| /// Checks whether a dictionary is empty. | ||||
| @pure | ||||
| fun dictIsEmpty(self: cell): int | ||||
| fun dictIsEmpty(self: cell): bool | ||||
|     asm "DICTEMPTY"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGet(self: cell, keyLen: int, key: int): (slice, int) | ||||
| fun iDictGet(self: cell, keyLen: int, key: int): (slice, bool) | ||||
|     asm(key self keyLen) "DICTIGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGet(self: cell, keyLen: int, key: int): (slice, int) | ||||
| fun uDictGet(self: cell, keyLen: int, key: int): (slice, bool) | ||||
|     asm(key self keyLen) "DICTUGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictGet(self: cell, keyLen: int, key: slice): (slice, int) | ||||
| fun sDictGet(self: cell, keyLen: int, key: slice): (slice, bool) | ||||
|     asm(key self keyLen) "DICTGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -63,33 +63,33 @@ fun sDictSetRef(mutate self: cell, keyLen: int, key: slice, value: cell): void | |||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int | ||||
| fun iDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): bool | ||||
|     asm(value key self keyLen) "DICTIADD"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): int | ||||
| fun uDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): bool | ||||
|     asm(value key self keyLen) "DICTUADD"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int | ||||
| fun iDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): bool | ||||
|     asm(value key self keyLen) "DICTIREPLACE"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): int | ||||
| fun uDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): bool | ||||
|     asm(value key self keyLen) "DICTUREPLACE"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetRef(self: cell, keyLen: int, key: int): (cell, int) | ||||
| fun iDictGetRef(self: cell, keyLen: int, key: int): (cell, bool) | ||||
|     asm(key self keyLen) "DICTIGETREF" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetRef(self: cell, keyLen: int, key: int): (cell, int) | ||||
| fun uDictGetRef(self: cell, keyLen: int, key: int): (cell, bool) | ||||
|     asm(key self keyLen) "DICTUGETREF" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictGetRef(self: cell, keyLen: int, key: slice): (cell, int) | ||||
| fun sDictGetRef(self: cell, keyLen: int, key: slice): (cell, bool) | ||||
|     asm(key self keyLen) "DICTGETREF" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -107,28 +107,28 @@ fun sDictGetRefOrNull(self: cell, keyLen: int, key: slice): cell | |||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictDelete(mutate self: cell, keyLen: int, key: int): int | ||||
| fun iDictDelete(mutate self: cell, keyLen: int, key: int): bool | ||||
|     asm(key self keyLen) "DICTIDEL"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictDelete(mutate self: cell, keyLen: int, key: int): int | ||||
| fun uDictDelete(mutate self: cell, keyLen: int, key: int): bool | ||||
|     asm(key self keyLen) "DICTUDEL"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictDelete(mutate self: cell, keyLen: int, key: slice): int | ||||
| fun sDictDelete(mutate self: cell, keyLen: int, key: slice): bool | ||||
|     asm(key self keyLen) "DICTDEL"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int) | ||||
| fun iDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, bool) | ||||
|     asm(value key self keyLen) "DICTISETGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, int) | ||||
| fun uDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, bool) | ||||
|     asm(value key self keyLen) "DICTUSETGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictSetAndGet(mutate self: cell, keyLen: int, key: slice, value: slice): (slice, int) | ||||
| fun sDictSetAndGet(mutate self: cell, keyLen: int, key: slice, value: slice): (slice, bool) | ||||
|     asm(value key self keyLen) "DICTSETGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -142,15 +142,15 @@ fun uDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cel | |||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int) | ||||
| fun iDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, bool) | ||||
|     asm(key self keyLen) "DICTIDELGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, int) | ||||
| fun uDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, bool) | ||||
|     asm(key self keyLen) "DICTUDELGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictDeleteAndGet(mutate self: cell, keyLen: int, key: slice): (slice, int) | ||||
| fun sDictDeleteAndGet(mutate self: cell, keyLen: int, key: slice): (slice, bool) | ||||
|     asm(key self keyLen) "DICTDELGET" "NULLSWAPIFNOT"; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -168,129 +168,129 @@ fun sDictSetBuilder(mutate self: cell, keyLen: int, key: slice, value: builder): | |||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int | ||||
| fun iDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): bool | ||||
|     asm(value key self keyLen) "DICTIADDB"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): int | ||||
| fun uDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): bool | ||||
|     asm(value key self keyLen) "DICTUADDB"; | ||||
| 
 | ||||
| @pure | ||||
| fun iDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int | ||||
| fun iDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): bool | ||||
|     asm(value key self keyLen) "DICTIREPLACEB"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): int | ||||
| fun uDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): bool | ||||
|     asm(value key self keyLen) "DICTUREPLACEB"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int) | ||||
| fun iDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, bool) | ||||
|     asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, int) | ||||
| fun uDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, bool) | ||||
|     asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (slice, slice, int) | ||||
| fun sDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (slice, slice, bool) | ||||
|     asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int) | ||||
| fun iDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, bool) | ||||
|     asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, int) | ||||
| fun uDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, bool) | ||||
|     asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictDeleteLastAndGet(mutate self: cell, keyLen: int): (slice, slice, int) | ||||
| fun sDictDeleteLastAndGet(mutate self: cell, keyLen: int): (slice, slice, bool) | ||||
|     asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetFirst(self: cell, keyLen: int): (int, slice, int) | ||||
| fun iDictGetFirst(self: cell, keyLen: int): (int, slice, bool) | ||||
|     asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetFirst(self: cell, keyLen: int): (int, slice, int) | ||||
| fun uDictGetFirst(self: cell, keyLen: int): (int, slice, bool) | ||||
|     asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictGetFirst(self: cell, keyLen: int): (slice, slice, int) | ||||
| fun sDictGetFirst(self: cell, keyLen: int): (slice, slice, bool) | ||||
|     asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, int) | ||||
| fun iDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, bool) | ||||
|     asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, int) | ||||
| fun uDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, bool) | ||||
|     asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictGetFirstAsRef(self: cell, keyLen: int): (slice, cell, int) | ||||
| fun sDictGetFirstAsRef(self: cell, keyLen: int): (slice, cell, bool) | ||||
|     asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetLast(self: cell, keyLen: int): (int, slice, int) | ||||
| fun iDictGetLast(self: cell, keyLen: int): (int, slice, bool) | ||||
|     asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetLast(self: cell, keyLen: int): (int, slice, int) | ||||
| fun uDictGetLast(self: cell, keyLen: int): (int, slice, bool) | ||||
|     asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictGetLast(self: cell, keyLen: int): (slice, slice, int) | ||||
| fun sDictGetLast(self: cell, keyLen: int): (slice, slice, bool) | ||||
|     asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetLastAsRef(self: cell, keyLen: int): (int, cell, int) | ||||
| fun iDictGetLastAsRef(self: cell, keyLen: int): (int, cell, bool) | ||||
|     asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetLastAsRef(self: cell, keyLen: int): (int, cell, int) | ||||
| fun uDictGetLastAsRef(self: cell, keyLen: int): (int, cell, bool) | ||||
|     asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun sDictGetLastAsRef(self: cell, keyLen: int): (slice, cell, int) | ||||
| fun sDictGetLastAsRef(self: cell, keyLen: int): (slice, cell, bool) | ||||
|     asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int) | ||||
| fun iDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, bool) | ||||
|     asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, int) | ||||
| fun uDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, bool) | ||||
|     asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int) | ||||
| fun iDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool) | ||||
|     asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int) | ||||
| fun uDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool) | ||||
|     asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int) | ||||
| fun iDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, bool) | ||||
|     asm(pivot self keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, int) | ||||
| fun uDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, bool) | ||||
|     asm(pivot self keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun iDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int) | ||||
| fun iDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool) | ||||
|     asm(pivot self keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int) | ||||
| fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool) | ||||
|     asm(pivot self keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -299,13 +299,13 @@ fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, int) | |||
|  */ | ||||
| 
 | ||||
| @pure | ||||
| fun prefixDictGet(self: cell, keyLen: int, key: slice): (slice, slice, slice, int) | ||||
| fun prefixDictGet(self: cell, keyLen: int, key: slice): (slice, slice, slice, bool) | ||||
|     asm(key self keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2"; | ||||
| 
 | ||||
| @pure | ||||
| fun prefixDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): int | ||||
| fun prefixDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): bool | ||||
|     asm(value key self keyLen) "PFXDICTSET"; | ||||
| 
 | ||||
| @pure | ||||
| fun prefixDictDelete(mutate self: cell, keyLen: int, key: slice): int | ||||
| fun prefixDictDelete(mutate self: cell, keyLen: int, key: slice): bool | ||||
|     asm(key self keyLen) "PFXDICTDEL"; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| // A part of standard library for Tolk | ||||
| tolk 0.6 | ||||
| tolk 0.7 | ||||
| 
 | ||||
| /// 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`. | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ fun test88(x: int) { | |||
| } | ||||
| 
 | ||||
| @method_id(89) | ||||
| fun test89(last: int) { | ||||
| fun test89(last: int): (int, int, int, int) { | ||||
|     var t: tuple = createEmptyTuple(); | ||||
|     t.tuplePush(1); | ||||
|     t.tuplePush(2); | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ fun calc_phi(): int { | |||
|   repeat (70) { n*=10; }; | ||||
|   var p= 1; | ||||
|   var `q`=1; | ||||
|   _=`q`; | ||||
|   do { | ||||
|     (p,q)=(q,p+q); | ||||
|   } while (q <= n); //;; | ||||
|  | @ -27,7 +28,7 @@ fun calc_sqrt2(): int { | |||
|   return mulDivRound(p, n, q); | ||||
| } | ||||
| 
 | ||||
| fun calc_root(m: auto): auto { | ||||
| fun calc_root(m: int) { | ||||
|   var base: int=1; | ||||
|   repeat(70) { base *= 10; } | ||||
|   var (a, b, c) = (1,0,-m); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| @deprecated | ||||
| fun twice(f: auto, x: auto): auto { | ||||
| fun twice(f: int -> int, x: int) { | ||||
|   return f (f (x)); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,85 +2,112 @@ fun unsafe_tuple<X>(x: X): tuple | |||
|     asm "NOP"; | ||||
| 
 | ||||
| fun inc(x: int, y: int): (int, int) { | ||||
|   return (x + y, y * 10); | ||||
|     return (x + y, y * 10); | ||||
| } | ||||
| fun `~inc`(mutate self: int, y: int): int { | ||||
|   val (newX, newY) = inc(self, y); | ||||
|   self = newX; | ||||
|   return newY; | ||||
|     val (newX, newY) = inc(self, y); | ||||
|     self = newX; | ||||
|     return newY; | ||||
| } | ||||
| 
 | ||||
| fun eq<X>(v: X): X { return v; } | ||||
| fun eq2(v: (int, int)) { return v; } | ||||
| fun mul2(mutate dest: int, v: int): int { dest = v*2; return dest; } | ||||
| fun multens(mutate self: (int, int), v: (int, int)): (int, int) { var (f, s) = self; var (m1, m2) = v; self = (f*m1, s*m2); return self; } | ||||
| 
 | ||||
| @method_id(11) | ||||
| fun test_return(x: int): (int, int, int, int, int, int, int) { | ||||
|   return (x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x); | ||||
|     return (x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x); | ||||
| } | ||||
| 
 | ||||
| @method_id(12) | ||||
| fun test_assign(x: int): (int, int, int, int, int, int, int) { | ||||
|   var (x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int) = (x, x.`~inc`(x / 20), x, x=x*2, x, x+=1, x); | ||||
|   return (x1, x2, x3, x4, x5, x6, x7); | ||||
|     var (x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int) = (x, x.`~inc`(x / 20), x, x=x*2, x, x+=1, x); | ||||
|     return (x1, x2, x3, x4, x5, x6, x7); | ||||
| } | ||||
| 
 | ||||
| @method_id(13) | ||||
| fun test_tuple(x: int): tuple { | ||||
|   var t: tuple = unsafe_tuple([x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x]); | ||||
|   return t; | ||||
|     var t: tuple = unsafe_tuple([x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x]); | ||||
|     return t; | ||||
| } | ||||
| 
 | ||||
| @method_id(14) | ||||
| fun test_tuple_assign(x: int): (int, int, int, int, int, int, int) { | ||||
|   var [x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int] = [x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x]; | ||||
|   return (x1, x2, x3, x4, x5, x6, x7); | ||||
|     var [x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int] = [x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x]; | ||||
|     return (x1, x2, x3, x4, x5, x6, x7); | ||||
| } | ||||
| 
 | ||||
| fun foo1(x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int): (int, int, int, int, int, int, int) { | ||||
|   return (x1, x2, x3, x4, x5, x6, x7); | ||||
|     return (x1, x2, x3, x4, x5, x6, x7); | ||||
| } | ||||
| 
 | ||||
| @method_id(15) | ||||
| fun test_call_1(x: int): (int, int, int, int, int, int, int) { | ||||
|   return foo1(x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x); | ||||
|     return foo1(x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x); | ||||
| } | ||||
| 
 | ||||
| fun foo2(x1: int, x2: int, x3456: (int, int, int, int), x7: int): (int, int, int, int, int, int, int) { | ||||
|   var (x3: int, x4: int, x5: int, x6: int) = x3456; | ||||
|   return (x1, x2, x3, x4, x5, x6, x7); | ||||
|     var (x3: int, x4: int, x5: int, x6: int) = x3456; | ||||
|     return (x1, x2, x3, x4, x5, x6, x7); | ||||
| } | ||||
| 
 | ||||
| @method_id(16) | ||||
| fun test_call_2(x: int): (int, int, int, int, int, int, int) { | ||||
|   return foo2(x, x.`~inc`(x / 20), (x, x = x * 2, x, x += 1), x); | ||||
|     return foo2(x, x.`~inc`(x / 20), (x, x = x * 2, x, x += 1), x); | ||||
| } | ||||
| 
 | ||||
| fun asm_func(x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int): (int, int, int, int, int, int, int) | ||||
| asm | ||||
|         (x4 x5 x6 x7 x1 x2 x3->0 1 2 3 4 5 6) "NOP"; | ||||
|     asm (x4 x5 x6 x7 x1 x2 x3->0 1 2 3 4 5 6) "NOP"; | ||||
| 
 | ||||
| @method_id(17) | ||||
| fun test_call_asm_old(x: int): (int, int, int, int, int, int, int) { | ||||
|   return asm_func(x, x += 1, x, x, x.`~inc`(x / 20), x, x = x * 2); | ||||
|     return asm_func(x, x += 1, x, x, x.`~inc`(x / 20), x, x = x * 2); | ||||
| } | ||||
| 
 | ||||
| @method_id(18) | ||||
| fun test_call_asm_new(x: int): (int, int, int, int, int, int, int) { | ||||
|   return asm_func(x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x); | ||||
|     return asm_func(x, x.`~inc`(x / 20), x, x = x * 2, x, x += 1, x); | ||||
| } | ||||
| 
 | ||||
| global xx: int; | ||||
| @method_id(19) | ||||
| fun test_global(x: int): (int, int, int, int, int, int, int) { | ||||
|   xx = x; | ||||
|   return (xx, xx.`~inc`(xx / 20), xx, xx = xx * 2, xx, xx += 1, xx); | ||||
| fun test_global(x: int) { | ||||
|     xx = x; | ||||
|     return (x, xx, xx.`~inc`(xx / 20), eq(xx += (x *= 0)), xx = xx * 2, xx, xx += 1, xx, x); | ||||
| } | ||||
| 
 | ||||
| @method_id(20) | ||||
| fun test_if_else(x: int): (int, int, int, int, int) { | ||||
|   if (x > 10) { | ||||
|     return (x.`~inc`(8), x + 1, x = 1, x <<= 3, x); | ||||
|   } else { | ||||
|     xx = 9; | ||||
|     return (x, x.`~inc`(-4), x.`~inc`(-1), x >= 1, x = x + xx); | ||||
|   } | ||||
|     if (x > 10) { | ||||
|         return (x.`~inc`(8), x + 1, x = 1, x <<= 3, x); | ||||
|     } else { | ||||
|         xx = 9; | ||||
|         return (x, x.`~inc`(-4), x.`~inc`(-1), (x >= 1) as int, x = x + xx); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @method_id(21) | ||||
| fun test_assign_with_inner(x: int) { | ||||
|     return (x, x += 10, [(x, x += 20, eq(x -= 50), x)], eq2((x, x *= eq(x /= 2)))); | ||||
| } | ||||
| 
 | ||||
| @method_id(22) | ||||
| fun test_assign_with_mutate(x: int) { | ||||
|     return (x, mul2(mutate x, x += 5), x.`~inc`(mul2(mutate x, x)), x); | ||||
| } | ||||
| 
 | ||||
| @method_id(23) | ||||
| fun test_assign_tensor(x: (int, int)) { | ||||
|     var fs = (0, 0); | ||||
|     return (x, x = (20, 30), fs = x.multens((1, 2)), fs.multens(multens(mutate x, (-1, -1))), x, fs); | ||||
| } | ||||
| 
 | ||||
| global fs: (int, int); | ||||
| @method_id(24) | ||||
| fun test_assign_tensor_global(x: (int, int)) { | ||||
|     fs = (0, 0); | ||||
|     return (x, x = (20, 30), fs = x.multens((1, 2)), fs.multens(multens(mutate x, (-1, -1))), x, fs); | ||||
| } | ||||
| 
 | ||||
| fun main() { | ||||
|  | @ -96,9 +123,13 @@ fun main() { | |||
| @testcase | 16 | 100 | 100 50 105 210 210 211 211 | ||||
| @testcase | 17 | 100 | 101 50 106 212 100 101 101 | ||||
| @testcase | 18 | 100 | 210 210 211 211 100 50 105 | ||||
| @testcase | 19 | 100 | 100 50 105 210 210 211 211 | ||||
| @testcase | 19 | 100 | 100 100 50 105 210 210 211 211 0 | ||||
| @testcase | 20 | 80  | 80 89 1 8 8 | ||||
| @testcase | 20 | 9   | 9 -40 -10 -1 13 | ||||
| @testcase | 21 | 100 | 100 110 [ 110 130 80 80 ] 80 3200 | ||||
| @testcase | 22 | 100 | 100 210 4200 630 | ||||
| @testcase | 23 | 1 1 | 1 1 20 30 20 60 -400 -3600 -20 -60 -400 -3600 | ||||
| @testcase | 24 | 1 1 | 1 1 20 30 20 60 -400 -3600 -20 -60 -400 -3600 | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|  | @ -107,5 +138,5 @@ fun main() { | |||
|     inc CALLDICT // self newY | ||||
|   }> | ||||
| """ | ||||
| @code_hash 97139400653362069936987769894397430077752335662822462908581556703209313861576 | ||||
| @code_hash 7627024945492125068389905298530400936797031708759561372406088054030801992712 | ||||
| */ | ||||
|  |  | |||
							
								
								
									
										28
									
								
								tolk-tester/tests/assignment-tests.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tolk-tester/tests/assignment-tests.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| fun extractFromTypedTuple(params: [int]) { | ||||
|     var [payload: int] = params; | ||||
|     return payload + 10; | ||||
| } | ||||
| 
 | ||||
| @method_id(101) | ||||
| fun test101(x: int) { | ||||
|     var params = [x]; | ||||
|     return extractFromTypedTuple(params); | ||||
| } | ||||
| 
 | ||||
| fun autoInferIntNull(x: int) { | ||||
|     if (x > 10) { return null; } | ||||
|     return x; | ||||
| } | ||||
| 
 | ||||
| fun main(value: int) { | ||||
|     var (x: int, y) = (autoInferIntNull(value), autoInferIntNull(value * 2)); | ||||
|     if (x == null && y == null) { return null; } | ||||
|     return x == null || y == null ? -1 : x + y; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @testcase | 0   | 3      | 9 | ||||
| @testcase | 0   | 6      | -1 | ||||
| @testcase | 0   | 11     | (null) | ||||
| @testcase | 101 | 78     | 88 | ||||
| */ | ||||
|  | @ -1,20 +1,20 @@ | |||
| fun lshift(): int { | ||||
| fun lshift(): bool { | ||||
|     return (1 << 0) == 1; | ||||
| } | ||||
| 
 | ||||
| fun rshift(): int { | ||||
| fun rshift(): bool { | ||||
|     return (1 >> 0) == 1; | ||||
| } | ||||
| 
 | ||||
| fun lshift_var(i: int): int { | ||||
| fun lshift_var(i: int): bool { | ||||
|     return (1 << i) == 1; | ||||
| } | ||||
| 
 | ||||
| fun rshift_var(i: int): int { | ||||
| fun rshift_var(i: int): bool { | ||||
|     return (1 >> i) == 1; | ||||
| } | ||||
| 
 | ||||
| fun main(x: int): int { | ||||
| fun main(x: int): bool { | ||||
|     if (x == 0) { | ||||
|         return lshift(); | ||||
|     } else if (x == 1) { | ||||
|  | @ -31,12 +31,71 @@ fun main(x: int): int { | |||
| } | ||||
| 
 | ||||
| @method_id(11) | ||||
| fun is_claimed(index: int): int { | ||||
| fun is_claimed(index: int): bool { | ||||
|     var claim_bit_index: int = index % 256; | ||||
|     var mask: int = 1 << claim_bit_index; | ||||
|     return (255 & mask) == mask; | ||||
| } | ||||
| 
 | ||||
| @method_id(12) | ||||
| fun bit_not(i: int, b: bool): (int, bool, bool, bool, int, bool) { | ||||
|     var i2 = ~i; | ||||
|     var b2 = !b; | ||||
|     var (i3: int, b3: bool) = (i2, b2); | ||||
|     return (i3, b3, !i, !b, ~~~i, !!!b); | ||||
| } | ||||
| 
 | ||||
| @method_id(13) | ||||
| fun boolWithBitwiseConst() { | ||||
|     var found = true; | ||||
|     return (found & false, found | true, found ^ true, found & found); | ||||
| } | ||||
| 
 | ||||
| global g14: int; | ||||
| fun getBool() { return (g14 += 1) > 2; } | ||||
| 
 | ||||
| @method_id(14) | ||||
| fun boolWithBitwise(b: bool) { | ||||
|     g14 = 0; | ||||
|     return (b & getBool(), !b & getBool(), b | getBool(), !b | getBool(), b ^ getBool(), !b & getBool(), g14); | ||||
| } | ||||
| 
 | ||||
| @method_id(15) | ||||
| fun boolWithBitwiseSet(b1: bool, b2: bool) { | ||||
|     b1 &= b2; | ||||
|     b2 |= true; | ||||
|     b1 |= b1 == false; | ||||
|     b2 ^= (b1 ^= b2); | ||||
|     return (b1, b2); | ||||
| } | ||||
| 
 | ||||
| @method_id(16) | ||||
| fun testDoUntilCodegen(i: bool, n: int) { | ||||
|     var cnt = 0; | ||||
|     do { cnt += 1; } while (i); | ||||
|     do { cnt += 1; } while (!!i); | ||||
|     do { cnt += 1; } while (n); | ||||
|     return (cnt, !i, !n); | ||||
| } | ||||
| 
 | ||||
| @method_id(17) | ||||
| fun testConstNegateCodegen() { | ||||
|     return (!0, !1, !true, !false, !!true, !!false); | ||||
| } | ||||
| 
 | ||||
| @method_id(18) | ||||
| fun testBoolNegateOptimized(x: bool) { | ||||
|     return (x, !x, !!x, !!!x, !!!!true); | ||||
| } | ||||
| 
 | ||||
| fun eqX(x: bool) { return x; } | ||||
| 
 | ||||
| @method_id(19) | ||||
| fun testBoolCompareOptimized(x: bool) { | ||||
|     return (x == true, x != true, eqX(x) == false, eqX(x) != false, !!(x == !false)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|     method_id | in  | out | ||||
|  | @ -50,4 +109,96 @@ fun is_claimed(index: int): int { | |||
| @testcase | 11 | 1   | -1 | ||||
| @testcase | 11 | 256 | -1 | ||||
| @testcase | 11 | 8   |  0 | ||||
| @testcase | 12 | 0 0   | -1 -1 -1 -1 -1 -1 | ||||
| @testcase | 12 | -1 -1 | 0 0 0 0 0 0 | ||||
| @testcase | 12 | 7 0   | -8 -1 0 -1 -8 -1 | ||||
| @testcase | 14 | -1    | 0 0 -1 -1 0 0 6 | ||||
| @testcase | 14 | 0     | 0 0 -1 -1 -1 -1 6 | ||||
| @testcase | 15 | -1 -1 | 0 -1 | ||||
| @testcase | 15 | -1 0  | 0 -1 | ||||
| @testcase | 16 | 0 0   | 3 -1 -1 | ||||
| @testcase | 17 |       | -1 0 0 -1 -1 0 | ||||
| @testcase | 18 | 0     | 0 -1 0 -1 -1 | ||||
| @testcase | 18 | -1    | -1 0 -1 0 -1 | ||||
| @testcase | 19 | 0     | 0 -1 -1 0 0 | ||||
| @testcase | 19 | -1    | -1 0 0 -1 -1 | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   boolWithBitwiseConst PROC:<{ | ||||
|     // | ||||
|     0 PUSHINT	//  _3 | ||||
|     -1 PUSHINT	//  _3 _5 | ||||
|     0 PUSHINT	//  _3 _5 _7 | ||||
|     -1 PUSHINT	//  _3 _5 _7 _8 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   testDoUntilCodegen PROC:<{ | ||||
|     //  i n | ||||
|     0 PUSHINT	//  i n cnt=0 | ||||
|     UNTIL:<{ | ||||
|       INC	//  i n cnt | ||||
|       s2 PUSH	//  i n cnt i | ||||
|       NOT	//  i n cnt _6 | ||||
|     }>	//  i n cnt | ||||
|     UNTIL:<{ | ||||
|       INC	//  i n cnt | ||||
|       s2 PUSH	//  i n cnt i | ||||
|       NOT	//  i n cnt _9 | ||||
|     }>	//  i n cnt | ||||
|     UNTIL:<{ | ||||
|       INC	//  i n cnt | ||||
|       OVER	//  i n cnt n | ||||
|       0 EQINT	//  i n cnt _12 | ||||
|     }>	//  i n cnt | ||||
|     s0 s2 XCHG	//  cnt n i | ||||
|     NOT	//  cnt n _13 | ||||
|     SWAP	//  cnt _13 n | ||||
|     0 EQINT	//  cnt _13 _14 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   testConstNegateCodegen PROC:<{ | ||||
|     // | ||||
|     TRUE	//  _0 | ||||
|     FALSE	//  _0 _1 | ||||
|     FALSE	//  _0 _1 _2 | ||||
|     TRUE	//  _0 _1 _2 _3 | ||||
|     TRUE	//  _0 _1 _2 _3 _4 | ||||
|     FALSE	//  _0 _1 _2 _3 _4 _5 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   testBoolNegateOptimized PROC:<{ | ||||
|     //  x | ||||
|     DUP	//  x x | ||||
|     NOT	//  x _1 | ||||
|     OVER	//  x _1 x | ||||
|     NOT	//  x _1 _2 | ||||
|     s2 s(-1) PUXC | ||||
|     TRUE	//  x _1 x _2 _3 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   testBoolCompareOptimized PROC:<{ | ||||
|     //  x | ||||
|     DUP	//  x x | ||||
|     NOT	//  x _1 | ||||
|     OVER	//  x _1 x | ||||
|     eqX CALLDICT	//  x _1 _2 | ||||
|     NOT	//  x _1 _3 | ||||
|     s2 PUSH	//  x _1 _3 x | ||||
|     eqX CALLDICT	//  x _1 _3 _4 | ||||
|     s3 PUSH	//  x _1 _3 _4 x | ||||
|   }> | ||||
| """ | ||||
| */ | ||||
|  |  | |||
|  | @ -1,20 +1,21 @@ | |||
| global op: (int, int) -> int; | ||||
| 
 | ||||
| fun check_assoc(a: int, b: int, c: int): int { | ||||
| fun check_assoc(a: int, b: int, c: int): bool { | ||||
|   return op(op(a, b), c) == op(a, op(b, c)); | ||||
| } | ||||
| 
 | ||||
| fun unnamed_args(_: int, _: slice, _: auto): auto { | ||||
| fun unnamed_args(_: int, _: slice, _: int) { | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| fun main(x: int, y: int, z: int): int { | ||||
| fun main(x: int, y: int, z: int): bool { | ||||
|   op = `_+_`; | ||||
|   if (0) { return null; } | ||||
|   return check_assoc(x, y, z); | ||||
| } | ||||
| 
 | ||||
| @method_id(101) | ||||
| fun test101(x: int, z: int): auto { | ||||
| fun test101(x: int, z: int) { | ||||
|   return unnamed_args(x, "asdf", z); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| fun check_assoc(op: auto, a: int, b: int, c: int) { | ||||
| fun check_assoc(op: (int, int) -> int, a: int, b: int, c: int) { | ||||
|   return op(op(a, b), c) == op(a, op(b, c)); | ||||
| } | ||||
| 
 | ||||
| fun main(x: int, y: int, z: int): int { | ||||
| fun main(x: int, y: int, z: int): bool { | ||||
|   return check_assoc(`_+_`, x, y, z); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -162,8 +162,8 @@ fun test13() { | |||
| } | ||||
| 
 | ||||
| @method_id(114) | ||||
| fun test110(x: int) { | ||||
|     var s = beginCell().storeBool(x < 0).storeBool(0).storeBool(x).endCell().beginParse(); | ||||
| fun test110(x: bool) { | ||||
|     var s = beginCell().storeBool(x == true).storeBool(false).storeBool(x).endCell().beginParse(); | ||||
|     return (s.loadBool(), s.loadBool(), s.loadBool()); | ||||
| } | ||||
| 
 | ||||
|  | @ -179,15 +179,15 @@ fun test111() { | |||
|     if (s.addressIsNone()) { | ||||
|         s.skipBits(2); | ||||
|     } | ||||
|     if (s.loadBool() == 0) { | ||||
|         assert(s.loadBool() == 0) throw 444; | ||||
|     if (s.loadBool() == false) { | ||||
|         assert(!s.loadBool()) throw 444; | ||||
|         s.skipBouncedPrefix(); | ||||
|     } | ||||
|     var op2 = s.loadMessageOp(); | ||||
|     var q2 = s.loadMessageQueryId(); | ||||
|     s.skipBits(64); | ||||
|     s.assertEndOfSlice(); | ||||
|     assert(isMessageBounced(0x001)) throw 444; | ||||
|     assert(isMessageBounced(0x001) && !isMessageBounced(0x002)) throw 444; | ||||
|     return (op1, q1, op2, q2); | ||||
| } | ||||
| 
 | ||||
|  | @ -216,15 +216,15 @@ Note, that since 'compute-asm-ltr' became on be default, chaining methods codege | |||
| """ | ||||
|   test6 PROC:<{ | ||||
|     // | ||||
|     NEWC	//  _1 | ||||
|     1 PUSHINT	//  _1 _2=1 | ||||
|     SWAP	//  _2=1 _1 | ||||
|     NEWC	//  _0 | ||||
|     1 PUSHINT	//  _0 _1=1 | ||||
|     SWAP	//  _1=1 _0 | ||||
|     32 STU	//  _0 | ||||
|     2 PUSHINT	//  _0 _6=2 | ||||
|     SWAP	//  _6=2 _0 | ||||
|     2 PUSHINT	//  _0 _5=2 | ||||
|     SWAP	//  _5=2 _0 | ||||
|     32 STU	//  _0 | ||||
|     3 PUSHINT	//  _0 _10=3 | ||||
|     SWAP	//  _10=3 _0 | ||||
|     3 PUSHINT	//  _0 _9=3 | ||||
|     SWAP	//  _9=3 _0 | ||||
|     32 STU	//  _0 | ||||
|   }> | ||||
| """ | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| @method_id(101) | ||||
| fun test1(): int { | ||||
|     var x = false; | ||||
|     if (x == true) { | ||||
|     var x: int = false as int; | ||||
|     if (x == true as int) { | ||||
|         x= 100500; | ||||
|     } | ||||
|     return x; | ||||
|  | @ -35,7 +35,7 @@ Below, I just give examples of @fif_codegen tag: | |||
| """ | ||||
| main PROC:<{ | ||||
|   // s | ||||
|   17 PUSHINT     //  s _3=17 | ||||
|   17 PUSHINT     //  s _1=17 | ||||
|   OVER           //  s z=17 t | ||||
|   WHILE:<{ | ||||
|     ... | ||||
|  |  | |||
							
								
								
									
										150
									
								
								tolk-tester/tests/generics-1.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								tolk-tester/tests/generics-1.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,150 @@ | |||
| fun eq1<X>(value: X): X { return value; } | ||||
| fun eq2<X>(value: X)    { return value; } | ||||
| fun eq3<X>(value: X): X { var cp: [X] = [eq1(value)]; var ((([v: X]))) = cp; return v; } | ||||
| fun eq4<X>(value: X)    { return eq1<X>(value); } | ||||
| 
 | ||||
| @method_id(101) | ||||
| fun test101(x: int) { | ||||
|     var (a, b, c) = (x, (x,x), [x,x]); | ||||
|     return (eq1(a), eq1(b), eq1(c), eq2(a), eq2(b), eq2(c), eq3(a), eq4(b), eq3(createEmptyTuple())); | ||||
| } | ||||
| 
 | ||||
| fun getTwo<X>(): X { return 2 as X; } | ||||
| 
 | ||||
| fun takeInt(a: int) { return a; } | ||||
| 
 | ||||
| @method_id(102) | ||||
| fun test102(): (int, int, int, [(int, int)]) { | ||||
|     var a: int = getTwo(); | ||||
|     var _: int = getTwo(); | ||||
|     var b = getTwo() as int; | ||||
|     var c: int = 1 ? getTwo() : getTwo(); | ||||
|     var c redef = getTwo(); | ||||
|     return (eq1<int>(a), eq2<int>(b), takeInt(getTwo()), [(getTwo(), getTwo())]); | ||||
| } | ||||
| 
 | ||||
| @method_id(103) | ||||
| fun test103(first: int): (int, int, int) { | ||||
|     var t = createEmptyTuple(); | ||||
|     var cs = beginCell().storeInt(100, 32).endCell().beginParse(); | ||||
|     t.tuplePush(first); | ||||
|     t.tuplePush(2); | ||||
|     t.tuplePush(cs); | ||||
|     cs = t.tupleAt(2); | ||||
|     cs = t.tupleAt(2) as slice; | ||||
|     return (t.tupleAt(0), cs.loadInt(32), t.tupleAt<slice>(2).loadInt(32)); | ||||
| } | ||||
| 
 | ||||
| fun manyEq<T1, T2, T3>(a: T1, b: T2, c: T3): [T1, T2, T3] { | ||||
|     return [a, b, c]; | ||||
| } | ||||
| 
 | ||||
| @method_id(104) | ||||
| fun test104(f: int) { | ||||
|     return ( | ||||
|         manyEq(1 ? 1 : 1, f ? 0 : null, !f ? getTwo() as int : null), | ||||
|         manyEq((f ? null as int : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool()), 0, eq4(f)) | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| fun calcSum<X>(x: X, y: X) { return x + y; } | ||||
| 
 | ||||
| @method_id(105) | ||||
| fun test105() { | ||||
|     if (0) { calcSum(((0)), null); } | ||||
|     return (calcSum(1, 2)); | ||||
| } | ||||
| 
 | ||||
| fun calcYPlus1<Y>(value: Y) { return value + 1; } | ||||
| fun calcLoad32(cs: slice) { return cs.loadInt(32); } | ||||
| fun calcTensorPlus1(tens: (int, int)) { var (f, s) = tens; return (f + 1, s + 1); } | ||||
| fun calcTensorMul2(tens: (int, int)) { var (f, s) = tens; return (f * 2, s * 2); } | ||||
| fun cellToSlice(c: cell) { return c.beginParse(); } | ||||
| fun abstractTransform<X, Y, R>(xToY: (X) -> Y, yToR: (((Y))) -> R, initialX: X): R { | ||||
|     var y = xToY(initialX); | ||||
|     return yToR(y); | ||||
| } | ||||
| 
 | ||||
| @method_id(106) | ||||
| fun test106() { | ||||
|     var c = beginCell().storeInt(106, 32).endCell(); | ||||
|     return [ | ||||
|         abstractTransform(cellToSlice, calcLoad32, c), | ||||
|         abstractTransform(calcYPlus1<int>, calcYPlus1<int>, 0), | ||||
|         abstractTransform(calcTensorPlus1, calcTensorMul2, (2, 2)) | ||||
|     ]; | ||||
| } | ||||
| 
 | ||||
| fun callTupleFirst<X, Y>(t: X): Y { return t.tupleFirst(); } | ||||
| fun callTuplePush<T, V>(mutate self: T, v1: V, v2: V): self { self.tuplePush(v1); tuplePush(mutate self, v2); return self; } | ||||
| fun getTupleLastInt(t: tuple) { return t.tupleLast<int>(); } | ||||
| fun getTupleSize(t: tuple) { return t.tupleSize(); } | ||||
| fun callAnyFn<TObj, TResult>(f: (TObj) -> TResult, arg: TObj) { return f(arg); } | ||||
| fun callAnyFn2<TCallback>(f: TCallback, arg: tuple) { return f(arg); } | ||||
| 
 | ||||
| global t107: tuple; | ||||
| 
 | ||||
| @method_id(107) | ||||
| fun test107() { | ||||
|     t107 = createEmptyTuple(); | ||||
|     callTuplePush(mutate t107, 1, 2); | ||||
|     t107.callTuplePush(3, 4).callTuplePush(5, 6); | ||||
|     var first: int = t107.callTupleFirst(); | ||||
|     return ( | ||||
|         callAnyFn<tuple, int>(getTupleSize, t107), | ||||
|         callAnyFn2(getTupleSize, t107), | ||||
|         first, | ||||
|         callTupleFirst(t107) as int, | ||||
|         callAnyFn(getTupleLastInt, t107), | ||||
|         callAnyFn2(getTupleLastInt, t107) | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| global g108: int; | ||||
| 
 | ||||
| fun inc108(by: int) { g108 += by; } | ||||
| fun getInc108() { return inc108; } | ||||
| fun returnResult<RetT>(f: () -> RetT): RetT { return f(); } | ||||
| fun applyAndReturn<ArgT, RetT>(f: () -> (ArgT) -> RetT, arg: ArgT): () -> ArgT -> RetT { | ||||
|     f()(arg); | ||||
|     return f; | ||||
| } | ||||
| 
 | ||||
| @method_id(108) | ||||
| fun test108() { | ||||
|     g108 = 0; | ||||
|     getInc108()(1); | ||||
|     returnResult<(int) -> void>(getInc108)(2); | ||||
|     applyAndReturn<int, void>(getInc108, 10)()(10); | ||||
|     returnResult(getInc108)(2); | ||||
|     applyAndReturn(getInc108, 10)()(10); | ||||
|     return g108; | ||||
| } | ||||
| 
 | ||||
| fun main(x: int): (int, [[int, int]]) { | ||||
|     try { if(x) { throw (1, x); } } | ||||
|     catch (excNo, arg) { return (arg as int, [[eq2(arg as int), getTwo()]]); } | ||||
|     return (0, [[x, 1]]); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @testcase | 0   | 1 | 1 [ [ 1 2 ] ] | ||||
| @testcase | 101 | 0 | 0 0 0 [ 0 0 ] 0 0 0 [ 0 0 ] 0 0 0 [] | ||||
| @testcase | 102 |   | 2 2 2 [ 2 2 ] | ||||
| @testcase | 103 | 0 | 0 100 100 | ||||
| @testcase | 104 | 0 | [ 1 (null) 2 ] [ 2 -1 0 0 ] | ||||
| @testcase | 105 |   | 3 | ||||
| @testcase | 106 |   | [ 106 2 6 6 ] | ||||
| @testcase | 107 |   | 6 6 1 1 6 6 | ||||
| @testcase | 108 |   | 45 | ||||
| 
 | ||||
| @fif_codegen DECLPROC eq1<int> | ||||
| @fif_codegen DECLPROC eq1<tuple> | ||||
| @fif_codegen DECLPROC eq1<(int,int)> | ||||
| @fif_codegen DECLPROC eq1<[int,int]> | ||||
| @fif_codegen DECLPROC getTwo<int> | ||||
| 
 | ||||
| @fif_codegen_avoid DECLPROC eq1 | ||||
| @fif_codegen_avoid DECLPROC eq2 | ||||
| @fif_codegen_avoid DECLPROC eq3 | ||||
|  */ | ||||
|  | @ -11,7 +11,7 @@ fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell { | |||
| 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); | ||||
|         var (cur_key redef, cs: slice, found: bool) = 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) { | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| fun main() { | ||||
|     return true(); | ||||
| const asdf = 1; | ||||
| 
 | ||||
| fun main(x: int) { | ||||
|     return x.asdf(); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| The message is weird now, but later I'll rework error messages anyway. | ||||
| @stderr cannot apply expression of type int to an expression of type (): cannot unify type () -> ??3 with int | ||||
| @stderr calling a non-function | ||||
|  */ | ||||
|  |  | |||
|  | @ -8,6 +8,6 @@ fun main() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr rvalue expected | ||||
| @stderr `_` can't be used as a value; it's a placeholder for a left side of assignment | ||||
| @stderr inc(_) | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										10
									
								
								tolk-tester/tests/invalid-call-9.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tolk-tester/tests/invalid-call-9.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| fun getOne() { return 1; } | ||||
| 
 | ||||
| fun main() { | ||||
|     return getOne<int>(); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr calling a not generic function with generic T | ||||
|  */ | ||||
							
								
								
									
										8
									
								
								tolk-tester/tests/invalid-const-1.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tolk-tester/tests/invalid-const-1.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| fun main() { | ||||
|     return 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr invalid integer constant | ||||
|  */ | ||||
							
								
								
									
										13
									
								
								tolk-tester/tests/invalid-declaration-11.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tolk-tester/tests/invalid-declaration-11.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| // this function is declared incorrectly, | ||||
| // since it should return 2 values onto a stack (1 for returned slice, 1 for mutated int) | ||||
| // but contains not 2 numbers in asm ret_order | ||||
| fun loadAddress2(mutate self: int): slice | ||||
|     asm( -> 1 0 2) "LDMSGADDR"; | ||||
| 
 | ||||
| fun main(){} | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr ret_order (after ->) expected to contain 2 numbers | ||||
| @stderr asm( -> 1 0 2) | ||||
|  */ | ||||
							
								
								
									
										16
									
								
								tolk-tester/tests/invalid-declaration-12.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tolk-tester/tests/invalid-declaration-12.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| fun proxy(x: int) { | ||||
|     return factorial(x); | ||||
| } | ||||
| 
 | ||||
| fun factorial(x: int) { | ||||
|     if (x <= 0) { | ||||
|         return 1; | ||||
|     } | ||||
|     return x * proxy(x-1); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr could not infer return type of `factorial`, because it appears in a recursive call chain | ||||
| @stderr fun factorial | ||||
|  */ | ||||
							
								
								
									
										7
									
								
								tolk-tester/tests/invalid-declaration-13.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tolk-tester/tests/invalid-declaration-13.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| const c: slice = 123 + 456; | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr expression type does not match declared type | ||||
| @stderr const c | ||||
|  */ | ||||
							
								
								
									
										10
									
								
								tolk-tester/tests/invalid-generics-1.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tolk-tester/tests/invalid-generics-1.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| fun f<X>(v: int, x: X) {} | ||||
| 
 | ||||
| fun failCantDeduceWithoutArgument() { | ||||
|     return f(1); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not deduce X for generic function `f<X>` | ||||
|  */ | ||||
							
								
								
									
										9
									
								
								tolk-tester/tests/invalid-generics-10.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tolk-tester/tests/invalid-generics-10.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| fun invalidReferencingGenericMethodWithoutGeneric() { | ||||
|     var t = createEmptyTuple(); | ||||
|     var cb = t.tupleLast; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not use a generic function `tupleLast<T>` as non-call | ||||
|  */ | ||||
							
								
								
									
										11
									
								
								tolk-tester/tests/invalid-generics-11.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tolk-tester/tests/invalid-generics-11.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| global gVar: int; | ||||
| 
 | ||||
| fun main() { | ||||
|     var x = gVar<int>; | ||||
|     return x; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr generic T not expected here | ||||
|  */ | ||||
							
								
								
									
										10
									
								
								tolk-tester/tests/invalid-generics-2.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tolk-tester/tests/invalid-generics-2.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| fun f<T>(v: int, x: T) {} | ||||
| 
 | ||||
| fun failCantDeduceWithPlainNull() { | ||||
|     return f(0, null); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not deduce T for generic function `f<T>` | ||||
|  */ | ||||
							
								
								
									
										11
									
								
								tolk-tester/tests/invalid-generics-3.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tolk-tester/tests/invalid-generics-3.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fun f<T>(x: T, y: T) {} | ||||
| 
 | ||||
| fun failIncompatibleTypesForT() { | ||||
|     return f(32, ""); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr T is both int and slice for generic function `f<T>` | ||||
| @stderr f(32 | ||||
|  */ | ||||
							
								
								
									
										10
									
								
								tolk-tester/tests/invalid-generics-4.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tolk-tester/tests/invalid-generics-4.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| fun f<T>(x: T): void asm "NOP"; | ||||
| 
 | ||||
| fun failInstantiatingAsmFunctionWithNon1Slot() { | ||||
|     f((1, 2)); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not call `f<T>` with T=(int, int), because it occupies 2 stack slots in TVM, not 1 | ||||
|  */ | ||||
							
								
								
									
										10
									
								
								tolk-tester/tests/invalid-generics-5.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tolk-tester/tests/invalid-generics-5.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| fun f<T>(x: T): void asm "NOP"; | ||||
| 
 | ||||
| fun failUsingGenericFunctionPartially() { | ||||
|     var cb = f; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not use a generic function `f<T>` as non-call | ||||
|  */ | ||||
							
								
								
									
										10
									
								
								tolk-tester/tests/invalid-generics-6.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tolk-tester/tests/invalid-generics-6.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| fun eq<X>(t: X) { return t; } | ||||
| 
 | ||||
| fun failUsingGenericFunctionPartially() { | ||||
|     var cb = createEmptyTuple().eq().eq().tuplePush; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not use a generic function `tuplePush<T>` as non-call | ||||
|  */ | ||||
							
								
								
									
										18
									
								
								tolk-tester/tests/invalid-generics-7.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								tolk-tester/tests/invalid-generics-7.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| fun failOnInstantiation(a: slice) { | ||||
|     var b: slice = foo(a); | ||||
| } | ||||
| 
 | ||||
| fun bar<X>(value: X) : X { | ||||
|     return 1; | ||||
| } | ||||
| fun foo<X>(value: X) : X { | ||||
|     return bar(value); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr while instantiating generic function `foo<slice>` | ||||
| @stderr while instantiating generic function `bar<slice>` | ||||
| @stderr can not convert type `int` to return type `slice` | ||||
| @stderr return 1 | ||||
|  */ | ||||
							
								
								
									
										11
									
								
								tolk-tester/tests/invalid-generics-8.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tolk-tester/tests/invalid-generics-8.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fun withT1T2<T1, T2>(a: (T1, T2)) {} | ||||
| 
 | ||||
| fun wrongTCountPassed() { | ||||
|     withT1T2<int>((5, "")); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr wrong count of generic T: expected 2, got 1 | ||||
| @stderr <int> | ||||
|  */ | ||||
							
								
								
									
										8
									
								
								tolk-tester/tests/invalid-generics-9.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tolk-tester/tests/invalid-generics-9.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| fun invalidProvidingGenericTsToNotGeneric() { | ||||
|     beginCell<builder>(); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr calling a not generic function with generic T | ||||
|  */ | ||||
|  | @ -7,5 +7,5 @@ fun cantAssignToVal() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying an immutable variable `x` | ||||
| @stderr modifying immutable variable `x` | ||||
|  */ | ||||
|  |  | |||
|  | @ -4,5 +4,5 @@ fun load32(self: slice): int { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying `self` (call a mutating method), which is immutable by default | ||||
| @stderr modifying `self`, which is immutable by default | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										9
									
								
								tolk-tester/tests/invalid-mutate-16.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tolk-tester/tests/invalid-mutate-16.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| fun cantCallMutatingFunctionWithAssignmentLValue() { | ||||
|     var t: tuple = createEmptyTuple(); | ||||
|     (t = createEmptyTuple()).tuplePush(1); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr assignment can not be used as lvalue | ||||
|  */ | ||||
							
								
								
									
										13
									
								
								tolk-tester/tests/invalid-mutate-17.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tolk-tester/tests/invalid-mutate-17.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| @pure | ||||
| fun tupleMut(mutate self: tuple): int | ||||
|     asm "TLEN"; | ||||
| 
 | ||||
| fun main() { | ||||
|     var t = createEmptyTuple(); | ||||
|     return [[t.tupleMut]]; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr saving `tupleMut` into a variable is impossible, since it has `mutate` parameters | ||||
|  */ | ||||
|  | @ -6,5 +6,5 @@ fun cantAssignToVal() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying an immutable variable `x` | ||||
| @stderr modifying immutable variable `x` | ||||
|  */ | ||||
|  |  | |||
|  | @ -7,5 +7,5 @@ fun cantAssignToConst() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying an immutable variable `op_increase` | ||||
| @stderr modifying immutable constant | ||||
|  */ | ||||
|  |  | |||
|  | @ -10,5 +10,5 @@ fun cantPassToMutatingFunction() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying an immutable variable `myVal` | ||||
| @stderr modifying immutable variable `myVal` | ||||
|  */ | ||||
|  |  | |||
|  | @ -9,6 +9,6 @@ fun cantCallMutatingMethod(c: cell) { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying an immutable variable `s` (call a mutating method) | ||||
| @stderr modifying immutable variable `s` | ||||
| @stderr s.loadUint | ||||
|  */ | ||||
|  |  | |||
|  | @ -11,6 +11,6 @@ fun cantCallMutatingFunctionWithImmutable() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying an immutable variable `op_increase` (call a mutating function) | ||||
| @stderr modifying immutable constant | ||||
| @stderr inc(mutate op_increase) | ||||
|  */ | ||||
|  |  | |||
|  | @ -10,6 +10,6 @@ fun cantCallMutatingFunctionWithRvalue() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr lvalue expected (call a mutating function) | ||||
| @stderr literal can not be used as lvalue | ||||
| @stderr incBoth(mutate x, mutate 30) | ||||
|  */ | ||||
|  |  | |||
|  | @ -6,5 +6,5 @@ fun cantRedefImmutable() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying an immutable variable `x` (left side of assignment) | ||||
| @stderr `redef` for immutable variable | ||||
|  */ | ||||
|  |  | |||
|  | @ -4,6 +4,6 @@ fun increment(self: int) { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr modifying `self` (left side of assignment), which is immutable by default | ||||
| @stderr modifying `self`, which is immutable by default | ||||
| @stderr probably, you want to declare `mutate self` | ||||
|  */ | ||||
|  |  | |||
|  | @ -4,5 +4,5 @@ fun load_u32(cs: slice): (slice, int) { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr expected `(`, got `32` | ||||
| @stderr expected `;`, got `32` | ||||
|  */ | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ fun f_pure(): int { | |||
|     return f_impure(); | ||||
| } | ||||
| 
 | ||||
| fun f_impure(): int {} | ||||
| fun f_impure(): int { return 0; } | ||||
| 
 | ||||
| fun main(): int { | ||||
|     return f_pure(); | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| fun validate_input(input: cell): (int, int) { | ||||
|     var (x, y, z, correct) = calculateCellSize(input, 10); | ||||
|     assert(correct) throw 102; | ||||
|     return (x, y); | ||||
| } | ||||
| 
 | ||||
| @pure | ||||
|  |  | |||
							
								
								
									
										10
									
								
								tolk-tester/tests/invalid-redefinition-6.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tolk-tester/tests/invalid-redefinition-6.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| const s1 = "asdf"; | ||||
| 
 | ||||
| fun main() { | ||||
|     var s1 redef = "d"; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr `redef` for unknown variable | ||||
|  */ | ||||
|  | @ -4,6 +4,6 @@ fun cantReturnNothingFromSelf(mutate self: int): self { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr missing return; forgot `return self`? | ||||
| @stderr missing return | ||||
| @stderr } | ||||
|  */ | ||||
|  |  | |||
|  | @ -4,5 +4,5 @@ fun main(x: int) { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr null is not a function: use `null`, not `null()` | ||||
| @stderr calling a non-function | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										9
									
								
								tolk-tester/tests/invalid-syntax-6.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tolk-tester/tests/invalid-syntax-6.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| fun main() { | ||||
|   var a = 1; | ||||
|   (a += 1) += 2; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr assignment can not be used as lvalue | ||||
| */ | ||||
							
								
								
									
										9
									
								
								tolk-tester/tests/invalid-syntax-7.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tolk-tester/tests/invalid-syntax-7.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| fun main() { | ||||
|   var x = 1; | ||||
|   x += (var y = 2); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr expected <expression>, got `var` | ||||
| */ | ||||
|  | @ -6,5 +6,5 @@ fun main() { | |||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr .tolk:2 | ||||
| @stderr expected <type>, got `scli` | ||||
| @stderr unknown type name `scli` | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										8
									
								
								tolk-tester/tests/invalid-typing-10.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tolk-tester/tests/invalid-typing-10.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| fun failMathOnBoolean(c: cell) { | ||||
|     return (null == c) * 10; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not apply operator `*` to `bool` and `int` | ||||
|  */ | ||||
							
								
								
									
										11
									
								
								tolk-tester/tests/invalid-typing-11.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tolk-tester/tests/invalid-typing-11.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| fun failBitwiseNotOnBool() { | ||||
|     var eq = 1 == 0; | ||||
|     if (~eq) { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not apply operator `~` to `bool` | ||||
|  */ | ||||
							
								
								
									
										10
									
								
								tolk-tester/tests/invalid-typing-12.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tolk-tester/tests/invalid-typing-12.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| fun failAssignNullToTensor() { | ||||
|     var ab = (1, 2); | ||||
|     ab = null; | ||||
|     return ab; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not assign `null` to variable of type `(int, int)` | ||||
|  */ | ||||
|  | @ -1,9 +1,9 @@ | |||
| fun main() { | ||||
|     var tri: (int, bool) = (10, false); | ||||
|     var tri: (int, int) = (10, false); | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr bool type is not supported yet | ||||
| @stderr can not assign `(int, bool)` to variable of type `(int, int)` | ||||
|  */ | ||||
|  |  | |||
|  | @ -15,5 +15,5 @@ fun cantMixDifferentThis() { | |||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr cannot apply function appendBuilder : builder -> (builder, ()) to arguments of type int: cannot unify type int with builder | ||||
| @stderr can not call method for `builder` with object of type `int` | ||||
|  */ | ||||
|  |  | |||
|  | @ -7,8 +7,6 @@ fun cantCallNotChainedMethodsInAChain(x: int) { | |||
| } | ||||
| 
 | ||||
| /** | ||||
| The error is very weird, but nevertheless, the type system prevents of doing such errors. | ||||
| 
 | ||||
| @compilation_should_fail | ||||
| @stderr cannot apply function incNotChained : int -> (int, ()) to arguments of type (): cannot unify type () with int | ||||
| @stderr can not call method for `int` with object of type `void` | ||||
|  */ | ||||
|  |  | |||
|  | @ -7,8 +7,7 @@ fun failWhenReturnANotChainedValue(x: int): int { | |||
| } | ||||
| 
 | ||||
| /** | ||||
| The error is very weird, but nevertheless, the type system prevents of doing such errors. | ||||
| 
 | ||||
| @compilation_should_fail | ||||
| @stderr previous function return type int cannot be unified with return statement expression type (): cannot unify type () with int | ||||
| @stderr x.incNotChained() | ||||
| @stderr can not convert type `void` to return type `int` | ||||
|  */ | ||||
|  |  | |||
							
								
								
									
										8
									
								
								tolk-tester/tests/invalid-typing-6.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tolk-tester/tests/invalid-typing-6.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| fun failWhenTernaryConditionNotInt(cs: slice) { | ||||
|     return cs ? 1 : 0; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not use `slice` as a boolean condition | ||||
|  */ | ||||
							
								
								
									
										9
									
								
								tolk-tester/tests/invalid-typing-7.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tolk-tester/tests/invalid-typing-7.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| fun failAssignPlainNullToVariable() { | ||||
|     var x = null; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not infer type of `x`, it's always null | ||||
| @stderr specify its type with `x: <type>` or use `null as <type>` | ||||
|  */ | ||||
							
								
								
									
										8
									
								
								tolk-tester/tests/invalid-typing-8.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tolk-tester/tests/invalid-typing-8.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| fun failExplicitCastIncompatible(c: cell) { | ||||
|     return c as slice; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr type `cell` can not be cast to `slice` | ||||
|  */ | ||||
							
								
								
									
										13
									
								
								tolk-tester/tests/invalid-typing-9.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tolk-tester/tests/invalid-typing-9.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| fun getTupleLastGetter<X>(): tuple -> X { | ||||
|     return tupleLast<X>; | ||||
| } | ||||
| 
 | ||||
| fun failTypeMismatch() { | ||||
|     var t = createEmptyTuple(); | ||||
|     var c: cell = getTupleLastGetter<int>()(t); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @compilation_should_fail | ||||
| @stderr can not assign `int` to variable of type `cell` | ||||
|  */ | ||||
|  | @ -1,14 +1,14 @@ | |||
| import "imports/use-dicts.tolk" | ||||
| 
 | ||||
| fun simpleAllConst() { | ||||
|     return (!0, !!0 & !false, !!!0, !1, !!1, !-1, !!-1, (!5 == 0) == !0, !0 == true); | ||||
|     return (!0, !!0 & !false, !!!0, !1, !!1, !-1, !!-1, (!5 as int == 0) == !0, !0 == true); | ||||
| } | ||||
| 
 | ||||
| fun compileTimeEval1(x: int) { | ||||
|     // todo now compiler doesn't understand that bool can't be equal to number other than 0/-1 | ||||
|     // (but understands that it can't be positive) | ||||
|     // that's why for now, the last condition is evaluated at runtime | ||||
|     return (!x, !x > 10, !x < 10, !!x == 5, !x == -10); | ||||
|     return (!x, !x as int > 10, (!x as int) < 10, !!x as int == 5, !x as int == -10); | ||||
| } | ||||
| 
 | ||||
| @method_id(101) | ||||
|  | @ -23,13 +23,13 @@ fun withAndOr(x: int, y: int, z: int) { | |||
|     var return_at_end = -1; | ||||
|     if (!x & !y) { | ||||
|         if (!z & !y) { return 10; } | ||||
|         else if (z | !!y) { return_at_end = 20; } | ||||
|         else if ((z != 0) | !!y) { return_at_end = 20; } | ||||
|     } else if (!!x & !!y & !z) { | ||||
|         if (!z & (x > 10)) { return_at_end = 30; } | ||||
|         if ((x != 11) & !z) { return 40; } | ||||
|         return_at_end = 50; | ||||
|     } else { | ||||
|         return_at_end = !x ? !y : !z | 1; | ||||
|         return_at_end = !x ? !y as int : (!z as int) | 1; | ||||
|     } | ||||
|     return return_at_end; | ||||
| } | ||||
|  | @ -54,7 +54,8 @@ fun testDict(last: int) { | |||
| 
 | ||||
| @method_id(105) | ||||
| fun testNotNull(x: int) { | ||||
|     return [x == null, null == x, !(x == null), null == null, +(null != null)]; | ||||
|     // return [x == null, null == x, !(x == null), null == null, +(null != null)]; | ||||
|     return [x == null, null == x, !(x == null)]; | ||||
| } | ||||
| 
 | ||||
| @method_id(106) | ||||
|  | @ -123,6 +124,31 @@ fun testLogicalOps2(first: int) { | |||
|     return (s.getRemainingBitsCount(), sum); | ||||
| } | ||||
| 
 | ||||
| @method_id(112) | ||||
| fun mixLogicalIntsAndBools(first: int, cond: bool) { | ||||
|     return ( | ||||
|         (first && cond) || (!first && cond), | ||||
|         ((first & -1) & cond as int) == ((first && true) && cond) as int, | ||||
|         7 && cond, | ||||
|         first || cond || !cond || alwaysThrows(), | ||||
|         cond || first || !first || alwaysThrows() | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| @method_id(113) | ||||
| fun testConvertIfToIfnot(x: bool) { | ||||
|     assert(!!(x == false), 100); | ||||
|     assert(!x, 100); | ||||
|     if (x == !!false) { | ||||
|         return 1; | ||||
|     } | ||||
|     if (!!(x != !false)) { | ||||
|         return 1; | ||||
|     } | ||||
|     assert(!!x, 100); | ||||
|     return -4; | ||||
| } | ||||
| 
 | ||||
| fun main() { | ||||
| 
 | ||||
| } | ||||
|  | @ -144,8 +170,8 @@ fun main() { | |||
| @testcase | 104 | 50     | 3 5 -1 | ||||
| @testcase | 104 | 100    | 3 5 5 | ||||
| @testcase | 104 | 0      | 3 -1 5 | ||||
| @testcase | 105 | 0      | [ 0 0 -1 -1 0 ] | ||||
| @testcase | 105 | null   | [ -1 -1 0 -1 0 ] | ||||
| @testcase | 105 | 0      | [ 0 0 -1 ] | ||||
| @testcase | 105 | null   | [ -1 -1 0 ] | ||||
| @testcase | 106 |        | [ 0 0 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ 0 -1 ] | ||||
| @testcase | 107 |        | [ -1 -1 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ -1 0 ] | ||||
| @testcase | 108 | 1 2    | -1 | ||||
|  | @ -159,18 +185,21 @@ fun main() { | |||
| @testcase | 110 | 500    | -1 -1 0 -1 -1 3 | ||||
| @testcase | 111 | 0      | 32 4 | ||||
| @testcase | 111 | -1     | 0 8 | ||||
| @testcase | 112 | 5 0    | 0 -1 0 -1 -1 | ||||
| @testcase | 112 | 0 -1   | -1 -1 -1 -1 -1 | ||||
| @testcase | 113 | 0      | 1 | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   simpleAllConst PROC:<{ | ||||
|     // | ||||
|     -1 PUSHINT | ||||
|     TRUE | ||||
|     0 PUSHINT | ||||
|     -1 PUSHINT | ||||
|     0 PUSHINT | ||||
|     -1 PUSHINT | ||||
|     0 PUSHINT | ||||
|     -1 PUSHINT | ||||
|     TRUE | ||||
|     FALSE | ||||
|     TRUE | ||||
|     FALSE | ||||
|     TRUE | ||||
|     TRUE | ||||
|     TRUE | ||||
|   }> | ||||
|  | @ -292,4 +321,27 @@ These are moments of future optimizations. For now, it's more than enough. | |||
|   }> | ||||
| """ | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   testConvertIfToIfnot PROC:<{ | ||||
|     //  x | ||||
|     DUP	//  x x | ||||
|     100 THROWIF | ||||
|     DUP	//  x x | ||||
|     100 THROWIF | ||||
|     DUP	//  x x | ||||
|     IFNOTJMP:<{	//  x | ||||
|       DROP	// | ||||
|       1 PUSHINT	//  _7=1 | ||||
|     }>	//  x | ||||
|     DUP	//  x x | ||||
|     IFNOTJMP:<{	//  x | ||||
|       DROP	// | ||||
|       1 PUSHINT	//  _8=1 | ||||
|     }>	//  x | ||||
|     100 THROWIFNOT | ||||
|     -4 PUSHINT	//  _12=-4 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
|  */ | ||||
|  |  | |||
|  | @ -118,12 +118,19 @@ fun updateTwoItems(mutate self: (int, int), byValue: int) { | |||
|     self = (first + byValue, second + byValue); | ||||
| } | ||||
| 
 | ||||
| global t107_1: int; | ||||
| global t107_2: int; | ||||
| 
 | ||||
| @method_id(107) | ||||
| fun testMutableTensor() { | ||||
|     var t = (40, 50); | ||||
|     t.updateTwoItems(10); | ||||
|     updateTwoItems(mutate t, 10); | ||||
|     return t; | ||||
|     t107_1 = 1; | ||||
|     t107_2 = 2; | ||||
|     (t107_1, t107_2).updateTwoItems(10); | ||||
|     updateTwoItems(mutate (t107_1, t107_2), 10); | ||||
|     return (t, t107_1, t107_2); | ||||
| } | ||||
| 
 | ||||
| @pure | ||||
|  | @ -147,7 +154,7 @@ fun getSumOfNumbersInCell(c: cell): int { | |||
| 
 | ||||
| @method_id(110) | ||||
| fun testStoreChaining() { | ||||
|     var b = beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32); | ||||
|     var b = ((beginCell()).storeUint(1, 32)).storeUint(2, 32).storeUint(3, 32); | ||||
|     b.storeUint(4, 32); | ||||
|     b.myStoreUint(5, 32).storeUint(6, 32); | ||||
|     storeUint(mutate b, 7, 32); | ||||
|  | @ -191,7 +198,7 @@ fun testStoreAndMutateBoth() { | |||
|     b.myStoreU32_and_mutate_x(mutate x); | ||||
| 
 | ||||
|     var cs: slice = b.endCell().beginParse(); | ||||
|     var (n1,n2,n3,n4,n5) = (cs.loadUint(32),cs.loadUint(32),cs.loadUint(32),cs.loadUint(32),cs.loadUint(32)); | ||||
|     var (n1,n2,n3,n4,n5) = (cs.loadUint(32),((cs)).loadUint(32),cs.loadUint(32),cs.loadUint(32),cs.loadUint(32)); | ||||
|     assert(n5 == x) throw 100; | ||||
| 
 | ||||
|     return [n1,n2,n3,n4,n5]; | ||||
|  | @ -278,7 +285,7 @@ fun main(){} | |||
| @testcase | 104 |    | 1 2 3 | ||||
| @testcase | 105 |    | 5 5 110 | ||||
| @testcase | 106 |    | 160 110 | ||||
| @testcase | 107 |    | 60 70 | ||||
| @testcase | 107 |    | 60 70 21 22 | ||||
| @testcase | 110 |    | 320 | ||||
| @testcase | 111 |    | 55 55 | ||||
| @testcase | 112 |    | [ 1 13 3 23 33 ] | ||||
|  | @ -300,7 +307,7 @@ fun main(){} | |||
|     ... | ||||
|     incrementTwoInPlace CALLDICT        //  x y sum1 | ||||
|     -ROT | ||||
|     10 PUSHINT  //  sum1 x y _9=10 | ||||
|     10 PUSHINT  //  sum1 x y _8=10 | ||||
|     incrementTwoInPlace CALLDICT        //  sum1 x y sum2 | ||||
|     s1 s3 s0 XCHG3       //  x y sum1 sum2 | ||||
|   }> | ||||
|  | @ -310,8 +317,8 @@ fun main(){} | |||
| """ | ||||
|  load_next PROC:<{ | ||||
|     //  cs | ||||
|     32 LDI      //  _1 cs | ||||
|     SWAP        //  cs _1 | ||||
|     32 LDI      //  _3 cs | ||||
|     SWAP        //  cs _3 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
|  | @ -319,7 +326,7 @@ fun main(){} | |||
| """ | ||||
|   testStoreUintPureUnusedResult PROC:<{ | ||||
|     // | ||||
|     0 PUSHINT	//  _12=0 | ||||
|     0 PUSHINT	//  _11=0 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
|  | @ -330,7 +337,7 @@ fun main(){} | |||
|     NEWC	//  b | ||||
|     STIX	//  _2 | ||||
|     DROP	// | ||||
|     0 PUSHINT	//  _12=0 | ||||
|     0 PUSHINT	//  _11=0 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ global `some()var`:int; | |||
|     return `a`*-1*-(1)*---(1)*+just10()+-`just10`()*m1*-m1+-eq(m1)----0x1; | ||||
| } | ||||
| 
 | ||||
| @method_id(112) fun `bitwise~ops`(flags:int):[int,int] { | ||||
| @method_id(112) fun `bitwise~ops`(flags:int):[bool,bool] { | ||||
|     return[ | ||||
|         (just10()-3==just10()-(4)--1)|((2==2)&(eq(eq(10)) -3==just10()--13)), | ||||
|         ((flags&0xFF)!=0) | ||||
|  |  | |||
|  | @ -7,12 +7,14 @@ fun test1() { | |||
|     numbers = listPrepend(2, numbers); | ||||
|     numbers = listPrepend(3, numbers); | ||||
|     numbers = listPrepend(4, numbers); | ||||
|     var (h, numbers redef) = listSplit(numbers); | ||||
|     var (h: int, numbers redef) = listSplit(numbers); | ||||
|     h += listGetHead(numbers); | ||||
| 
 | ||||
|     _ = null; | ||||
|     (_, _) = (null, null); | ||||
|     var t = createEmptyTuple(); | ||||
|     do { | ||||
|         var num = numbers.listNext(); | ||||
|         var num: int = numbers.listNext(); | ||||
|         t.tuplePush(num); | ||||
|     } while (numbers != null); | ||||
| 
 | ||||
|  | @ -44,7 +46,7 @@ fun test3(x: int) { | |||
| } | ||||
| 
 | ||||
| fun getUntypedNull() { | ||||
|     var untyped = null; | ||||
|     var untyped: null = null; | ||||
|     if (true) { | ||||
|         return untyped; | ||||
|     } | ||||
|  | @ -52,8 +54,8 @@ fun getUntypedNull() { | |||
| } | ||||
| 
 | ||||
| @method_id(104) | ||||
| fun test4() { | ||||
|     var (_, (_, untyped)) = (3, (createEmptyTuple, null)); | ||||
| fun test4(): null { | ||||
|     var (_, (_, untyped: null)) = (3, (createEmptyTuple, null)); | ||||
|     if (true) { | ||||
|         return untyped; | ||||
|     } | ||||
|  | @ -62,21 +64,16 @@ fun test4() { | |||
| 
 | ||||
| @method_id(105) | ||||
| fun test5() { | ||||
|     var n = getUntypedNull(); | ||||
|     var n: slice = getUntypedNull(); | ||||
|     return !(null == n) ? n.loadInt(32) : 100; | ||||
| } | ||||
| 
 | ||||
| @method_id(106) | ||||
| fun test6(x: int) { | ||||
|     return x > null;  // this compiles (for now), but fails at runtime | ||||
| } | ||||
| 
 | ||||
| @method_id(107) | ||||
| fun test7() { | ||||
|     var b = beginCell().storeMaybeRef(null); | ||||
|     var s = b.endCell().beginParse(); | ||||
|     var c = s.loadMaybeRef(); | ||||
|     return (null == c) * 10 + (b != null); | ||||
|     return (null == c) as int * 10 + (b != null) as int; | ||||
| } | ||||
| 
 | ||||
| fun main() { | ||||
|  | @ -132,27 +129,18 @@ fun main() { | |||
|   }> | ||||
| """ | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   test6 PROC:<{ | ||||
|     //  x | ||||
|     PUSHNULL	//  x _1 | ||||
|     GREATER	//  _2 | ||||
|   }> | ||||
| """ | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|   test7 PROC:<{ | ||||
|     ... | ||||
|     LDOPTREF	//  b _20 _19 | ||||
|     LDOPTREF	//  b _18 _17 | ||||
|     DROP	    //  b c | ||||
|     ISNULL	    //  b _13 | ||||
|     10 MULCONST	//  b _15 | ||||
|     SWAP	    //  _15 b | ||||
|     ISNULL	    //  _15 _16 | ||||
|     0 EQINT     // _15 _17 | ||||
|     ADD	        //  _18 | ||||
|     ISNULL	    //  b _11 | ||||
|     10 MULCONST	//  b _13 | ||||
|     SWAP	    //  _13 b | ||||
|     ISNULL	    //  _13 _14 | ||||
|     NOT         // _13 _15 | ||||
|     ADD	        //  _16 | ||||
|   }> | ||||
| """ | ||||
| */ | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| fun justTrue(): int { return true; } | ||||
| fun justTrue(): bool { return true; } | ||||
| 
 | ||||
| fun unary_minus_1(a: int, b: int, c: int): int{return -(a+b) *c;} | ||||
| fun unary_minus_2(a: int, b: int, c: int): int{return(-(a+b))*c;} | ||||
|  | @ -6,17 +6,17 @@ fun unary_minus_3(a: int, b: int, c: int): int{return-((a+b) *c);} | |||
| 
 | ||||
| 
 | ||||
| @method_id(101) | ||||
| fun test1(x: int, y: int, z: int): int { | ||||
| fun test1(x: int, y: int, z: int): bool { | ||||
|     return (x > 0) & (y > 0) & (z > 0); | ||||
| } | ||||
| 
 | ||||
| @method_id(102) | ||||
| fun test2(x: int, y: int, z: int): int { | ||||
|     return x > (0 & (y > 0) & (z > 0)); | ||||
| fun test2(x: int, y: int, z: int): bool { | ||||
|     return x > (0 & (y > 0) as int & (z > 0) as int); | ||||
| } | ||||
| 
 | ||||
| @method_id(103) | ||||
| fun test3(x: int, y: int, z: int): int { | ||||
| fun test3(x: int, y: int, z: int): bool { | ||||
|     if ((x < 0) | (y < 0)) { | ||||
|         return z < 0; | ||||
|     } | ||||
|  | @ -24,29 +24,29 @@ fun test3(x: int, y: int, z: int): int { | |||
| } | ||||
| 
 | ||||
| @method_id(104) | ||||
| fun test4(x: int, y: int, mode: int): int { | ||||
| fun test4(x: int, y: int, mode: int): bool { | ||||
|     if (mode == 1) { | ||||
|         return (x == 10) | (y == 20); | ||||
|     } if (mode == 2) { | ||||
|         return (x == 10) | (y == 20); | ||||
|     } else { | ||||
|         return x == (10 | (y == 20)); | ||||
|         return x == (10 | (y == 20) as int); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @method_id(105) | ||||
| fun test5(status: int): int { | ||||
|   return justTrue() & (status == 1) & ((justTrue() & status) == 1); | ||||
| fun test5(status: int): bool { | ||||
|   return justTrue() & (status == 1) & ((justTrue() as int & status) == 1); | ||||
| } | ||||
| 
 | ||||
| @method_id(106) | ||||
| fun test6(a: int, b: int, c: int): int { | ||||
| fun test6(a: int, b: int, c: int): bool { | ||||
|     return (unary_minus_1(a,b,c) == unary_minus_2(a,b,c)) & (unary_minus_1(a,b,c) == unary_minus_3(a,b,c)); | ||||
| } | ||||
| 
 | ||||
| @method_id(107) | ||||
| fun test7(b: int): int { | ||||
|     var a = b == 3 ? 3 : b == 4 ? 4 : (b == 5) & 1 ? 5 : 100; | ||||
|     var a = b == 3 ? 3 : b == 4 ? 4 : (b == 5) & true ? 5 : 100; | ||||
|     return a; | ||||
| } | ||||
| 
 | ||||
|  | @ -56,14 +56,14 @@ fun test8(b: int): int { | |||
|     return a; | ||||
| } | ||||
| 
 | ||||
| fun `_<p`(a: auto, b: auto): int { return true; } | ||||
| fun `_<p`(a: int, b: int): bool { return true; } | ||||
| 
 | ||||
| fun main() { | ||||
|     // ok to parse | ||||
|     var c = [ | ||||
|         (3 & 3) > 0, 3 & (3 > 0), 3 & (`_<_`(3, 0)), | ||||
|         3 & `_<p`(3, 0), (1 & 2) ^ (3 | 4), | ||||
|         1 & ((1) == 1) | ||||
|         (3 & 3) > 0, 3 & (3 > 0) as int, 3 & (`_<_`(3, 0)), | ||||
|         3 & `_<p`(3, 0) as int, (1 & 2) ^ (3 | 4), | ||||
|         true & ((1) == 1) | ||||
|     ]; | ||||
| } | ||||
| 
 | ||||
|  | @ -32,7 +32,7 @@ fun test1(): int { | |||
| @method_id(102) | ||||
| fun test2(value: int): int { | ||||
|     save_contract_data(value); | ||||
|     var (_, restored: auto) = get_contract_data(); | ||||
|     var (_, restored) = get_contract_data(); | ||||
|     return restored; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,9 +10,9 @@ fun used_from_noncall2(): int { return int20; } | |||
| fun used_as_noncall2(): int { return 0 * 0 + used_from_noncall2() + (0 << 0); } | ||||
| 
 | ||||
| global unused_gv: int; | ||||
| global used_gv: auto; | ||||
| global used_gv: int; | ||||
| 
 | ||||
| fun receiveGetter(): (() -> int) { return used_as_noncall2; } | ||||
| fun receiveGetter(): () -> int { return used_as_noncall2; } | ||||
| 
 | ||||
| @pure | ||||
| fun usedButOptimizedOut(x: int): int { return x + 2; } | ||||
|  |  | |||
|  | @ -158,6 +158,44 @@ fun testNotMutatingChainableSelfMutateAnother(initial: int) { | |||
|     return (arg, c108, c109, x); | ||||
| } | ||||
| 
 | ||||
| fun pickG110(mutate self: int, mutate pushTo: tuple): self { | ||||
|     self += 10; | ||||
|     pushTo.tuplePush(c110); | ||||
|     return self; | ||||
| } | ||||
| 
 | ||||
| global tup110: tuple; | ||||
| global c110: int; | ||||
| 
 | ||||
| @method_id(110) | ||||
| fun testMutateGlobalsLValue(init: int) { | ||||
|     c110 = init; | ||||
|     tup110 = createEmptyTuple(); | ||||
|     c110.incChained().incChained().pickG110(mutate tup110).incChained().pickG110(mutate tup110).incChained(); | ||||
|     return (c110, tup110); | ||||
| } | ||||
| 
 | ||||
| fun myTuplePush<T>(mutate self: tuple, value: T): self { | ||||
|     self.tuplePush(value); | ||||
|     return self; | ||||
| } | ||||
| 
 | ||||
| fun myTupleAt<T>(self: tuple, idx: int): T { | ||||
|     return self.tupleAt(idx); | ||||
| } | ||||
| 
 | ||||
| global tup111: tuple; | ||||
| 
 | ||||
| @method_id(111) | ||||
| fun testForallFunctionsWithSelf(): (int, int, tuple) { | ||||
|     var t = createEmptyTuple(); | ||||
|     tup111 = createEmptyTuple(); | ||||
|     t.myTuplePush(10); | ||||
|     tup111.myTuplePush(1).myTuplePush(2).myTuplePush(3); | ||||
|     return (t.myTupleAt(0), tup111.myTupleAt(tup111.tupleSize() - 1), tup111); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| fun main() { } | ||||
| 
 | ||||
|  | @ -179,6 +217,8 @@ fun main() { } | |||
| @testcase | 109 | 200 | 200 3 1 2 | ||||
| @testcase | 109 | 100 | 100 0 0 1 | ||||
| @testcase | 109 | 102 | 102 2 1 2 | ||||
| @testcase | 110 | 0   | 24 [ 2 13 ] | ||||
| @testcase | 111 |     | 10 3 [ 1 2 3 ] | ||||
| 
 | ||||
| @fif_codegen | ||||
| """ | ||||
|  |  | |||
|  | @ -218,7 +218,7 @@ fun fixed248_log2_const(): int { | |||
| @pure | ||||
| @inline | ||||
| fun Pi_const_f254(): int { | ||||
|     var (c: auto, _) = Pi_xconst_f254(); | ||||
|     var (c, _) = Pi_xconst_f254(); | ||||
|     return c; | ||||
| } | ||||
| 
 | ||||
|  | @ -661,7 +661,7 @@ fun fixed248_pow(x: int, y: int): int { | |||
|     return 1 << 248;   // x^0 = 1 | ||||
|     } | ||||
|     if (x <= 0) { | ||||
|     var bad: int = (x | y) < 0; | ||||
|     var bad: int = ((x | y) < 0) as int; | ||||
|     return 0 >> bad;          // 0^y = 0 if x=0 and y>=0; "out of range" exception otherwise | ||||
|     } | ||||
|     var (l, s) = log2_aux_f256(x); | ||||
|  | @ -677,7 +677,7 @@ fun fixed248_pow(x: int, y: int): int { | |||
|     // now log_2(x^y) = y*log_2(x) = q + ll, ss integer, ll fixed257, -1/2<=ll<1/2 | ||||
|     var sq: int = q + 248; | ||||
|     if (sq <= 0) { | ||||
|     return -(sq == 0);   // underflow | ||||
|     return -((sq == 0) as int);   // underflow | ||||
|     } | ||||
|     y = expm1_f257(mulrshiftr256(ll, log2_const_f256())); | ||||
|     return (y ~>> (9 - q)) - (-1 << sq); | ||||
|  | @ -986,7 +986,7 @@ fun tset<X>(mutate self: tuple, idx: int, value: X): void | |||
| // fixed256 acos_prepare_slow(fixed255 x); | ||||
| @inline | ||||
| fun acos_prepare_slow_f255(x: int): int { | ||||
|   x -= (x == 0); | ||||
|   x -= (x == 0) as int; | ||||
|   var t: int = 1; | ||||
|   repeat (255) { | ||||
|     t = t * sign(x) * 2 + 1;    // decode Gray code (sign(x_0), sign(x_1), ...) | ||||
|  | @ -1019,7 +1019,8 @@ fun test_nrand(n: int): tuple { | |||
|   repeat (n) { | ||||
|     var x: int = fixed248_nrand(); | ||||
|     var bucket: int = (abs(x) >> 243);   // 255 buckets starting from x=0, each 1/32 wide | ||||
|     t.tset(bucket, t.tupleAt(bucket) + 1); | ||||
|     var at_bucket: int = t.tupleAt(bucket); | ||||
|     t.tset(bucket, at_bucket + 1); | ||||
|   } | ||||
|   return t; | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,3 @@ | |||
| fun unsafeGetInt<X>(any: X): int | ||||
|     asm "NOP"; | ||||
| 
 | ||||
| @method_id(11) | ||||
| fun foo(x: int): int { | ||||
|   try { | ||||
|     if (x == 7) { | ||||
|  | @ -14,7 +10,6 @@ fun foo(x: int): int { | |||
| } | ||||
| 
 | ||||
| @inline | ||||
| @method_id(12) | ||||
| fun foo_inline(x: int): int { | ||||
|   try { | ||||
|     assert(!(x == 7)) throw 44; | ||||
|  | @ -25,36 +20,34 @@ fun foo_inline(x: int): int { | |||
| } | ||||
| 
 | ||||
| @inline_ref | ||||
| @method_id(13) | ||||
| fun foo_inlineref(x: int): int { | ||||
|   try { | ||||
|     if (x == 7) { throw (44, 2); } | ||||
|     return x; | ||||
|   } catch (_, arg) { | ||||
|     return unsafeGetInt(arg); | ||||
|     return arg as int; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @method_id(1) | ||||
| @method_id(101) | ||||
| fun test(x: int, y: int, z: int): int { | ||||
|   y = foo(y); | ||||
|   return x * 100 + y * 10 + z; | ||||
| } | ||||
| 
 | ||||
| @method_id(2) | ||||
| @method_id(102) | ||||
| fun test_inline(x: int, y: int, z: int): int { | ||||
|   y = foo_inline(y); | ||||
|   return x * 100 + y * 10 + z; | ||||
| } | ||||
| 
 | ||||
| @method_id(3) | ||||
| @method_id(103) | ||||
| fun test_inlineref(x: int, y: int, z: int): int { | ||||
|   y = foo_inlineref(y); | ||||
|   return x * 100 + y * 10 + z; | ||||
| } | ||||
| 
 | ||||
| @inline | ||||
| @method_id(14) | ||||
| fun foo_inline_big( | ||||
|         x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int, x8: int, x9: int, x10: int, | ||||
|         x11: int, x12: int, x13: int, x14: int, x15: int, x16: int, x17: int, x18: int, x19: int, x20: int | ||||
|  | @ -69,7 +62,7 @@ fun foo_inline_big( | |||
|   } | ||||
| } | ||||
| 
 | ||||
| @method_id(4) | ||||
| @method_id(104) | ||||
| fun test_inline_big(x: int, y: int, z: int): int { | ||||
|   y = foo_inline_big( | ||||
|           y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9, | ||||
|  | @ -77,7 +70,6 @@ fun test_inline_big(x: int, y: int, z: int): int { | |||
|   return x * 1000000 + y * 1000 + z; | ||||
| } | ||||
| 
 | ||||
| @method_id(15) | ||||
| fun foo_big( | ||||
|         x1: int, x2: int, x3: int, x4: int, x5: int, x6: int, x7: int, x8: int, x9: int, x10: int, | ||||
|         x11: int, x12: int, x13: int, x14: int, x15: int, x16: int, x17: int, x18: int, x19: int, x20: int | ||||
|  | @ -88,11 +80,11 @@ fun foo_big( | |||
|     } | ||||
|     return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20; | ||||
|   } catch (code, arg) { | ||||
|     return unsafeGetInt(arg); | ||||
|     return arg as int; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @method_id(5) | ||||
| @method_id(105) | ||||
| fun test_big(x: int, y: int, z: int): int { | ||||
|   y = foo_big( | ||||
|           y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9, | ||||
|  | @ -100,7 +92,7 @@ fun test_big(x: int, y: int, z: int): int { | |||
|   return x * 1000000 + y * 1000 + z; | ||||
| } | ||||
| 
 | ||||
| @method_id(16) | ||||
| @method_id(106) | ||||
| fun test_catch_into_same(x: int): int { | ||||
|   var code = x; | ||||
|   try { | ||||
|  | @ -112,7 +104,7 @@ fun test_catch_into_same(x: int): int { | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| @method_id(17) | ||||
| @method_id(107) | ||||
| fun test_catch_into_same_2(x: int): int { | ||||
|   var code = x; | ||||
|   try { | ||||
|  | @ -124,28 +116,77 @@ fun test_catch_into_same_2(x: int): int { | |||
|   return code; | ||||
| } | ||||
| 
 | ||||
| global after046: int; | ||||
| 
 | ||||
| // this bug existed in FunC and is fixed in v0.4.6 | ||||
| fun bug_046_internal(op: int) { | ||||
|   if (op == 1) { | ||||
|     return; | ||||
|   } else if (op == 2) { | ||||
|     return; | ||||
|   } else { | ||||
|     throw 1; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| fun bug_046_called() { | ||||
|   after046 = 0; | ||||
|   try { | ||||
|     bug_046_internal(1337); | ||||
|     after046 = 1;  // shouldn't be called | ||||
|   } catch(n) { | ||||
|     return; | ||||
|   } | ||||
|   return; | ||||
| } | ||||
| 
 | ||||
| @method_id(108) | ||||
| fun bug_046_entrypoint() { | ||||
|   bug_046_called(); | ||||
|   return after046; | ||||
| } | ||||
| 
 | ||||
| global g_reg: int; | ||||
| 
 | ||||
| @method_id(109) | ||||
| fun test109(): (int, int) { | ||||
|   var l_reg = 10; | ||||
|   g_reg = 10; | ||||
|   try { | ||||
|     // note, that regardless of assignment, an exception RESTORES them to previous (to 10) | ||||
|     // it's very unexpected, but is considered to be a TVM feature, not a bug | ||||
|     g_reg = 999; | ||||
|     l_reg = 999; | ||||
|     bug_046_internal(999);  // throws | ||||
|   } catch { | ||||
|   } | ||||
|   // returns (10,10) because of an exception, see a comment above | ||||
|   return (g_reg, l_reg); | ||||
| } | ||||
| 
 | ||||
| fun main() { | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|     method_id | in    | out | ||||
| @testcase | 1  | 1 2 3 | 123 | ||||
| @testcase | 1  | 3 8 9 | 389 | ||||
| @testcase | 1  | 3 7 9 | 329 | ||||
| @testcase | 2  | 1 2 3 | 123 | ||||
| @testcase | 2  | 3 8 9 | 389 | ||||
| @testcase | 2  | 3 7 9 | 329 | ||||
| @testcase | 3  | 1 2 3 | 123 | ||||
| @testcase | 3  | 3 8 9 | 389 | ||||
| @testcase | 3  | 3 7 9 | 329 | ||||
| @testcase | 4  | 4 8 9 | 4350009 | ||||
| @testcase | 4  | 4 7 9 | 4001009 | ||||
| @testcase | 5  | 4 8 9 | 4350009 | ||||
| @testcase | 5  | 4 7 9 | 4001009 | ||||
| @testcase | 16 | 5     | 5 | ||||
| @testcase | 16 | 20    | 44 | ||||
| @testcase | 17 | 5     | 5 | ||||
| @testcase | 17 | 20    | 20 | ||||
|     method_id   | in    | out | ||||
| @testcase | 101 | 1 2 3 | 123 | ||||
| @testcase | 101 | 3 8 9 | 389 | ||||
| @testcase | 101 | 3 7 9 | 329 | ||||
| @testcase | 102 | 1 2 3 | 123 | ||||
| @testcase | 102 | 3 8 9 | 389 | ||||
| @testcase | 102 | 3 7 9 | 329 | ||||
| @testcase | 103 | 1 2 3 | 123 | ||||
| @testcase | 103 | 3 8 9 | 389 | ||||
| @testcase | 103 | 3 7 9 | 329 | ||||
| @testcase | 104 | 4 8 9 | 4350009 | ||||
| @testcase | 104 | 4 7 9 | 4001009 | ||||
| @testcase | 105 | 4 8 9 | 4350009 | ||||
| @testcase | 105 | 4 7 9 | 4001009 | ||||
| @testcase | 106 | 5     | 5 | ||||
| @testcase | 106 | 20    | 44 | ||||
| @testcase | 107 | 5     | 5 | ||||
| @testcase | 107 | 20    | 20 | ||||
| @testcase | 108 |       | 0 | ||||
| 
 | ||||
| @code_hash 73240939343624734070640372352271282883450660826541545137654364443860257436623 | ||||
| @code_hash 39307974281105539319288356721945232226028429128341177951717392648324358675585 | ||||
| */ | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ fun foo_until(x: int): int { | |||
| } | ||||
| 
 | ||||
| @method_id(4) | ||||
| fun test4(x: int): (int, int) { | ||||
| fun test4(x: int): (int, bool) { | ||||
|   var s = 0; | ||||
|   var reached = false; | ||||
|   do { | ||||
|  |  | |||
							
								
								
									
										14
									
								
								tolk-tester/tests/unreachable-1.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tolk-tester/tests/unreachable-1.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| fun main(x: int) { | ||||
|     if (x) { | ||||
|         x = 10;;;;; | ||||
|         return x;;; | ||||
|         x = 20; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @testcase | 0 | 1 | 10 | ||||
| @stderr warning: unreachable code | ||||
| @stderr x = 20; | ||||
|  */ | ||||
							
								
								
									
										22
									
								
								tolk-tester/tests/unreachable-2.tolk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tolk-tester/tests/unreachable-2.tolk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| fun main(x: int) { | ||||
|     if (x) { | ||||
|         if (x > 10) { | ||||
|             return 1; // throw 1; | ||||
|         } else if (true) { | ||||
|             return -1; | ||||
|         } else { | ||||
|             return 2; // throw 2; | ||||
|         } | ||||
|     } else { | ||||
|         {{return 1;} | ||||
|             x = 30;} | ||||
|     } | ||||
|     assert(false, 10); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @testcase | 0 | 1 | -1 | ||||
| @stderr warning: unreachable code | ||||
| @stderr assert(false, 10) | ||||
| @stderr x = 30 | ||||
|  */ | ||||
|  | @ -15,8 +15,127 @@ fun testVarApply1() { | |||
|     return (s.loadInt(32), s.loadInt(32)); | ||||
| } | ||||
| 
 | ||||
| @inline | ||||
| fun my_throw_always() { | ||||
|     throw 1000; | ||||
| } | ||||
| 
 | ||||
| @inline | ||||
| fun get_raiser() { | ||||
|     return my_throw_always; | ||||
| } | ||||
| 
 | ||||
| @method_id(102) | ||||
| fun testVarApplyWithoutSavingResult() { | ||||
|     try { | ||||
|         var raiser = get_raiser(); | ||||
|         raiser();   // `some_var()` is always impure, the compiler has no considerations about its runtime value | ||||
|         return 0; | ||||
|     } catch (code) { | ||||
|         return code; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @inline | ||||
| fun sum(a: int, b: int) { | ||||
|     assert(a + b < 24, 1000); | ||||
|     return a + b; | ||||
| } | ||||
| 
 | ||||
| @inline | ||||
| fun mul(a: int, b: int) { | ||||
|     assert(a * b < 24, 1001); | ||||
|     return a * b; | ||||
| } | ||||
| 
 | ||||
| fun demo_handler(op: int, query_id: int, a: int, b: int): int { | ||||
|     if (op == 0xF2) { | ||||
|         val func = query_id % 2 == 0 ? sum : mul; | ||||
|         val result = func(a, b); | ||||
|         return 0;  // result not used, we test that func is nevertheless called | ||||
|     } | ||||
|     if (op == 0xF4) { | ||||
|         val func = query_id % 2 == 0 ? sum : mul; | ||||
|         val result = func(a, b); | ||||
|         return result; | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| @method_id(103) | ||||
| fun testVarApplyInTernary() { | ||||
|     var t: tuple = createEmptyTuple(); | ||||
|     try { | ||||
|         t.tuplePush(demo_handler(0xF2, 122, 100, 200)); | ||||
|     } catch(code) { | ||||
|         t.tuplePush(code); | ||||
|     } | ||||
|     try { | ||||
|         t.tuplePush(demo_handler(0xF4, 122, 100, 200)); | ||||
|     } catch(code) { | ||||
|         t.tuplePush(code); | ||||
|     } | ||||
|     try { | ||||
|         t.tuplePush(demo_handler(0xF2, 122, 10, 10)); | ||||
|     } catch(code) { | ||||
|         t.tuplePush(code); | ||||
|     } | ||||
|     try { | ||||
|         t.tuplePush(demo_handler(0xF2, 123, 10, 10)); | ||||
|     } catch(code) { | ||||
|         t.tuplePush(code); | ||||
|     } | ||||
|     return t; | ||||
| } | ||||
| 
 | ||||
| fun always_throw2(x: int) { | ||||
|     throw 239 + x; | ||||
| } | ||||
| 
 | ||||
| global global_f: int -> void; | ||||
| 
 | ||||
| @method_id(104) | ||||
| fun testGlobalVarApply() { | ||||
|     try { | ||||
|         global_f = always_throw2; | ||||
|         global_f(1); | ||||
|         return 0; | ||||
|     } catch (code) { | ||||
|         return code; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @method_id(105) | ||||
| fun testVarApply2() { | ||||
|     var creator = createEmptyTuple; | ||||
|     var t = creator(); | ||||
|     t.tuplePush(1); | ||||
|     var sizer = t.tupleSize; | ||||
|     return sizer(t); | ||||
| } | ||||
| 
 | ||||
| fun getTupleLastGetter<X>(): (tuple) -> X { | ||||
|     return tupleLast<X>; | ||||
| } | ||||
| 
 | ||||
| @method_id(106) | ||||
| fun testVarApply3() { | ||||
|     var t = createEmptyTuple(); | ||||
|     t.tuplePush(1); | ||||
|     t.tuplePush([2]); | ||||
|     var getIntAt = t.tupleAt<int>; | ||||
|     var getTupleFirstInt = createEmptyTuple().tupleFirst<int>; | ||||
|     var getTupleLastTuple = getTupleLastGetter<tuple>(); | ||||
|     return (getIntAt(t, 0), getTupleFirstInt(t), getTupleLastTuple(t), getTupleLastGetter<tuple>()(t)); | ||||
| } | ||||
| 
 | ||||
| fun main() {} | ||||
| 
 | ||||
| /** | ||||
| @testcase | 101 |    | 1 2 | ||||
| @testcase | 102 |    | 1000 | ||||
| @testcase | 103 |    | [ 1000 1000 0 1001 ] | ||||
| @testcase | 104 |    | 240 | ||||
| @testcase | 105 |    | 1 | ||||
| @testcase | 106 |    | 1 1 [ 2 ] [ 2 ] | ||||
|  */ | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ fun main(x: int): int { | |||
|     if (i > 5) { | ||||
|       return 1; | ||||
|     } | ||||
|     var f: int = (i * i == 64); | ||||
|     var f: bool = (i * i == 64); | ||||
|   } while (!f); | ||||
|   return -1; | ||||
| } | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ fun test(y: int): int { | |||
|   if (y > 0) { | ||||
|     return 1; | ||||
|   } | ||||
|   return x > 0; | ||||
|   return x > 0 ? -1 : 0; | ||||
| } | ||||
| 
 | ||||
| @method_id(2) | ||||
|  |  | |||
|  | @ -347,11 +347,11 @@ class TolkTestFile { | |||
|         if (exit_code === 0 && this.compilation_should_fail) | ||||
|             throw new TolkCompilationSucceededError("compilation succeeded, but it should have failed") | ||||
| 
 | ||||
|         if (exit_code !== 0 && this.compilation_should_fail) { | ||||
|             for (let should_include of this.stderr_includes) | ||||
|                 should_include.check(stderr) | ||||
|         for (let should_include of this.stderr_includes)  // @stderr is used to check errors and warnings
 | ||||
|             should_include.check(stderr) | ||||
| 
 | ||||
|         if (exit_code !== 0 && this.compilation_should_fail) | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|         if (exit_code !== 0 && !this.compilation_should_fail) | ||||
|             throw new TolkCompilationFailedError(`tolk exit_code = ${exit_code}`, stderr) | ||||
|  |  | |||
|  | @ -327,9 +327,10 @@ class TolkTestFile: | |||
|         if exit_code == 0 and self.compilation_should_fail: | ||||
|             raise TolkCompilationSucceededError("compilation succeeded, but it should have failed") | ||||
| 
 | ||||
|         for should_include in self.stderr_includes: # @stderr is used to check errors and warnings | ||||
|             should_include.check(stderr) | ||||
| 
 | ||||
|         if exit_code != 0 and self.compilation_should_fail: | ||||
|             for should_include in self.stderr_includes: | ||||
|                 should_include.check(stderr) | ||||
|             return | ||||
| 
 | ||||
|         if exit_code != 0 and not self.compilation_should_fail: | ||||
|  |  | |||
|  | @ -7,14 +7,24 @@ set(TOLK_SOURCE | |||
|         compiler-state.cpp | ||||
|         ast.cpp | ||||
|         ast-from-tokens.cpp | ||||
|         constant-evaluator.cpp | ||||
|         pipe-discover-parse-sources.cpp | ||||
|         pipe-register-symbols.cpp | ||||
|         pipe-resolve-identifiers.cpp | ||||
|         pipe-calc-rvalue-lvalue.cpp | ||||
|         pipe-detect-unreachable.cpp | ||||
|         pipe-infer-types-and-calls.cpp | ||||
|         pipe-refine-lvalue-for-mutate.cpp | ||||
|         pipe-check-rvalue-lvalue.cpp | ||||
|         pipe-check-pure-impure.cpp | ||||
|         pipe-constant-folding.cpp | ||||
|         pipe-optimize-boolean-expr.cpp | ||||
|         pipe-ast-to-legacy.cpp | ||||
|         pipe-find-unused-symbols.cpp | ||||
|         pipe-generate-fif-output.cpp | ||||
|         unify-types.cpp | ||||
|         type-system.cpp | ||||
|         generics-helpers.cpp | ||||
|         abscode.cpp | ||||
|         gen-abscode.cpp | ||||
|         analyzer.cpp | ||||
|         asmops.cpp | ||||
|         builtins.cpp | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| */ | ||||
| #include "tolk.h" | ||||
| #include "compiler-state.h" | ||||
| #include "type-system.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
|  | @ -25,21 +26,10 @@ namespace tolk { | |||
|  *  | ||||
|  */ | ||||
| 
 | ||||
| TmpVar::TmpVar(var_idx_t _idx, TypeExpr* _type, sym_idx_t sym_idx, SrcLocation loc) | ||||
|     : v_type(_type), idx(_idx), sym_idx(sym_idx), coord(0), where(loc) { | ||||
|   if (!_type) { | ||||
|     v_type = TypeExpr::new_hole(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void TmpVar::set_location(SrcLocation loc) { | ||||
|   where = loc; | ||||
| } | ||||
| 
 | ||||
| void TmpVar::dump(std::ostream& os) const { | ||||
|   show(os); | ||||
|   os << " : " << v_type << " (width "; | ||||
|   v_type->show_width(os); | ||||
|   os << v_type->calc_width_on_stack(); | ||||
|   os << ")"; | ||||
|   if (coord > 0) { | ||||
|     os << " = _" << (coord >> 8) << '.' << (coord & 255); | ||||
|  | @ -55,8 +45,8 @@ void TmpVar::dump(std::ostream& os) const { | |||
| } | ||||
| 
 | ||||
| void TmpVar::show(std::ostream& os, int omit_idx) const { | ||||
|   if (!is_unnamed()) { | ||||
|     os << G.symbols.get_name(sym_idx); | ||||
|   if (v_sym) { | ||||
|     os << v_sym->name; | ||||
|     if (omit_idx >= 2) { | ||||
|       return; | ||||
|     } | ||||
|  | @ -149,10 +139,6 @@ void VarDescr::set_const(std::string value) { | |||
|   val = _Const; | ||||
| } | ||||
| 
 | ||||
| void VarDescr::set_const_nan() { | ||||
|   set_const(td::make_refint()); | ||||
| } | ||||
| 
 | ||||
| void VarDescr::operator|=(const VarDescr& y) { | ||||
|   val &= y.val; | ||||
|   if (is_int_const() && y.is_int_const() && cmp(int_const, y.int_const) != 0) { | ||||
|  | @ -273,7 +259,7 @@ void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx | |||
|     case _Call: | ||||
|       os << pfx << dis << "CALL: "; | ||||
|       show_var_list(os, left, vars); | ||||
|       os << " := " << (fun_ref ? fun_ref->name() : "(null)") << " "; | ||||
|       os << " := " << (f_sym ? f_sym->name : "(null)") << " "; | ||||
|       if ((mode & 4) && args.size() == right.size()) { | ||||
|         show_var_list(os, args, vars); | ||||
|       } else { | ||||
|  | @ -332,11 +318,11 @@ void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx | |||
|     case _GlobVar: | ||||
|       os << pfx << dis << "GLOBVAR "; | ||||
|       show_var_list(os, left, vars); | ||||
|       os << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl; | ||||
|       os << " := " << (g_sym ? g_sym->name : "(null)") << std::endl; | ||||
|       break; | ||||
|     case _SetGlob: | ||||
|       os << pfx << dis << "SETGLOB "; | ||||
|       os << (fun_ref ? fun_ref->name() : "(null)") << " := "; | ||||
|       os << (g_sym ? g_sym->name : "(null)") << " := "; | ||||
|       show_var_list(os, right, vars); | ||||
|       os << std::endl; | ||||
|       break; | ||||
|  | @ -458,22 +444,22 @@ void CodeBlob::print(std::ostream& os, int flags) const { | |||
|   os << "-------- END ---------\n\n"; | ||||
| } | ||||
| 
 | ||||
| var_idx_t CodeBlob::create_var(TypeExpr* var_type, var_idx_t sym_idx, SrcLocation location) { | ||||
|   vars.emplace_back(var_cnt, var_type, sym_idx, location); | ||||
| var_idx_t CodeBlob::create_var(TypePtr var_type, const LocalVarData* v_sym, SrcLocation location) { | ||||
|   vars.emplace_back(var_cnt, var_type, v_sym, location); | ||||
|   return var_cnt++; | ||||
| } | ||||
| 
 | ||||
| bool CodeBlob::import_params(FormalArgList arg_list) { | ||||
| bool CodeBlob::import_params(FormalArgList&& arg_list) { | ||||
|   if (var_cnt || in_var_cnt) { | ||||
|     return false; | ||||
|   } | ||||
|   std::vector<var_idx_t> list; | ||||
|   for (const auto& par : arg_list) { | ||||
|     TypeExpr* arg_type; | ||||
|     SymDef* arg_sym; | ||||
|     TypePtr arg_type; | ||||
|     const LocalVarData* arg_sym; | ||||
|     SrcLocation arg_loc; | ||||
|     std::tie(arg_type, arg_sym, arg_loc) = par; | ||||
|     list.push_back(create_var(arg_type, arg_sym ? arg_sym->sym_idx : 0, arg_loc)); | ||||
|     list.push_back(create_var(arg_type, arg_sym, arg_loc)); | ||||
|   } | ||||
|   emplace_back(loc, Op::_Import, list); | ||||
|   in_var_cnt = var_cnt; | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| */ | ||||
| #include "tolk.h" | ||||
| #include "compiler-state.h" | ||||
| #include "type-system.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
|  | @ -25,38 +26,30 @@ namespace tolk { | |||
|  *  | ||||
|  */ | ||||
| 
 | ||||
| void CodeBlob::simplify_var_types() { | ||||
|   for (TmpVar& var : vars) { | ||||
|     TypeExpr::remove_indirect(var.v_type); | ||||
|     var.v_type->recompute_width(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| int CodeBlob::split_vars(bool strict) { | ||||
|   int n = var_cnt, changes = 0; | ||||
|   for (int j = 0; j < var_cnt; j++) { | ||||
|     TmpVar& var = vars[j]; | ||||
|     if (strict && var.v_type->minw != var.v_type->maxw) { | ||||
|     int width_j = var.v_type->calc_width_on_stack(); | ||||
|     if (strict && width_j < 0) { | ||||
|       throw ParseError{var.where, "variable does not have fixed width, cannot manipulate it"}; | ||||
|     } | ||||
|     std::vector<TypeExpr*> comp_types; | ||||
|     int k = var.v_type->extract_components(comp_types); | ||||
|     tolk_assert(k <= 254 && n <= 0x7fff00); | ||||
|     tolk_assert((unsigned)k == comp_types.size()); | ||||
|     if (k != 1) { | ||||
|       var.coord = ~((n << 8) + k); | ||||
|       for (int i = 0; i < k; i++) { | ||||
|         auto v = create_var(comp_types[i], vars[j].sym_idx, vars[j].where); | ||||
|         tolk_assert(v == n + i); | ||||
|         tolk_assert(vars[v].idx == v); | ||||
|         vars[v].coord = ((int)j << 8) + i + 1; | ||||
|       } | ||||
|       n += k; | ||||
|       ++changes; | ||||
|     } else if (strict && var.v_type->minw != 1) { | ||||
|       throw ParseError{var.where, | ||||
|                             "cannot work with variable or variable component of width greater than one"}; | ||||
|     if (width_j == 1) { | ||||
|       continue; | ||||
|     } | ||||
|     std::vector<TypePtr> comp_types; | ||||
|     var.v_type->extract_components(comp_types); | ||||
|     tolk_assert(width_j <= 254 && n <= 0x7fff00); | ||||
|     tolk_assert((unsigned)width_j == comp_types.size()); | ||||
|     var.coord = ~((n << 8) + width_j); | ||||
|     for (int i = 0; i < width_j; i++) { | ||||
|       auto v = create_var(comp_types[i], vars[j].v_sym, vars[j].where); | ||||
|       tolk_assert(v == n + i); | ||||
|       tolk_assert(vars[v].idx == v); | ||||
|       vars[v].coord = ((int)j << 8) + i + 1; | ||||
|     } | ||||
|     n += width_j; | ||||
|     ++changes; | ||||
|   } | ||||
|   if (!changes) { | ||||
|     return 0; | ||||
|  | @ -687,7 +680,7 @@ void CodeBlob::fwd_analyze() { | |||
|   tolk_assert(ops && ops->cl == Op::_Import); | ||||
|   for (var_idx_t i : ops->left) { | ||||
|     values += i; | ||||
|     if (vars[i].v_type->is_int()) { | ||||
|     if (vars[i].v_type == TypeDataInt::create()) { | ||||
|       values[i]->val |= VarDescr::_Int; | ||||
|     } | ||||
|   } | ||||
|  | @ -732,15 +725,18 @@ VarDescrList Op::fwd_analyze(VarDescrList values) { | |||
|     } | ||||
|     case _Call: { | ||||
|       prepare_args(values); | ||||
|       auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value); | ||||
|       if (func) { | ||||
|       if (!f_sym->is_code_function()) { | ||||
|         std::vector<VarDescr> res; | ||||
|         res.reserve(left.size()); | ||||
|         for (var_idx_t i : left) { | ||||
|           res.emplace_back(i); | ||||
|         } | ||||
|         AsmOpList tmp; | ||||
|         func->compile(tmp, res, args, where);  // abstract interpretation of res := f (args)
 | ||||
|         if (f_sym->is_asm_function()) { | ||||
|           std::get<FunctionBodyAsm*>(f_sym->body)->compile(tmp);  // abstract interpretation of res := f (args)
 | ||||
|         } else { | ||||
|           std::get<FunctionBodyBuiltin*>(f_sym->body)->compile(tmp, res, args, where); | ||||
|         } | ||||
|         int j = 0; | ||||
|         for (var_idx_t i : left) { | ||||
|           values.add_newval(i).set_value(res[j++]); | ||||
|  | @ -878,27 +874,10 @@ bool Op::set_noreturn(bool flag) { | |||
|   return flag; | ||||
| } | ||||
| 
 | ||||
| void Op::set_impure(const CodeBlob &code) { | ||||
|   // todo calling this function with `code` is a bad design (flags are assigned after Op is constructed)
 | ||||
|   // later it's better to check this somewhere in code.emplace_back()
 | ||||
|   if (code.flags & CodeBlob::_ForbidImpure) { | ||||
|     throw ParseError(where, "an impure operation in a pure function"); | ||||
|   } | ||||
| void Op::set_impure_flag() { | ||||
|   flags |= _Impure; | ||||
| } | ||||
| 
 | ||||
| void Op::set_impure(const CodeBlob &code, bool flag) { | ||||
|   if (flag) { | ||||
|     if (code.flags & CodeBlob::_ForbidImpure) { | ||||
|       throw ParseError(where, "an impure operation in a pure function"); | ||||
|     } | ||||
|     flags |= _Impure; | ||||
|   } else { | ||||
|     flags &= ~_Impure; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool Op::mark_noreturn() { | ||||
|   switch (cl) { | ||||
|     case _Nop: | ||||
|  |  | |||
|  | @ -52,10 +52,10 @@ std::ostream& operator<<(std::ostream& os, AsmOp::SReg stack_reg) { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| AsmOp AsmOp::Const(int arg, std::string push_op, td::RefInt256 origin) { | ||||
| AsmOp AsmOp::Const(int arg, const std::string& push_op) { | ||||
|   std::ostringstream os; | ||||
|   os << arg << ' ' << push_op; | ||||
|   return AsmOp::Const(os.str(), origin); | ||||
|   return AsmOp::Const(os.str()); | ||||
| } | ||||
| 
 | ||||
| AsmOp AsmOp::make_stk2(int a, int b, const char* str, int delta) { | ||||
|  | @ -161,36 +161,36 @@ AsmOp AsmOp::UnTuple(int a) { | |||
|   return AsmOp::Custom(os.str(), 1, a); | ||||
| } | ||||
| 
 | ||||
| AsmOp AsmOp::IntConst(td::RefInt256 x) { | ||||
| AsmOp AsmOp::IntConst(const td::RefInt256& x) { | ||||
|   if (x->signed_fits_bits(8)) { | ||||
|     return AsmOp::Const(dec_string(x) + " PUSHINT", x); | ||||
|     return AsmOp::Const(dec_string(x) + " PUSHINT"); | ||||
|   } | ||||
|   if (!x->is_valid()) { | ||||
|     return AsmOp::Const("PUSHNAN", x); | ||||
|     return AsmOp::Const("PUSHNAN"); | ||||
|   } | ||||
|   int k = is_pos_pow2(x); | ||||
|   if (k >= 0) { | ||||
|     return AsmOp::Const(k, "PUSHPOW2", x); | ||||
|     return AsmOp::Const(k, "PUSHPOW2"); | ||||
|   } | ||||
|   k = is_pos_pow2(x + 1); | ||||
|   if (k >= 0) { | ||||
|     return AsmOp::Const(k, "PUSHPOW2DEC", x); | ||||
|     return AsmOp::Const(k, "PUSHPOW2DEC"); | ||||
|   } | ||||
|   k = is_pos_pow2(-x); | ||||
|   if (k >= 0) { | ||||
|     return AsmOp::Const(k, "PUSHNEGPOW2", x); | ||||
|     return AsmOp::Const(k, "PUSHNEGPOW2"); | ||||
|   } | ||||
|   if (!x->mod_pow2_short(23)) { | ||||
|     return AsmOp::Const(dec_string(x) + " PUSHINTX", x); | ||||
|     return AsmOp::Const(dec_string(x) + " PUSHINTX"); | ||||
|   } | ||||
|   return AsmOp::Const(dec_string(x) + " PUSHINT", x); | ||||
|   return AsmOp::Const(dec_string(x) + " PUSHINT"); | ||||
| } | ||||
| 
 | ||||
| AsmOp AsmOp::BoolConst(bool f) { | ||||
|   return AsmOp::Const(f ? "TRUE" : "FALSE"); | ||||
| } | ||||
| 
 | ||||
| AsmOp AsmOp::Parse(std::string custom_op) { | ||||
| AsmOp AsmOp::Parse(const std::string& custom_op) { | ||||
|   if (custom_op == "NOP") { | ||||
|     return AsmOp::Nop(); | ||||
|   } else if (custom_op == "SWAP") { | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -16,12 +16,10 @@ | |||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "src-file.h" | ||||
| #include "fwd-declarations.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| struct ASTNodeBase; | ||||
| 
 | ||||
| const ASTNodeBase* parse_src_file_to_ast(const SrcFile* file); | ||||
| AnyV parse_src_file_to_ast(const SrcFile* file); | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
|  |  | |||
|  | @ -35,25 +35,39 @@ namespace tolk { | |||
| 
 | ||||
| class ASTReplacer { | ||||
| protected: | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE static AnyV replace_children(const ASTNodeLeaf* v) { | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE static AnyExprV replace_children(const ASTExprLeaf* v) { | ||||
|     return v; | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyV replace_children(const ASTNodeUnary* v) { | ||||
|     auto* v_mutable = const_cast<ASTNodeUnary*>(v); | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyExprV replace_children(const ASTExprUnary* v) { | ||||
|     auto* v_mutable = const_cast<ASTExprUnary*>(v); | ||||
|     v_mutable->child = replace(v_mutable->child); | ||||
|     return v_mutable; | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyV replace_children(const ASTNodeBinary* v) { | ||||
|     auto* v_mutable = const_cast<ASTNodeBinary*>(v); | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyExprV replace_children(const ASTExprBinary* v) { | ||||
|     auto* v_mutable = const_cast<ASTExprBinary*>(v); | ||||
|     v_mutable->lhs = replace(v->lhs); | ||||
|     v_mutable->rhs = replace(v->rhs); | ||||
|     return v_mutable; | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyV replace_children(const ASTNodeVararg* v) { | ||||
|     auto* v_mutable = const_cast<ASTNodeVararg*>(v); | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyExprV replace_children(const ASTExprVararg* v) { | ||||
|     auto* v_mutable = const_cast<ASTExprVararg*>(v); | ||||
|     for (AnyExprV& child : v_mutable->children) { | ||||
|       child = replace(child); | ||||
|     } | ||||
|     return v_mutable; | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyV replace_children(const ASTStatementUnary* v) { | ||||
|     auto* v_mutable = const_cast<ASTStatementUnary*>(v); | ||||
|     v_mutable->child = replace(v_mutable->child); | ||||
|     return v_mutable; | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyV replace_children(const ASTStatementVararg* v) { | ||||
|     auto* v_mutable = const_cast<ASTStatementVararg*>(v); | ||||
|     for (AnyV& child : v_mutable->children) { | ||||
|       child = replace(child); | ||||
|     } | ||||
|  | @ -64,97 +78,120 @@ public: | |||
|   virtual ~ASTReplacer() = default; | ||||
| 
 | ||||
|   virtual AnyV replace(AnyV v) = 0; | ||||
|   virtual AnyExprV replace(AnyExprV v) = 0; | ||||
| }; | ||||
| 
 | ||||
| class ASTReplacerInFunctionBody : public ASTReplacer { | ||||
| protected: | ||||
|   using parent = ASTReplacerInFunctionBody; | ||||
| 
 | ||||
|   virtual AnyV replace(V<ast_empty> v)                   { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_parenthesized_expr> v)      { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_tensor> v)                  { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_tensor_square> v)           { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_identifier> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_int_const> v)               { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_string_const> v)            { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_bool_const> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_null_keyword> v)            { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_function_call> v)           { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_dot_method_call> v)         { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_underscore> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_unary_operator> v)          { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_binary_operator> v)         { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_ternary_operator> v)        { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_return_statement> v)        { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_sequence> v)                { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_repeat_statement> v)        { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_while_statement> v)         { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_do_while_statement> v)      { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_throw_statement> v)         { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_assert_statement> v)        { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_try_catch_statement> v)     { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_if_statement> v)            { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_local_var> v)               { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_local_vars_declaration> v)  { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_asm_body> v)                { return replace_children(v); } | ||||
|   // expressions
 | ||||
|   virtual AnyExprV replace(V<ast_empty_expression> v)          { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_parenthesized_expression> v)  { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_tensor> v)                    { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_typed_tuple> v)               { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_reference> v)                 { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_local_var_lhs> v)             { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_local_vars_declaration> v)    { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_int_const> v)                 { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_string_const> v)              { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_bool_const> v)                { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_null_keyword> v)              { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_argument> v)                  { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_argument_list> v)             { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_dot_access> v)                { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_function_call> v)             { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_underscore> v)                { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_assign> v)                    { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_set_assign> v)                { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_unary_operator> v)            { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_binary_operator> v)           { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_ternary_operator> v)          { return replace_children(v); } | ||||
|   virtual AnyExprV replace(V<ast_cast_as_operator> v)          { return replace_children(v); } | ||||
|   // statements
 | ||||
|   virtual AnyV replace(V<ast_empty_statement> v)               { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_sequence> v)                      { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_return_statement> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_if_statement> v)                  { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_repeat_statement> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_while_statement> v)               { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_do_while_statement> v)            { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_throw_statement> v)               { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_assert_statement> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_try_catch_statement> v)           { return replace_children(v); } | ||||
| 
 | ||||
|   AnyV replace(AnyV v) final { | ||||
|   AnyExprV replace(AnyExprV v) final { | ||||
|     switch (v->type) { | ||||
|       case ast_empty:                           return replace(v->as<ast_empty>()); | ||||
|       case ast_parenthesized_expr:              return replace(v->as<ast_parenthesized_expr>()); | ||||
|       case ast_empty_expression:                return replace(v->as<ast_empty_expression>()); | ||||
|       case ast_parenthesized_expression:        return replace(v->as<ast_parenthesized_expression>()); | ||||
|       case ast_tensor:                          return replace(v->as<ast_tensor>()); | ||||
|       case ast_tensor_square:                   return replace(v->as<ast_tensor_square>()); | ||||
|       case ast_identifier:                      return replace(v->as<ast_identifier>()); | ||||
|       case ast_typed_tuple:                     return replace(v->as<ast_typed_tuple>()); | ||||
|       case ast_reference:                       return replace(v->as<ast_reference>()); | ||||
|       case ast_local_var_lhs:                   return replace(v->as<ast_local_var_lhs>()); | ||||
|       case ast_local_vars_declaration:          return replace(v->as<ast_local_vars_declaration>()); | ||||
|       case ast_int_const:                       return replace(v->as<ast_int_const>()); | ||||
|       case ast_string_const:                    return replace(v->as<ast_string_const>()); | ||||
|       case ast_bool_const:                      return replace(v->as<ast_bool_const>()); | ||||
|       case ast_null_keyword:                    return replace(v->as<ast_null_keyword>()); | ||||
|       case ast_self_keyword:                    return replace(v->as<ast_self_keyword>()); | ||||
|       case ast_argument:                        return replace(v->as<ast_argument>()); | ||||
|       case ast_argument_list:                   return replace(v->as<ast_argument_list>()); | ||||
|       case ast_dot_access:                      return replace(v->as<ast_dot_access>()); | ||||
|       case ast_function_call:                   return replace(v->as<ast_function_call>()); | ||||
|       case ast_dot_method_call:                 return replace(v->as<ast_dot_method_call>()); | ||||
|       case ast_underscore:                      return replace(v->as<ast_underscore>()); | ||||
|       case ast_assign:                          return replace(v->as<ast_assign>()); | ||||
|       case ast_set_assign:                      return replace(v->as<ast_set_assign>()); | ||||
|       case ast_unary_operator:                  return replace(v->as<ast_unary_operator>()); | ||||
|       case ast_binary_operator:                 return replace(v->as<ast_binary_operator>()); | ||||
|       case ast_ternary_operator:                return replace(v->as<ast_ternary_operator>()); | ||||
|       case ast_return_statement:                return replace(v->as<ast_return_statement>()); | ||||
|       case ast_cast_as_operator:                return replace(v->as<ast_cast_as_operator>()); | ||||
|       default: | ||||
|         throw UnexpectedASTNodeType(v, "ASTReplacerInFunctionBody::replace"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   AnyV replace(AnyV v) final { | ||||
|     switch (v->type) { | ||||
|       case ast_empty_statement:                 return replace(v->as<ast_empty_statement>()); | ||||
|       case ast_sequence:                        return replace(v->as<ast_sequence>()); | ||||
|       case ast_return_statement:                return replace(v->as<ast_return_statement>()); | ||||
|       case ast_if_statement:                    return replace(v->as<ast_if_statement>()); | ||||
|       case ast_repeat_statement:                return replace(v->as<ast_repeat_statement>()); | ||||
|       case ast_while_statement:                 return replace(v->as<ast_while_statement>()); | ||||
|       case ast_do_while_statement:              return replace(v->as<ast_do_while_statement>()); | ||||
|       case ast_throw_statement:                 return replace(v->as<ast_throw_statement>()); | ||||
|       case ast_assert_statement:                return replace(v->as<ast_assert_statement>()); | ||||
|       case ast_try_catch_statement:             return replace(v->as<ast_try_catch_statement>()); | ||||
|       case ast_if_statement:                    return replace(v->as<ast_if_statement>()); | ||||
|       case ast_local_var:                       return replace(v->as<ast_local_var>()); | ||||
|       case ast_local_vars_declaration:          return replace(v->as<ast_local_vars_declaration>()); | ||||
|       case ast_asm_body:                        return replace(v->as<ast_asm_body>()); | ||||
|       default: | ||||
|         throw UnexpectedASTNodeType(v, "ASTReplacerInFunctionBody::visit"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   void start_replacing_in_function(V<ast_function_declaration> v) { | ||||
|     replace(v->get_body()); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| class ASTReplacerAllFunctionsInFile : public ASTReplacerInFunctionBody { | ||||
| protected: | ||||
|   using parent = ASTReplacerAllFunctionsInFile; | ||||
| 
 | ||||
|   virtual bool should_enter_function(V<ast_function_declaration> v) = 0; | ||||
| 
 | ||||
| public: | ||||
|   void start_replacing_in_file(V<ast_tolk_file> v_file) { | ||||
|     for (AnyV v : v_file->get_toplevel_declarations()) { | ||||
|       if (auto v_function = v->try_as<ast_function_declaration>()) { | ||||
|         if (should_enter_function(v_function)) { | ||||
|           replace(v_function->get_body()); | ||||
|         } | ||||
| #ifdef TOLK_DEBUG | ||||
|       case ast_asm_body: | ||||
|         throw UnexpectedASTNodeType(v, "ASTReplacer::replace"); | ||||
| #endif | ||||
|       default: { | ||||
|         // be very careful, don't forget to handle all statements (not expressions) above!
 | ||||
|         AnyExprV as_expr = reinterpret_cast<const ASTNodeExpressionBase*>(v); | ||||
|         return replace(as_expr); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   virtual bool should_visit_function(const FunctionData* fun_ref) = 0; | ||||
| 
 | ||||
|   void start_replacing_in_function(const FunctionData* fun_ref, V<ast_function_declaration> v_function) { | ||||
|     replace(v_function->get_body()); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| const std::vector<const FunctionData*>& get_all_not_builtin_functions(); | ||||
| 
 | ||||
| template<class BodyReplacerT> | ||||
| void replace_ast_of_all_functions() { | ||||
|   BodyReplacerT visitor; | ||||
|   for (const FunctionData* fun_ref : get_all_not_builtin_functions()) { | ||||
|     if (visitor.should_visit_function(fun_ref)) { | ||||
|       visitor.start_replacing_in_function(fun_ref, fun_ref->ast_root->as<ast_function_declaration>()); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
|  |  | |||
							
								
								
									
										255
									
								
								tolk/ast-replicator.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								tolk/ast-replicator.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,255 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "platform-utils.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| class ASTReplicator { | ||||
| protected: | ||||
|   virtual AnyV clone(AnyV v) = 0; | ||||
|   virtual AnyExprV clone(AnyExprV v) = 0; | ||||
|   virtual TypePtr clone(TypePtr) = 0; | ||||
| 
 | ||||
| public: | ||||
|   virtual ~ASTReplicator() = default; | ||||
| }; | ||||
| 
 | ||||
| class ASTReplicatorFunction : public ASTReplicator { | ||||
| protected: | ||||
|   using parent = ASTReplicatorFunction; | ||||
| 
 | ||||
|   std::vector<AnyV> clone(const std::vector<AnyV>& items) { | ||||
|     std::vector<AnyV> result; | ||||
|     result.reserve(items.size()); | ||||
|     for (AnyV item : items) { | ||||
|       result.push_back(clone(item)); | ||||
|     } | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   std::vector<AnyExprV> clone(const std::vector<AnyExprV>& items) { | ||||
|     std::vector<AnyExprV> result; | ||||
|     result.reserve(items.size()); | ||||
|     for (AnyExprV item : items) { | ||||
|       result.push_back(clone(item)); | ||||
|     } | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   // expressions
 | ||||
| 
 | ||||
|   virtual V<ast_empty_expression> clone(V<ast_empty_expression> v) { | ||||
|     return createV<ast_empty_expression>(v->loc); | ||||
|   } | ||||
|   virtual V<ast_parenthesized_expression> clone(V<ast_parenthesized_expression> v) { | ||||
|     return createV<ast_parenthesized_expression>(v->loc, clone(v->get_expr())); | ||||
|   } | ||||
|   virtual V<ast_tensor> clone(V<ast_tensor> v) { | ||||
|     return createV<ast_tensor>(v->loc, clone(v->get_items())); | ||||
|   } | ||||
|   virtual V<ast_typed_tuple> clone(V<ast_typed_tuple> v) { | ||||
|     return createV<ast_typed_tuple>(v->loc, clone(v->get_items())); | ||||
|   } | ||||
|   virtual V<ast_reference> clone(V<ast_reference> v) { | ||||
|     return createV<ast_reference>(v->loc, clone(v->get_identifier()), v->has_instantiationTs() ? clone(v->get_instantiationTs()) : nullptr); | ||||
|   } | ||||
|   virtual V<ast_local_var_lhs> clone(V<ast_local_var_lhs> v) { | ||||
|     return createV<ast_local_var_lhs>(v->loc, clone(v->get_identifier()), clone(v->declared_type), v->is_immutable, v->marked_as_redef); | ||||
|   } | ||||
|   virtual V<ast_local_vars_declaration> clone(V<ast_local_vars_declaration> v) { | ||||
|     return createV<ast_local_vars_declaration>(v->loc, clone(v->get_expr())); | ||||
|   } | ||||
|   virtual V<ast_int_const> clone(V<ast_int_const> v) { | ||||
|     return createV<ast_int_const>(v->loc, v->intval, v->orig_str); | ||||
|   } | ||||
|   virtual V<ast_string_const> clone(V<ast_string_const> v) { | ||||
|     return createV<ast_string_const>(v->loc, v->str_val, v->modifier); | ||||
|   } | ||||
|   virtual V<ast_bool_const> clone(V<ast_bool_const> v) { | ||||
|     return createV<ast_bool_const>(v->loc, v->bool_val); | ||||
|   } | ||||
|   virtual V<ast_null_keyword> clone(V<ast_null_keyword> v) { | ||||
|     return createV<ast_null_keyword>(v->loc); | ||||
|   } | ||||
|   virtual V<ast_argument> clone(V<ast_argument> v) { | ||||
|     return createV<ast_argument>(v->loc, clone(v->get_expr()), v->passed_as_mutate); | ||||
|   } | ||||
|   virtual V<ast_argument_list> clone(V<ast_argument_list> v) { | ||||
|     return createV<ast_argument_list>(v->loc, clone(v->get_arguments())); | ||||
|   } | ||||
|   virtual V<ast_dot_access> clone(V<ast_dot_access> v) { | ||||
|     return createV<ast_dot_access>(v->loc, clone(v->get_obj()), clone(v->get_identifier()), v->has_instantiationTs() ? clone(v->get_instantiationTs()) : nullptr); | ||||
|   } | ||||
|   virtual V<ast_function_call> clone(V<ast_function_call> v) { | ||||
|     return createV<ast_function_call>(v->loc, clone(v->get_callee()), clone(v->get_arg_list())); | ||||
|   } | ||||
|   virtual V<ast_underscore> clone(V<ast_underscore> v) { | ||||
|     return createV<ast_underscore>(v->loc); | ||||
|   } | ||||
|   virtual V<ast_assign> clone(V<ast_assign> v) { | ||||
|     return createV<ast_assign>(v->loc, clone(v->get_lhs()), clone(v->get_rhs())); | ||||
|   } | ||||
|   virtual V<ast_set_assign> clone(V<ast_set_assign> v) { | ||||
|     return createV<ast_set_assign>(v->loc, v->operator_name, v->tok, clone(v->get_lhs()), clone(v->get_rhs())); | ||||
|   } | ||||
|   virtual V<ast_unary_operator> clone(V<ast_unary_operator> v) { | ||||
|     return createV<ast_unary_operator>(v->loc, v->operator_name, v->tok, clone(v->get_rhs())); | ||||
|   } | ||||
|   virtual V<ast_binary_operator> clone(V<ast_binary_operator> v) { | ||||
|     return createV<ast_binary_operator>(v->loc, v->operator_name, v->tok, clone(v->get_lhs()), clone(v->get_rhs())); | ||||
|   } | ||||
|   virtual V<ast_ternary_operator> clone(V<ast_ternary_operator> v) { | ||||
|     return createV<ast_ternary_operator>(v->loc, clone(v->get_cond()), clone(v->get_when_true()), clone(v->get_when_false())); | ||||
|   } | ||||
|   virtual V<ast_cast_as_operator> clone(V<ast_cast_as_operator> v) { | ||||
|     return createV<ast_cast_as_operator>(v->loc, clone(v->get_expr()), clone(v->cast_to_type)); | ||||
|   } | ||||
| 
 | ||||
|   // statements
 | ||||
| 
 | ||||
|   virtual V<ast_empty_statement> clone(V<ast_empty_statement> v) { | ||||
|     return createV<ast_empty_statement>(v->loc); | ||||
|   } | ||||
|   virtual V<ast_sequence> clone(V<ast_sequence> v) { | ||||
|     return createV<ast_sequence>(v->loc, v->loc_end, clone(v->get_items())); | ||||
|   } | ||||
|   virtual V<ast_return_statement> clone(V<ast_return_statement> v) { | ||||
|     return createV<ast_return_statement>(v->loc, clone(v->get_return_value())); | ||||
|   } | ||||
|   virtual V<ast_if_statement> clone(V<ast_if_statement> v) { | ||||
|     return createV<ast_if_statement>(v->loc, v->is_ifnot, clone(v->get_cond()), clone(v->get_if_body()), clone(v->get_else_body())); | ||||
|   } | ||||
|   virtual V<ast_repeat_statement> clone(V<ast_repeat_statement> v) { | ||||
|     return createV<ast_repeat_statement>(v->loc, clone(v->get_cond()), clone(v->get_body())); | ||||
|   } | ||||
|   virtual V<ast_while_statement> clone(V<ast_while_statement> v) { | ||||
|     return createV<ast_while_statement>(v->loc, clone(v->get_cond()), clone(v->get_body())); | ||||
|   } | ||||
|   virtual V<ast_do_while_statement> clone(V<ast_do_while_statement> v) { | ||||
|     return createV<ast_do_while_statement>(v->loc, clone(v->get_body()), clone(v->get_cond())); | ||||
|   } | ||||
|   virtual V<ast_throw_statement> clone(V<ast_throw_statement> v) { | ||||
|     return createV<ast_throw_statement>(v->loc, clone(v->get_thrown_code()), clone(v->get_thrown_arg())); | ||||
|   } | ||||
|   virtual V<ast_assert_statement> clone(V<ast_assert_statement> v) { | ||||
|     return createV<ast_assert_statement>(v->loc, clone(v->get_cond()), clone(v->get_thrown_code())); | ||||
|   } | ||||
|   virtual V<ast_try_catch_statement> clone(V<ast_try_catch_statement> v) { | ||||
|     return createV<ast_try_catch_statement>(v->loc, clone(v->get_try_body()), clone(v->get_catch_expr()), clone(v->get_catch_body())); | ||||
|   } | ||||
|   virtual V<ast_asm_body> clone(V<ast_asm_body> v) { | ||||
|     return createV<ast_asm_body>(v->loc, v->arg_order, v->ret_order, clone(v->get_asm_commands())); | ||||
|   } | ||||
| 
 | ||||
|   // other
 | ||||
| 
 | ||||
|   virtual V<ast_identifier> clone(V<ast_identifier> v) { | ||||
|     return createV<ast_identifier>(v->loc, v->name); | ||||
|   } | ||||
|   virtual V<ast_instantiationT_item> clone(V<ast_instantiationT_item> v) { | ||||
|     return createV<ast_instantiationT_item>(v->loc, clone(v->substituted_type)); | ||||
|   } | ||||
|   virtual V<ast_instantiationT_list> clone(V<ast_instantiationT_list> v) { | ||||
|     return createV<ast_instantiationT_list>(v->loc, clone(v->get_items())); | ||||
|   } | ||||
|   virtual V<ast_parameter> clone(V<ast_parameter> v) { | ||||
|     return createV<ast_parameter>(v->loc, v->param_name, clone(v->declared_type), v->declared_as_mutate); | ||||
|   } | ||||
|   virtual V<ast_parameter_list> clone(V<ast_parameter_list> v) { | ||||
|     return createV<ast_parameter_list>(v->loc, clone(v->get_params())); | ||||
|   } | ||||
| 
 | ||||
|   AnyExprV clone(AnyExprV v) final { | ||||
|     switch (v->type) { | ||||
|       case ast_empty_expression:                return clone(v->as<ast_empty_expression>()); | ||||
|       case ast_parenthesized_expression:        return clone(v->as<ast_parenthesized_expression>()); | ||||
|       case ast_tensor:                          return clone(v->as<ast_tensor>()); | ||||
|       case ast_typed_tuple:                     return clone(v->as<ast_typed_tuple>()); | ||||
|       case ast_reference:                       return clone(v->as<ast_reference>()); | ||||
|       case ast_local_var_lhs:                   return clone(v->as<ast_local_var_lhs>()); | ||||
|       case ast_local_vars_declaration:          return clone(v->as<ast_local_vars_declaration>()); | ||||
|       case ast_int_const:                       return clone(v->as<ast_int_const>()); | ||||
|       case ast_string_const:                    return clone(v->as<ast_string_const>()); | ||||
|       case ast_bool_const:                      return clone(v->as<ast_bool_const>()); | ||||
|       case ast_null_keyword:                    return clone(v->as<ast_null_keyword>()); | ||||
|       case ast_argument:                        return clone(v->as<ast_argument>()); | ||||
|       case ast_argument_list:                   return clone(v->as<ast_argument_list>()); | ||||
|       case ast_dot_access:                      return clone(v->as<ast_dot_access>()); | ||||
|       case ast_function_call:                   return clone(v->as<ast_function_call>()); | ||||
|       case ast_underscore:                      return clone(v->as<ast_underscore>()); | ||||
|       case ast_assign:                          return clone(v->as<ast_assign>()); | ||||
|       case ast_set_assign:                      return clone(v->as<ast_set_assign>()); | ||||
|       case ast_unary_operator:                  return clone(v->as<ast_unary_operator>()); | ||||
|       case ast_binary_operator:                 return clone(v->as<ast_binary_operator>()); | ||||
|       case ast_ternary_operator:                return clone(v->as<ast_ternary_operator>()); | ||||
|       case ast_cast_as_operator:                return clone(v->as<ast_cast_as_operator>()); | ||||
|       default: | ||||
|         throw UnexpectedASTNodeType(v, "ASTReplicatorFunction::clone"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   AnyV clone(AnyV v) final { | ||||
|     switch (v->type) { | ||||
|       case ast_empty_statement:                 return clone(v->as<ast_empty_statement>()); | ||||
|       case ast_sequence:                        return clone(v->as<ast_sequence>()); | ||||
|       case ast_return_statement:                return clone(v->as<ast_return_statement>()); | ||||
|       case ast_if_statement:                    return clone(v->as<ast_if_statement>()); | ||||
|       case ast_repeat_statement:                return clone(v->as<ast_repeat_statement>()); | ||||
|       case ast_while_statement:                 return clone(v->as<ast_while_statement>()); | ||||
|       case ast_do_while_statement:              return clone(v->as<ast_do_while_statement>()); | ||||
|       case ast_throw_statement:                 return clone(v->as<ast_throw_statement>()); | ||||
|       case ast_assert_statement:                return clone(v->as<ast_assert_statement>()); | ||||
|       case ast_try_catch_statement:             return clone(v->as<ast_try_catch_statement>()); | ||||
|       case ast_asm_body:                        return clone(v->as<ast_asm_body>()); | ||||
|       // other AST nodes that can be children of ast nodes of function body
 | ||||
|       case ast_identifier:                      return clone(v->as<ast_identifier>()); | ||||
|       case ast_instantiationT_item:             return clone(v->as<ast_instantiationT_item>()); | ||||
|       case ast_instantiationT_list:             return clone(v->as<ast_instantiationT_list>()); | ||||
|       case ast_parameter:                       return clone(v->as<ast_parameter>()); | ||||
|       case ast_parameter_list:                  return clone(v->as<ast_parameter_list>()); | ||||
| 
 | ||||
|       default: { | ||||
|         // be very careful, don't forget to handle all statements/other (not expressions) above!
 | ||||
|         AnyExprV as_expr = reinterpret_cast<const ASTNodeExpressionBase*>(v); | ||||
|         return clone(as_expr); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   TypePtr clone(TypePtr t) override { | ||||
|     return t; | ||||
|   } | ||||
| 
 | ||||
|  public: | ||||
|   virtual V<ast_function_declaration> clone_function_body(V<ast_function_declaration> v_function) { | ||||
|     return createV<ast_function_declaration>( | ||||
|       v_function->loc, | ||||
|       clone(v_function->get_identifier()), | ||||
|       clone(v_function->get_param_list()), | ||||
|       clone(v_function->get_body()->as<ast_sequence>()), | ||||
|       clone(v_function->declared_return_type), | ||||
|       v_function->genericsT_list, | ||||
|       v_function->method_id, | ||||
|       v_function->flags | ||||
|     ); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
|  | @ -20,6 +20,7 @@ | |||
| 
 | ||||
| #include "ast.h" | ||||
| #include "ast-visitor.h" | ||||
| #include "type-system.h" | ||||
| #include <sstream> | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -31,46 +32,55 @@ namespace tolk { | |||
| 
 | ||||
| class ASTStringifier final : public ASTVisitor { | ||||
|   constexpr static std::pair<ASTNodeType, const char*> name_pairs[] = { | ||||
|     {ast_empty, "ast_empty"}, | ||||
|     {ast_parenthesized_expr, "ast_parenthesized_expr"}, | ||||
|     {ast_tensor, "ast_tensor"}, | ||||
|     {ast_tensor_square, "ast_tensor_square"}, | ||||
|     {ast_identifier, "ast_identifier"}, | ||||
|     // expressions
 | ||||
|     {ast_empty_expression, "ast_empty_expression"}, | ||||
|     {ast_parenthesized_expression, "ast_parenthesized_expression"}, | ||||
|     {ast_tensor, "ast_tensor"}, | ||||
|     {ast_typed_tuple, "ast_typed_tuple"}, | ||||
|     {ast_reference, "ast_reference"}, | ||||
|     {ast_local_var_lhs, "ast_local_var_lhs"}, | ||||
|     {ast_local_vars_declaration, "ast_local_vars_declaration"}, | ||||
|     {ast_int_const, "ast_int_const"}, | ||||
|     {ast_string_const, "ast_string_const"}, | ||||
|     {ast_bool_const, "ast_bool_const"}, | ||||
|     {ast_null_keyword, "ast_null_keyword"}, | ||||
|     {ast_self_keyword, "ast_self_keyword"}, | ||||
|     {ast_argument, "ast_argument"}, | ||||
|     {ast_argument_list, "ast_argument_list"}, | ||||
|     {ast_dot_access, "ast_dot_access"}, | ||||
|     {ast_function_call, "ast_function_call"}, | ||||
|     {ast_dot_method_call, "ast_dot_method_call"}, | ||||
|     {ast_global_var_declaration, "ast_global_var_declaration"}, | ||||
|     {ast_constant_declaration, "ast_constant_declaration"}, | ||||
|     {ast_underscore, "ast_underscore"}, | ||||
|     {ast_assign, "ast_assign"}, | ||||
|     {ast_set_assign, "ast_set_assign"}, | ||||
|     {ast_unary_operator, "ast_unary_operator"}, | ||||
|     {ast_binary_operator, "ast_binary_operator"}, | ||||
|     {ast_ternary_operator, "ast_ternary_operator"}, | ||||
|     {ast_return_statement, "ast_return_statement"}, | ||||
|     {ast_cast_as_operator, "ast_cast_as_operator"}, | ||||
|     // statements
 | ||||
|     {ast_empty_statement, "ast_empty_statement"}, | ||||
|     {ast_sequence, "ast_sequence"}, | ||||
|     {ast_return_statement, "ast_return_statement"}, | ||||
|     {ast_if_statement, "ast_if_statement"}, | ||||
|     {ast_repeat_statement, "ast_repeat_statement"}, | ||||
|     {ast_while_statement, "ast_while_statement"}, | ||||
|     {ast_do_while_statement, "ast_do_while_statement"}, | ||||
|     {ast_throw_statement, "ast_throw_statement"}, | ||||
|     {ast_assert_statement, "ast_assert_statement"}, | ||||
|     {ast_try_catch_statement, "ast_try_catch_statement"}, | ||||
|     {ast_if_statement, "ast_if_statement"}, | ||||
|     {ast_asm_body, "ast_asm_body"}, | ||||
|     // other
 | ||||
|     {ast_genericsT_item, "ast_genericsT_item"}, | ||||
|     {ast_genericsT_list, "ast_genericsT_list"}, | ||||
|     {ast_instantiationT_item, "ast_instantiationT_item"}, | ||||
|     {ast_instantiationT_list, "ast_instantiationT_list"}, | ||||
|     {ast_parameter, "ast_parameter"}, | ||||
|     {ast_parameter_list, "ast_parameter_list"}, | ||||
|     {ast_asm_body, "ast_asm_body"}, | ||||
|     {ast_annotation, "ast_annotation"}, | ||||
|     {ast_function_declaration, "ast_function_declaration"}, | ||||
|     {ast_local_var, "ast_local_var"}, | ||||
|     {ast_local_vars_declaration, "ast_local_vars_declaration"}, | ||||
|     {ast_global_var_declaration, "ast_global_var_declaration"}, | ||||
|     {ast_constant_declaration, "ast_constant_declaration"}, | ||||
|     {ast_tolk_required_version, "ast_tolk_required_version"}, | ||||
|     {ast_import_statement, "ast_import_statement"}, | ||||
|     {ast_import_directive, "ast_import_directive"}, | ||||
|     {ast_tolk_file, "ast_tolk_file"}, | ||||
|   }; | ||||
| 
 | ||||
|  | @ -114,62 +124,94 @@ class ASTStringifier final : public ASTVisitor { | |||
|     switch (v->type) { | ||||
|       case ast_identifier: | ||||
|         return static_cast<std::string>(v->as<ast_identifier>()->name); | ||||
|       case ast_reference: { | ||||
|         std::string result(v->as<ast_reference>()->get_name()); | ||||
|         if (v->as<ast_reference>()->has_instantiationTs()) { | ||||
|           result += specific_str(v->as<ast_reference>()->get_instantiationTs()); | ||||
|         } | ||||
|         return result; | ||||
|       } | ||||
|       case ast_int_const: | ||||
|         return static_cast<std::string>(v->as<ast_int_const>()->int_val); | ||||
|         return static_cast<std::string>(v->as<ast_int_const>()->orig_str); | ||||
|       case ast_string_const: | ||||
|         if (char modifier = v->as<ast_string_const>()->modifier) { | ||||
|           return "\"" + static_cast<std::string>(v->as<ast_string_const>()->str_val) + "\"" + std::string(1, modifier); | ||||
|         } else { | ||||
|           return "\"" + static_cast<std::string>(v->as<ast_string_const>()->str_val) + "\""; | ||||
|         } | ||||
|       case ast_function_call: { | ||||
|         if (auto v_lhs = v->as<ast_function_call>()->get_called_f()->try_as<ast_identifier>()) { | ||||
|           return static_cast<std::string>(v_lhs->name) + "()"; | ||||
|       case ast_bool_const: | ||||
|         return v->as<ast_bool_const>()->bool_val ? "true" : "false"; | ||||
|       case ast_dot_access: { | ||||
|         std::string result = "." + static_cast<std::string>(v->as<ast_dot_access>()->get_field_name()); | ||||
|         if (v->as<ast_dot_access>()->has_instantiationTs()) { | ||||
|           result += specific_str(v->as<ast_dot_access>()->get_instantiationTs()); | ||||
|         } | ||||
|         return {}; | ||||
|         return result; | ||||
|       } | ||||
|       case ast_function_call: { | ||||
|         std::string inner = specific_str(v->as<ast_function_call>()->get_callee()); | ||||
|         if (int n_args = v->as<ast_function_call>()->get_num_args()) { | ||||
|           return inner + "(..."  + std::to_string(n_args) + ")"; | ||||
|         } | ||||
|         return inner + "()"; | ||||
|       } | ||||
|       case ast_dot_method_call: | ||||
|         return static_cast<std::string>(v->as<ast_dot_method_call>()->method_name); | ||||
|       case ast_global_var_declaration: | ||||
|         return static_cast<std::string>(v->as<ast_global_var_declaration>()->get_identifier()->name); | ||||
|       case ast_constant_declaration: | ||||
|         return static_cast<std::string>(v->as<ast_constant_declaration>()->get_identifier()->name); | ||||
|       case ast_assign: | ||||
|         return "="; | ||||
|       case ast_set_assign: | ||||
|         return static_cast<std::string>(v->as<ast_set_assign>()->operator_name) + "="; | ||||
|       case ast_unary_operator: | ||||
|         return static_cast<std::string>(v->as<ast_unary_operator>()->operator_name); | ||||
|       case ast_binary_operator: | ||||
|         return static_cast<std::string>(v->as<ast_binary_operator>()->operator_name); | ||||
|       case ast_cast_as_operator: | ||||
|         return v->as<ast_cast_as_operator>()->cast_to_type->as_human_readable(); | ||||
|       case ast_sequence: | ||||
|         return "↓" + std::to_string(v->as<ast_sequence>()->get_items().size()); | ||||
|       case ast_instantiationT_item: | ||||
|         return v->as<ast_instantiationT_item>()->substituted_type->as_human_readable(); | ||||
|       case ast_if_statement: | ||||
|         return v->as<ast_if_statement>()->is_ifnot ? "ifnot" : ""; | ||||
|       case ast_annotation: | ||||
|         return annotation_kinds[static_cast<int>(v->as<ast_annotation>()->kind)].second; | ||||
|       case ast_parameter: { | ||||
|         std::ostringstream os; | ||||
|         os << v->as<ast_parameter>()->param_type; | ||||
|         return static_cast<std::string>(v->as<ast_parameter>()->get_identifier()->name) + ": " + os.str(); | ||||
|         os << v->as<ast_parameter>()->declared_type; | ||||
|         return static_cast<std::string>(v->as<ast_parameter>()->param_name) + ": " + os.str(); | ||||
|       } | ||||
|       case ast_function_declaration: { | ||||
|         std::string param_names; | ||||
|         for (int i = 0; i < v->as<ast_function_declaration>()->get_num_params(); i++) { | ||||
|           if (!param_names.empty()) | ||||
|             param_names += ","; | ||||
|           param_names += v->as<ast_function_declaration>()->get_param(i)->get_identifier()->name; | ||||
|           param_names += v->as<ast_function_declaration>()->get_param(i)->param_name; | ||||
|         } | ||||
|         return "fun " + static_cast<std::string>(v->as<ast_function_declaration>()->get_identifier()->name) + "(" + param_names + ")"; | ||||
|       } | ||||
|       case ast_local_var: { | ||||
|       case ast_local_var_lhs: { | ||||
|         std::ostringstream os; | ||||
|         os << v->as<ast_local_var>()->declared_type; | ||||
|         if (auto v_ident = v->as<ast_local_var>()->get_identifier()->try_as<ast_identifier>()) { | ||||
|           return static_cast<std::string>(v_ident->name) + ":" + os.str(); | ||||
|         os << (v->as<ast_local_var_lhs>()->inferred_type ? v->as<ast_local_var_lhs>()->inferred_type : v->as<ast_local_var_lhs>()->declared_type); | ||||
|         if (v->as<ast_local_var_lhs>()->get_name().empty()) { | ||||
|           return "_: " + os.str(); | ||||
|         } | ||||
|         return "_: " + os.str(); | ||||
|         return static_cast<std::string>(v->as<ast_local_var_lhs>()->get_name()) + ":" + os.str(); | ||||
|       } | ||||
|       case ast_instantiationT_list: { | ||||
|         std::string result = "<"; | ||||
|         for (AnyV item : v->as<ast_instantiationT_list>()->get_items()) { | ||||
|           if (result.size() > 1) | ||||
|             result += ","; | ||||
|           result += item->as<ast_instantiationT_item>()->substituted_type->as_human_readable(); | ||||
|         } | ||||
|         return result + ">"; | ||||
|       } | ||||
|       case ast_tolk_required_version: | ||||
|         return static_cast<std::string>(v->as<ast_tolk_required_version>()->semver); | ||||
|       case ast_import_statement: | ||||
|         return static_cast<std::string>(v->as<ast_import_statement>()->get_file_leaf()->str_val); | ||||
|       case ast_import_directive: | ||||
|         return static_cast<std::string>(v->as<ast_import_directive>()->get_file_leaf()->str_val); | ||||
|       case ast_tolk_file: | ||||
|         return v->as<ast_tolk_file>()->file->rel_filename; | ||||
|       default: | ||||
|  | @ -202,46 +244,55 @@ public: | |||
| 
 | ||||
|   void visit(AnyV v) override { | ||||
|     switch (v->type) { | ||||
|       case ast_empty:                         return handle_vertex(v->as<ast_empty>()); | ||||
|       case ast_parenthesized_expr:            return handle_vertex(v->as<ast_parenthesized_expr>()); | ||||
|       case ast_tensor:                        return handle_vertex(v->as<ast_tensor>()); | ||||
|       case ast_tensor_square:                 return handle_vertex(v->as<ast_tensor_square>()); | ||||
|       case ast_identifier:                    return handle_vertex(v->as<ast_identifier>()); | ||||
|       // expressions
 | ||||
|       case ast_empty_expression:              return handle_vertex(v->as<ast_empty_expression>()); | ||||
|       case ast_parenthesized_expression:      return handle_vertex(v->as<ast_parenthesized_expression>()); | ||||
|       case ast_tensor:                        return handle_vertex(v->as<ast_tensor>()); | ||||
|       case ast_typed_tuple:                   return handle_vertex(v->as<ast_typed_tuple>()); | ||||
|       case ast_reference:                     return handle_vertex(v->as<ast_reference>()); | ||||
|       case ast_local_var_lhs:                 return handle_vertex(v->as<ast_local_var_lhs>()); | ||||
|       case ast_local_vars_declaration:        return handle_vertex(v->as<ast_local_vars_declaration>()); | ||||
|       case ast_int_const:                     return handle_vertex(v->as<ast_int_const>()); | ||||
|       case ast_string_const:                  return handle_vertex(v->as<ast_string_const>()); | ||||
|       case ast_bool_const:                    return handle_vertex(v->as<ast_bool_const>()); | ||||
|       case ast_null_keyword:                  return handle_vertex(v->as<ast_null_keyword>()); | ||||
|       case ast_self_keyword:                  return handle_vertex(v->as<ast_self_keyword>()); | ||||
|       case ast_argument:                      return handle_vertex(v->as<ast_argument>()); | ||||
|       case ast_argument_list:                 return handle_vertex(v->as<ast_argument_list>()); | ||||
|       case ast_dot_access:                    return handle_vertex(v->as<ast_dot_access>()); | ||||
|       case ast_function_call:                 return handle_vertex(v->as<ast_function_call>()); | ||||
|       case ast_dot_method_call:               return handle_vertex(v->as<ast_dot_method_call>()); | ||||
|       case ast_global_var_declaration:        return handle_vertex(v->as<ast_global_var_declaration>()); | ||||
|       case ast_constant_declaration:          return handle_vertex(v->as<ast_constant_declaration>()); | ||||
|       case ast_underscore:                    return handle_vertex(v->as<ast_underscore>()); | ||||
|       case ast_assign:                        return handle_vertex(v->as<ast_assign>()); | ||||
|       case ast_set_assign:                    return handle_vertex(v->as<ast_set_assign>()); | ||||
|       case ast_unary_operator:                return handle_vertex(v->as<ast_unary_operator>()); | ||||
|       case ast_binary_operator:               return handle_vertex(v->as<ast_binary_operator>()); | ||||
|       case ast_ternary_operator:              return handle_vertex(v->as<ast_ternary_operator>()); | ||||
|       case ast_return_statement:              return handle_vertex(v->as<ast_return_statement>()); | ||||
|       case ast_cast_as_operator:              return handle_vertex(v->as<ast_cast_as_operator>()); | ||||
|       // statements
 | ||||
|       case ast_empty_statement:               return handle_vertex(v->as<ast_empty_statement>()); | ||||
|       case ast_sequence:                      return handle_vertex(v->as<ast_sequence>()); | ||||
|       case ast_return_statement:              return handle_vertex(v->as<ast_return_statement>()); | ||||
|       case ast_if_statement:                  return handle_vertex(v->as<ast_if_statement>()); | ||||
|       case ast_repeat_statement:              return handle_vertex(v->as<ast_repeat_statement>()); | ||||
|       case ast_while_statement:               return handle_vertex(v->as<ast_while_statement>()); | ||||
|       case ast_do_while_statement:            return handle_vertex(v->as<ast_do_while_statement>()); | ||||
|       case ast_throw_statement:               return handle_vertex(v->as<ast_throw_statement>()); | ||||
|       case ast_assert_statement:              return handle_vertex(v->as<ast_assert_statement>()); | ||||
|       case ast_try_catch_statement:           return handle_vertex(v->as<ast_try_catch_statement>()); | ||||
|       case ast_if_statement:                  return handle_vertex(v->as<ast_if_statement>()); | ||||
|       case ast_asm_body:                      return handle_vertex(v->as<ast_asm_body>()); | ||||
|       // other
 | ||||
|       case ast_genericsT_item:                return handle_vertex(v->as<ast_genericsT_item>()); | ||||
|       case ast_genericsT_list:                return handle_vertex(v->as<ast_genericsT_list>()); | ||||
|       case ast_instantiationT_item:           return handle_vertex(v->as<ast_instantiationT_item>()); | ||||
|       case ast_instantiationT_list:           return handle_vertex(v->as<ast_instantiationT_list>()); | ||||
|       case ast_parameter:                     return handle_vertex(v->as<ast_parameter>()); | ||||
|       case ast_parameter_list:                return handle_vertex(v->as<ast_parameter_list>()); | ||||
|       case ast_asm_body:                      return handle_vertex(v->as<ast_asm_body>()); | ||||
|       case ast_annotation:                    return handle_vertex(v->as<ast_annotation>()); | ||||
|       case ast_function_declaration:          return handle_vertex(v->as<ast_function_declaration>()); | ||||
|       case ast_local_var:                     return handle_vertex(v->as<ast_local_var>()); | ||||
|       case ast_local_vars_declaration:        return handle_vertex(v->as<ast_local_vars_declaration>()); | ||||
|       case ast_global_var_declaration:        return handle_vertex(v->as<ast_global_var_declaration>()); | ||||
|       case ast_constant_declaration:          return handle_vertex(v->as<ast_constant_declaration>()); | ||||
|       case ast_tolk_required_version:         return handle_vertex(v->as<ast_tolk_required_version>()); | ||||
|       case ast_import_statement:              return handle_vertex(v->as<ast_import_statement>()); | ||||
|       case ast_import_directive:              return handle_vertex(v->as<ast_import_directive>()); | ||||
|       case ast_tolk_file:                     return handle_vertex(v->as<ast_tolk_file>()); | ||||
|       default: | ||||
|         throw UnexpectedASTNodeType(v, "ASTStringifier::visit"); | ||||
|  |  | |||
|  | @ -37,20 +37,40 @@ namespace tolk { | |||
| 
 | ||||
| class ASTVisitor { | ||||
| protected: | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE static void visit_children(const ASTNodeLeaf* v) { | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE static void visit_children(const ASTExprLeaf* v) { | ||||
|     static_cast<void>(v); | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTNodeUnary* v) { | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTExprUnary* v) { | ||||
|     visit(v->child); | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTNodeBinary* v) { | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTExprBinary* v) { | ||||
|     visit(v->lhs); | ||||
|     visit(v->rhs); | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTNodeVararg* v) { | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTExprVararg* v) { | ||||
|     for (AnyExprV child : v->children) { | ||||
|       visit(child); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTStatementUnary* v) { | ||||
|     visit(v->child); | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTStatementVararg* v) { | ||||
|     for (AnyV child : v->children) { | ||||
|       visit(child); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE static void visit_children(const ASTOtherLeaf* v) { | ||||
|     static_cast<void>(v); | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTOtherVararg* v) { | ||||
|     for (AnyV child : v->children) { | ||||
|       visit(child); | ||||
|     } | ||||
|  | @ -66,90 +86,105 @@ class ASTVisitorFunctionBody : public ASTVisitor { | |||
| protected: | ||||
|   using parent = ASTVisitorFunctionBody; | ||||
| 
 | ||||
|   virtual void visit(V<ast_empty> v)                     { return visit_children(v); } | ||||
|   virtual void visit(V<ast_parenthesized_expr> v)        { return visit_children(v); } | ||||
|   // expressions
 | ||||
|   virtual void visit(V<ast_empty_expression> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_parenthesized_expression> v)  { return visit_children(v); } | ||||
|   virtual void visit(V<ast_tensor> v)                    { return visit_children(v); } | ||||
|   virtual void visit(V<ast_tensor_square> v)             { return visit_children(v); } | ||||
|   virtual void visit(V<ast_identifier> v)                { return visit_children(v); } | ||||
|   virtual void visit(V<ast_typed_tuple> v)               { return visit_children(v); } | ||||
|   virtual void visit(V<ast_reference> v)                 { return visit_children(v); } | ||||
|   virtual void visit(V<ast_local_var_lhs> v)             { return visit_children(v); } | ||||
|   virtual void visit(V<ast_local_vars_declaration> v)    { return visit_children(v); } | ||||
|   virtual void visit(V<ast_int_const> v)                 { return visit_children(v); } | ||||
|   virtual void visit(V<ast_string_const> v)              { return visit_children(v); } | ||||
|   virtual void visit(V<ast_bool_const> v)                { return visit_children(v); } | ||||
|   virtual void visit(V<ast_null_keyword> v)              { return visit_children(v); } | ||||
|   virtual void visit(V<ast_self_keyword> v)              { return visit_children(v); } | ||||
|   virtual void visit(V<ast_argument> v)                  { return visit_children(v); } | ||||
|   virtual void visit(V<ast_argument_list> v)             { return visit_children(v); } | ||||
|   virtual void visit(V<ast_dot_access> v)                { return visit_children(v); } | ||||
|   virtual void visit(V<ast_function_call> v)             { return visit_children(v); } | ||||
|   virtual void visit(V<ast_dot_method_call> v)            { return visit_children(v); } | ||||
|   virtual void visit(V<ast_underscore> v)                { return visit_children(v); } | ||||
|   virtual void visit(V<ast_assign> v)                    { return visit_children(v); } | ||||
|   virtual void visit(V<ast_set_assign> v)                { return visit_children(v); } | ||||
|   virtual void visit(V<ast_unary_operator> v)            { return visit_children(v); } | ||||
|   virtual void visit(V<ast_binary_operator> v)           { return visit_children(v); } | ||||
|   virtual void visit(V<ast_ternary_operator> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_return_statement> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_cast_as_operator> v)          { return visit_children(v); } | ||||
|   // statements
 | ||||
|   virtual void visit(V<ast_empty_statement> v)           { return visit_children(v); } | ||||
|   virtual void visit(V<ast_sequence> v)                  { return visit_children(v); } | ||||
|   virtual void visit(V<ast_return_statement> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_if_statement> v)              { return visit_children(v); } | ||||
|   virtual void visit(V<ast_repeat_statement> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_while_statement> v)           { return visit_children(v); } | ||||
|   virtual void visit(V<ast_do_while_statement> v)        { return visit_children(v); } | ||||
|   virtual void visit(V<ast_throw_statement> v)           { return visit_children(v); } | ||||
|   virtual void visit(V<ast_assert_statement> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_try_catch_statement> v)       { return visit_children(v); } | ||||
|   virtual void visit(V<ast_if_statement> v)              { return visit_children(v); } | ||||
|   virtual void visit(V<ast_local_var> v)                 { return visit_children(v); } | ||||
|   virtual void visit(V<ast_local_vars_declaration> v)    { return visit_children(v); } | ||||
|   virtual void visit(V<ast_asm_body> v)                  { return visit_children(v); } | ||||
| 
 | ||||
|   void visit(AnyV v) final { | ||||
|     switch (v->type) { | ||||
|       case ast_empty:                           return visit(v->as<ast_empty>()); | ||||
|       case ast_parenthesized_expr:              return visit(v->as<ast_parenthesized_expr>()); | ||||
|       // expressions
 | ||||
|       case ast_empty_expression:                return visit(v->as<ast_empty_expression>()); | ||||
|       case ast_parenthesized_expression:        return visit(v->as<ast_parenthesized_expression>()); | ||||
|       case ast_tensor:                          return visit(v->as<ast_tensor>()); | ||||
|       case ast_tensor_square:                   return visit(v->as<ast_tensor_square>()); | ||||
|       case ast_identifier:                      return visit(v->as<ast_identifier>()); | ||||
|       case ast_typed_tuple:                     return visit(v->as<ast_typed_tuple>()); | ||||
|       case ast_reference:                       return visit(v->as<ast_reference>()); | ||||
|       case ast_local_var_lhs:                   return visit(v->as<ast_local_var_lhs>()); | ||||
|       case ast_local_vars_declaration:          return visit(v->as<ast_local_vars_declaration>()); | ||||
|       case ast_int_const:                       return visit(v->as<ast_int_const>()); | ||||
|       case ast_string_const:                    return visit(v->as<ast_string_const>()); | ||||
|       case ast_bool_const:                      return visit(v->as<ast_bool_const>()); | ||||
|       case ast_null_keyword:                    return visit(v->as<ast_null_keyword>()); | ||||
|       case ast_self_keyword:                    return visit(v->as<ast_self_keyword>()); | ||||
|       case ast_argument:                        return visit(v->as<ast_argument>()); | ||||
|       case ast_argument_list:                   return visit(v->as<ast_argument_list>()); | ||||
|       case ast_dot_access:                      return visit(v->as<ast_dot_access>()); | ||||
|       case ast_function_call:                   return visit(v->as<ast_function_call>()); | ||||
|       case ast_dot_method_call:                 return visit(v->as<ast_dot_method_call>()); | ||||
|       case ast_underscore:                      return visit(v->as<ast_underscore>()); | ||||
|       case ast_assign:                          return visit(v->as<ast_assign>()); | ||||
|       case ast_set_assign:                      return visit(v->as<ast_set_assign>()); | ||||
|       case ast_unary_operator:                  return visit(v->as<ast_unary_operator>()); | ||||
|       case ast_binary_operator:                 return visit(v->as<ast_binary_operator>()); | ||||
|       case ast_ternary_operator:                return visit(v->as<ast_ternary_operator>()); | ||||
|       case ast_return_statement:                return visit(v->as<ast_return_statement>()); | ||||
|       case ast_cast_as_operator:                return visit(v->as<ast_cast_as_operator>()); | ||||
|       // statements
 | ||||
|       case ast_empty_statement:                 return visit(v->as<ast_empty_statement>()); | ||||
|       case ast_sequence:                        return visit(v->as<ast_sequence>()); | ||||
|       case ast_return_statement:                return visit(v->as<ast_return_statement>()); | ||||
|       case ast_if_statement:                    return visit(v->as<ast_if_statement>()); | ||||
|       case ast_repeat_statement:                return visit(v->as<ast_repeat_statement>()); | ||||
|       case ast_while_statement:                 return visit(v->as<ast_while_statement>()); | ||||
|       case ast_do_while_statement:              return visit(v->as<ast_do_while_statement>()); | ||||
|       case ast_throw_statement:                 return visit(v->as<ast_throw_statement>()); | ||||
|       case ast_assert_statement:                return visit(v->as<ast_assert_statement>()); | ||||
|       case ast_try_catch_statement:             return visit(v->as<ast_try_catch_statement>()); | ||||
|       case ast_if_statement:                    return visit(v->as<ast_if_statement>()); | ||||
|       case ast_local_var:                       return visit(v->as<ast_local_var>()); | ||||
|       case ast_local_vars_declaration:          return visit(v->as<ast_local_vars_declaration>()); | ||||
|       case ast_asm_body:                        return visit(v->as<ast_asm_body>()); | ||||
| #ifdef TOLK_DEBUG | ||||
|       case ast_asm_body: | ||||
|         throw UnexpectedASTNodeType(v, "ASTVisitor; forgot to filter out asm functions in should_visit_function()?"); | ||||
| #endif | ||||
|       default: | ||||
|         throw UnexpectedASTNodeType(v, "ASTVisitorFunctionBody::visit"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   void start_visiting_function(V<ast_function_declaration> v_function) { | ||||
|   virtual bool should_visit_function(const FunctionData* fun_ref) = 0; | ||||
| 
 | ||||
|   virtual void start_visiting_function(const FunctionData* fun_ref, V<ast_function_declaration> v_function) { | ||||
|     visit(v_function->get_body()); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| class ASTVisitorAllFunctionsInFile : public ASTVisitorFunctionBody { | ||||
| protected: | ||||
|   using parent = ASTVisitorAllFunctionsInFile; | ||||
| 
 | ||||
|   virtual bool should_enter_function(V<ast_function_declaration> v) = 0; | ||||
| const std::vector<const FunctionData*>& get_all_not_builtin_functions(); | ||||
| 
 | ||||
| public: | ||||
|   void start_visiting_file(V<ast_tolk_file> v_file) { | ||||
|     for (AnyV v : v_file->get_toplevel_declarations()) { | ||||
|       if (auto v_func = v->try_as<ast_function_declaration>()) { | ||||
|         if (should_enter_function(v_func)) { | ||||
|           visit(v_func->get_body()); | ||||
|         } | ||||
|       } | ||||
| template<class BodyVisitorT> | ||||
| void visit_ast_of_all_functions() { | ||||
|   BodyVisitorT visitor; | ||||
|   for (const FunctionData* fun_ref : get_all_not_builtin_functions()) { | ||||
|     if (visitor.should_visit_function(fun_ref)) { | ||||
|       visitor.start_visiting_function(fun_ref, fun_ref->ast_root->as<ast_function_declaration>()); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
|  |  | |||
							
								
								
									
										101
									
								
								tolk/ast.cpp
									
										
									
									
									
								
							
							
						
						
									
										101
									
								
								tolk/ast.cpp
									
										
									
									
									
								
							|  | @ -15,8 +15,9 @@ | |||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #include "ast.h" | ||||
| #ifdef TOLK_DEBUG | ||||
| #include "ast-stringifier.h" | ||||
| #include <iostream> | ||||
| #endif | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
|  | @ -79,7 +80,7 @@ int Vertex<ast_genericsT_list>::lookup_idx(std::string_view nameT) const { | |||
| 
 | ||||
| int Vertex<ast_parameter_list>::lookup_idx(std::string_view param_name) const { | ||||
|   for (size_t idx = 0; idx < children.size(); ++idx) { | ||||
|     if (children[idx] && children[idx]->as<ast_parameter>()->get_identifier()->name == param_name) { | ||||
|     if (children[idx] && children[idx]->as<ast_parameter>()->param_name == param_name) { | ||||
|       return static_cast<int>(idx); | ||||
|     } | ||||
|   } | ||||
|  | @ -96,8 +97,100 @@ int Vertex<ast_parameter_list>::get_mutate_params_count() const { | |||
|   return n; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_import_statement>::mutate_set_src_file(const SrcFile* file) const { | ||||
|   const_cast<Vertex*>(this)->file = file; | ||||
| // ---------------------------------------------------------
 | ||||
| // "assign" methods
 | ||||
| //
 | ||||
| // From the user's point of view, all AST vertices are constant, fields are public, but can't be modified.
 | ||||
| // The only way to modify a field is to call "mutate()" and then use these "assign_*" methods.
 | ||||
| // Therefore, there is a guarantee, that all AST mutations are done via these methods,
 | ||||
| // easily searched by usages, and there is no another way to modify any other field.
 | ||||
| 
 | ||||
| void ASTNodeExpressionBase::assign_inferred_type(TypePtr type) { | ||||
|   this->inferred_type = type; | ||||
| } | ||||
| 
 | ||||
| void ASTNodeExpressionBase::assign_rvalue_true() { | ||||
|   this->is_rvalue = true; | ||||
| } | ||||
| 
 | ||||
| void ASTNodeExpressionBase::assign_lvalue_true() { | ||||
|   this->is_lvalue = true; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_reference>::assign_sym(const Symbol* sym) { | ||||
|   this->sym = sym; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_function_call>::assign_fun_ref(const FunctionData* fun_ref) { | ||||
|   this->fun_maybe = fun_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_cast_as_operator>::assign_resolved_type(TypePtr cast_to_type) { | ||||
|   this->cast_to_type = cast_to_type; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_global_var_declaration>::assign_var_ref(const GlobalVarData* var_ref) { | ||||
|   this->var_ref = var_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_global_var_declaration>::assign_resolved_type(TypePtr declared_type) { | ||||
|   this->declared_type = declared_type; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_constant_declaration>::assign_const_ref(const GlobalConstData* const_ref) { | ||||
|   this->const_ref = const_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_constant_declaration>::assign_resolved_type(TypePtr declared_type) { | ||||
|   this->declared_type = declared_type; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_instantiationT_item>::assign_resolved_type(TypePtr substituted_type) { | ||||
|   this->substituted_type = substituted_type; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_parameter>::assign_param_ref(const LocalVarData* param_ref) { | ||||
|   this->param_ref = param_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_parameter>::assign_resolved_type(TypePtr declared_type) { | ||||
|   this->declared_type = declared_type; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_set_assign>::assign_fun_ref(const FunctionData* fun_ref) { | ||||
|   this->fun_ref = fun_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_unary_operator>::assign_fun_ref(const FunctionData* fun_ref) { | ||||
|   this->fun_ref = fun_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_binary_operator>::assign_fun_ref(const FunctionData* fun_ref) { | ||||
|   this->fun_ref = fun_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_dot_access>::assign_target(const DotTarget& target) { | ||||
|   this->target = target; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_function_declaration>::assign_fun_ref(const FunctionData* fun_ref) { | ||||
|   this->fun_ref = fun_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_function_declaration>::assign_resolved_type(TypePtr declared_return_type) { | ||||
|   this->declared_return_type = declared_return_type; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_local_var_lhs>::assign_var_ref(const LocalVarData* var_ref) { | ||||
|   this->var_ref = var_ref; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_local_var_lhs>::assign_resolved_type(TypePtr declared_type) { | ||||
|   this->declared_type = declared_type; | ||||
| } | ||||
| 
 | ||||
| void Vertex<ast_import_directive>::assign_src_file(const SrcFile* file) { | ||||
|   this->file = file; | ||||
| } | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
|  |  | |||
							
								
								
									
										959
									
								
								tolk/ast.h
									
										
									
									
									
								
							
							
						
						
									
										959
									
								
								tolk/ast.h
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -16,86 +16,57 @@ | |||
| */ | ||||
| #include "tolk.h" | ||||
| #include "compiler-state.h" | ||||
| #include "type-system.h" | ||||
| #include "generics-helpers.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| using namespace std::literals::string_literals; | ||||
| 
 | ||||
| /*
 | ||||
|  * | ||||
|  *   SYMBOL VALUES | ||||
|  *  | ||||
|  */ | ||||
| 
 | ||||
| SymDef* define_builtin_func_impl(const std::string& name, SymValAsmFunc* func_val) { | ||||
|   sym_idx_t name_idx = G.symbols.lookup_add(name); | ||||
|   SymDef* def = define_global_symbol(name_idx); | ||||
|   tolk_assert(!def->value); | ||||
| 
 | ||||
|   def->value = func_val; | ||||
| #ifdef TOLK_DEBUG | ||||
|   def->value->sym_name = name; | ||||
| #endif | ||||
|   return def; | ||||
| } | ||||
| 
 | ||||
| // given func_type = `(slice, int) -> slice` and func flags, create SymDef for parameters
 | ||||
| // given func_type = `(slice, int) -> slice` and func flags, create SymLocalVarOrParameter
 | ||||
| // currently (see at the bottom) parameters of built-in functions are unnamed:
 | ||||
| // built-in functions are created using a resulting type
 | ||||
| static std::vector<SymDef*> define_builtin_parameters(const TypeExpr* func_type, int func_flags) { | ||||
| static std::vector<LocalVarData> define_builtin_parameters(const std::vector<TypePtr>& params_types, int func_flags) { | ||||
|   // `loadInt()`, `storeInt()`: they accept `self` and mutate it; no other options available in built-ins for now
 | ||||
|   bool is_mutate_self = func_flags & SymValFunc::flagHasMutateParams; | ||||
|   // func_type a map (params_type -> ret_type), probably surrounded by forall (internal representation of <T>)
 | ||||
|   TypeExpr* params_type = func_type->constr == TypeExpr::te_ForAll ? func_type->args[0]->args[0] : func_type->args[0]; | ||||
|   std::vector<SymDef*> parameters; | ||||
|   bool is_mutate_self = func_flags & FunctionData::flagHasMutateParams; | ||||
|   std::vector<LocalVarData> parameters; | ||||
|   parameters.reserve(params_types.size()); | ||||
| 
 | ||||
|   if (params_type->constr == TypeExpr::te_Tensor) {   // multiple parameters: it's a tensor
 | ||||
|     parameters.reserve(params_type->args.size()); | ||||
|     for (int i = 0; i < static_cast<int>(params_type->args.size()); ++i) { | ||||
|       SymDef* sym_def = define_parameter(i, {}); | ||||
|       SymValVariable* sym_val = new SymValVariable(i, params_type->args[i]); | ||||
|       if (i == 0 && is_mutate_self) { | ||||
|         sym_val->flags |= SymValVariable::flagMutateParameter; | ||||
|       } | ||||
|       sym_def->value = sym_val; | ||||
|       parameters.emplace_back(sym_def); | ||||
|     } | ||||
|   } else {  // single parameter
 | ||||
|     SymDef* sym_def = define_parameter(0, {}); | ||||
|     SymValVariable* sym_val = new SymValVariable(0, params_type); | ||||
|     if (is_mutate_self) { | ||||
|       sym_val->flags |= SymValVariable::flagMutateParameter; | ||||
|     } | ||||
|     sym_def->value = sym_val; | ||||
|     parameters.emplace_back(sym_def); | ||||
|   for (int i = 0; i < static_cast<int>(params_types.size()); ++i) { | ||||
|     LocalVarData p_sym("", {}, params_types[i], (i == 0 && is_mutate_self) * LocalVarData::flagMutateParameter, i); | ||||
|     parameters.push_back(std::move(p_sym)); | ||||
|   } | ||||
| 
 | ||||
|   return parameters; | ||||
| } | ||||
| 
 | ||||
| static SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, int flags) { | ||||
|   return define_builtin_func_impl(name, new SymValAsmFunc(define_builtin_parameters(func_type, flags), func_type, func, flags | SymValFunc::flagBuiltinFunction)); | ||||
| static void define_builtin_func(const std::string& name, const std::vector<TypePtr>& params_types, TypePtr return_type, const GenericsDeclaration* genericTs, const simple_compile_func_t& func, int flags) { | ||||
|   auto* f_sym = new FunctionData(name, {}, return_type, define_builtin_parameters(params_types, flags), flags, genericTs, nullptr, new FunctionBodyBuiltin(func), nullptr); | ||||
|   G.symtable.add_function(f_sym); | ||||
| } | ||||
| 
 | ||||
| static SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const AsmOp& macro, int flags) { | ||||
|   return define_builtin_func_impl(name, new SymValAsmFunc(define_builtin_parameters(func_type, flags), func_type, make_simple_compile(macro), flags | SymValFunc::flagBuiltinFunction)); | ||||
| static void define_builtin_func(const std::string& name, const std::vector<TypePtr>& params_types, TypePtr return_type, const GenericsDeclaration* genericTs, const AsmOp& macro, int flags) { | ||||
|   auto* f_sym = new FunctionData(name, {}, return_type, define_builtin_parameters(params_types, flags), flags, genericTs, nullptr, new FunctionBodyBuiltin(make_simple_compile(macro)), nullptr); | ||||
|   G.symtable.add_function(f_sym); | ||||
| } | ||||
| 
 | ||||
| static SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, int flags, | ||||
|                                    std::initializer_list<int> arg_order, std::initializer_list<int> ret_order) { | ||||
|   return define_builtin_func_impl(name, new SymValAsmFunc(define_builtin_parameters(func_type, flags), func_type, func, flags | SymValFunc::flagBuiltinFunction, arg_order, ret_order)); | ||||
| static void define_builtin_func(const std::string& name, const std::vector<TypePtr>& params_types, TypePtr return_type, const GenericsDeclaration* genericTs, const simple_compile_func_t& func, int flags, | ||||
|                                 std::initializer_list<int> arg_order, std::initializer_list<int> ret_order) { | ||||
|   auto* f_sym = new FunctionData(name, {}, return_type, define_builtin_parameters(params_types, flags), flags, genericTs, nullptr, new FunctionBodyBuiltin(func), nullptr); | ||||
|   f_sym->arg_order = arg_order; | ||||
|   f_sym->ret_order = ret_order; | ||||
|   G.symtable.add_function(f_sym); | ||||
| } | ||||
| 
 | ||||
| bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in, | ||||
|                             SrcLocation where) const { | ||||
|   if (simple_compile) { | ||||
|     return dest.append(simple_compile(out, in, where)); | ||||
|   } else if (ext_compile) { | ||||
|     return ext_compile(dest, out, in); | ||||
|   } else { | ||||
|     return false; | ||||
|   } | ||||
| void FunctionBodyBuiltin::compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in, | ||||
|                                      SrcLocation where) const { | ||||
|   dest.append(simple_compile(out, in, where)); | ||||
| } | ||||
| 
 | ||||
| void FunctionBodyAsm::compile(AsmOpList& dest) const { | ||||
|   dest.append(ops); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  *  | ||||
|  *   DEFINE BUILT-IN FUNCTIONS | ||||
|  | @ -504,7 +475,7 @@ AsmOp compile_unary_plus(std::vector<VarDescr>& res, std::vector<VarDescr>& args | |||
|   return AsmOp::Nop(); | ||||
| } | ||||
| 
 | ||||
| AsmOp compile_logical_not(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation where) { | ||||
| AsmOp compile_logical_not(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation where, bool for_int_arg) { | ||||
|   tolk_assert(res.size() == 1 && args.size() == 1); | ||||
|   VarDescr &r = res[0], &x = args[0]; | ||||
|   if (x.is_int_const()) { | ||||
|  | @ -513,7 +484,9 @@ AsmOp compile_logical_not(std::vector<VarDescr>& res, std::vector<VarDescr>& arg | |||
|     return push_const(r.int_const); | ||||
|   } | ||||
|   r.val = VarDescr::ValBool; | ||||
|   return exec_op("0 EQINT", 1); | ||||
|   // for integers, `!var` is `var != 0`
 | ||||
|   // for booleans, `!var` can be shortened to `~var` (works the same for 0/-1 but consumes less)
 | ||||
|   return for_int_arg ? exec_op("0 EQINT", 1) : exec_op("NOT", 1); | ||||
| } | ||||
| 
 | ||||
| AsmOp compile_bitwise_and(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation where) { | ||||
|  | @ -1076,7 +1049,7 @@ AsmOp compile_fetch_slice(std::vector<VarDescr>& res, std::vector<VarDescr>& arg | |||
|   return exec_op(fetch ? "LDSLICEX" : "PLDSLICEX", 2, 1 + (unsigned)fetch); | ||||
| } | ||||
| 
 | ||||
| // fun at<X>(t: tuple, index: int): X   asm "INDEXVAR";
 | ||||
| // fun tupleAt<X>(t: tuple, index: int): X   asm "INDEXVAR";
 | ||||
| AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation) { | ||||
|   tolk_assert(args.size() == 2 && res.size() == 1); | ||||
|   auto& y = args[1]; | ||||
|  | @ -1087,7 +1060,7 @@ AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args, | |||
|   return exec_op("INDEXVAR", 2, 1); | ||||
| } | ||||
| 
 | ||||
| // fun __isNull<X>(X arg): int
 | ||||
| // fun __isNull<X>(X arg): bool
 | ||||
| AsmOp compile_is_null(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation) { | ||||
|   tolk_assert(args.size() == 1 && res.size() == 1); | ||||
|   res[0].val = VarDescr::ValBool; | ||||
|  | @ -1098,143 +1071,188 @@ AsmOp compile_is_null(std::vector<VarDescr>& res, std::vector<VarDescr>& args, S | |||
| void define_builtins() { | ||||
|   using namespace std::placeholders; | ||||
| 
 | ||||
|   TypeExpr* Unit = TypeExpr::new_unit(); | ||||
|   TypeExpr* Int = TypeExpr::new_atomic(TypeExpr::_Int); | ||||
|   TypeExpr* Slice = TypeExpr::new_atomic(TypeExpr::_Slice); | ||||
|   TypeExpr* Builder = TypeExpr::new_atomic(TypeExpr::_Builder); | ||||
|   TypeExpr* Tuple = TypeExpr::new_atomic(TypeExpr::_Tuple); | ||||
|   TypeExpr* Int2 = TypeExpr::new_tensor({Int, Int}); | ||||
|   TypeExpr* Int3 = TypeExpr::new_tensor({Int, Int, Int}); | ||||
|   TypeExpr* TupleInt = TypeExpr::new_tensor({Tuple, Int}); | ||||
|   TypeExpr* SliceInt = TypeExpr::new_tensor({Slice, Int}); | ||||
|   TypeExpr* X = TypeExpr::new_var(0); | ||||
|   TypeExpr* arith_bin_op = TypeExpr::new_map(Int2, Int); | ||||
|   TypeExpr* arith_un_op = TypeExpr::new_map(Int, Int); | ||||
|   TypeExpr* impure_un_op = TypeExpr::new_map(Int, Unit); | ||||
|   TypeExpr* fetch_int_op_mutate = TypeExpr::new_map(SliceInt, SliceInt); | ||||
|   TypeExpr* prefetch_int_op = TypeExpr::new_map(SliceInt, Int); | ||||
|   TypeExpr* store_int_mutate = TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), TypeExpr::new_tensor({Builder, Unit})); | ||||
|   TypeExpr* fetch_slice_op_mutate = TypeExpr::new_map(SliceInt, TypeExpr::new_tensor({Slice, Slice})); | ||||
|   TypeExpr* prefetch_slice_op = TypeExpr::new_map(SliceInt, Slice); | ||||
|   TypeExpr* throw_arg_op = TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({X, Int}), Unit)); | ||||
|   TypePtr Unit = TypeDataVoid::create(); | ||||
|   TypePtr Int = TypeDataInt::create(); | ||||
|   TypePtr Bool = TypeDataBool::create(); | ||||
|   TypePtr Slice = TypeDataSlice::create(); | ||||
|   TypePtr Builder = TypeDataBuilder::create(); | ||||
|   TypePtr Tuple = TypeDataTuple::create(); | ||||
| 
 | ||||
|   define_builtin_func("_+_", arith_bin_op, compile_add, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_-_", arith_bin_op, compile_sub, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("-_", arith_un_op, compile_unary_minus, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("+_", arith_un_op, compile_unary_plus, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_*_", arith_bin_op, compile_mul, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_~/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 0), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_^/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_<<_", arith_bin_op, compile_lshift, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 0), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("!_", arith_un_op, compile_logical_not, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("~_", arith_un_op, compile_bitwise_not, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_&_", arith_bin_op, compile_bitwise_and, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_|_", arith_bin_op, compile_bitwise_or, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_^_", arith_bin_op, compile_bitwise_xor, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_+=_", arith_bin_op, compile_add, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_-=_", arith_bin_op, compile_sub, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_*=_", arith_bin_op, compile_mul, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_<<=_", arith_bin_op, compile_lshift, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_&=_", arith_bin_op, compile_bitwise_and, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_|=_", arith_bin_op, compile_bitwise_or, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("^_^=_", arith_bin_op, compile_bitwise_xor, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_!=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 5), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_<_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 4), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("mulDivFloor", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, -1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("mulDivRound", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 0), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("mulDivCeil", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, _3, 1), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("mulDivMod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("__true", TypeExpr::new_map(TypeExpr::new_unit(), Int), /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("__false", TypeExpr::new_map(TypeExpr::new_unit(), Int), /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("__null", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_unit(), X)), AsmOp::Const("PUSHNULL"), | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("__isNull", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null, | ||||
|                                 SymValFunc::flagMarkedAsPure); | ||||
|   define_builtin_func("__throw", impure_un_op, compile_throw, | ||||
|   std::vector<GenericsDeclaration::GenericsItem> itemsT; | ||||
|   itemsT.emplace_back("T"); | ||||
|   TypePtr typeT = TypeDataGenericT::create("T"); | ||||
|   const GenericsDeclaration* declGenericT = new GenericsDeclaration(std::move(itemsT)); | ||||
| 
 | ||||
|   std::vector ParamsInt1 = {Int}; | ||||
|   std::vector ParamsInt2 = {Int, Int}; | ||||
|   std::vector ParamsInt3 = {Int, Int, Int}; | ||||
|   std::vector ParamsSliceInt = {Slice, Int}; | ||||
| 
 | ||||
|   // builtin operators
 | ||||
|   // they are internally stored as functions, because at IR level, there is no difference
 | ||||
|   // between calling `userAdd(a,b)` and `_+_(a,b)`
 | ||||
|   // since they are registered in a global symtable, technically, they can even be referenced from Tolk code,
 | ||||
|   // though it's a "hidden feature" and won't work well for overloads (`==` for int and bool, for example)
 | ||||
| 
 | ||||
|   // unary operators
 | ||||
|   define_builtin_func("-_", ParamsInt1, Int, nullptr, | ||||
|                               compile_unary_minus, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("+_", ParamsInt1, Int, nullptr, | ||||
|                               compile_unary_plus, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("!_", ParamsInt1, Bool, nullptr, | ||||
|                               std::bind(compile_logical_not, _1, _2, _3, true), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("!b_", {Bool}, Bool, nullptr,   // "overloaded" separate version for bool
 | ||||
|                               std::bind(compile_logical_not, _1, _2, _3, false), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("~_", ParamsInt1, Int, nullptr, | ||||
|                               compile_bitwise_not, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
| 
 | ||||
|   // binary operators
 | ||||
|   define_builtin_func("_+_", ParamsInt2, Int, nullptr, | ||||
|                               compile_add, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_-_", ParamsInt2, Int, nullptr, | ||||
|                               compile_sub, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_*_", ParamsInt2, Int, nullptr, | ||||
|                               compile_mul, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_/_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_div, _1, _2, _3, -1), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_~/_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_div, _1, _2, _3, 0), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_^/_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_div, _1, _2, _3, 1), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_%_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_mod, _1, _2, _3, -1), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_<<_", ParamsInt2, Int, nullptr, | ||||
|                               compile_lshift, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_>>_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_rshift, _1, _2, _3, -1), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_~>>_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_rshift, _1, _2, _3, 0), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_^>>_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_rshift, _1, _2, _3, 1), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_&_", ParamsInt2, Int, nullptr,        // also works for bool
 | ||||
|                               compile_bitwise_and, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_|_", ParamsInt2, Int, nullptr,        // also works for bool
 | ||||
|                               compile_bitwise_or, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_^_", ParamsInt2, Int, nullptr,        // also works for bool
 | ||||
|                               compile_bitwise_xor, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_==_", ParamsInt2, Int, nullptr,       // also works for bool
 | ||||
|                               std::bind(compile_cmp_int, _1, _2, 2), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_!=_", ParamsInt2, Int, nullptr,       // also works for bool
 | ||||
|                               std::bind(compile_cmp_int, _1, _2, 5), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_<_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_cmp_int, _1, _2, 4), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_>_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_cmp_int, _1, _2, 1), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_<=_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_cmp_int, _1, _2, 6), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_>=_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_cmp_int, _1, _2, 3), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("_<=>_", ParamsInt2, Int, nullptr, | ||||
|                               std::bind(compile_cmp_int, _1, _2, 7), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
| 
 | ||||
|   // special function used for internal compilation of some lexical constructs
 | ||||
|   // for example, `throw 123;` is actually calling `__throw(123)`
 | ||||
|   define_builtin_func("__true", {}, Bool, nullptr, /* AsmOp::Const("TRUE") */ | ||||
|                               std::bind(compile_bool_const, _1, _2, true), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("__false", {}, Bool, nullptr, /* AsmOp::Const("FALSE") */ | ||||
|                               std::bind(compile_bool_const, _1, _2, false), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("__null", {}, typeT, declGenericT, | ||||
|                               AsmOp::Const("PUSHNULL"), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("__isNull", {typeT}, Bool, declGenericT, | ||||
|                               compile_is_null, | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("__throw", ParamsInt1, Unit, nullptr, | ||||
|                               compile_throw, | ||||
|                                 0); | ||||
|   define_builtin_func("__throw_arg", throw_arg_op, compile_throw_arg, | ||||
|   define_builtin_func("__throw_arg", {typeT, Int}, Unit, declGenericT, | ||||
|                               compile_throw_arg, | ||||
|                                 0); | ||||
|   define_builtin_func("__throw_if_unless", TypeExpr::new_map(Int3, Unit), compile_throw_if_unless, | ||||
|   define_builtin_func("__throw_if_unless", ParamsInt3, Unit, nullptr, | ||||
|                               compile_throw_if_unless, | ||||
|                                 0); | ||||
|   define_builtin_func("loadInt", fetch_int_op_mutate, std::bind(compile_fetch_int, _1, _2, true, true), | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf, {}, {1, 0}); | ||||
|   define_builtin_func("loadUint", fetch_int_op_mutate, std::bind(compile_fetch_int, _1, _2, true, false), | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf, {}, {1, 0}); | ||||
|   define_builtin_func("loadBits", fetch_slice_op_mutate, std::bind(compile_fetch_slice, _1, _2, true), | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf, {}, {1, 0}); | ||||
|   define_builtin_func("preloadInt", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true), | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagAcceptsSelf); | ||||
|   define_builtin_func("preloadUint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false), | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagAcceptsSelf); | ||||
|   define_builtin_func("preloadBits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false), | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagAcceptsSelf); | ||||
|   define_builtin_func("storeInt", store_int_mutate, std::bind(compile_store_int, _1, _2, true), | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf | SymValFunc::flagReturnsSelf, {1, 0, 2}, {}); | ||||
|   define_builtin_func("storeUint", store_int_mutate, std::bind(compile_store_int, _1, _2, false), | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagHasMutateParams | SymValFunc::flagAcceptsSelf | SymValFunc::flagReturnsSelf, {1, 0, 2}, {}); | ||||
|   define_builtin_func("tupleAt", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at, | ||||
|                                 SymValFunc::flagMarkedAsPure | SymValFunc::flagAcceptsSelf); | ||||
|   define_builtin_func("debugPrint", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Unit)), | ||||
| 
 | ||||
|   // functions from stdlib marked as `builtin`, implemented at compiler level for optimizations
 | ||||
|   // (for example, `loadInt(1)` is `1 LDI`, but `loadInt(n)` for non-constant requires it be on a stack and `LDIX`)
 | ||||
|   define_builtin_func("mulDivFloor", ParamsInt3, Int, nullptr, | ||||
|                               std::bind(compile_muldiv, _1, _2, _3, -1), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("mulDivRound", ParamsInt3, Int, nullptr, | ||||
|                               std::bind(compile_muldiv, _1, _2, _3, 0), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("mulDivCeil", ParamsInt3, Int, nullptr, | ||||
|                               std::bind(compile_muldiv, _1, _2, _3, 1), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("mulDivMod", ParamsInt3, TypeDataTensor::create({Int, Int}), nullptr, | ||||
|                               AsmOp::Custom("MULDIVMOD", 3, 2), | ||||
|                                 FunctionData::flagMarkedAsPure); | ||||
|   define_builtin_func("loadInt", ParamsSliceInt, Int, nullptr, | ||||
|                               std::bind(compile_fetch_int, _1, _2, true, true), | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagHasMutateParams | FunctionData::flagAcceptsSelf, | ||||
|                                 {}, {1, 0}); | ||||
|   define_builtin_func("loadUint", ParamsSliceInt, Int, nullptr, | ||||
|                               std::bind(compile_fetch_int, _1, _2, true, false), | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagHasMutateParams | FunctionData::flagAcceptsSelf, | ||||
|                                 {}, {1, 0}); | ||||
|   define_builtin_func("loadBits", ParamsSliceInt, Slice, nullptr, | ||||
|                               std::bind(compile_fetch_slice, _1, _2, true), | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagHasMutateParams | FunctionData::flagAcceptsSelf, | ||||
|                                 {}, {1, 0}); | ||||
|   define_builtin_func("preloadInt", ParamsSliceInt, Int, nullptr, | ||||
|                               std::bind(compile_fetch_int, _1, _2, false, true), | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagAcceptsSelf); | ||||
|   define_builtin_func("preloadUint", ParamsSliceInt, Int, nullptr, | ||||
|                               std::bind(compile_fetch_int, _1, _2, false, false), | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagAcceptsSelf); | ||||
|   define_builtin_func("preloadBits", ParamsSliceInt, Slice, nullptr, | ||||
|                               std::bind(compile_fetch_slice, _1, _2, false), | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagAcceptsSelf); | ||||
|   define_builtin_func("storeInt", {Builder, Int, Int}, Unit, nullptr, | ||||
|                               std::bind(compile_store_int, _1, _2, true), | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagHasMutateParams | FunctionData::flagAcceptsSelf | FunctionData::flagReturnsSelf, | ||||
|                                 {1, 0, 2}, {}); | ||||
|   define_builtin_func("storeUint", {Builder, Int, Int}, Unit, nullptr, | ||||
|                               std::bind(compile_store_int, _1, _2, false), | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagHasMutateParams | FunctionData::flagAcceptsSelf | FunctionData::flagReturnsSelf, | ||||
|                                 {1, 0, 2}, {}); | ||||
|   define_builtin_func("tupleAt", {Tuple, Int}, typeT, declGenericT, | ||||
|                               compile_tuple_at, | ||||
|                                 FunctionData::flagMarkedAsPure | FunctionData::flagAcceptsSelf); | ||||
|   define_builtin_func("debugPrint", {typeT}, Unit, declGenericT, | ||||
|                                 AsmOp::Custom("s0 DUMP DROP", 1, 1), | ||||
|                                 0); | ||||
|   define_builtin_func("debugPrintString", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Unit)), | ||||
|   define_builtin_func("debugPrintString", {typeT}, Unit, declGenericT, | ||||
|                                 AsmOp::Custom("STRDUMP DROP", 1, 1), | ||||
|                                 0); | ||||
|   define_builtin_func("debugDumpStack", TypeExpr::new_map(Unit, Unit), | ||||
|   define_builtin_func("debugDumpStack", {}, Unit, nullptr, | ||||
|                                 AsmOp::Custom("DUMPSTK", 0, 0), | ||||
|                                 0); | ||||
| } | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| */ | ||||
| #include "tolk.h" | ||||
| #include "compiler-state.h" | ||||
| #include "type-system.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
|  | @ -314,7 +315,7 @@ bool Op::generate_code_step(Stack& stack) { | |||
|       return true; | ||||
|     } | ||||
|     case _GlobVar: | ||||
|       if (dynamic_cast<const SymValGlobVar*>(fun_ref->value)) { | ||||
|       if (g_sym) { | ||||
|         bool used = false; | ||||
|         for (auto i : left) { | ||||
|           auto p = next->var_info[i]; | ||||
|  | @ -325,8 +326,7 @@ bool Op::generate_code_step(Stack& stack) { | |||
|         if (!used || disabled()) { | ||||
|           return true; | ||||
|         } | ||||
|         std::string name = G.symbols.get_name(fun_ref->sym_idx); | ||||
|         stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1); | ||||
|         stack.o << AsmOp::Custom(g_sym->name + " GETGLOB", 0, 1); | ||||
|         if (left.size() != 1) { | ||||
|           tolk_assert(left.size() <= 15); | ||||
|           stack.o << AsmOp::UnTuple((int)left.size()); | ||||
|  | @ -343,25 +343,28 @@ bool Op::generate_code_step(Stack& stack) { | |||
|         } | ||||
|         stack.o << "CONT:<{"; | ||||
|         stack.o.indent(); | ||||
|         auto func = dynamic_cast<SymValAsmFunc*>(fun_ref->value); | ||||
|         if (func) { | ||||
|         if (f_sym->is_asm_function() || f_sym->is_builtin_function()) { | ||||
|           // TODO: create and compile a true lambda instead of this (so that arg_order and ret_order would work correctly)
 | ||||
|           std::vector<VarDescr> args0, res; | ||||
|           TypeExpr::remove_indirect(func->sym_type); | ||||
|           tolk_assert(func->get_type()->is_map()); | ||||
|           auto wr = func->get_type()->args.at(0)->get_width(); | ||||
|           auto wl = func->get_type()->args.at(1)->get_width(); | ||||
|           tolk_assert(wl >= 0 && wr >= 0); | ||||
|           for (int i = 0; i < wl; i++) { | ||||
|           int w_arg = 0; | ||||
|           for (const LocalVarData& param : f_sym->parameters) { | ||||
|             w_arg += param.declared_type->calc_width_on_stack(); | ||||
|           } | ||||
|           int w_ret = f_sym->inferred_return_type->calc_width_on_stack(); | ||||
|           tolk_assert(w_ret >= 0 && w_arg >= 0); | ||||
|           for (int i = 0; i < w_ret; i++) { | ||||
|             res.emplace_back(0); | ||||
|           } | ||||
|           for (int i = 0; i < wr; i++) { | ||||
|           for (int i = 0; i < w_arg; i++) { | ||||
|             args0.emplace_back(0); | ||||
|           } | ||||
|           func->compile(stack.o, res, args0, where);  // compile res := f (args0)
 | ||||
|           if (f_sym->is_asm_function()) { | ||||
|             std::get<FunctionBodyAsm*>(f_sym->body)->compile(stack.o);  // compile res := f (args0)
 | ||||
|           } else { | ||||
|             std::get<FunctionBodyBuiltin*>(f_sym->body)->compile(stack.o, res, args0, where);  // compile res := f (args0)
 | ||||
|           } | ||||
|         } else { | ||||
|           std::string name = G.symbols.get_name(fun_ref->sym_idx); | ||||
|           stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size()); | ||||
|           stack.o << AsmOp::Custom(f_sym->name + " CALLDICT", (int)right.size(), (int)left.size()); | ||||
|         } | ||||
|         stack.o.undent(); | ||||
|         stack.o << "}>"; | ||||
|  | @ -438,10 +441,9 @@ bool Op::generate_code_step(Stack& stack) { | |||
|       if (disabled()) { | ||||
|         return true; | ||||
|       } | ||||
|       // fun_ref can be nullptr for Op::_CallInd (invoke a variable, not a function)
 | ||||
|       SymValFunc* func = (fun_ref ? dynamic_cast<SymValFunc*>(fun_ref->value) : nullptr); | ||||
|       auto arg_order = (func ? func->get_arg_order() : nullptr); | ||||
|       auto ret_order = (func ? func->get_ret_order() : nullptr); | ||||
|       // f_sym can be nullptr for Op::_CallInd (invoke a variable, not a function)
 | ||||
|       const std::vector<int>* arg_order = f_sym ? f_sym->get_arg_order() : nullptr; | ||||
|       const std::vector<int>* ret_order = f_sym ? f_sym->get_ret_order() : nullptr; | ||||
|       tolk_assert(!arg_order || arg_order->size() == right.size()); | ||||
|       tolk_assert(!ret_order || ret_order->size() == left.size()); | ||||
|       std::vector<var_idx_t> right1; | ||||
|  | @ -455,14 +457,12 @@ bool Op::generate_code_step(Stack& stack) { | |||
|             right1.push_back(arg.idx); | ||||
|           } | ||||
|         } | ||||
|       } else if (arg_order) { | ||||
|         for (int i = 0; i < (int)right.size(); i++) { | ||||
|           right1.push_back(right.at(arg_order->at(i))); | ||||
|         } | ||||
|       } else { | ||||
|         tolk_assert(!arg_order); | ||||
|         right1 = right; | ||||
|       } | ||||
|       std::vector<bool> last; | ||||
|       last.reserve(right1.size()); | ||||
|       for (var_idx_t x : right1) { | ||||
|         last.push_back(var_info[x] && var_info[x]->is_last()); | ||||
|       } | ||||
|  | @ -488,23 +488,25 @@ bool Op::generate_code_step(Stack& stack) { | |||
|       }; | ||||
|       if (cl == _CallInd) { | ||||
|         exec_callxargs((int)right.size() - 1, (int)left.size()); | ||||
|       } else if (auto asm_fv = dynamic_cast<const SymValAsmFunc*>(fun_ref->value)) { | ||||
|       } else if (!f_sym->is_code_function()) { | ||||
|         std::vector<VarDescr> res; | ||||
|         res.reserve(left.size()); | ||||
|         for (var_idx_t i : left) { | ||||
|           res.emplace_back(i); | ||||
|         } | ||||
|         asm_fv->compile(stack.o, res, args, where);  // compile res := f (args)
 | ||||
|         if (f_sym->is_asm_function()) { | ||||
|           std::get<FunctionBodyAsm*>(f_sym->body)->compile(stack.o);  // compile res := f (args)
 | ||||
|         } else { | ||||
|           std::get<FunctionBodyBuiltin*>(f_sym->body)->compile(stack.o, res, args, where);  // compile res := f (args)
 | ||||
|         } | ||||
|       } else { | ||||
|         auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value); | ||||
|         std::string name = G.symbols.get_name(fun_ref->sym_idx); | ||||
|         if (fv->is_inline() || fv->is_inline_ref()) { | ||||
|           stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size()); | ||||
|         } else if (fv->code && fv->code->require_callxargs) { | ||||
|           stack.o << AsmOp::Custom(name + (" PREPAREDICT"), 0, 2); | ||||
|         if (f_sym->is_inline() || f_sym->is_inline_ref()) { | ||||
|           stack.o << AsmOp::Custom(f_sym->name + " INLINECALLDICT", (int)right.size(), (int)left.size()); | ||||
|         } else if (f_sym->is_code_function() && std::get<FunctionBodyCode*>(f_sym->body)->code->require_callxargs) { | ||||
|           stack.o << AsmOp::Custom(f_sym->name + (" PREPAREDICT"), 0, 2); | ||||
|           exec_callxargs((int)right.size() + 1, (int)left.size()); | ||||
|         } else { | ||||
|           stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size()); | ||||
|           stack.o << AsmOp::Custom(f_sym->name + " CALLDICT", (int)right.size(), (int)left.size()); | ||||
|         } | ||||
|       } | ||||
|       stack.s.resize(k); | ||||
|  | @ -515,7 +517,7 @@ bool Op::generate_code_step(Stack& stack) { | |||
|       return true; | ||||
|     } | ||||
|     case _SetGlob: { | ||||
|       tolk_assert(fun_ref && dynamic_cast<const SymValGlobVar*>(fun_ref->value)); | ||||
|       tolk_assert(g_sym); | ||||
|       std::vector<bool> last; | ||||
|       for (var_idx_t x : right) { | ||||
|         last.push_back(var_info[x] && var_info[x]->is_last()); | ||||
|  | @ -534,8 +536,7 @@ bool Op::generate_code_step(Stack& stack) { | |||
|         stack.o << AsmOp::Tuple((int)right.size()); | ||||
|       } | ||||
|       if (!right.empty()) { | ||||
|         std::string name = G.symbols.get_name(fun_ref->sym_idx); | ||||
|         stack.o << AsmOp::Custom(name + " SETGLOB", 1, 0); | ||||
|         stack.o << AsmOp::Custom(g_sym->name + " SETGLOB", 1, 0); | ||||
|       } | ||||
|       stack.s.resize(k); | ||||
|       return true; | ||||
|  | @ -826,6 +827,8 @@ bool Op::generate_code_step(Stack& stack) { | |||
|       catch_stack.push_new_var(left[1]); | ||||
|       stack.rearrange_top(catch_vars, catch_last); | ||||
|       stack.opt_show(); | ||||
|       stack.o << "c1 PUSH"; | ||||
|       stack.o << "c3 PUSH"; | ||||
|       stack.o << "c4 PUSH"; | ||||
|       stack.o << "c5 PUSH"; | ||||
|       stack.o << "c7 PUSH"; | ||||
|  | @ -842,6 +845,8 @@ bool Op::generate_code_step(Stack& stack) { | |||
|       stack.o << "c7 SETCONT"; | ||||
|       stack.o << "c5 SETCONT"; | ||||
|       stack.o << "c4 SETCONT"; | ||||
|       stack.o << "c3 SETCONT"; | ||||
|       stack.o << "c1 SETCONT"; | ||||
|       for (size_t begin = catch_vars.size(), end = begin; end > 0; end = begin) { | ||||
|         begin = end >= block_size ? end - block_size : 0; | ||||
|         stack.o << std::to_string(end - begin) + " PUSHINT"; | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue