diff --git a/CHANGELOG.md b/CHANGELOG.md index e3bf3d9..445e67f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [v2.0](https://github.com/deleterium/SmartC/tree/v2.0) (2022-07-03) + +[Commits](https://github.com/deleterium/SmartC/commits/v2.0) + +- **Support all new features from Signum Rainbow Hard Fork** +- New 42 built-in functions: **easy use of Signum API** +- Documentation updated with refactored examples, devs must read it again +- **Fixed point numbers** to handle balance, much easier in calculations +- Checks for **type castings** are stronger and issuing warnings for implicit conversions +- Optimization level 3 uses VM to trace variables values (beta version, not default) +- Showing many errors after failed compilations (if possible) +- Many changes in `#pragma` and `#program` to allow **integration with SC-Simulator** + ## [v1.0](https://github.com/deleterium/SmartC/tree/v1.0) (2022-01-16) [Commits](https://github.com/deleterium/SmartC/commits/v1.0) diff --git a/README.md b/README.md index 1d67305..c0abc56 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,16 @@ This library can be obtained through npm: npm install smartc-signum-compiler ``` +The stable version is released under tag `@latest` and the development under `@next`. + # Usage + +## Web User Interface +A web user interface project is available at https://github.com/deleterium/smartc-web-ui If you want just to code with SmartC use https://deleterium.info/SmartC/ + +## Documentation / FAQ / Lessons +Docs files can be found in this repo, at `doc` folder. + ## Node ```ts import { SmartC } from 'smartc-signum-compiler'; @@ -38,7 +47,7 @@ try { ## Browser Import the minified javascript file. SmartC will be imported as global. ```html - + ``` Then in your javascript file, just use it: @@ -58,12 +67,6 @@ try { } ``` -## Web User Interface -To be done - -## Documentation / FAQ / Lessons -Docs files can be found in this repo, at `doc` folder. - ## Changelog Find [here](https://deleterium.github.io/SmartC/CHANGELOG) major upgrades between releases. diff --git a/debug.html b/debug.html new file mode 100644 index 0000000..936f307 --- /dev/null +++ b/debug.html @@ -0,0 +1,106 @@ + + + + + + + + + Simple debugger page + + + + +
Simple debugger page
+

Use this page to debug or inspect SmartC process, using a browser.

+
+
+
+
+
+
+

+                

+                

+            
+
+
+ + \ No newline at end of file diff --git a/docs/1-Basis.md b/docs/1-Basis.md index ebf018c..ba81270 100644 --- a/docs/1-Basis.md +++ b/docs/1-Basis.md @@ -1,4 +1,4 @@ -[Back](./) +[Back](./README.md) ## Language rules This project aims to be as close to C as possible. But given special characteristics in Signum assembly language (derived from CIYAM) some differences will occur. @@ -14,51 +14,23 @@ Some keywords have the same meaning and use in C: `asm`, `break`, `continue`, `d * `case`: The standard C is fully supported, featuring also that expressions can be added using parenthesis. If standard switch is used, an expression can be used: `switch (a) { case (b/2): ... }` and it will be evaluated as `if (a == b/2) ... `. If the logical switch statement is used, then it is possible to add a logical expression in `case` using parenthesis, in the form `switch (true) { case (a>5): ...}` and it will be evaluated as `if (a>5) ...` There are also additional keywords: -* `sleep N`: Puts the contract in 'sleep' mode during N blocks. Argument N must be specified and can be an expression. `sleep 1;` makes your contract to stop being processed at current block and resumes it at next one. -* `exit`: Puts the contract in 'stop' mode and set program to restart from main function ('finished' mode). It will be inactive until a new transaction is received. Once a tx is received, it will start execution at `void main(void)` function. If main function is not defined, it will start again from beginning of code. `exit` takes no argument. If contract activation amount is zero, contract will resume execution on next block. +* `fixed`: Declares a variable that is a fixed point number. All fixed numbers have 8 decimals, so it is handyful to make Signa calculations with it. It supports positives values from 0.00000001 to 92,233,720,368.54775807 and negative from -0.00000001 to -92,233,720,368.54775808. They are internally a signed 64-bit number. +* `sleep`: Puts the contract in 'sleep' mode and resumes contract execution on next block. Alternativelly it can have an argument to indicate the number of blocks to sleep and the argument can be an expression. This tree sentences have the same result `sleep;`, `sleep 0;`, and `sleep 1;`, but it is prefered the first one because the instruction is smaller. +* `exit`: Puts the contract in 'stop' mode and set program to restart from main function ('finished' mode). It will be inactive until a new transaction is received. Once a tx is received, it will start execution at `void main()` function. If main function is not defined, the execution will start again from beginning of code, running again all global statements. If the main function is defined, the global statements will be executed only in the first activations of the contract. `exit` takes no argument. If contract activation amount is zero, contract will resume execution on next block (similar to `sleep`). * `halt`: Puts the contract in 'stop' mode. It will be inactive until a new transaction is received, then it will resume execution at next instruction. It takes no argument. If contract activation amount is zero, contract will resume execution on next block. Others keyword have no assembly support. They are disabled: `auto`, `double`, `float`, `register`, `volatile`. For future implementation these keywords can be added: `char`, `enum`, `extern`, `int`, `short`, `signed`, `static`, `typedef`, `union`, `unsigned`. ### Preprocessor -Some special features can be enabled/disable via preprocessor directives: -#### #program -* `#program name YourProgramName`: Set program's name. Only regular letters and numbers allowed, max 30 chars in length. -* `#program description Your program description`: Set program's description. No new lines and max length is 1000 chars. -* `#program activationAmount 100000000`: Set program's activation amount in NQT (1 Signum = 100000000 NQT). If an incoming transaction has an amount is less than this value, it will not be processed by program (but the amount will be received!). Set a low value but bigger than worst case amount needed to run in your program. If set too low, your program will be frozen during execution (out of gas). If set too high, program balance will be high after execution (unburned balance). Remember to handle this case if creating serious program! -#### #include -* `#include APIFunctions [true/false/1/0/]`: Make Signum API functions available for use as functions. Default value is `false`. It can be enabled by declaring it with empty argument, `true` or `1`. Function names follow the [ciyam at documentation](https://ciyam.org/at/at_api.html). All API names and a pseudo-code are avaliable also in section **API Pseudo-Code**. -#### #define -* `#define CNAME`: Just define CNAME with an empty value, or delete its content if it was previously defined. -* `#define CNAME value or expression`: Replaces all ocurrences of 'CNAME' to 'value or expression' starting on next line. Compiler defines: `true` for 1; `false` and `NULL` for 0; `SMARTC` with empty value. -* `#undef CNAME`: Undefine CNAME. -* `#ifdef CNAME`: Start a block to be included if CNAME is defined. Note that CNAME value does not matter and can be empty value. Block must end with a '#endif' directive. Blocks can be nested. -* `#ifndef CNAME`: Complementary of '#ifdef'. Includes a block if CNAME is not defined. -* `#else`: Can be used with '#ifdef' or '#ifndef' to toggle the addition of some source code block. -* `#endif`: Ends a block to be included. - -#### #pragma -* `#pragma codeStackPages N`: Code pages are used during function calls, to store the instruction pointer return position (also know as Program Counter). Default value is zero if not needed, or one if needed. Every page allows to store 16 values. Tweak this value if using many nested functions or recursive functions. Maximum value is 10 pages. -* `#pragma userStackPages N`: User pages are used during function calls to pass arguments values, to store function return value, or to store function scope variables during recursive calls. Default value is zero if not needed, or one if needed. Tweak this value if using more than 16 arguments on functions or recursive functions. Maximum value is 10 pages. -* `#pragma enableRandom [true/false/1/0/]`: Makes labels for jumps and conditionals receive a random value. Default value is `false`. Default behaviour is labels having an increasing number starting with 1 (number is base 36). -* `#pragma enableLineLabels [true/false/1/0/]`: Adds line number to labels in assembly. Only usefull for debug purposes. Default value is `false`. -* `#pragma maxAuxVars N`: Used to tell compiler how many auxiliary variables will be available (they are used as registers). Default value is `3`, min value is `1` and max is `10`. If you are under memory pressure, try to reduce to minimal necessary for compiling. Simple contracts will use around 2 values, but this number depends on nested operations. -* `#pragma maxConstVars N`: Compiler will create variable from 1 to maxConstVars. Variables will be named 'n1', 'n2', ... 'n10'. It is very usefull to use, because compiler will change all numbers references to these variables and optimize code, making code much much smaller! Default min value is `0` (deactivated) and max is `10`. -* `#pragma optimizationLevel N`: Choose strategy for code optimizer. It can be between 0 and 3. - * 0: No optimization. - * 1: Very basic optimization, just remove silly and unused code. - * 2: **Default**. Safely change and/or delete code for smarter outcome. - * 3: Dangerous optimizations no well tested. Result must be inspected by developer. -* `#pragma reuseAssignedVar [true/false/1/0/]`: When set, compiler will try to use a variable on left side of and `Assignment` as a register. If variable is also used on right side, the compiler will not reuse it. This can save one assembly instruction for every expression used! Default value is `true` and it is highly recomended to maintain it active. -* `#pragma version N`: Informs which compiler's version the code was developed. Must be set if not using development version. To skip this check, set it to `dev`. -* `#pragma warningToError [true/false/1/0/]`: All warnings to compiler errors. Default value is `true`. Warning messages begin with WARNING, other ones are actually errors. -* `#pragma outputSourceLineNumber[true/false/1/0/]`: Adds a comment in assembly output with the corresponding line number to the C source code. Very usefull for debug. - -#### Escaping new line -Just end a line with `\` and it will be joined to the next one. It can be used anywhere, even inside "strings". Usefull for multiline program description, #define for a function, or in middle of a variable name! +Some special features can be enabled/disable via preprocessor directives. Check chapter 1.2. + ### Variables -At the moment, only `long` values are implemented. User can assign them with decimal values (default) (floating point not allowed) `i=2;`, hexadecimal values `i=0xff;`, strings (up to 8 bytes) `msg="Hello!";` or Signum addresses `addr="S-297Z-EKMN-4AVV-7YWXP";` (also valid starting with BURST or TS). Long values can be assigned during their declaration. +Variables can be `long`, `fixed` or pointers. User can assign them: +* With decimal values: `i=2;` for longs or `i=2.0;` for fixeds, +* Hexadecimal values: `i=0xff;` for longs and not allowed for fixeds +* Strings (up to 8 bytes) `msg="Hello!";` or Signum addresses `addr="S-297Z-EKMN-4AVV-7YWXP";` (also valid starting with BURST or TS). +Variables can be assigned during their declaration. Arrays can be declared but can be initialized only at a later instruction. Declaration of an array with 5 elements (0 to 4): `long arr[5];`. Use as in C: `arr[1]=4;`. Multi-long values can be set `arr[]='This is a text message';`. To clear the entire array: `arr[]=0;`. It is possible to get an array length a member operation: `size = arr.length;` Structs use same notation in C. Structs pointers can also be used. To access a member, use `.` or `->` depending if struct is already allocated in memory or if it is a pointer to the memory location. Arrays of structs, arrays inside structs and recursive pointer definition are also supported. All variables are initialized with value `0` at the first time the contract is executed, unless other value is set by `const` statement. @@ -66,10 +38,27 @@ All variables are similar to `static` in C. So every time a function is called o Global variables are available in all functions. Functions variables can only be used inside the function. Variables declarations can be inside other sentences, like `for (long i; i<10; i++)` or `if (a){ long i=0; ...}`, but their scope can only be 'global' or 'function', in other words, the result is the same as declaring variables at the program start (if global variables) or at the function start (if function variables). +### Implicit types casting +The compiler will convert numbers and variables between fixed and long if used in binary operators. Most of times long will be converted to fixed, unless there is an assigment and the left side is long. In this case (=, +=, -=, ...) fixed values will be transformed into long. Data can be lost in this transformation, so keep an eye on it! + +### Explict types casting +Same as in C `fixedVariable = (fixed)longVariable`. The compiler will make the transformation. It is also possible to use some built-in functions if the desired transformation is just to copy the value in memory (memcopy) or use in arguments for functions (bcftol). Check chapter 1.5 Built-in functions. + ### Functions -As avaliable in C, the developer can make use of functions to make coding easier or reuse code from other projects. There is no need to put function prototypes at the beginning, the function can be used before it is declared, because their definitions are collected a step before the compiling process. Functions arguments and return values are passed using user stack. Recursive functions are allowed but developer must set manually and carefully a new size for "user stack pages" thru macro definition. There are two special functions: `void main(void)` explained before and `void catch(void)` explained at **Contract states** topic. It is not obligatory to use them. +As avaliable in C, the developer can make use of functions to make coding easier or reuse code from other projects. There is no need to put function prototypes at the beginning, the function can be used before it is declared, because their definitions are collected a step before the compiling process. Functions arguments and return values are passed using user stack. Recursive functions are allowed but developer must set manually and carefully a new size for "user stack pages" thru preprocessor directives. There are two special functions: `void main()` explained before and `void catch()` explained at **Contract states** topic. It is not obligatory to use them. Functions can return also arrays and structs; the returning values can be used directly: example `if ( arrFn(a)[2] == 25 )` or `b = structFn(a)->value;` +### Built-in functions +From SmartC version 2.0 built-in functions were introduced. +No declaration is needed to use them. +They are coded to hide some internal components of Signum machine code and make the development easier. +Read the chapter (1.5-Built-in functions.md)[./1.5-Built-in-functions.md] but just after reading this one. + +### API functions +Low level functions to handle assembly level superregister. +Only needed for very complex contracts or to hand-picked optimizations code. +Read the chapter (2-API Pseudo Code.md)[./2-API-Pseudo-Code.md] only if you plan to program at low level. + ### Global statements All global statements are grouped at the beginning of assembly code (even if after functions or end of file). When the contract is executed first time, it does not begin at main function, but will start at the beginning of file and run all global statements. If there is a main function, it will be then executed during this first run. If you stop execution in global statements (with `exit`), the main function will not be processed and the starting point for next transactions will be the start of code. In this case (not using main function) use `halt` keyword to wait next transaction. @@ -77,10 +66,12 @@ All global statements are grouped at the beginning of assembly code (even if aft * Finished: Contract execution ended at a `exit` instruction or at the end of 'main' function. On next activation it will start at 'main'. * Stopped: Contract execution ended at a `halt` of `sleep` instruction. On next resume it will start just after current point. * Frozen: Execution was suspended because there was no more balance in contract account (no gas!). To resume execution, contract must receive a new transaction with an amount greater or equal its minimum activation. If transaction is below this amount, it will stay frozen even with some balance. -* Dead: Execution raised one of these exceptions: 1) division by zero; 2) trying to read/set a variable outside memory range; or 3) stack overflow/underflow for user/code stack. The default behaviour is all contract balance to be distributed as fee for current block forger. Also any next transaction to that dead contract will be transformed in fee. To avoid this situation, it is possible to define a special function `void catch(void)`. When the exception is found, the execution will jump to 'catch' function and a new entry point for next incoming transactions will be set. A use case for 'catch' function is to send all balance to creator to avoid losing contract balance. When using 'catch' function the contract will never reach dead state. +* Dead: Execution raised one of these exceptions: 1) division by zero; 2) trying to read/set a variable outside memory range; or 3) stack overflow/underflow for user/code stack. The default behaviour is all contract balance to be distributed as fee for current block forger. Also any next transaction to that dead contract will be transformed in fee. To avoid this situation, it is possible to define a special function `void catch()`. When the exception is found, the execution will jump to 'catch' function and a new entry point for next incoming transactions will be set. A use case for 'catch' function is to send all balance to creator to avoid losing contract balance. When using 'catch' function the contract will never reach dead state. ### Designing tips -If you plan to use a number many times, declare it globally with `const` keyword and name it with `nVALUE`: example `const long n65535=65535`. This can save one instruction for each use and also make your code smaller. But if you use it only a few times, or is under memory pressure, you can use constants at your code but making machine code bigger. For big programs it is more common be under codesize pressure, so this is a great exchange. The exception is Zero. Setting a variable to zero has an special assembly code. Comparisons against zero are also smaller than comparisons against variables. Comparisons against numbers are long assembly instrunctions. Try it to see assembly code genereated! If you are under memory pressure (or want to code smallest code possible) use global variables, because exchanging variables thru functions will cause they to be declared twice, pushed onto stack and popped at function. +If you plan to use a number many times, declare it globally with `const` keyword and name it with `nVALUE`: example `const long n65535=65535`. +If the value is fixed, use the name with all decimals: example `const fixed f2000001=.02000001`. +This can save one instruction for each use and also make your code smaller. But if you use it only a few times, or is under memory pressure, you can use constants at your code but making machine code bigger. For big programs it is more common be under codesize pressure, so this is a great exchange. The exception is Zero. Setting a variable to zero has an special assembly code. Comparisons against zero are also smaller than comparisons against variables. Comparisons against numbers are long assembly instrunctions. Try it to see assembly code genereated! If you are under memory pressure (or want to code smallest code possible) use global variables, because exchanging variables thru functions will cause they to be declared twice, pushed onto stack and popped at function. ### Main differences from C * signed or unsigned: There is no difference between signed and unsigned longs. The rule is that all values behave as signed when comparing values or during arithmetic operations, but treated as unsigned during bit operations. Keep this in mind if developing with gcc. @@ -92,7 +83,6 @@ If you plan to use a number many times, declare it globally with `const` keyword * register: By default there are 3, from r0..r2. They can be used without declaration, but inspect assembly code to ensure they are not changed during other instructions. Different from registers in modern CPUs, these registers in SmartC are just regular variables created and used by compiler. ## Notes -* Run testcases to check tested operations. It shall be no failed cases. * Please report a bug if any strange behavior is found. -[Back](./) +[Back](./README.md) diff --git a/docs/1.2-Preprocessor-directives.md b/docs/1.2-Preprocessor-directives.md new file mode 100644 index 0000000..d875b32 --- /dev/null +++ b/docs/1.2-Preprocessor-directives.md @@ -0,0 +1,183 @@ +[Back](./README.md) + +# Preprocessor +Some special features can be enabled/disable via preprocessor directives. + +## #program +* `#program name YourProgramName`: Set program's name. Only regular letters and numbers allowed, max 30 chars in length. +A value is mandatory for deployment. +* `#program description Your program description`: Set program's description. No new lines and max length is 1000 chars. +This is optional. +* `#program activationAmount VALUE`: Set program's activation amount. If VALUE is fixed point (Example: `.34`), it is used as Signa amount. If not, the value will be set in NQT (Example: `3400_0000`). If an incoming transaction has an amount is less than this value, it will not be processed by program (but the amount will be received!). Set a low value but bigger than worst case amount needed to run in your program. If set too low, your program will be frozen during execution (out of gas). If set too high, program balance will be high after execution (unspent balance). Remember to handle this case if creating serious program! +A value is mandatory for deployment. +* `#program codeHashId N`: Ensure the compiled program will have this exact code hash id. +Use `0` to make this information available at assembly output (during development). +Use the actual number if you plan do distribute the source code, so the compiler will raise an error on divergency. +This is optional. +* `#program creator N`: Valid only in SC-Simulator. N must be decimal number. When set, this will set the creator ID of the contract. Use to simulate many contracts deployed from diferent users. It is ignored in machine code generation or during actual deployment. +* `#program contract N`: Valid only in SC-Simulator. N must be decimal number. When set, this will set the contract ID. Use if deploying many contracts, then the deployment can be made in any order. It is ignored in machine code generation or during actual deployment. +* `#program codeStackPages N`: Code pages are used during function calls, to store the instruction pointer return position (also know as Program Counter). Default value is zero if not needed, or one if needed. Every page allows to store 16 values. Tweak this value if using many nested functions or recursive functions. Maximum value is 10 pages. +* `#program userStackPages N`: User pages are used during function calls to pass arguments values, to store function return value, or to store function scope variables during recursive calls. Default value is zero if not needed, or one if needed. Tweak this value if using more than 16 arguments on functions or recursive functions. Maximum value is 10 pages. + +## #include + +### API functions +* `#include APIFunctions [true/false/1/0/]`: Make Signum API functions available for use as functions. Default value is `false`. It can be enabled by declaring it with empty argument, `true` or `1`. Function names follow the [ciyam at documentation](https://ciyam.org/at/at_api.html). All API names and a pseudo-code are avaliable also in section **API Pseudo-Code**. The prototypes of available functions are: + +```c +#define APIFunctions + +// Get/Set functions for “pseudo registers” +long Get_A1(void); +long Get_A2(void); +long Get_A3(void); +long Get_A4(void); +long Get_B1(void); +long Get_B2(void); +long Get_B3(void); +long Get_B4(void) +void Set_A1(long); +void Set_A2(long); +void Set_A3(long); +void Set_A4(long); +void Set_A1_A2(long); +void Set_A3_A4(long); +void Set_B1(long); +void Set_B2(long); +void Set_B3(long); +void Set_B4(long); +void Set_B1_B2(long); +void Set_B3_B4(long); +void Clear_A(void); +void Clear_B(void); +void Clear_A_And_B(void); +void Copy_A_From_B(void); +void Copy_B_From_A(void); +long Check_A_Is_Zero(void); +long Check_B_Is_Zero(void); +long Check_A_Equals_B(void); +void Swap_A_and_B(void); +void OR_A_with_B(void); +void OR_B_with_A(void); +void AND_A_with_B(void); +void AND_B_with_A(void); +void XOR_A_with_B(void); +void XOR_B_with_A(void); +void Add_A_To_B(void); +void Add_B_To_A(void); +void Sub_A_From_B(void); +void Sub_B_From_A(void); +void Mul_A_By_B(void); +void Mul_B_By_A(void); +void Div_A_By_B(void); +void Div_B_By_A(void); + +// Functions that perform hash operations +void MD5_A_To_B(void); +long Check_MD5_A_With_B(void); +void HASH160_A_To_B(void); +long Check_HASH160_A_With_B(void); +void SHA256_A_To_B(void); +long Check_SHA256_A_With_B(void); +long Check_Sig_B_With_A(void); + +// Generic functions that get block and tx info +long Get_Block_Timestamp(void); +long Get_Creation_Timestamp(void); +long Get_Last_Block_Timestamp(void); +void Put_Last_Block_Hash_In_A(void); +void A_To_Tx_After_Timestamp(long) +long Get_Type_For_Tx_In_A(void); +long Get_Amount_For_Tx_In_A(void); +long Get_Timestamp_For_Tx_In_A(void); +long Get_Random_Id_For_Tx_In_A(void); +void Message_From_Tx_In_A_To_B(void); +void B_To_Address_Of_Tx_In_A(void); +void B_To_Address_Of_Creator(void); +long Get_Code_Hash_Id(void); +void B_To_Assets_Of_Tx_In_A(void); + +// Generic functions that check balances and perform ops +long Get_Current_Balance(void); +long Get_Previous_Balance(void); +void Send_To_Address_In_B(long); +void Send_All_To_Address_In_B(void); +void Send_Old_To_Address_In_B(void); +void Send_A_To_Address_In_B(void); +long Add_Minutes_To_Timestamp(long, long); +long Get_Map_Value_Keys_In_A(void); +void Set_Map_Value_Keys_In_A(void); +long Issue_Asset(void); +void Mint_Asset(void); +void Distribute_To_Asset_Holders(void); +long Get_Asset_Holders_Count(void); +long Get_Asset_Circulating(void); +long Get_Activation_Fee(void); +void Put_Last_Block_GSig_In_A(void); +``` + +### Fixed API functions +* `#include fixedAPIFunctions [true/false/1/0/]`: Make the fixed numbers versions of Signum API functions available for use. Default value is `false`. It can be enabled by declaring it with empty argument, `true` or `1`. Function names are similar to the regular versions, but prepended with 'F_'. The prototypes of available functions are: + +```c +#define fixedAPIFunctions + +fixed F_Get_A1(void); +fixed F_Get_A2(void); +fixed F_Get_A3(void); +fixed F_Get_A4(void); +fixed F_Get_B1(void); +fixed F_Get_B2(void); +fixed F_Get_B3(void); +fixed F_Get_B4(void); +void F_Set_A1(fixed); +void F_Set_A2(fixed); +void F_Set_A3(fixed); +void F_Set_A4(fixed); +void F_Set_B1(fixed); +void F_Set_B2(fixed); +void F_Set_B3(fixed); +void F_Set_B4(fixed); + +fixed F_Get_Amount_For_Tx_In_A(void); +fixed F_Get_Current_Balance(void); +fixed F_Get_Previous_Balance(void); +void F_Send_To_Address_In_B(fixed); + +fixed F_Get_Map_Value_Keys_In_A(void); +fixed F_Get_Activation_Fee(void); +fixed F_Get_Asset_Circulating(void); +``` + +## #define +Preprocessor definitions will change code before compilation. +* `#define CNAME`: Just define CNAME with an empty value, or delete its content if it was previously defined. +* `#define CNAME value or expression`: Replaces all ocurrences of 'CNAME' to 'value or expression' starting on next line. Compiler defines: `true` for 1; `false` and `NULL` for 0; `SMARTC` with empty value. +* `#define MACRO(arguments) (expression)`: Replaces all ocurrences of 'MACRO' to 'expression', starting on next line. +It works similar to a function, but no function call is executed. +Must be one line instruction (or use `\` at the end of line to escape the newline char). +Many arguments can be used. +* `#undef CNAME`: Undefine CNAME. +* `#ifdef CNAME`: Start a block to be included if CNAME is defined. Note that CNAME value does not matter and can be empty value. Block must end with a '#endif' directive. Blocks can be nested. +* `#ifndef CNAME`: Complementary of '#ifdef'. Includes a block if CNAME is not defined. +* `#else`: Can be used with '#ifdef' or '#ifndef' to toggle the addition of some source code block. +* `#endif`: Ends a block to be included. + +## #pragma +Special features used by compiler. +* `#pragma maxAuxVars N`: Used to tell compiler how many auxiliary variables will be available (they are used as registers). Default value is `3`, min value is `0` and max is `10`. If you are under memory pressure, try to reduce to minimal necessary for compiling. Simple contracts will use around 2 values, but this number depends on nested operations. +* `#pragma maxConstVars N`: Compiler will create variable from 1 to maxConstVars. Variables will be named 'n1', 'n2', ... 'n10'. It is very usefull to use, because compiler will change all numbers references to these variables and optimize code, making code much much smaller! Default min value is `0` (deactivated) and max is `10`. +* `#pragma optimizationLevel N`: Choose strategy for code optimizer. It can be between 0 and 3. + * 0: No optimization. + * 1: Very basic optimization, just remove silly and unused code. + * 2: **Default**. Safely change and/or delete code for smarter outcome. + * 3: Use a VM to trace variable's content and remove redundant code. Beta feature, to be included as default once more tests are done. Can generate a good optimization reducing the number of calls to API functions. + * 4: Dangerous optimizations no well tested. Result must be inspected by developer. +* `#pragma reuseAssignedVar [true/false/1/0/]`: When set, compiler will try to use a variable on left side of and `Assignment` as a register. If variable is also used on right side, the compiler will not reuse it. This can save one assembly instruction for every expression used! Default value is `true` and it is highly recomended to maintain it active. +* `#pragma version VALUE`: Informs which compiler's version the code was developed. This is optional but can help future generations. VALUE can be any string or remarks. +* `#pragma verboseAssembly [true/false/1/0/]`: Adds a comment in assembly output with the corresponding line number and the source code. Very usefull for debug. + +### Escaping new line +Just end a line with `\` and it will be joined to the next one. It can be used anywhere, even inside "strings". Usefull for multiline program description, #define for a macro, or in middle of a variable name! + +[Back](./README.md) diff --git a/docs/1.5-Built-in-functions.md b/docs/1.5-Built-in-functions.md new file mode 100644 index 0000000..6998781 --- /dev/null +++ b/docs/1.5-Built-in-functions.md @@ -0,0 +1,551 @@ +[Back](./README.md) + +# Built-in functions +Since version 2.0 many built-in functions were added. +Some functions have a version to handle fixed point numbers, in special the ones to handle Signa balance. +When using the fixed point to Signa, the values are straigth-forward as used in wallets and show in block explorer, they can have up to 8 decimals. +But using the long values version, the Signa balance are handled as NQT values (1 NQT = 0.00000001 Signa). +When handling assets, all values are expressed as QNT. 1 QNT is the least quantity of asset and depends on the decimals. If decimals is set to 4, 1 QNT is 0.0001 assets. + +
+ + +## Prototypes summary + + +```c +// Handling transactions loop +long getNextTx(); +long getNextTxFromBlockheight(long blockheight); + +// Receiving transactions +long getBlockheight(long transaction); +long getAmount(long transaction); +fixed getAmountFx(long transaction); +long getSender(long transaction); +long getType(long transaction); +void readMessage(long transaction, long page, long * buffer); +void readAssets(long transaction, long * buffer); +long getQuantity(long transaction, long assetId); + +// Sending transaction +void sendAmount(long amount, long accountId); +void sendAmountFx(fixed amount, long accountId); +void sendMessage(long * buffer, long accountId); +void sendAmountAndMessage(long amount, long * buffer, long accountId); +void sendAmountAndMessageFx(fixed amount, long * buffer, long accountId); +void sendBalance(long accountId); +void sendQuantity(long quantity, long assetId, long accountId); +void sendQuantityAndAmount(long quantity, long assetId, long amount, long accountId); +void sendQuantityAndAmountFx(long quantity, long assetId, fixed amount, long accountId); + +// Blockchain details +long getCurrentBlockheight(); +long getWeakRandomNumber(); + +// Contract details +long getCreator(); +long getCreatorOf(long contractId); +long getCodeHashOf(long contractId); +long getActivationOf(long contractId); +fixed getActivationOfFx(long contractId); +long getCurrentBalance(); +fixed getCurrentBalanceFx(); +long getAssetBalance(long assetId); + +// Using maps +void setMapValue(long key1, long key2, long value); +void setMapValueFx(long key1, long key2, fixed value); +long getMapValue(long key1, long key2); +fixed getMapValueFx(long key1, long key2); +long getExtMapValue(long key1, long key2, long contractId); +fixed getExtMapValueFx(long key1, long key2, long contractId); + +// Verifying messages +long checkSignature( + long message2, + long message3, + long message4, + long transaction, + long page, + long accountId +); + +// Handling assets +long issueAsset(long name1, long name2, long decimals); +void mintAsset(long assetId, long quantity); +void distributeToHolders( + long holdersAssetMinQuantity, + long holdersAsset, + long amountToDistribute, + long assetToDistribute, + long quantityToDistribute +); +void distributeToHoldersFx( + long holdersAssetMinQuantity, + long holdersAsset, + fixed amountToDistribute, + long assetToDistribute, + long quantityToDistribute +); +long getAssetHoldersCount(long minimumQuantity, long assetId); +long getAssetCirculating(long assetId); + +// Special instructions +long mdv(long m1, long m2, long div); +long pow(long base, long expBy1e8); +long powf(long base, fixed exp); + +// SmartC internal +void memcopy(void * destination, void * source); +long bcftol(fixed value); +fixed bcltof(long value); +``` +
+
+ + +## Handling transactions loop + + +### getNextTx +* Prototype: +`long getNextTx();` +* Description: +Keep track of incoming transactions and returns the next transaction Id. +If there is no new transaction, zero is returned. +A internal variable '_counterTimestamp' is used to store the last transaction received and it is updated by this function. +* Note: +If it is needed to come back to a given transaction later on, it is possible to save the contents of auto counter to another variable minus one. Later just overwrite the auto counter and then call `getNextTx` function. In this way it is possible to loop again thru all messages starting at that giving point. Example: +```c +// Save current loop point +currrentTransaction = getNextTx(); +rewindPoint = _counterTimestamp - 1; +// ... + +// Restore loop point +_counterTimestamp = rewindPoint; +currrentTransaction = getNextTx(); +// currrentTransaction will be the same from save point +// and all messages after that one can be visited again. +``` + +### getNextTxFromBlockheight +* Prototype: +`long getNextTxFromBlockheight(long blockheight);` +* Description: +Returns the transaction Id of the first transaction received at block 'blockheight' or later. +If there is no transaction, zero is returned. +This function also sets the internal variable '_counterTimestamp' and can be used together `getNextTx`. +
+
+ + +## Receiving transactions + + +### getBlockheight +* Prototype: +`long getBlockheight(long transaction);` +* Description: +Returns the blockheight of 'transaction'. +If transaction is invalid, 4294967295 is returned. + +### getAmount, getAmountFx +* Prototype: +`long getAmount(long transaction);` +* Fixed version prototype: +`fixed getAmountFx(long transaction);` +* Description: +Returns the Signa amount from 'transaction'. +The returned value is the original amount sent minus the activation amount from the contract. +If transaction is invalid, -1 is returned (-0.00000001 in fixed). + +### getSender +* Prototype: +`long getSender(long transaction);` +* Description: +Returns the sender's Id from 'transaction'. +If transaction is invalid, 0 is returned. + +### getType +* Prototype: +`long getType(long transaction);` +* Description: +Returns the type from 'transaction'. +All transactions types can be fetch at http api [getConstants](https://europe.signum.network/api-doc?requestTag=INFO). +If transaction is invalid, -1 is returned. + +### readMessage +* Prototype: +`void readMessage(long transaction, long page, long * buffer);` +* Description: +Reads the incoming message from 'transaction' at 'page' and store it at 'buffer'. +Each page has 32 bytes (4 longs), so the buffer size must be at least 4 longs or the function will overflow the buffer. +First page is 0 and there is no indicator of message size. Control the message expecting zeros after the end of message. +If 'page' is lower than zero or greater than 32, buffer is filled with zeros. +If 'transaction' is invalid or there is no message attached, buffer is filled with zeros. + +### readAssets +* Prototype: +`void readAssets(long transaction, long * buffer);` +* Description: +Reads all assets Id (up to 4) of 'transaction' and store them at 'buffer'. +Four values will be read, so the buffer size must be at least 4 longs or the function will overflow the buffer. +If 'transaction' is invalid, or no assets are found, buffer is filled with zeros. +If less than 4 assets are found, the firsts values will have the assetId and the remaining will be zeros. + +### getQuantity +* Prototype: +`long getQuantity(long transaction, long assetId);` +* Description: +Returns the quantity (QNT) of 'assetId' transfered in 'transaction'. +If transaction is invalid, -1 is returned. +If transaction valid and there is asset transfers that match 'assetId', zero is returned. +
+
+ + +## Sending transaction + + +### sendAmount, sendAmountFx +* Prototype: +`void sendAmount(long amount, long accountId);` +* Fixed version prototype: +`void sendAmountFx(fixed amount, long accountId);` +* Description: +Enqueues a transaction to send 'amount' of Signa to 'accountId'. +For sending Signa and Messages, only one transaction will be sent each block. The amounts are added. +If 'amount' is greater than contract's current balance, all balance is sent and contract halts execution (no gas). +No empty transactions are sent, they must send at least 1 NQT (or 0.00000001 Signa). + +### sendMessage +* Prototype: +`void sendMessage(long * buffer, long accountId);` +* Description: +Enqueues a transaction to send the content of 'buffer' as one message page (32 bytes or 4 longs) to 'accountId'. +Buffer size must be at least 4 longs or the function will overflow reading the buffer. +If the function is used more than once in a block, the messages are concatenated up to 992 bytes (31 pages). +If a 32th page is sent, the first 31 pages are disregarded and the loop restarts. +VERIFY: Transaction is sent with empty message? A empty message is one containing only zeros. + +### sendAmountAndMessage, sendAmountAndMessageFx +* Prototype: +`void sendAmountAndMessage(long amount, long * buffer, long accountId);` +* Fixed version prototype: +`void sendAmountAndMessageFx(fixed amount, long * buffer, long accountId);` +* Description: +Shorthand for use `sendAmount` and `sendMessage` (optimized code). Same restrictions apply. + +### sendBalance +* Prototype: +`void sendBalance(long accountId);` +* Description: +Enqueues a transaction to send all current balance (Signa) to 'accountId'. +Same restrictions from `sendAmount` apply. +Contract will halt execution (no gas). + +### sendQuantity +* Prototype: +`void sendQuantity(long quantity, long assetId, long accountId);` +* Description: +Sends a transaction to transfer 'quantity' of 'assetId' to 'accountId'. +If contract balance of 'assetId' is lower than 'quantity', all balance of 'assetId' is sent. +If the same asset is transfered two times at same block, their quantities are added and only one transaction is sent. +Transactions from smart contracts can transfer only one asset. If two different assets are transfered in same block, two transactions will be sent. +No empty transactions are sent, they must transfer at least 1 QNT of some asset. + +### sendQuantityandAmount, sendQuantityandAmountFx +* Prototype: +`void sendQuantityandAmount(long quantity, long assetId, long amount, long accountId);` +* Fixed version prototype: +`void sendQuantityandAmountFx(long quantity, long assetId, fixed amount, long accountId);` +* Description: +Sends a transaction to transfer 'quantity' of 'assetId' and 'amount' of Signa to 'accountId'. +This function ensure the asset and Signa are sent in the same transaction and with optimized code. +If contract balance of 'assetId' is lower than 'quantity', all balance of 'assetId' is sent. +If contract balance is lower than 'amount', all Signa balance is sent. +Transactions to transfer assets can not have messages or Signa attached. +If the same asset is transfered two times at same block, their quantities are added and only one transaction is sent. +Transactions from smart contracts can transfer only one asset. If two different assets are transfered in same block, two transactions will be sent. +No empty transactions are sent, they must transfer at least 1 QNT of some asset. +
+
+ + +## Blockchain details + + +### getCurrentBlockheight +* Prototype: +`long getCurrentBlockheight();` +* Description: +Returns the current blockheight when the instruction is executed. + +### getWeakRandomNumber +* Prototype: +`long getWeakRandomNumber();` +* Description: +Returns a simple random number based on last block signature. +It is very unlikely someone to tamper this number, but it can be done in theory. +Attention needed for contracts dealing with big amount of coins. +Return value can be negative, and negative number MOD positive number results in negative numbers. Hint: use shift right to get rid of negative values `positiveRnd = getWeakRandomNumber() >> 1;`. +
+
+ + +## Contract details + + +### getCreator +* Prototype: +`long getCreator();` +* Description: +Returns the account Id of the creator from current contract. + +### getCreatorOf +* Prototype: +`long getCreatorOf(long contractId);` +* Description: +Returns the account Id of the creator of 'contractId'. +If 'contractId' is not a contract, zero is returned. + +### getCodeHashOf +* Prototype: +`long getCodeHashOf(long contractId);` +* Description: +Returns the code hash id of 'contractId'. +If 'contractId' is zero, it is returned the code hash from the contract itself. +If 'contractId' is not a contract, zero is returned. + +### getActivationOf, getActivationOfFx +* Prototype: +`long getActivationOf(long contractId);` +* Fixed version prototype: +`fixed getActivationOfFx(long contractId);` +* Description: +Returns the minimum amount of Signa needed activate 'contractId'. +If 'contractId' is zero, it is returned the minimum activation amount from the contract itself. +If 'contractId' is not a contract, zero is returned. + +### getCurrentBalance, getCurrentBalanceFx +* Prototype: +`long getCurrentBalance();` +* Fixed version prototype: +`fixed getCurrentBalanceFx();` +* Description: +Returns the contract balance (Signa) at the time the instruction is executed. + + +### getAssetBalance +* Prototype: +`long getAssetBalance(long assetId);` +* Description: +Returns the contract balance of the given 'assetId' at the time the instruction is executed. +If 'assetId' is zero, the return value is the same as `getCurrentBalance`. +
+
+ + +## Using maps + + +Maps offer 'unlimited' space to store values. Each stored value (64-bit long or fixed) can be found using two keys (64-bit longs) to be read or written. +Any item that was not previously set, has zero value. No deletion is possible, just set to zero if needed. + +### setMapValue, setMapValueFx +* Prototype: +`void setMapValue(long key1, long key2, long value);` +* Fixed version prototype: +`void setMapValueFx(long key1, long key2, fixed value);` +* Description: +Sets to 'value' the map at 'currentContract[key1][key2]'. + +### getMapValue, getMapValueFx +* Prototype: +`long getMapValue(long key1, long key2);` +* Fixed version prototype: +`fixed getMapValueFx(long key1, long key2);` +* Description: +Returns the value stored at the map 'currentContract[key1][key2]'. + +### getExtMapValue, getExtMapValueFx +* Prototype: +`long getExtMapValue(long key1, long key2, long contractId);` +* Fixed version prototype: +`fixed getExtMapValueFx(long key1, long key2, long contractId);` +* Description: +Gets the map stored at external contract 'contractId[key1][key2]'. +If the contract has no map, or 'contractId' is not a contract, zero is returned. +Unlike the contract memory, the map values from other contracts can be retrieved using this function. +
+
+ + +## Verifying messages + + +### checkSignature +* Prototype: +```c +long checkSignature( + long message2, + long message3, + long message4, + long transaction, + long page, + long accountId +); +``` +* Description: +Checks if the signature of the given 'accountId' in 'transaction' at 'page' and 'page+1' matches for the given 'message2..4'. +Returns 1 (true) if the signature is valid, 0 otherwise. +
+
+ + +## Handling assets + + +### issueAsset +* Prototype: +`long issueAsset(long name1, long name2, long decimals);` +* Description: +Issue a new asset and returns its Id. +Asset name must have between 3 and 10 chars. Only uppercase letters, lowercase letters, and numbers are allowed. +The first 8 chars are specified in 'name1' and the remaining in 'name2'. +Set 'name2' to 0 or "" if the name has 8 or less chars. +The decimal limits are 0 to 8 decimals. +It costs 150 Signa to issue an asset. The contract execution will be halted at this instruction until the balance is reached. + +### mintAsset +* Prototype: +`void mintAsset(long quantity, long assetId);` +* Description: +Mint the 'quantity' of 'assetId'. +The asset must be issued by the contract. +No negative quantity allowed, send them send to accountId 0 to burn. +Minted quantity is available right after the instruction. + +### distributeToHolders, distributeToHoldersFx +* Prototype: +```c +void distributeToHolders( + long holdersAssetMinQuantity, + long holdersAsset, + long amountToDistribute, + long assetToDistribute, + long quantityToDistribute +); +``` +* Fixed version prototype: +```c +void distributeToHoldersFx( + long holdersAssetMinQuantity, + long holdersAsset, + fixed amountToDistribute, + long assetToDistribute, + long quantityToDistribute +); +``` +* Description: +Distribute the Signa 'amountToDistribute' and 'quantityToDistribute' of 'assetToDistribute' to accounts that hold at least 'holdersAssetMinQuantity' of 'holdersAsset'. +If 'amountToDistribute' and 'quantityToDistribute' are zero, no distribution is done. +Both 'amountToDistribute' and 'quantityToDistribute' can be distributed in same transaction. +Only the free balance is taken in account, this means, if there is quantity in sell orders, they will not be considered. +If current block already has the maximum indirect transactions, no distribution is done. +If no holders have more than the minimum quantity, no distribution is done. +Configured treasury accounts will not join dividends distributed. +The 'assetToDistribute' can be the same as 'holdersAsset' and, in this case, the contract will join the distribution (verify). + + +### getAssetHoldersCount +* Prototype: +`long getAssetHoldersCount(long minimumQuantity, long assetId);` +* Description: +Returns the number of holders that have at least 'minimumQuantity' of 'assetId'. +Only the free balance is taken in account, this means, if there is quantity in sell orders, they will not be considered. + +### getAssetCirculating +* Prototype: +`long getAssetCirculating(long assetId);` +* Description: +Returns the quantity of 'assetId' currently in circulation. +Quantities in treasury accounts are not considered. +
+
+ + +## Special instructions + + +### mdv +* Prototype: +`long mdv(long m1, long m2, long div);` +* Description: +Computes the value of `m1` multiplied by `m2` with 128-bit precision (no overflow) and then divides this result by `div`. +The calculation is returned as value. +* Notes: + 1) This instruction will be used in optimizations, even if not explicit declared. Use this form to ensure the instruction, or check generated assembly code if in doubt. + +### pow +* Prototype: +`long pow(long base, long expBy1e8);` +* Description: +Computes the value of `base` to the power of `expBy1e8`, where expBy1e8 is used as fixed point representation with 8 decimals (like the values in Signa). The result is returned as long value, decimals are truncated. +* Examples: + * sqrt(49) = 7 :: `val = pow(49, 5000_0000);` + * 5 * 5 * 5 * 5 = 5^4 = 625 :: `val = pow(5, 4_0000_0000);` + * sqrt(48) = 6 :: `val = pow(48, 5000_0000);` +* Notes + 1) pow will return zero if the result is matematically undefined; + 2) pow will return zero if base is negative; + 3) pow will return zero if result is greater than 9223372036854775807 (max positive long). + +### powf +* Prototype: +`long powf(long base, fixed exp);` +* Description: +Same as `pow` but using fixed point number for the exponent. +* Examples: + * sqrt(49) = 7 :: `val = powf(49, 0.5);` + * 5 * 5 * 5 * 5 = 5^4 = 625 :: `val = pow(5, 4.0);` + * sqrt(48) = 6 :: `val = pow(48, .5);` +* Notes + 1) pow will return zero if the result is matematically undefined; + 2) pow will return zero if base is negative; + 3) pow will return zero if result is greater than 9223372036854775807 (max positive long). +
+
+ + +## SmartC internal + + +### memcopy +* Prototype: +`void memcopy(void * destination, void * source);` +* Description: +Copies the binary value from source to destination. Handyful to copy variables content without type casting modifying them. +* Example: + * `fixed f; long l; memcopy(&f, &l);` This will copy the binary data from variable `l` to `f` without transformations. If l is 50, then f will be 0.00000050. + +### bcftol - binary casting fixed to long +* Prototype: +`long bcftol(fixed value);` +* Description: +Creates a binary casting (do not change values in memory) from a fixed value to long. Useful to change arguments types for API functions. Example: `Set_A1_A2(longValue, bcftol(fixedValue))` +* Examples: + * `long val; val = bcftol(0.5);` Output: val with have content 5000_0000. + +### bcltof - binary casting long to fixed +* Prototype: +`fixed bcltof(long value);` +* Description: +Creates a binary casting (do not change values in memory) from a long value to fixed. +* Examples: + * `fixed val; val = bcltof(5000_0000);` Output: val will have content 0.5. +
+ +[Back](./README.md) diff --git a/docs/2-API-Pseudo-Code.md b/docs/2-API-Pseudo-Code.md index 664e92a..5a3f6cd 100644 --- a/docs/2-API-Pseudo-Code.md +++ b/docs/2-API-Pseudo-Code.md @@ -1,21 +1,27 @@ -[Back](./) +[Back](./README.md) # API Functions pseudo code operations +Ok, you are unleashing the full potencial of machine code. To use this functions, you must include them by `#include APIFunctions` for regular functions or `#include fixedAPIFunctions` if using the fixed version of them (prepended by 'F_'). # Keep in mind! -### A and B registers +### A and B superregisters They are 256-bit registers and can be used as one big number (called A or B) or in 64-bit pieces (called A1..A4 or B1..B4). A1 is the least significative long. They have the same "mixed" mode as long vars: unsigned for bit operations and signed for aritmetics. ### Timestamps They are actually two integer 32-bit values joined in a 64-bit value. The most significant part (MSP) is the blockheight and the LSP is the transaction order when the block was forged. +Do not be confused by transactions timestamps, that are the number of seconds since the genesis block (11-oct-2014 02:00:00 UTC) ### Messages An encrypted message to contract is the same as not sending a message. +Messages are read and sent in batches of 32 bytes, they are called pages. The superregisters are used to store these values. -# Pseudo code -The code below is not valid for SmartC, but can give an idea what is happening when an API function is called. +# Pseudo code for regular functions +The code below is not valid for SmartC, but can give an idea what is happening when an API function is called. Good luck! +
+ -## Get/Set functions for "pseudo registers" +## Get/Set functions for "superregisters" + ``` c long Get_A1(void){ @@ -127,7 +133,6 @@ void Clear_A(void) { A1 = A2 = A3 = A4 = 0; } - void Clear_B(void) { // Assembly name: clear_B B1 = B2 = B3 = B4 = 0; @@ -157,20 +162,16 @@ void Copy_B_From_A(void) { long Check_A_Is_Zero(void) { // Assembly name: check_A_Is_Zero - /* Note that boolean logic is inverted. - * Try not use this function */ if (A1 == 0 && A2 == 0 && A3 == 0 && A4 == 0) - return 0; - return 1; + return 1; + return 0; } long Check_B_Is_Zero(void) { // Assembly name: check_B_Is_Zero - /* Note that boolean logic is inverted. - * Try not use this function */ if (B1 == 0 && B2 == 0 && B3 == 0 && B4 == 0) - return 0; - return 1; + return 1; + return 0; } long Check_A_Equals_B(void) { @@ -286,8 +287,12 @@ void Div_B_By_A(void) { A = B / A; } ``` +
+
+ ## Functions that perform hash operations + ``` c void MD5_A_To_B(void) { @@ -337,9 +342,18 @@ long Check_SHA256_A_With_B(void) { return 1; return 0; } + +long Check_Sig_B_With_A(void) { + // Assembly name: Check_Sig_B_With_A + // Checks if the signature of [AT ID, B2, B3, B4] can be verified with the message attached on tx id in A1 (page in A2) for account id in A3 +} ``` +
+
+ ## Generic functions that get block and tx info + ``` c long Get_Block_Timestamp(void) { @@ -359,6 +373,8 @@ long Get_Last_Block_Timestamp(void) { void Put_Last_Block_Hash_In_A(void) { // Assembly name: put_Last_Block_Hash_In_A + // For randomness source use the function Put_Last_Block_GSig_In_A. + // It is less prone to manipulations. A = Blockchain.LastBlock.Hash; } @@ -372,25 +388,29 @@ void A_To_Tx_After_Timestamp(long value) { long Get_Type_For_Tx_In_A(void) { // Assembly name: get_Type_for_Tx_in_A + // Transaction types can be found via http api request: 'getConstants' if (Blockchain.IsThisTxValid(A1) == false) return -1; - if (Blockchain.IsThereMessageinTx(A1) == false) - return 0; - return 1; + return Blockchain.GetTransaction(A1).type; } long Get_Amount_For_Tx_In_A(void) { // Assembly name: get_Amount_for_Tx_in_A - if (Blockchain.IsThisTxValid(A1) == false) + asset = B2; + tx = Blockchain.GetTransactionWithId(A1); + if (!tx) return -1; - return Blockchain.GetAmountFromTx(A1) - ContractActivationAmount; + if (asset == 0 ) + return tx.amount - ContractActivationAmount; + return tx.GetQuantityFromAsset(asset); } long Get_Timestamp_For_Tx_In_A(void) { // Assembly name: get_Timestamp_for_Tx_in_A if (Blockchain.IsThisTxValid(A1) == false) return -1; - return Blockchain.GetTimestampFromTx(A1) - ContractActivationAmount;} + return Blockchain.GetTimestampFromTx(A1); +} long Get_Random_Id_For_Tx_In_A(void) { // Assembly name: get_Ticket_Id_for_Tx_in_A @@ -403,12 +423,11 @@ long Get_Random_Id_For_Tx_In_A(void) { void Message_From_Tx_In_A_To_B(void) { // Assembly name: message_from_Tx_in_A_to_B - if (Blockchain.IsThisTxValid(A1) == false) - return -1; B = 0; - + if (Blockchain.IsThisTxValid(A1) == false) + return; if (Blockchain.IsThereMessageinTx(A1)) - B = Blockchain.GetMessageFromTx(A1) + B = Blockchain.GetMessageFromTx(A1).AtPage(A2) } void B_To_Address_Of_Tx_In_A(void) { @@ -421,16 +440,62 @@ void B_To_Address_Of_Tx_In_A(void) { void B_To_Address_Of_Creator(void) { // Assembly name: B_to_Address_of_Creator B = 0; - B1 = ContractCreator; + if (B2 == 0) { + B1 = ContractCreator; + return; + } + if (Blockchain.IsAT(B2)) { + B1 = Blockchain.GetCreatorFromAT(B2); + } +} + +long Get_Code_Hash_Id(void) { + // Assembly name: Get_Code_Hash_Id + if (B2 == 0) { + return ThisContract.CodeHashId; + } + if (Blockchain.IsAT(B2)) { + return Blockchain.GetCodeHashIdFromAT(B2); + } + return 0; +} + +void B_To_Assets_Of_Tx_In_A(void) { + // Assembly name: B_To_Assets_Of_Tx_In_A + B = 0; + tx = Blockchain.GetTransactionWithId(A1); + if (!tx) { + return; + } + if (!HasAssets(tx)) { + return; + } + B1 = tx.asset[0].id; + if (tx.asset[1]) { + B2 = tx.asset[1].id; + } + if (tx.asset[2]) { + B3 = tx.asset[2].id; + } + if (tx.asset[3]) { + B4 = tx.asset[3].id; + } } ``` +
+
+ ## Generic functions that check balances and perform ops + ``` c long Get_Current_Balance(void) { // Assembly name: get_Current_Balance - return Blockchain.GetMyBalanceNow(); + if (B2 == 0) { + return Blockchain.GetMyBalanceNow(); + } + return Blockchain.GetMyBalanceFromAsset(B2); } long Get_Previous_Balance(void) { @@ -438,13 +503,25 @@ long Get_Previous_Balance(void) { return Blockchain.GetMyBalanceLastTimeIWasFrozen(); } -void Send_To_Address_In_B(long value) { +void Send_To_Address_In_B(long amountOrQuantity) { // Assembly name: send_to_Address_in_B - long ContractBalance = Blockchain.GetMyBalanceNow(); - if (value > ContractBalance) - Blockchain.SendAllMyBalanceTo(B1); + recipient = B1; + asset = B2; + if (asset == 0) { + // Send Signa + long ContractBalance = Blockchain.GetMyBalanceNow(); + if (amountOrQuantity > ContractBalance) + Blockchain.SendAllMyBalanceTo(recipient); + else + Blockchain.SendBalanceTo(amountOrQuantity, recipient); + return; + } + // Send asset + long ContractQuantity = Blockchain.GetMyBalanceFromAsset(asset); + if (amountOrQuantity > ContractQuantity) + Blockchain.SendAllMyBalanceFromAssetTo(asset, recipient); else - Blockchain.SendBalanceTo(value, B1); + Blockchain.SendBalanceFromAssetTo(asset, recipient, amountOrQuantity); } void Send_All_To_Address_In_B(void) { @@ -464,13 +541,287 @@ void Send_Old_To_Address_In_B(void) { void Send_A_To_Address_In_B(void) { // Assembly name: send_A_to_Address_in_B - Blockchain.SendMessageInATo(B1); + pendingMessage = ThisContract.GetPendingMessageTo(B1); + if (pendingMessage) { + pendingMessage += [A1..A4]; + return; + } + thisContract.AddMessageTo(B1, [A1..A4]) } long Add_Minutes_To_Timestamp(long timestamp,long minutes) { // Assembly name: add_Minutes_to_Timestamp return ((minutes/4) << 32) + timestamp } + +long Get_Map_Value_Keys_In_A(void) { + // Assembly name: Get_Map_Value_Keys_In_A + if (A3 == 0) { + if (isSet(ThisContract.map[A1][A2])) { + return ThisContract.map[A1][A2]; + } + return 0; + } + if (IsAT(A3)) { + otherContract = Blockchain.GetContractFromId(A3); + if (isSet(otherContract.map[A1][A2])) { + return otherContract.map[A1][A2]; + } + } + return 0; +} + +void Set_Map_Value_Keys_In_A(void) { + // Assembly name: Set_Map_Value_Keys_In_A + ThisContract.map[A1][A2] = A4; +} + +long Issue_Asset(void) { + // Assembly name: Issue_Asset + // This API costs 150 signa to be executed! Returns the assetId from created asset. + assetName = [A1, A2] // Limited to 10 chars + return Blockchain.IssueNewAssetWithName(assetName); +} + +void Mint_Asset(void) { + // Assembly name: Mint_Asset + quantity = B1; + asset = B2; + if (!WasIssuedByMe(asset)) + return; + ThisContract.BalanceFromAsset(B2) += quantity; +} + +void Distribute_To_Asset_Holders(void) { + // Assembly name: Distribute_To_Asset_Holders + // amount refers to Signa values, quantity refers to asset values + // holders refers to asset to used in distribution calculation + // toDistribute refers to asset that will be distributed. + holdersAssetMinimumQuantity = B1 + holdersAsset = B2 + amountToDistribute = A1 + assetToDistribute = A3 + quantityToDistribute = A4 + + /* + 1) Distribute 'amountToDistribute' Signa to holders of asset 'holdersAsset' + that have at least 'holdersAssetMinimumQuantity' in account. + 2) Distribute 'quantityToDistribute' from 'assetToDistribute' to holders of + asset 'holdersAsset' that have at least 'holdersAssetMinimumQuantity' + in account. + Notes: + * Treasury accounts will not be included in this distribuition. + * This distribution is done thru indirect transaction. + * The 'assetToDistribute' and 'holdersAsset' can be the same + * Only the free quantity will be taken in account for distribution. Quantity + frozen in sell orders will be disregarded. + * All values will be rounded down (QNT or NQT). This remaining value will be added in + the transaction to the holder that has the greatest quantity. + */ +} + +long Get_Asset_Holders_Count(void) { + // Assembly name: Get_Asset_Holders_Count + mininumQuantity = B1; + asset = B2; + + /* + 1) Return the number of accounts that have at least 'mininumQuantity' of + 'asset'. Does not consider treasury account. + */ +} + +long Get_Activation_Fee(void) { + // Assembly name: Get_Activation_Fee + if (B2 == 0) { + return ThisContract.ActivationAmount; + } + if (Blockchain.IsAT(B2)) { + return Blockchain.GetActivationAmountFromAT(B2); + } + return 0; +} + +void Put_Last_Block_GSig_In_A(void) { + // Assembly name: Put_Last_Block_GSig_In_A + // Generation Signature is a better random source to be used. + A = Blockchain.LastBlock.GenerationSignature; +} + +long Get_Asset_Circulating(void) { + // Assembly name: Get_Asset_Circulating + asset = B2; + + /* + 1) Return the circulating quantity of 'asset', excluding the quantity in + treasury account. + */ +} +``` +
+
+ + +# Pseudo code for fixed version functions + + +``` c +// Get/Set functions for "superregisters" + +fixed F_Get_A1(void){ + // Assembly name: get_A1 + return A1; +} + +fixed F_Get_A2(void) { + // Assembly name: get_A2 + return A2; +} + +fixed F_Get_A3(void) { + // Assembly name: get_A3 + return A3; +} + +fixed F_Get_A4(void) { + // Assembly name: get_A4 + return A4; +} + +fixed F_Get_B1(void) { + // Assembly name: get_B1 + return B1; +} + +fixed F_Get_B2(void) { + // Assembly name: get_B2 + return B2; +} + +fixed F_Get_B3(void) { + // Assembly name: get_B3 + return B3; +} + +fixed F_Get_B4(void) { + // Assembly name: get_B4 + return B4; +} + +void F_Set_A1(fixed value) { + // Assembly name: set_A1 + A1 = value; +} + +void F_Set_A2(fixed value) { + // Assembly name: set_A2 + A2 = value; +} + +void F_Set_A3(fixed value) { + // Assembly name: set_A3 + A3 = value; +} + +void F_Set_A4(fixed value) { + // Assembly name: set_A4 + A4 = value; +} + +void F_Set_B1(fixed value) { + // Assembly name: set_B1 + B1 = value; +} + +void F_Set_B2(fixed value) { + // Assembly name: set_B2 + B2 = value; +} + +void F_Set_B3(fixed value) { + // Assembly name: set_B3 + B3 = value; +} + +void F_Set_B4(fixed value) { + // Assembly name: set_B4 + B4 = value; +} + + +// Generic functions that get block and tx info + +fixed F_Get_Amount_For_Tx_In_A(void) { + // Assembly name: get_Amount_for_Tx_in_A + asset = B2; + tx = Blockchain.GetTransactionWithId(A1); + if (!tx) + return -1; + if (asset == 0 ) + return tx.amount - ContractActivationAmount; + return tx.GetQuantityFromAsset(asset); +} + + +// Generic functions that check balances and perform ops + +fixed F_Get_Current_Balance(void) { + // Assembly name: get_Current_Balance + if (B2 == 0) { + return Blockchain.GetMyBalanceNow(); + } + return Blockchain.GetMyBalanceFromAsset(B2); +} + +void F_Send_To_Address_In_B(fixed amountOrQuantity) { + // Assembly name: send_to_Address_in_B + recipient = B1; + asset = B2; + if (asset == 0) { + // Send Signa + long ContractBalance = Blockchain.GetMyBalanceNow(); + if (amountOrQuantity > ContractBalance) + Blockchain.SendAllMyBalanceTo(recipient); + else + Blockchain.SendBalanceTo(amountOrQuantity, recipient); + return; + } + // Send asset + long ContractQuantity = Blockchain.GetMyBalanceFromAsset(asset); + if (amountOrQuantity > ContractQuantity) + Blockchain.SendAllMyBalanceFromAssetTo(asset, recipient); + else + Blockchain.SendBalanceFromAssetTo(asset, recipient, amountOrQuantity); +} + +fixed F_Get_Map_Value_Keys_In_A(void) { + // Assembly name: Get_Map_Value_Keys_In_A + if (A3 == 0) { + if (isSet(ThisContract.map[A1][A2])) { + return ThisContract.map[A1][A2]; + } + return 0; + } + if (IsAT(A3)) { + otherContract = Blockchain.GetContractFromId(A3); + if (isSet(otherContract.map[A1][A2])) { + return otherContract.map[A1][A2]; + } + } + return 0; +} + +fixed F_Get_Activation_Fee(void) { + // Assembly name: Get_Activation_Fee + if (B2 == 0) { + return ThisContract.ActivationAmount; + } + if (Blockchain.IsAT(B2)) { + return Blockchain.GetActivationAmountFromAT(B2); + } + return 0; +} ``` +
-[Back](./) +[Back](./README.md) diff --git a/docs/3-Learning-with-examples.md b/docs/3-Learning-with-examples.md index 36fb94c..697f5e9 100644 --- a/docs/3-Learning-with-examples.md +++ b/docs/3-Learning-with-examples.md @@ -1,57 +1,69 @@ -[Back](./) +[Back](./README.md) # Lessons and examples to create smart contracts in Signum network -Following guide will show examples with progressive complexity and comments how they works. It is expected that you know C language. It is a good idea to read all docs from SmartC. If you plan to debug programs it is good to read also ciyam official documentation available [here](https://ciyam.org/at/) for more specific knowledge in assembly code. There is also some videos compiling these examples at my [personal Youtube channel](https://www.youtube.com/playlist?list=PLyu0NNtb1eg3Gcg2JCrOle8MjtuFPb-Gi). +Following guide will show examples with progressive complexity and comments how they works. It is expected that you know C language. It is a good idea to read all docs from SmartC. If you plan to be expert, read ciyam official documentation available [here](https://ciyam.org/at/) and Signum [SIP-37](https://github.com/signum-network/SIPs/blob/master/SIP/sip-37.md), [SIP-38](https://github.com/signum-network/SIPs/blob/master/SIP/sip-38.md), [SIP-39](https://github.com/signum-network/SIPs/blob/master/SIP/sip-39.md) for major changes introduced in JUN/2022. There is also some videos compiling these examples at my [personal Youtube channel](https://www.youtube.com/playlist?list=PLyu0NNtb1eg3Gcg2JCrOle8MjtuFPb-Gi). -## Basic contracts +
+ -### Always Running, doing nothing -```c -#pragma version dev +## Always Running, doing nothing + +```c #program name alwaysRuning #program description Always Running, doing nothing -#pragma maxAuxVars 1 +#program activationAmount 0 +#pragma maxAuxVars 0 while (true) { - sleep 1; + sleep; } ``` * This contract has no functions, API, nor variable declared. -* Macro `#pragma version` is very important to pin source code with a compiler version. It is to ensure the compiled code will result in the same machine code. Not needed if working in 'dev' branch. -* Macro `#pragma` can set some specific behaviour of compiler, in this case it will set compiler to use only one auxiliary variable (they act as registers for operations). Default value is 3, but here we will use the minimum allowed. +* Macro `#pragma` can set some specific behaviour of compiler, in this case it will set compiler to use no auxiliary variable (they act as registers for operations). Default value is 3, but here we do not need any. * Only one global statement, `while (true)` is used to make an infinite loop. * Keyword `sleep 1` will stop the contract process at current block and resumes execution at the next block. -* It will keep in this loop until there is no more balance at the contract, then it will be frozen until it receives a new activation. +* It will keep in this loop until there is no more balance at the contract, then it will be frozen until it receives more balance. +* Activation amount zero means that the contract will be always active, even if there was a `exit` statement. +
+
+ -### Counting transactions, easy way -```c -#pragma version dev +## Counting transactions, easy way + +```c #program name CountingTxDelayed #program description Counting transactions, easy way +#program activationAmount 0.1 void main(void) { - long counter; + long counter, txid; + getNextTx(); counter++; } ``` -* The `main` function is the entry point when contract gets an activation. Contract can be finished via `return`, `exit` or closing main function. +* The `main` function is the entry point when contract gets an activation. Contract can be finished in this function via `return`, `exit` or at the end of function. * If two transactions are received by this contract, the first one will be processed and the contract will enter finished state. In the next block it will be activated again with the second transaction that was not processed in previous block height. This means, if this contract receives 10 messages at some block, it will take 10 blocks to finish counting them. * When the contract is created, all memory is set to zero. So variable counter will start from zero and keep increasing every block it has received TXs. +* Activation amount 0.1 means that the contract will only count the transactions that send at least this amount. If a transaction with .99999999 is received, the balance will the added to the contract but it will not be counted. +
+
+ -### Counting transactions without delay. -```c -#pragma version dev +## Counting transactions without delay. + +```c #program name CountingTx #program description Counting transactions without delay -#include APIFunctions +#program activationAmount 0.1 + +long counter; void main(void) { - for (A_To_Tx_After_Timestamp(currentTX.timestamp); Get_A1() != 0; A_To_Tx_After_Timestamp(currentTX.timestamp)) { - // Update transaction variables - getTxDetails(); + long txid; + while ((txid = getNextTx() != 0) { // Process transaction in a specific function processTX(); } @@ -60,178 +72,143 @@ void main(void) { } void processTX(void){ - const long counter = 10; + const counter = 10; counter++; } - -struct TXINFO { - long timestamp, - sender, - amount, - message[4]; -} currentTX; - -// A must have a TX_Timestamp! -void getTxDetails(void) { - currentTX.amount = Get_Amount_For_Tx_In_A(); - currentTX.timestamp = Get_Timestamp_For_Tx_In_A(); - Message_From_Tx_In_A_To_B(); - currentTX.message[0]=Get_B1(); - currentTX.message[1]=Get_B2(); - currentTX.message[2]=Get_B3(); - currentTX.message[3]=Get_B4(); - B_To_Address_Of_Tx_In_A(); - currentTX.sender = Get_B1(); -} ``` -* To get details from incoming transaction, we will use the API functions. Tell the compiler you will need them with macro `#include APIFunctions`. -* It is presented the function `getTxDetails()` that will get all details from incoming message to a global variable `currentTX`. This struct has members to store all information that can be retrieved from a TX. Remember to comment values not needed you your code, because every API call costs 0.00735 signa to be executed. -* The `main` function will loop thru all TX received in same block. When the API `A_To_Tx_After_Timestamp` returns timestamp zero or -1, it means there is no more pending transactions, so the contract can be finished. -* Counter value will be set to 10 during contract deployment (keyword const!). Then it will be increased for each new valid tx received. -* Global variable is used because it needs less instructions to make same thing. It is important to note, because every assembly instructions will be charged a fee of 0.000735 signa for execution and there are limitations for code and memory sizes when deploying an smart contract in the blockchain. +* It is presented the built-in function `getNextTx()` that will return the transaction Id of the next transaction. It stores internally the timestamp of last received transaction and returns zero if there is no more pending transactions. +* The while loop will be executed for all pending messages. When txid is zero, the contract can be finished. +* Counter value will be set to 10 during contract deployment (keyword const!). Then it will be increased for each new valid tx received. Const expressions are 'executed' only at the deployment. +* counter is global variable just to show how to declare it. It is more effective to have a global variable than sending it to functions. If a variable is used only in one function, use the local scope. +
+
+ -### Sending signa -```c -#pragma version dev +## Echo signa + -#program name SendSigna -#program description Using a function to send signa. -#program activationAmount 2000_0000 - -#include APIFunctions - -const long ONE_SIGNUM = 1_0000_0000; +```c +#program name EchoSigna +#program description Returns the received amount to the sender. +#program activationAmount 0.5 -void main(void) { - for (A_To_Tx_After_Timestamp(currentTX.timestamp); Get_A1() != 0; A_To_Tx_After_Timestamp(currentTX.timestamp)) { - // Update transaction variables - getTxDetails(); - if (currentTX.amount > 5 * ONE_SIGNUM) { - // If TX is bigger than 5 signa, send 5 signa to creator and refund - // remaining amount to sender. - B_To_Address_Of_Creator(); - Send_To_Address_In_B(5 * ONE_SIGNUM); - Set_B1(currentTX.sender); - Send_To_Address_In_B(currentTX.amount - (5 * ONE_SIGNUM)); - } else { - // Send tx amount to creator. - B_To_Address_Of_Creator(); - Send_To_Address_In_B(currentTX.amount); - } +while (true) { + long txid; + while ((txid = getNextTx() != 0) { + sendAmount(getAmount(txid), getSender(txid)) } // After all transactions processed - if (Get_Current_Balance() > ONE_SIGNUM) { - B_To_Address_Of_Creator(); - Send_To_Address_In_B(7000_0000); - } + sendBalance(getCreator()); } +``` +* `#program activationAmount 0.5` ensures that only transactions with an amount greater or equal 0.5 signa will be processed. The returned amount will be only the value above activation amount. +* For every transaction processed, some unspent balance will build up in the contract. To avoid this situation, after all transactions were processed in current block, the contract sends all remaining balance to the creator. +* `getAmount`: returns the amount in NQT of a given transaction id. +* `getSender`: returns the account id of sender of a given transaction id. +* `sendAmount`: sends a given amount of Signa to a recipient. +* `sendBalance`: sends all contract balance to a recipient. The execution is halted after the transaction is sent. +
+
+ + +## Echo message + + +```c +#program name EchoMessage +#program description Reads first page of message (32 bytes) and sends back to sender. \ + Also sends back 95% of the amount sent. +#program activationAmount 0.5 struct TXINFO { - long timestamp; + long txid; long sender; - long amount; + fixed amount; long message[4]; } currentTX; -// A must have a TX_Timestamp! -void getTxDetails(void) { - currentTX.amount = Get_Amount_For_Tx_In_A(); - currentTX.timestamp = Get_Timestamp_For_Tx_In_A(); - Message_From_Tx_In_A_To_B(); - currentTX.message[0]=Get_B1(); - currentTX.message[1]=Get_B2(); - currentTX.message[2]=Get_B3(); - currentTX.message[3]=Get_B4(); - B_To_Address_Of_Tx_In_A(); - currentTX.sender = Get_B1(); +void main () { + while ((currentTX.txid = getNextTx()) != 0) { + getDetails(); + processTX(); + } + // After all transactions processed + // cleanUp(); +} + +void getDetails() { + currentTX.sender = getSender(currentTX.txid); + currentTX.amount = getAmountFx(currentTX.txid); + readMessage(currentTX.txid, 0, currentTX.message); } +void processTX() { + sendAmountAndMessageFx( + currentTX.amount * 0.95, + currentTX.message, + currentTX.sender + ); +} ``` -* `#program activationAmount 2000_0000` ensures that only transactions with an amount greater or equal 0.2 signa will be processed. -* For every transaction, if value is greater than 5 signa, contract sends 5 signa to creator and refund excess amount to sender. Else, sends all incoming amount to creator. -* To avoid balance accumulation, when all transactions were processed, contract checks if current balance is greater than 1 signum and then sends 0.7 signa to creator. -* Signum quantity is always specified in NQT. 1 signum is 100000000 NQT. I prefer to group the 'decimal amount' in in two parts with 4 zeros for easier counting. Signa is plural of signum. +* In this contract the Signa amount is handled with fixed point variables. They have the same 8 decimal numbers the people is used to and can be calculated with operators + - * / >> and <<. Useful to make calculations. All built-in functions that handle Signa balance have a fixed point version that ends with Fx. +* Balance will build up in the contract because there is no cleanUp function defined. +
+
+ -### Sending a message -```c -#pragma version dev +## Echo token + -#program name SendMessage -#program description Easy way to send messages -#program activationAmount 2000_0000 +```c +#program name EchoToken +#program description Returns the assets received. +#program activationAmount 0.5 -#include APIFunctions +struct TXINFO { + long txid; + long sender; + long assets[4]; +} currentTX; -// main loop -while (true) { - for (A_To_Tx_After_Timestamp(currentTX.timestamp); Get_A1() != 0; A_To_Tx_After_Timestamp(currentTX.timestamp)) { - // Update transaction variables - getTxDetails(); - - // Process TX - send_message.recipient = currentTX.sender; - send_message.message[]="Thanks for donation!"; - Send_Message(); +void main () { + while ((currentTX.txid = getNextTx()) != 0) { + getDetails(); + processTX(); } - // After all processed, send all balance to creator. - B_To_Address_Of_Creator(); - Send_All_To_Address_In_B(); + // After all transactions processed + cleanUp(); } -struct SENDMESSAGE { - long recipient; - long message[4]; -} send_message; - -void Send_Message(void) { - Set_B1(send_message.recipient); - Set_A1_A2(send_message.message[0], send_message.message[1]); - Set_A3_A4(send_message.message[2], send_message.message[3]); - Send_A_To_Address_In_B(); +void getDetails() { + currentTX.sender = getSender(currentTX.txid); + readAssets(currentTX.txid, currentTX.assets); } -struct TXINFO { - long timestamp; - long sender; - long amount; - long message[4]; -} currentTX; - -// A must have a TX_Timestamp! -void getTxDetails(void) { - currentTX.amount = Get_Amount_For_Tx_In_A(); - currentTX.timestamp = Get_Timestamp_For_Tx_In_A(); - Message_From_Tx_In_A_To_B(); - currentTX.message[0]=Get_B1(); - currentTX.message[1]=Get_B2(); - currentTX.message[2]=Get_B3(); - currentTX.message[3]=Get_B4(); - B_To_Address_Of_Tx_In_A(); - currentTX.sender = Get_B1(); +void processTX() { + for (long i = 0; i < 4; i++) { + if (currentTX.assets[i] == 0) { + // Zero means no more assets in incoming transaction + return; + } + sendQuantity( + getQuantity(currentTX.txid, currentTX.assets[i]), + currentTX.assets[i], + currentTX.sender + ); + } } -``` -* Presenting function `Send_Message()` thats sends a message with content in global variable `send_message`. -* If the contract sends two messages to same recipient in same block, recipient will receive only the last one. -* Messages are limited to 32 bytes, the size of superregister A. Text is encoded with UTF-8, so some characters need more than one byte. -* There is no main function. The main loop will process transactions and then send all contract balance to creator. When the API `Send_All_To_Address_In_B` sends all balance, then the contract execution will stop because there is no more balance to run it (it will be frozen). When a new transaction above activation amoun is received, contract resumes execution and process all incoming transactions again. -### Sending a message and signa -```c -//function and global variable shown -struct SENDMESSAGESIGNA { - long recipient; - long message[4]; -} send_message; - -void Send_Message_And_Signa(long amount) { - Set_B1(send_message.recipient); - Set_A1_A2(send_message.message[0], send_message.message[1]); - Set_A3_A4(send_message.message[2], send_message.message[3]); - Send_A_To_Address_In_B(); - Send_To_Address_In_B(amount); +void cleanUp() { + fixed excessBalance; + excessBalance = getCurrentBalanceFx() - 0.5; + if (excessBalance > 0) { + sendAmountFx(excessBalance, getCreator()); + } } ``` -* Easy, just join the API call to send message and send signa. Function and global variable is show in example. -* Signum node will join message and signa instructions and recipient will receive only one transaction. +* Assets amount is called "quantity" and always returned as long (QNT), because it is not possible to know how many decimals they have. +* To avoid balance build up in the contract, any Signa amount and unspent activation amount is sent to creator if more than 0.5 Signa. +* If `sendBalance` is used in cleanUp, the contract would also stop execution. But the next transaction will reactivate the contract and it will reach the end of void function, so halting again. Only when another transaction is received, the contract will start again at the main function and process the two enqueued transactions. +
-[Back](./) +[Back](./README.md) diff --git a/docs/4-Functions-repository.md b/docs/4-Functions-repository.md index d95ee21..c025012 100644 --- a/docs/4-Functions-repository.md +++ b/docs/4-Functions-repository.md @@ -1,9 +1,37 @@ -[Back](./) +[Back](./README.md) # Functions repository -These functions can be added to projects and speed up development time! +These functions and macros can be added to projects and speed up development time! +
+ -## Text to number: atol() +## Macros + + +Macro functions are elaborated substitutions done during compilation. Use for very simple functions: + +```c + +#define add2(val) (val + 2) + +#define sendSigna(recipient, amount) (\ + Set_B1_B2(recipient, 0), \ + F_Send_To_Address_In_B(amount)) + +#define sendMessage1(recipient, m1) (\ + Clear_A_And_B(), \ + Set_B1(recipient), \ + Set_A1(m1), \ + Send_A_To_Address_In_B()) + +// You got it! +``` +
+
+ + +## Text to long: atol() + ```c // ASCII to Long (base10 positive and less than 100.000.000) @@ -26,7 +54,13 @@ long atol(long val) return ret; } ``` -## Number to text: ltoa() +
+
+ + +## Long to text: ltoa() + + ```c // Integer to ASCII (base10 positive and less than 100.000.000) // Iterative function to implement itoa() clone function in C @@ -48,125 +82,133 @@ long ltoa(long val) return ret; } ``` +
+
+ -## Splitting a text array into fields: split() -```c -// Split string function in C -// Expects: -// 'separator' to be used (only LSB will be used). -// 'source' is the array with text to be splitted. -// 'source_length' is the size of source (or the numbers of longs that -// will be processed -// 'ret' is return buffer array. -// 'ret_length' is the size of return buffer, to avoid buffer overflow -// Returns: number of fields filled -// The function will keep adding chars until fill return buffer. If a -// string is bigger than 8 chars, only 8 last chars will be returned -// at that field. -const long n8 = 8, n255 = 0xff; -long split(long separator, long * source, long source_length, long * ret, long ret_length) -{ - long field, i_act_arg, i_ret, i_param, act_arg, chr ; - - //clear destination buffer - for (i_ret = 0; i_ret < ret_length; i_ret++) { - ret[i_ret] = 0; - } +## Text to fixed: decodeAmount() + - i_act_arg = 0; //cycle bytes in actual string beeing processed(param buffer) (0 to 8) - i_param = 0; //current element in param buffer (0 to 4) - field = 0; //current element in return buffer (ret)(0 to 10 in this example) - i_ret = 0; //var to cycle bytes for each return buffer (from 0 to 8 ) - - while (i_param < source_length) { - act_arg = source[i_param]; - chr = act_arg & 0xff; - while (chr != 0) { - if (chr == separator){ - field++; - i_ret = 0; - } else { - if (i_ret == 8) { // ret[i_ret] is full, shift and continue - ret[field] >>= 8; - i_ret--; - } - ret[field] += chr << (8 * i_ret); - i_ret++; - } - i_act_arg++; - if (field == ret_length) { // End of destination buffer, go to end - return ++field; - } - if (i_act_arg == 8) { //end of actual va_arg, go to next va_arg - break; //break second while loop - } else { // prepare char for next merge - chr = act_arg & (0xff << (8 * i_act_arg)); - chr >>= 8 * i_act_arg; - } +```c +// ASCII to fixed (base10 positive) +// Expects a string in currentTX.message. If any byte is not a char numeric +// representation or decimal point, then stop and return. Only positive +// numbers, base10 are converted. Returns zero if no number was processed. +fixed decodeAmountInMessage(long startingAt) { + long multiplier = 1_0000_0000; + long retVal = 0; + long ch; + long decimals = false; + for (long i = long startingAt; i < 32; i++) { + ch = currentTX.message[i / 8] >> ((i % 8) * 8); + ch &= 0xff; + if (ch == 0 || ch == ' ') break; + if (ch == '.') { + decimals = true; + continue; + } + if (ch < '0' || ch > '9' ) { + // invalid char + retVal = 0; + break; + } + if (decimals) { + multiplier /= 10; + } else { + retVal *= 10; } - i_param++; - i_act_arg = 0; + ch &= 0xF; + ch *= multiplier; + retVal += ch; } - - return ++field; + return bcltof(retVal); } ``` +
+
+ -## Concatenate text into a text array: concat() -```c -// String concatenation function in C -// Expects: -// 'source' is an array with content -// 'source_length' is size of source (in longs) or the numbers of longs to be processed -// 'ret' is return buffer array. -// 'ret_length' is the size of ret (in longs) to avoid buffer overflow. -// Function returns the number of bytes processed. A number equal ret_length*8 -// can denote that buffer was too short for the content in 'source'. -const long n8 = 8, n255 = 0xff; -long concat(long * source, long source_length, long * ret, long ret_length) -{ - long i_param, act_arg, chr, i_ret, i_buffer, i_act_arg; +## RS-Address to accounId: decodeRS() + - //clear destination buffer - for (i_buffer = 0; i_buffer < ret_length; i_buffer++) { - ret[i_buffer] = 0; - } - - i_ret = 0; //var to cycle bytes for each return buffer (from 0 to 8 ) - i_buffer = 0; //var to cycle for each buffer available (from 0 to ret_length) - i_param = 0; //var to cycle for each source items provided (from 0 to source_length) - i_act_arg= 0; //var to cycle bytes in actual source(long) beeing processed (0 to 8) - - while (i_param < source_length) { //loop thru source_length - act_arg = source[i_param]; // access source - chr = act_arg & 0xff; //this always first char, no need to shift - - while (chr != 0) { //loop bytes in va_arg beeing processed (act_arg) - ret[i_buffer] += chr << (8 * i_ret); - i_act_arg++; - i_ret++; - - if (i_ret == 8) { // ret[i_buffer] is full, go to next ret value - i_buffer++; - i_ret = 0; - if (i_buffer == ret_length) { // End of destination buffer, go to end - goto all_loops_end; //break 2 loops - } - } - if (i_act_arg == 8) { //end of actual va_arg, go to next va_arg - break; //break second while loop - } else { // prepare char for next merge - chr = act_arg & ( 0xff << (8 * i_act_arg) ); - chr >>= 8 * i_act_arg; - } +```c +// RS-Address to accountId +// Expects a string containing an address in currentTX.message +// starting at index zero. +// Ex: S-AAAA-BBBB-CCCC-DDDDD. +// On error returns -1. +// Actually the first group can be any text, so matches 'S', 'TS' +// or 'BURST'. +// No error checks!! + +/** Decode an RS address at currentTX.message + * returns -1 if there is an error on decoding. + * This function does not verify error correction bytes + * */ +long decodeRS(void) { + long position = 0, ch, group = 0; + long idPosition, value, result = 0; + for (i = 0; i < 32; i++) { + ch = currentTX.message[i / 8] >> ((i % 8) * 8); + ch &= 0xff; + if (ch == '-') { + group++; + continue; + } + if (group == 0 || group == 3) { + continue; + } + value = rscharToIndex(ch); + if (value == minus1) { + // ERROR + return minus1; + } + idPosition = (codeword_map >> (position * 4)) & 15; + result |= value << (idPosition * 5); + position++; + if (position == 13) { + return result; } - i_param++; - i_act_arg = 0; } + return minus1; +} - all_loops_end: - return i_ret + (8 * i_buffer); +const long codeword_map = + 3 + + 2 *16 + + 1 *16*16 + + 0 *16*16*16 + + 7 *16*16*16*16 + + 6 *16*16*16*16*16 + + 5 *16*16*16*16*16*16 + + 4 *16*16*16*16*16*16*16 + + 12 *16*16*16*16*16*16*16*16 + + 8 *16*16*16*16*16*16*16*16*16 + + 9 *16*16*16*16*16*16*16*16*16*16 + + 10 *16*16*16*16*16*16*16*16*16*16*16 + + 11 *16*16*16*16*16*16*16*16*16*16*16*16; + +long rscharToIndex(long in) { + switch (true) { + case (in < '2'): + case (in == 'O'): + case (in == 'I'): + return minus1; + case (in <= '9'): + return in - '2'; + case (in < 'A'): + return minus1; + case (in < 'I'): + return in - '9'; + case (in < 'O'): + return in - ':'; + case (in <= 'Z'): + return in - ';'; + default: + return minus1; + } } ``` +
-[Back](./) +[Back](./README.md) diff --git a/docs/5-Comprehensive-example.md b/docs/5-Comprehensive-example.md index 8e3c295..6788a55 100644 --- a/docs/5-Comprehensive-example.md +++ b/docs/5-Comprehensive-example.md @@ -1,290 +1,7 @@ -[Back](./) +[Back](./README.md) -```c -/* ****************************************************************** * - * Program: calculator.c - * Author: Rui Deleterium - * - * This contract is online at S-6CPX-Y8EH-6G3E-FW3R3 - * - * Contract expects a message with 3 arguments: Number Operator Number. - * Arguments must have only one space between them. - * Numbers >= 0, base 10 and < 100.000.000 - * Operator can be: + - * / - * If no message is given, contract thanks the donation. - * If message was not undestood, contract sends an explanation - * If message was undestood, contract sends a message with operation - * and the result MOD 100.000.000 - * ****************************************************************** */ -#pragma version dev +## More examples +* Check [samples](https://github.com/deleterium/SmartC/tree/main/samples) on github. +* Check [commemorative](./commemorative/README.md) contracts. -#program name Calculator -#program description Do operations as requested by message. -#program activationAmount 10000000 - -#include APIFunctions - -#pragma maxAuxVars 3 - -long ONE_SIGNUM = 1_0000_0000; -long n8 = 8, n10 = 10, n255 = 0xff; - -void process_TX(void) { - - long values[3], beauty_msg[7], result; - - if (currentTX.message[0] == 0) { //no message - send_message.recipient = currentTX.sender; - beauty_msg[0]="Thanks "; - beauty_msg[1]="for "; - beauty_msg[2]=ltoa(currentTX.amount / ONE_SIGNUM ); - beauty_msg[3]=" signum "; - beauty_msg[4]="donation"; - beauty_msg[5]="!"; - concat(beauty_msg, 6, send_message.message, 4); - Send_Message(); - return; - } - - if (split(" ", currentTX.message, 4, values, 3) != 3) { - send_message.recipient = currentTX.sender; - send_message.message[]="Send: number [+-*/] number."; - Send_Message(); - return; - } - - if (values[1] == "+") { - result = ltoa(atol(values[0]) + atol(values[2])); - - } else if (values[1] == "*") { - result = ltoa(atol(values[0]) * atol(values[2])); - - } else if (values[1] == "-") { - result = atol(values[0]) - atol(values[2]); - if (result < 0){ - beauty_msg[0] = '-'; - beauty_msg[1] = ltoa(-result); - concat(beauty_msg, 2, &result, 1); - } else { - result = ltoa(result); - } - - } else if (values[1] == "/") { - result = atol(values[2]); - if (result == 0) { - result = "div/0"; - } else { - result = ltoa(atol(values[0]) / result); - } - - } else { - send_message.recipient = currentTX.sender; - send_message.message[] = "Unknow operator. Use +-*/"; - Send_Message(); - return; - } - - beauty_msg[0] = values[0]; - beauty_msg[1] = " "; - beauty_msg[2] = values[1]; - beauty_msg[3] = " "; - beauty_msg[4] = values[2]; - beauty_msg[5] = " = "; - beauty_msg[6] = result; - - send_message.recipient = currentTX.sender; - concat(beauty_msg, 7, send_message.message, 4); - Send_Message(); -} - -struct TXINFO { - long timestamp; - long sender; - long amount; - long message[4]; -} currentTX; - -// A must have a TX_Timestamp! -void getTxDetails(void) { - currentTX.amount = Get_Amount_For_Tx_In_A(); - currentTX.timestamp = Get_Timestamp_For_Tx_In_A(); - Message_From_Tx_In_A_To_B(); - currentTX.message[0]=Get_B1(); - currentTX.message[1]=Get_B2(); - currentTX.message[2]=Get_B3(); - currentTX.message[3]=Get_B4(); - B_To_Address_Of_Tx_In_A(); - currentTX.sender = Get_B1(); -} - -struct SENDMESSAGE { - long recipient; - long message[4]; -} send_message; -void Send_Message(void) { - Set_B1(send_message.recipient); - Set_A1_A2(send_message.message[0], send_message.message[1]); - Set_A3_A4(send_message.message[2], send_message.message[3]); - Send_A_To_Address_In_B(); -} - -// Split string function in C -// Expects: -// 'separator' to be used (only LSB will be used). -// 'source' is the array with text to be splitted. -// 'source_length' is the size of source (or the numbers of longs that -// will be processed -// 'ret' is return buffer array. -// 'ret_length' is the size of return buffer, to avoid buffer overflow -// Returns: number of fields filled -// The function will keep adding chars until fill return buffer. If a -// string is bigger than 8 chars, only 8 last chars will be returned -// at that field. -long split(long separator, long * source, long source_length, long * ret, long ret_length) -{ - long field, i_act_arg, i_ret, i_param, act_arg, chr ; - - //clear destination buffer - for (i_ret = 0; i_ret < ret_length; i_ret++) { - ret[i_ret] = 0; - } - - i_act_arg = 0; //cycle bytes in actual string beeing processed(param buffer) (0 to 8) - i_param = 0; //current element in param buffer (0 to 4) - field = 0; //current element in return buffer (ret)(0 to 10 in this example) - i_ret = 0; //var to cycle bytes for each return buffer (from 0 to 8 ) - - while (i_param < source_length) { - act_arg = source[i_param]; - chr = act_arg & 0xff; - while (chr != 0) { - if (chr == separator){ - field++; - i_ret = 0; - } else { - if (i_ret == 8) { // ret[i_ret] is full, shift and continue - ret[field] >>= 8; - i_ret--; - } - ret[field] += chr << (8 * i_ret); - i_ret++; - } - i_act_arg++; - if (field == ret_length) { // End of destination buffer, go to end - return ++field; - } - if (i_act_arg == 8) { //end of actual va_arg, go to next va_arg - break; //break second while loop - } else { // prepare char for next merge - chr = act_arg & (0xff << (8 * i_act_arg)); - chr >>= 8 * i_act_arg; - } - } - i_param++; - i_act_arg = 0; - } - - return ++field; -} - -// String concatenation function in C -// Expects: -// 'source' is an array with content -// 'source_length' is size of source (in longs) or the numbers of longs to be processed -// 'ret' is return buffer array. -// 'ret_length' is the size of ret (in longs) to avoid buffer overflow. -// Function returns the number of bytes processed. A number equal ret_length*8 -// can denote that buffer was too short for the content in 'source'. -long concat(long * source, long source_length, long * ret, long ret_length) -{ - long i_param, act_arg, chr, i_ret, i_buffer, i_act_arg; - - //clear destination buffer - for (i_buffer = 0; i_buffer < ret_length; i_buffer++) { - ret[i_buffer] = 0; - } - - i_ret = 0; //var to cycle bytes for each return buffer (from 0 to 8 ) - i_buffer = 0; //var to cycle for each buffer available (from 0 to ret_length) - i_param = 0; //var to cycle for each source items provided (from 0 to source_length) - i_act_arg= 0; //var to cycle bytes in actual source(long) beeing processed (0 to 8) - - while (i_param < source_length) { //loop thru source_length - act_arg = source[i_param]; // access source - chr = act_arg & 0xff; //this always first char, no need to shift - - while (chr != 0) { //loop bytes in va_arg beeing processed (act_arg) - ret[i_buffer] += chr << (8 * i_ret); - i_act_arg++; - i_ret++; - - if (i_ret == 8) { // ret[i_buffer] is full, go to next ret value - i_buffer++; - i_ret = 0; - if (i_buffer == ret_length) { // End of destination buffer, go to end - goto all_loops_end; //break 2 loops - } - } - if (i_act_arg == 8) { //end of actual va_arg, go to next va_arg - break; //break second while loop - } else { // prepare char for next merge - chr = act_arg & ( 0xff << (8 * i_act_arg) ); - chr >>= 8 * i_act_arg; - } - } - i_param++; - i_act_arg = 0; - } - - all_loops_end: - return i_ret + (8 * i_buffer); -} - -// ASCII to Long (base10 positive and less than 100.000.000) -// Iterative function to implement atoi() clone function in C -// Expects a long containing a string. If any byte is not a char numeric -// representation, then stop and return. Only positive numbers, base10, -// and integers are converted. Returns zero if no number was processed. -long atol(long val) -{ - long ret = 0, chr; - do { - chr = (0xff & val) - '0'; - if (chr < 0 || chr >= 10) - break; - ret *= 10; - ret += chr; - val >>= 8; - } while (true); - return ret; -} -// Integer to ASCII (base10 positive and less than 100.000.000) -// Iterative function to implement itoa() clone function in C -// Expects a long. If number is negative or bigger than MAX_LTOA -// (it will not fit in a long), returns long meaning "#error". -#define MAX_LTOA 99999999 -long ltoa(long val) -{ - long ret; - if (val < 0 || val > MAX_LTOA) - return "#error"; - ret = 0; - do { - ret <<= 8; - ret += '0' + val % 10; - val /= 10; - } while (val != 0); - return ret; -} - -void main(void) { - for (A_To_Tx_After_Timestamp(currentTX.timestamp); Get_A1() != 0; A_To_Tx_After_Timestamp(currentTX.timestamp)) { - // Update transaction variables - getTxDetails(); - process_TX(); - } - //clean_up(); -} -``` - -[Back](./) +[Back](./README.md) diff --git a/docs/6-Deeper-into-SmartC.md b/docs/6-Deeper-into-SmartC.md index 4602c7b..9e72c51 100644 --- a/docs/6-Deeper-into-SmartC.md +++ b/docs/6-Deeper-into-SmartC.md @@ -1,4 +1,4 @@ -[Back](./) +[Back](./README.md) ### Operators precedence Following table presents operators precedence order that are [based on C](https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence) but with some simplifications. When two or more symbols with same precedence are in an expression, the operations will be evaluated from left to right, with exception for unary operators, assignment and keyword. Example: `a=16/4/4` will be evaluated as `a=(16/4)/4`, just like in C. If in doubt, use parenthesis! @@ -15,7 +15,7 @@ Following table presents operators precedence order that are [based on C](https: | 7 | `&` `^` `|` | Bitwise AND XOR OR | Left-to-right | | 8 | `&&` | Logical AND | Left-to-right | | 9 | `||` | Logical OR | Left-to-right | -| 10 | `=` `+=` `-=` `*=` `/=` `%=` `&=` `\=` `;=` `^=` `<<=` `>>=` | Assignment operators| Right-to-left | +| 10 | `=` `+=` `-=` `/=` `*=` `%=` `&=` `^=` `|=` `<<=` `>>=` | Assignment operators| Right-to-left | | 11 | `,` | Delimiter, comma | Left-to-right | | 12 | `;` `keywords` | Terminator, semi, keywords other than sizeof | Right-to-left | @@ -34,7 +34,7 @@ Tokens are divided in groups and later on checked if their combinations are synt | UnaryOperator | `!` `~` | Tokens that are undoubtly unary operators and have no other interpretation. | | SetUnaryOperator | `++` `--` | Special unary operations with same meaning in C - pre/post increment/decrement | | Assignment | `=` | Common assignment operation | -| SetOperator | `+=` `-=` `/=` `*=` `%=` `<<=` `>>=` `&=` `|=` | Special assignment operations | +| SetOperator | `+=` `-=` `/=` `*=` `%=` `&=` `^=` `|=` `<<=` `>>=` | Special assignment operations | | Comparision | `==` `<=` `<` `>` `>=` `!=` `&&` `||` | Logical comparisions operations | | CheckOperator | `+` `-` `*` `&` | Tokens that have two meanings and need to be checked agains previous tokens to know their behaviour. After parsed they are treated as UnaryOperator or Operator | | Arr | `[expr]` | Representation of an array index. Must have a variable before it. | @@ -48,4 +48,4 @@ Tokens are divided in groups and later on checked if their combinations are synt ### Internal object structure Please refer to typescript source code for details. -[Back](./) +[Back](./README.md) diff --git a/docs/Non-Technical-FAQ.md b/docs/Non-Technical-FAQ.md index 80a8350..ad9d25c 100644 --- a/docs/Non-Technical-FAQ.md +++ b/docs/Non-Technical-FAQ.md @@ -1,4 +1,4 @@ -[Back](./) +[Back](./README.md) # Non-Technical frequently asked questions @@ -32,4 +32,4 @@ Just set a higher minimum activation amount, to ensure the contract will have ba ### What happens if I send a value higher than activation amount to the contract? The contract will be activated in next block and do what it is programed to do. The value above the activation amount will be the value that the contract reads as transaction amount received. -[Back](./) +[Back](./README.md) diff --git a/docs/README.md b/docs/README.md index 8caa5b0..0cce105 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,6 +14,8 @@ To empower developers, allowing them to create complex and highly optimized smar ### Technical documentation: * [Basis](./1-Basis.md) +* [Preprocessor directives](./1.2-Preprocessor-directives.md) +* [Built-in functions](./1.5-Built-in-functions.md) * [API Functions](./2-API-Pseudo-Code.md) * [Learning with examples](./3-Learning-with-examples.md) * [Functions repository](./4-Functions-repository.md) diff --git a/docs/commemorative/README.md b/docs/commemorative/README.md index 6344290..ed5e1d2 100644 --- a/docs/commemorative/README.md +++ b/docs/commemorative/README.md @@ -9,3 +9,9 @@ Avoid eavesdroppers to track your transactions and add a layer of obfuscation in ## v1.0 - SmartC NFT with muscles Upgraded version of SmartC NFT, featuring dividends distribuiton for owners. Dividends will be provided by NFT sell/transfer fee and from future projects. This smart contract replaces the old one and owners have to migrate. [Details](./v1.0_SmartC_NFT.md) + +## v2.0 - The Mining Game +Every hour one TMG token is mined by smart contracts deployed by players. +Holders of TMG can even receive Signa from the game. +A kind of "proof-of-donation", where more you donate to players, more chance to mine a block. +[Details](./v2.0_The_Mining_Game.md) diff --git a/docs/commemorative/v0.1_SmartC_NFT.md b/docs/commemorative/v0.1_SmartC_NFT.md index dbd7f52..a377da0 100644 --- a/docs/commemorative/v0.1_SmartC_NFT.md +++ b/docs/commemorative/v0.1_SmartC_NFT.md @@ -1,3 +1,7 @@ +**DEPRECATION NOTICE:** + +This contract may be or not be compatible with SmartC version greater or equal 2 because Signum Rainbow Hard Fork broke some compatibilities. Test before use or convert the API calls to new built-in functions. + # SmartC NFT Be owner of SmartC keywords, support the project and also make an investment! The first multi function and multi items smart contract on Signum blockchain diff --git a/docs/commemorative/v0.2_PromotionalRaffle227.md b/docs/commemorative/v0.2_PromotionalRaffle227.md index 730b7ff..008aea4 100644 --- a/docs/commemorative/v0.2_PromotionalRaffle227.md +++ b/docs/commemorative/v0.2_PromotionalRaffle227.md @@ -1,3 +1,7 @@ +**DEPRECATION NOTICE:** + +This contract may be or not be compatible with SmartC version greater or equal 2 because Signum Rainbow Hard Fork broke some compatibilities. Test before use or convert the API calls to new built-in functions. + # Promotional Raffle 227 Advertise your brand making a fair raffle on signum blockchain! Contract online at S-GWV4-S4EK-HAG3-EWCJV. **This is a promotional raffle, where the raffle starter or contract creator does not earn any fee!** diff --git a/docs/commemorative/v0.3_Hive_The_Tumbler.md b/docs/commemorative/v0.3_Hive_The_Tumbler.md index c603404..106a6d8 100644 --- a/docs/commemorative/v0.3_Hive_The_Tumbler.md +++ b/docs/commemorative/v0.3_Hive_The_Tumbler.md @@ -1,3 +1,7 @@ +**DEPRECATION NOTICE:** + +This contract may be or not be compatible with SmartC version greater or equal 2 because Signum Rainbow Hard Fork broke some compatibilities. Test before use or convert the API calls to new built-in functions. + # Hive, the tumbler The tumbler objective is to split and bounce transactions, making dificult to trace back transactions. **This contract swarm does not make the payments anonymous, but it adds a layer of obfuscation against eavesdroppers** diff --git a/docs/commemorative/v1.0_SmartC_NFT.md b/docs/commemorative/v1.0_SmartC_NFT.md index 452f9f2..4806b99 100644 --- a/docs/commemorative/v1.0_SmartC_NFT.md +++ b/docs/commemorative/v1.0_SmartC_NFT.md @@ -1,3 +1,7 @@ +**DEPRECATION NOTICE:** + +This contract may be or not be compatible with SmartC version greater or equal 2 because Signum Rainbow Hard Fork broke some compatibilities. Test before use or convert the API calls to new built-in functions. + # SmartC NFT with muscles Be owner of SmartC keyword and receive dividends for this and future projects! diff --git a/docs/commemorative/v2.0_The_Mining_Game.md b/docs/commemorative/v2.0_The_Mining_Game.md new file mode 100644 index 0000000..9cffe82 --- /dev/null +++ b/docs/commemorative/v2.0_The_Mining_Game.md @@ -0,0 +1,279 @@ +# The Mining Game +Did you ever though about a token that is mined by smart contract? And proof-of-donation? + +## How it works +The system consists in two types of smart contracts: TMGMiner and TMGPicker. Check the concepts: + +### Miner +This smart contract is deployed by users. After add balance to the contract it will start to send deadlines to the main Picker contract. By default it will start by mining with the least amount possible (0.32 Signa per hour or 7.6 Signa per day). The creator can change the mining intensity. Send at least 0.5 Signa and add a plain message with the new value. Ex: `1.344`. The contract will continue the loop until there is no more balance. At any time creator can send some amount of Signa to top up the balance and the mining intesity will continue the same. To stop the contract and receive the remaining balance, send a transaction with amount over 0.5 and the plain text `stop`. In less than one hour the contract will be activated again and the balance will be sent back. + +### Picker +Smart contract deployed by deleterium, will centralize all received deadlines, pick the best, forge one token and send directly to the creator of that Miner. The picker will inspect the contract that send the deadline to ensure it is a valid Miner contract, to avoid deadline tampering. When a new deadline is submited after 15 or more blocks from previous forging, the contract will mint one token and transfer to the creator of the contract that sent the best deadline. This process will cause balance to build up in the contract and once a day it will be distributed to the holders of TMG asset, if the balance is greater than 250 Signa. + +### Deadlines +The miner contract will send a transaction to the picker. This transaction has an ID that can not be controlled by the creators. It depends on the time that the block was forged, so it is suposed to be random (note 1). To calculate the deadline, the Picker will: +1. Get the transaction ID value and divide by two. This is the base deadline. Division is needed to avoid negative values internally. +2. Get the transaction amount (mining intensity) and divide by the minimum amount (0.32 Signa). This factor (always greater or equal one) is called mining factor. +3. The final deadline is the base deadline divided by the mining factor. So the greater the mining factor, lower the deadline. +In this way, users can adjust the mining intensity to increase change to win a block, and with bigger overall mining intensity, more Signa will be distributed to TMG token holders. + +### Signa distribution +Once per day the Picker will check the accumulated balance in the contract. If this amount is greater than 250 Signa, 98% of it will be distributed to holders that have at least 1.0 TMG available in account (not on sell orders!). There is also a creator fee of 2%, that will be forwarded to SmartC NFT contract and distributed to the holders of SmartC Keywords. + +### Burned fees +When the contracts are running at the least mining intensity, almost all Signa will be consumed by contract execution. This is the way the smart contracts work, because each machine code instructions costs 0.001 Signa. With more players with bigger mining intensity, more balance will accumulate in the Picker contract to be distributed. + +## The web site +In the future a web site will show the network status, the the current overall mining factor, best deadline, last winner, last winner and more! Users will have option to link their accounts and deploy, check and control their Miner. + +## Notes: +1. In theory the transaction ID from a smart contract can be manipulated during an attack from a bad actor with more than 50% Signum mining power. This is unlikely and, if done, the entire blockchain will be compromised. For this game purpose this situation is negligible. + +## Contracts source code + +### Miner +```c +// Select the picker contract accordingly: +// 999 for SIMULATOR +// 8749786126809286749 for TESTNET +// YYY for MAINNET +#define PICKER_CONTRACT 8749786126809286749 +// #pragma verboseAssembly + +/* Do not change below this line */ + +/* Details for carbon copy TESTNET + * Fullhash: 7ab1ed13566118fb8f36c05e25963d184875b4f0651d234f754161da5fd88059 + * feeNQT: 10000000 + */ + +#define ACTIVATION_AMOUNT .5 + +#program name TMGminer +#program description The Mining Game - Standard miner smart contract +#program activationAmount ACTIVATION_AMOUNT +#program creator 123124 +#program codeHashId 5817622329198284865 + +#pragma maxConstVars 1 +#pragma optimizationLevel 3 +#pragma version 2.0 + +#define CLOCK 15 +#define DEFAULT_MINING_INTENSITY .32 + +// Constants initialization +const long pickerContract = PICKER_CONTRACT; +const fixed defaultIntensity = DEFAULT_MINING_INTENSITY; +const long n8 = 8, n10 = 10, n15 = 15, n16 = 16, n32 = 32, n46 = 46; +const long n48 = 48, n57 = 57; +const long n255 = 255, n100000000 = 100000000; + + +// Other variables +fixed miningIntensity, availableBalance; +long txid, message[4]; +fixed messageIntensity; + +// Initialization +miningIntensity = defaultIntensity; + +while (true){ + while ((txid = getNextTx()) != 0) { + // process transaction + if (getSender(txid) != getCreator()) { + // only process transactions from creator + continue; + } + readMessage(txid, 0, message); + if (message[0] == 'stop') { + miningIntensity = defaultIntensity; + sendBalance(getCreator()); + continue; + } + // start of decodeAmountInMessage + long multiplier = 1_0000_0000; + long retVal = 0; + long ch; + long decimals = false; + for (long i = 0; i < 16; i++) { + ch = message[i / 8] >> ((i % 8) * 8); + ch &= 0xff; + if (ch == 0 || ch == ' ') break; + if (ch == '.') { + decimals = true; + continue; + } + if (ch < '0' || ch > '9' ) { + // invalid char + retVal = 0; + break; + } + if (decimals) { + multiplier /= 10; + } else { + retVal *= 10; + } + ch &= 0xF; + ch *= multiplier; + retVal += ch; + } + memcopy(&messageIntensity, &retVal); + // end of decodeAmountInMessage + if (messageIntensity < defaultIntensity) { + // Error decoding, no message sent or value too low + continue; + } + miningIntensity = messageIntensity; + } + sendAmountFx(miningIntensity, pickerContract); + sleep CLOCK; +} +``` + +### Picker +```c +// Choose the target (only one of ): +//#define SIMULATOR +//#define TESTNET + #define MAINNET + +// #pragma verboseAssembly + +/* Do not change below this line */ + +#program name TMGpicker +#program description The Mining Game - Picker smart contract +#define ACTIVATION_AMOUNT 0.32 +#program activationAmount ACTIVATION_AMOUNT + +#define CLOCK 15 +#define GAME_FEE .02 +#define MINIMUM_AMOUNT_TO_DISTRIBUTE 250.0 +#define DIVIDENDS_MSG 0x83CDA47900000000 +#define authorizedCodeHashId 5817622329198284865 + +#ifdef SIMULATOR + #program codeHashId 1136995200036281707 + #define SMARTCNFT 12341234 + #define TRY_DISTRIBUTION_INTERVAL 1 +#endif +#ifdef TESTNET +#program codeHashId 13677852211975617521 + #define SMARTCNFT "TS-J8X4-6WB2-62W5-6ZTGZ" + #define TRY_DISTRIBUTION_INTERVAL 24 +#endif +#ifdef MAINNET + #program codeHashId 18180423945324926420 + #define SMARTCNFT "S-NFT2-6MA4-KLA2-DNM8T" + #define TRY_DISTRIBUTION_INTERVAL 24 +#endif + +#pragma maxConstVars 2 +#pragma maxAuxVars 3 +#pragma optimizationLevel 3 +// #pragma version 2.0.0 + +const long n0 = 0, n100 = 100, n100000000 = 100000000; +const long maxPositive = 0x7fffffffffffffff; +const fixed activationAmount = ACTIVATION_AMOUNT; + +long tokenId; +long dividendsMessage[4]; + +struct TXINFO { + long txId, + baseDeadline, + sender; + fixed miningIntensity; +} currentTX; + +struct BEST { + long deadline; + long sender; +} best; + +struct STATS { + fixed overallMiningFactor, + lastOverallMiningFactor; + long processedDeadlines, + currentHeight, + lastWinnerId, + lastWinnerDeadline; +} stats; + +// startUp routine +tokenId = issueAsset("TMG", "", 2); +best.deadline = maxPositive; +dividendsMessage[] = DIVIDENDS_MSG; + +void main() { + while ((currentTX.txId = getNextTx()) != 0) { + // Get transaction details + currentTX.sender = getSender(currentTX.txId); + currentTX.miningIntensity = getAmountFx(currentTX.txId) + activationAmount; + // base deadline is unsigned long txId divided by two + currentTX.baseDeadline = currentTX.txId / 2; + if (currentTX.baseDeadline < 0) { + currentTX.baseDeadline += maxPositive; + currentTX.baseDeadline++; + } + processTX(); + } + // All transactions processed, try to forge new token + forgeTokens(); +} + +void processTX() { + fixed miningFactor = currentTX.miningIntensity / activationAmount; + long currentDeadline = mdv(currentTX.baseDeadline, 100000000, bcftol(miningFactor)); + stats.processedDeadlines++; + stats.overallMiningFactor += miningFactor; + if (currentDeadline < best.deadline) { + if (getCodeHashOf(currentTX.sender) != authorizedCodeHashId) { + // Avoid deadline tampering + return; + } + best.deadline = currentDeadline; + best.sender = currentTX.sender; + } +} + +void forgeTokens() { + long lastForging; + long currentBlock = getCurrentBlockheight(); + if (best.deadline == maxPositive || currentBlock - lastForging < CLOCK ) { + return; + } + // Mint new token and send to winner + stats.lastOverallMiningFactor = stats.overallMiningFactor; + stats.lastWinnerDeadline = best.deadline; + lastForging = currentBlock; + stats.lastWinnerId = getCreatorOf(best.sender); + mintAsset(100, tokenId); + sendQuantity(100, tokenId, stats.lastWinnerId); + // Try distribuition + if (stats.currentHeight % TRY_DISTRIBUTION_INTERVAL == 0) { + distributeBalance(); + } + // Prepare variables for next round + best.deadline = maxPositive; + best.sender = 0; + stats.overallMiningFactor = 0.0; + stats.currentHeight++; +} + +void distributeBalance() { + fixed currentAvailableBalance = getCurrentBalanceFx() - activationAmount; + if (currentAvailableBalance < MINIMUM_AMOUNT_TO_DISTRIBUTE) { + return; + } + distributeToHoldersFx(100, tokenId, currentAvailableBalance * (1.0 - GAME_FEE), n0, n0); + if (getCurrentBalanceFx() > currentAvailableBalance / 2) { + // Do not pay fee if the distribution was unsucessful + return; + } + // Pay Game Fee + sendAmountAndMessageFx(currentAvailableBalance * GAME_FEE, dividendsMessage, SMARTCNFT); +} +``` diff --git a/package-lock.json b/package-lock.json index d369734..e55cdf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,26 +1,27 @@ { "name": "smartc-signum-compiler", - "version": "1.0.1", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "smartc-signum-compiler", - "version": "1.0.1", + "version": "2.0.0", "license": "BSD-3-Clause", "devDependencies": { - "@types/jest": "^27.0.2", - "@typescript-eslint/eslint-plugin": "^4.29.1", - "@typescript-eslint/parser": "^4.29.1", - "esbuild": "^0.13.8", + "@types/jest": "^27.5.1", + "@typescript-eslint/eslint-plugin": "^4.33.0", + "@typescript-eslint/parser": "^4.33.0", + "esbuild": "^0.13.15", "eslint": "^7.32.0", "eslint-config-standard": "^16.0.3", - "eslint-plugin-import": "^2.24.0", + "eslint-plugin-import": "^2.26.0", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.1.0", - "jest": "^27.3.1", - "ts-jest": "^27.0.7", - "typescript": "^4.3.5" + "eslint-plugin-promise": "^5.2.0", + "jest": "^27.5.1", + "light-server": "^2.9.1", + "ts-jest": "^27.1.5", + "typescript": "^4.4.4" } }, "node_modules/@ampproject/remapping": { @@ -55,21 +56,21 @@ } }, "node_modules/@babel/core": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.10.tgz", - "integrity": "sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.0.tgz", + "integrity": "sha512-Xyw74OlJwDijToNi0+6BBI5mLLR5+5R3bcSH80LXzjzEGEUlvNzujEE71BaD/ApEZHAvFI/Mlmp4M5lIkdeeWw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", + "@babel/generator": "^7.18.0", "@babel/helper-compilation-targets": "^7.17.10", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.10", + "@babel/helper-module-transforms": "^7.18.0", + "@babel/helpers": "^7.18.0", + "@babel/parser": "^7.18.0", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.10", - "@babel/types": "^7.17.10", + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -106,19 +107,33 @@ } }, "node_modules/@babel/generator": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz", - "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.0.tgz", + "integrity": "sha512-81YO9gGx6voPXlvYdZBliFXAZU8vZ9AZ6z+CjlmcnaeOcYSFbMTpdeDUO9xD9dh/68Vq03I8ZspfUTPfitcDHg==", "dev": true, "dependencies": { - "@babel/types": "^7.17.10", - "@jridgewell/gen-mapping": "^0.1.0", + "@babel/types": "^7.18.0", + "@jridgewell/gen-mapping": "^0.3.0", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", + "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.17.10", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", @@ -196,9 +211,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz", + "integrity": "sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.16.7", @@ -207,17 +222,17 @@ "@babel/helper-split-export-declaration": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz", + "integrity": "sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -266,23 +281,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", - "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.0.tgz", + "integrity": "sha512-AE+HMYhmlMIbho9nbvicHyxFwhrO+xhKB6AhRxzl8w46Yj0VXTZjEsAoBVC7rB2I0jzX+yWyVybnO08qkfx6kg==", "dev": true, "dependencies": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0" + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", + "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -365,9 +380,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", - "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-AqDccGC+m5O/iUStSJy3DGRIUFu7WbY/CppZYwrEUB4N0tZlnI8CSTsgL7v5fHVFmUbRv2sd+yy27o8Ydt4MGg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -524,12 +539,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.10.tgz", - "integrity": "sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ==", + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz", + "integrity": "sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.17.12" }, "engines": { "node": ">=6.9.0" @@ -565,19 +580,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz", - "integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.0.tgz", + "integrity": "sha512-oNOO4vaoIQoGjDQ84LgtF/IAlxlyqL4TUuoQ7xLkQETFaHkY1F7yazhB4Kt3VcZGL0ZF/jhrEpnXqUb0M7V3sw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", + "@babel/generator": "^7.18.0", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.10", - "@babel/types": "^7.17.10", + "@babel/parser": "^7.18.0", + "@babel/types": "^7.18.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -607,9 +622,9 @@ } }, "node_modules/@babel/types": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz", - "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.0.tgz", + "integrity": "sha512-vhAmLPAiC8j9K2GnsnLPCIH5wCrPpYIVBCWRBFDCB7Y/BXLqi/O+1RSTTM2bsmg6U/551+FCf9PNPxjABmxHTw==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -1220,13 +1235,13 @@ "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "node_modules/@types/node": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.33.tgz", - "integrity": "sha512-miWq2m2FiQZmaHfdZNcbpp9PuXg34W5JZ5CrJ/BaS70VuhoJENBEQybeiYSaPBRNq6KQGnjfEnc/F3PN++D+XQ==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz", + "integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==", "dev": true }, "node_modules/@types/prettier": { @@ -1312,6 +1327,33 @@ "eslint": "*" } }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/parser": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", @@ -1339,28 +1381,42 @@ } } }, - "node_modules/@typescript-eslint/scope-manager": { + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", "dev": true, "dependencies": { "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^10.12.0 || >=12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/types": { + "node_modules/@typescript-eslint/scope-manager": { "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", + "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0" + }, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, @@ -1369,31 +1425,17 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/types": { "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, "node_modules/@typescript-eslint/visitor-keys": { @@ -1419,6 +1461,19 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -1488,9 +1543,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.2.tgz", - "integrity": "sha512-cEG18jjLG0O74o/33eEfnmtXYDEY196ZjL0eQEISULF+Imi7vr25l6ntGYmqS5lIrQIEeze+CqUtPVItywE7ZQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -1627,7 +1682,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "node_modules/babel-jest": { @@ -1728,6 +1783,24 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1953,12 +2026,90 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect-injector": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/connect-injector/-/connect-injector-0.4.4.tgz", + "integrity": "sha512-hdBG8nXop42y2gWCqOV8y1O3uVk4cIU+SoxLCPyCUKRImyPiScoNiSulpHjoktRU1BdI0UzoUdxUa87thrcmHw==", + "dev": true, + "dependencies": { + "debug": "^2.0.0", + "q": "^1.0.1", + "stream-buffers": "^0.2.3", + "uberproto": "^1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/connect-injector/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect-injector/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -1968,6 +2119,12 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2089,6 +2246,21 @@ "node": ">=0.4.0" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -2098,6 +2270,12 @@ "node": ">=8" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, "node_modules/diff-sequences": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", @@ -2152,6 +2330,12 @@ "node": ">=8" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, "node_modules/electron-to-chromium": { "version": "1.4.137", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", @@ -2176,6 +2360,15 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -2198,9 +2391,9 @@ } }, "node_modules/es-abstract": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.0.tgz", - "integrity": "sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", @@ -2222,7 +2415,7 @@ "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.4.1", + "regexp.prototype.flags": "^1.4.3", "string.prototype.trimend": "^1.0.5", "string.prototype.trimstart": "^1.0.5", "unbox-primitive": "^1.0.2" @@ -2519,6 +2712,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3068,6 +3267,21 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3191,6 +3405,39 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -3222,6 +3469,26 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", + "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -3236,6 +3503,15 @@ "node": ">= 6" } }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3295,6 +3571,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3437,6 +3722,12 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3506,16 +3797,52 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "dev": true, "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" } }, "node_modules/html-escaper": { @@ -3524,6 +3851,56 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -3897,6 +4274,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4753,6 +5136,31 @@ "node": ">= 0.8.0" } }, + "node_modules/light-server": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/light-server/-/light-server-2.9.1.tgz", + "integrity": "sha512-8uerqP4ffFbTJZ2QGR1225TqZUWEFkl/kGnJ+vRGiaqnLr6pFj8XLFGyO1XgO8ib9NQKxd7gsq3pEYN3AB+Q2g==", + "dev": true, + "dependencies": { + "commander": "^6.0.0", + "connect": "^3.7.0", + "connect-history-api-fallback": "^1.6.0", + "connect-injector": "^0.4.4", + "gaze": "^1.1.3", + "http-proxy": "^1.18.1", + "morgan": "~1.10.0", + "opener": "^1.5.1", + "parseurl": "^1.3.3", + "serve-index": "^1.9.1", + "serve-static": "~1.14.1", + "spdy": "^4.0.2", + "strip-json-comments": "^3.1.1", + "ws": "^7.3.1" + }, + "bin": { + "light-server": "bin/light-server" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -4875,6 +5283,18 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4905,6 +5325,12 @@ "node": ">=6" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4923,6 +5349,37 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dev": true, + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4935,6 +5392,15 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5027,6 +5493,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5051,6 +5544,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -5137,6 +5639,15 @@ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -5323,6 +5834,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -5360,6 +5877,16 @@ "node": ">=6" } }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5380,12 +5907,35 @@ } ] }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -5433,6 +5983,12 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "node_modules/resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -5561,6 +6117,12 @@ "node": ">=10" } }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, "node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -5576,6 +6138,136 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/send/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/send/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5668,6 +6360,36 @@ "source-map": "^0.6.0" } }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -5695,6 +6417,53 @@ "node": ">=8" } }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-buffers": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-0.2.6.tgz", + "integrity": "sha512-ZRpmWyuCdg0TtNKk8bEqvm13oQvXMmzXDsfD4cBgcx5LouborvU5pm3JMkdTP3HcszyUI08AM1dHMXA5r2g6Sg==", + "dev": true, + "engines": { + "node": ">= 0.3.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5942,6 +6711,15 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, "node_modules/tough-cookie": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", @@ -5969,9 +6747,9 @@ } }, "node_modules/ts-jest": { - "version": "27.1.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", - "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", + "version": "27.1.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", + "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -6108,9 +6886,9 @@ } }, "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6120,6 +6898,15 @@ "node": ">=4.2.0" } }, + "node_modules/uberproto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/uberproto/-/uberproto-1.2.0.tgz", + "integrity": "sha1-YdTqsCT5CcTm6lK+hnxIlKS+63Y=", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -6144,6 +6931,15 @@ "node": ">= 4.0.0" } }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6153,6 +6949,21 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -6212,6 +7023,15 @@ "makeerror": "1.0.12" } }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -6428,21 +7248,21 @@ "dev": true }, "@babel/core": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.10.tgz", - "integrity": "sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.0.tgz", + "integrity": "sha512-Xyw74OlJwDijToNi0+6BBI5mLLR5+5R3bcSH80LXzjzEGEUlvNzujEE71BaD/ApEZHAvFI/Mlmp4M5lIkdeeWw==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", + "@babel/generator": "^7.18.0", "@babel/helper-compilation-targets": "^7.17.10", - "@babel/helper-module-transforms": "^7.17.7", - "@babel/helpers": "^7.17.9", - "@babel/parser": "^7.17.10", + "@babel/helper-module-transforms": "^7.18.0", + "@babel/helpers": "^7.18.0", + "@babel/parser": "^7.18.0", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.10", - "@babel/types": "^7.17.10", + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -6468,14 +7288,27 @@ } }, "@babel/generator": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.10.tgz", - "integrity": "sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.0.tgz", + "integrity": "sha512-81YO9gGx6voPXlvYdZBliFXAZU8vZ9AZ6z+CjlmcnaeOcYSFbMTpdeDUO9xD9dh/68Vq03I8ZspfUTPfitcDHg==", "dev": true, "requires": { - "@babel/types": "^7.17.10", - "@jridgewell/gen-mapping": "^0.1.0", + "@babel/types": "^7.18.0", + "@jridgewell/gen-mapping": "^0.3.0", "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", + "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } } }, "@babel/helper-compilation-targets": { @@ -6536,9 +7369,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", - "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz", + "integrity": "sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", @@ -6547,14 +7380,14 @@ "@babel/helper-split-export-declaration": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", - "@babel/types": "^7.17.0" + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0" } }, "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz", + "integrity": "sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==", "dev": true }, "@babel/helper-simple-access": { @@ -6588,20 +7421,20 @@ "dev": true }, "@babel/helpers": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", - "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.0.tgz", + "integrity": "sha512-AE+HMYhmlMIbho9nbvicHyxFwhrO+xhKB6AhRxzl8w46Yj0VXTZjEsAoBVC7rB2I0jzX+yWyVybnO08qkfx6kg==", "dev": true, "requires": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.9", - "@babel/types": "^7.17.0" + "@babel/traverse": "^7.18.0", + "@babel/types": "^7.18.0" } }, "@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", + "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -6668,9 +7501,9 @@ } }, "@babel/parser": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", - "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-AqDccGC+m5O/iUStSJy3DGRIUFu7WbY/CppZYwrEUB4N0tZlnI8CSTsgL7v5fHVFmUbRv2sd+yy27o8Ydt4MGg==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -6782,12 +7615,12 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.10.tgz", - "integrity": "sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ==", + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz", + "integrity": "sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.17.12" } }, "@babel/template": { @@ -6813,19 +7646,19 @@ } }, "@babel/traverse": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz", - "integrity": "sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.0.tgz", + "integrity": "sha512-oNOO4vaoIQoGjDQ84LgtF/IAlxlyqL4TUuoQ7xLkQETFaHkY1F7yazhB4Kt3VcZGL0ZF/jhrEpnXqUb0M7V3sw==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.10", + "@babel/generator": "^7.18.0", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.10", - "@babel/types": "^7.17.10", + "@babel/parser": "^7.18.0", + "@babel/types": "^7.18.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -6848,9 +7681,9 @@ } }, "@babel/types": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.10.tgz", - "integrity": "sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.0.tgz", + "integrity": "sha512-vhAmLPAiC8j9K2GnsnLPCIH5wCrPpYIVBCWRBFDCB7Y/BXLqi/O+1RSTTM2bsmg6U/551+FCf9PNPxjABmxHTw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -7353,13 +8186,13 @@ "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "@types/node": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.33.tgz", - "integrity": "sha512-miWq2m2FiQZmaHfdZNcbpp9PuXg34W5JZ5CrJ/BaS70VuhoJENBEQybeiYSaPBRNq6KQGnjfEnc/F3PN++D+XQ==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.35.tgz", + "integrity": "sha512-vu1SrqBjbbZ3J6vwY17jBs8Sr/BKA+/a/WtjRG+whKg1iuLFOosq872EXS0eXWILdO36DHQQeku/ZcL6hz2fpg==", "dev": true }, "@types/prettier": { @@ -7417,6 +8250,23 @@ "@typescript-eslint/typescript-estree": "4.33.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + } } }, "@typescript-eslint/parser": { @@ -7429,6 +8279,23 @@ "@typescript-eslint/types": "4.33.0", "@typescript-eslint/typescript-estree": "4.33.0", "debug": "^4.3.1" + }, + "dependencies": { + "@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + } } }, "@typescript-eslint/scope-manager": { @@ -7447,21 +8314,6 @@ "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", "dev": true }, - "@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, "@typescript-eslint/visitor-keys": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", @@ -7478,6 +8330,16 @@ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", "dev": true }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -7529,9 +8391,9 @@ } }, "ansi-colors": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.2.tgz", - "integrity": "sha512-cEG18jjLG0O74o/33eEfnmtXYDEY196ZjL0eQEISULF+Imi7vr25l6ntGYmqS5lIrQIEeze+CqUtPVItywE7ZQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-escapes": { @@ -7625,7 +8487,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "babel-jest": { @@ -7705,6 +8567,21 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -7870,12 +8747,82 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "connect-injector": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/connect-injector/-/connect-injector-0.4.4.tgz", + "integrity": "sha512-hdBG8nXop42y2gWCqOV8y1O3uVk4cIU+SoxLCPyCUKRImyPiScoNiSulpHjoktRU1BdI0UzoUdxUa87thrcmHw==", + "dev": true, + "requires": { + "debug": "^2.0.0", + "q": "^1.0.1", + "stream-buffers": "^0.2.3", + "uberproto": "^1.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, "convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -7885,6 +8832,12 @@ "safe-buffer": "~5.1.1" } }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7979,12 +8932,30 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, "diff-sequences": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", @@ -8026,6 +8997,12 @@ } } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, "electron-to-chromium": { "version": "1.4.137", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", @@ -8044,6 +9021,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -8063,9 +9046,9 @@ } }, "es-abstract": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.0.tgz", - "integrity": "sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -8087,7 +9070,7 @@ "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.4.1", + "regexp.prototype.flags": "^1.4.3", "string.prototype.trimend": "^1.0.5", "string.prototype.trimstart": "^1.0.5", "unbox-primitive": "^1.0.2" @@ -8263,6 +9246,12 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -8665,6 +9654,18 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -8767,6 +9768,38 @@ "to-regex-range": "^5.0.1" } }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -8792,6 +9825,12 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "follow-redirects": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", + "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "dev": true + }, "form-data": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", @@ -8803,6 +9842,12 @@ "mime-types": "^2.1.12" } }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -8846,6 +9891,12 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -8943,6 +9994,12 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -8988,6 +10045,44 @@ "has-symbols": "^1.0.2" } }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -9003,6 +10098,49 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, "http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -9259,6 +10397,12 @@ "call-bind": "^1.0.2" } }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -9925,6 +11069,28 @@ "type-check": "~0.4.0" } }, + "light-server": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/light-server/-/light-server-2.9.1.tgz", + "integrity": "sha512-8uerqP4ffFbTJZ2QGR1225TqZUWEFkl/kGnJ+vRGiaqnLr6pFj8XLFGyO1XgO8ib9NQKxd7gsq3pEYN3AB+Q2g==", + "dev": true, + "requires": { + "commander": "^6.0.0", + "connect": "^3.7.0", + "connect-history-api-fallback": "^1.6.0", + "connect-injector": "^0.4.4", + "gaze": "^1.1.3", + "http-proxy": "^1.18.1", + "morgan": "~1.10.0", + "opener": "^1.5.1", + "parseurl": "^1.3.3", + "serve-index": "^1.9.1", + "serve-static": "~1.14.1", + "spdy": "^4.0.2", + "strip-json-comments": "^3.1.1", + "ws": "^7.3.1" + } + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -10028,6 +11194,12 @@ "picomatch": "^2.3.1" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -10049,6 +11221,12 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -10064,6 +11242,36 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dev": true, + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -10076,6 +11284,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -10144,6 +11358,27 @@ "es-abstract": "^1.19.1" } }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -10162,6 +11397,12 @@ "mimic-fn": "^2.1.0" } }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -10227,6 +11468,12 @@ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -10360,6 +11607,12 @@ } } }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -10388,18 +11641,41 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -10429,6 +11705,12 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -10514,6 +11796,12 @@ "xmlchars": "^2.2.0" } }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -10523,6 +11811,127 @@ "lru-cache": "^6.0.0" } }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + } + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10594,6 +12003,33 @@ "source-map": "^0.6.0" } }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -10617,6 +12053,35 @@ } } }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + }, + "stream-buffers": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-0.2.6.tgz", + "integrity": "sha512-ZRpmWyuCdg0TtNKk8bEqvm13oQvXMmzXDsfD4cBgcx5LouborvU5pm3JMkdTP3HcszyUI08AM1dHMXA5r2g6Sg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -10805,6 +12270,12 @@ "is-number": "^7.0.0" } }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, "tough-cookie": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", @@ -10826,9 +12297,9 @@ } }, "ts-jest": { - "version": "27.1.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.4.tgz", - "integrity": "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==", + "version": "27.1.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", + "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", "dev": true, "requires": { "bs-logger": "0.x", @@ -10916,9 +12387,15 @@ } }, "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "dev": true + }, + "uberproto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/uberproto/-/uberproto-1.2.0.tgz", + "integrity": "sha1-YdTqsCT5CcTm6lK+hnxIlKS+63Y=", "dev": true }, "unbox-primitive": { @@ -10939,6 +12416,12 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -10948,6 +12431,18 @@ "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -11000,6 +12495,15 @@ "makeerror": "1.0.12" } }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/package.json b/package.json index d5ab4a3..c25691e 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,12 @@ { "name": "smartc-signum-compiler", - "version": "1.0.3", + "version": "2.0.0", "description": "C Compiler for smart contracts on Signum network", "main": "dist/smartc.js", "types": "dist/smartc.d.ts", - "files": ["dist"], + "files": [ + "dist" + ], "repository": { "type": "git", "url": "git+https://github.com/deleterium/SmartC.git" @@ -27,20 +29,22 @@ "lint": "npx eslint './src/**/*'", "test": "JEST=true npx jest", "test:ci": "JEST=true npx jest --coverage", + "debug": "node esbuild.config.js && npx light-server -s . -p 7002 --no-reload --historyindex '/debug.html'", "build": "npm run lint && node esbuild.config.js && npx tsc" }, "devDependencies": { - "@types/jest": "^27.0.2", - "@typescript-eslint/eslint-plugin": "^4.29.1", - "@typescript-eslint/parser": "^4.29.1", - "esbuild": "^0.13.8", + "@types/jest": "^27.5.1", + "@typescript-eslint/eslint-plugin": "^4.33.0", + "@typescript-eslint/parser": "^4.33.0", + "esbuild": "^0.13.15", "eslint": "^7.32.0", "eslint-config-standard": "^16.0.3", - "eslint-plugin-import": "^2.24.0", + "eslint-plugin-import": "^2.26.0", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^5.1.0", - "jest": "^27.3.1", - "ts-jest": "^27.0.7", - "typescript": "^4.3.5" + "eslint-plugin-promise": "^5.2.0", + "jest": "^27.5.1", + "light-server": "^2.9.1", + "ts-jest": "^27.1.5", + "typescript": "^4.4.4" } } diff --git a/samples/ConfigurableTimer.md b/samples/ConfigurableTimer.md index 1d2add5..664d8e6 100644 --- a/samples/ConfigurableTimer.md +++ b/samples/ConfigurableTimer.md @@ -1,3 +1,7 @@ +**DEPRECATION NOTICE:** + +This contract may be or not be compatible with SmartC version greater or equal 2 because Signum Rainbow Hard Fork broke some compatibilities. Test before use or convert the API calls to new built-in functions. + # Configurable timer The contract will act as a timer for any incoming transactions, dispatching all balance to target address on next block that is multiple of a configurable number. Creator must configure target address with a message. diff --git a/samples/DeadSmartContract.md b/samples/DeadSmartContract.md index 5c27521..4f9acda 100644 --- a/samples/DeadSmartContract.md +++ b/samples/DeadSmartContract.md @@ -1,5 +1,5 @@ # Dead Smart Contract -Just a dead smart contract. Any balance sent to this address will be included as fee on the next block. Useful to tip a random miner. Online at `S-DEAD-DTMA-RY8B-FWL3D` +Just a dead smart contract. ~~Any balance sent to this address will be included as fee on the next block. Useful to tip a random miner.~~ Signum Rainbow Hard Fork (june 2022) introduced a change where the contract fees are burned, so this contract now only can be used for burning Signa. Online at `S-DEAD-DTMA-RY8B-FWL3D` ## Source code Very simple source, can be compiled and deployed by SmartC if changed C to Assembly source code. diff --git a/samples/EchoAnySize.md b/samples/EchoAnySize.md new file mode 100644 index 0000000..202d036 --- /dev/null +++ b/samples/EchoAnySize.md @@ -0,0 +1,63 @@ +# Echo Any Size +Simple contract that reads the incoming text message until a zero byte is found on last byte of last page read. Clears the rest of message buffer and then send the "same message" to sender. Smart contracts only can send 32 bytes each time, so expect the received message to be multiple of 32, padded with zero bytes. Online at tesnet `TS-LZYH-PE75-JZTB-FJ88Y`. Note that there is no API to get the message size, so the program must handle the input end in some way. Activation amount is huge because the fees to read/send info for a smart contract are much higher than sending manually and the contract must handle input text up the 1000 bytes, the current blockchain limit. + +## Source code + +```c +#program name EchoAnySize +#program description Reads the incoming message until a zero byte\ + is found on last byte of last page read. Clears the rest of buffer\ + and then send the same message to sender. Expect text messages. +#program activationAmount 5_0000_0000 + +struct TXINFO { + long txId; + long sender; + long amount; + long message[132]; +} currentTX; + +long zero; + +while (true) +{ + while ((currentTX.txId = getNextTx()) != 0) { + currentTX.sender = getSender(currentTX.txId); + currentTX.amount = getAmount(currentTX.txId); + readMessage(currentTX.txId, 0, currentTX.message); + + processTX(); + } + sendBalance(getCreator()); +} + +// just echoes a received message back to sender. +void processTX(void) { + + long messagePage, currentLong; + + // Last read on getNextTxDetails + currentLong = 4; + while (currentLong < currentTX.message.length) { + if (((currentTX.message[currentLong - 1]) >> 56) == 0) { + // Found a null byte at last byte of last page that was read. + break; + } + messagePage = currentLong / 4; + readMessage(currentTX.txId, messagePage, currentTX.message + currentLong); + currentLong += 4; + } + while (currentLong < currentTX.message.length) { + // clear the rest of buffer. + currentTX.message[currentLong++] = zero; + currentTX.message[currentLong++] = zero; + currentTX.message[currentLong++] = zero; + currentTX.message[currentLong++] = zero; + } + currentLong = 0; + do { + // send message loop + sendMessage(currentTX.message + currentLong, currentTX.sender); + } while (((currentTX.message[currentLong - 1]) >> 56) != 0 && currentLong < currentTX.message.length); +} +``` diff --git a/samples/GetATCreatorID.md b/samples/GetATCreatorID.md new file mode 100644 index 0000000..22852e2 --- /dev/null +++ b/samples/GetATCreatorID.md @@ -0,0 +1,87 @@ +# Get AT Creator ID +Simple contract that receives a message with some AT ID (text message in decimal representation) and return to sender a message with the creator's ID of that AT (also in text unsigned decimal representation). Online at tesnet `TS-7MUA-SSZ8-W6QR-6M892` + +## Source code +Note that this contract is currently not working on stable. It is for reference only and to be updated when the hard fork is online. +```c +#program name GetATCreator +#program description Receives a message with some AT ID and return to sender a\ + message with the creator`s ID of that AT. +#program activationAmount 1_5000_0000 + +struct TXINFO { + long txId; + long sender; + long amount; + long message[4]; +} currentTX; + +long messageToSend[4]; + +while (true) +{ + while ((currentTX.txId = getNextTx()) != 0) { + currentTX.sender = getSender(currentTX.txId); + currentTX.amount = getAmount(currentTX.txId); + readMessage(currentTX.txId, 0, currentTX.message); + + processTX(); + } + sendBalance(getCreator()); +} + +// Return to sender the creator of a given AT. +void processTX(void) { + long atId = messageToId(); + long creatorID = getCreatorOf(atId); + IdToMessage(creatorID); + sendMessage(messageToSend, currentTX.sender); +} + + +long i, auxDiv, auxShift, auxMask, auxNum; +const long n8 = 8, n10 = 10, n15 = 15, n48 = 48, n57 = 57, n255 = 255; +void IdToMessage(long id){ + long currDiv = 10; + messageToSend[] = "00000000000000000000 "; + // using i as temp var; + i = (id >> 1) / 5; + messageToSend[2] |= (id - (i * 10)) << 24; + id = i; + + for (i = 18; id != 0; i--) { + auxNum = id % currDiv; + id /= 10; + auxDiv = i/8; + auxShift = (i % 8) * 8; + auxMask = 0xff << auxShift; + messageToSend[i/8] |= auxNum << auxShift; + } +} + +// Expects a numeric ID in currentTX.message[0] to [3] +// return its long representation +long messageToId(void) { + long currMul = 1; + long ret=0; + + for (i = 19; i>=0; i--) { + auxDiv = i/8; + auxShift = (i % 8) * 8; + auxMask = 0xff << auxShift; + auxNum = (currentTX.message[i/8] & auxMask) >> auxShift; + if (auxNum == 0) { + continue; + } + if (auxNum < '0' || auxNum > '9' ) { + // invalid char + return 0; + } + auxNum &= 0xF; + auxNum *= currMul; + ret += auxNum; + currMul *= 10; + } + return ret; +} +``` diff --git a/samples/PublicMethodsOnSmartC.md b/samples/PublicMethodsOnSmartC.md index 48fd961..9b18836 100644 --- a/samples/PublicMethodsOnSmartC.md +++ b/samples/PublicMethodsOnSmartC.md @@ -33,9 +33,6 @@ To automate the process you can use CyberChef with the recipe https://gchq.githu ## SmartC program skeleton ```c -#include APIFunctions -#pragma version 1.0 - // global variables, will be available in all functions long myVar; @@ -57,7 +54,11 @@ constructor(); void main(void) { blockStarted(); - while (getNewTxDetails()) { + while ((currentTX.txId = getNextTx()) != 0) { + currentTX.sender = getSender(currentTX.txId); + currentTX.amount = getAmount(currentTX.txId); + readMessage(currentTX.txId, 0, currentTX.message); + switch (currentTX.message[0]) { case GET_SNACKS: GetSnacks(currentTX.message[1]); @@ -71,35 +72,23 @@ void main(void) { } blockFinished(); } - -long getNewTxDetails(void) { - A_To_Tx_After_Timestamp(currentTX.timestamp); - currentTX.txId = Get_A1(); - if (currentTX.txId == 0) { - return false; - } - currentTX.amount = Get_Amount_For_Tx_In_A(); - currentTX.timestamp = Get_Timestamp_For_Tx_In_A(); - Message_From_Tx_In_A_To_B(); - currentTX.message[0] = Get_B1(); - currentTX.message[1] = Get_B2(); - currentTX.message[2] = Get_B3(); - currentTX.message[3] = Get_B4(); - B_To_Address_Of_Tx_In_A(); - currentTX.sender = Get_B1(); - return true; -} // ****** end of hidden part ****** // ****** These are public methods in SmartJ ****** void GetSnacks(long bites) { // Do your stuff + myPrivateMethod(); } void GetDrinks(long type, long quantity) { // Do your stuff } +// ****** These are private methods in SmartJ ****** +void myPrivateMethod() { + // Do your stuff +} + // ****** These are protected methods in SmartJ ****** void constructor(void) { // this function will be called only once on first activation. @@ -167,8 +156,13 @@ public class Skeleton extends Contract { */ public void GetDrinks(long type, long quantity) { // Do your stuff + myPrivateMethod(); } + private void myPrivateMethod() { + // Do your stuff + } + /** * Iterates over every transaction received */ diff --git a/samples/RSHall.md b/samples/RSHall.md index 6eeb70d..23b390e 100644 --- a/samples/RSHall.md +++ b/samples/RSHall.md @@ -1,3 +1,7 @@ +**DEPRECATION NOTICE:** + +This contract may be not compatible with SmartC version greater or equal 2 because Signum Rainbow Hard Fork broke some compatibilities. Test before use or convert the API calls to new built-in functions. + # Hall of RS-Addresses Check neat addresses generated by other people. Latest 180 registered addresses and the top 10 most voted will be listed on site. diff --git a/samples/XmasContest.smartc.c b/samples/XmasContest.smartc.c index 90d9ab3..d446758 100644 --- a/samples/XmasContest.smartc.c +++ b/samples/XmasContest.smartc.c @@ -1,3 +1,9 @@ +/* DEPRECATION NOTICE: + * This contract may be not compatible with SmartC version greater or equal 2 + * because Signum Rainbow Hard Fork broke some compatibilities. Test before + * use or convert the API calls to new built-in functions. + */ + #program name XmasContest #program description Creator adds up balance to the contest, by sending transactions\ without message. Participants send theirs messages, suposed to be right answer.\ diff --git a/samples/dropper.smartc.c b/samples/dropper.smartc.c index ef6cdce..e929bad 100644 --- a/samples/dropper.smartc.c +++ b/samples/dropper.smartc.c @@ -1,28 +1,23 @@ #program name Dropper #program description Sends a fixed amount of signa to a defined account every N blocks. -#program activationAmount 0_2000_0000 - -#pragma maxAuxVars 1 -#pragma globalOptimization - -#include APIFunctions +#program activationAmount 0.2 // Configure how many blocks to sleep until next send. -const long SLP_BLOCKS = 2; +#define SLP_BLOCKS 2 // Configure balance to stay on contract -const long CONTRACT_MIN_BALANCE = 1_0000_0000; +#define CONTRACT_MIN_BALANCE 1.0 // Configure the amount to send every time -const long sendEachBlockNQT = 2000_0000; +#define EACH_BLOCK .2 // Set the desired account -Set_B1 ("S-3A2N-ZD7P-KZKU-FPWKQ"); +#define RECIPIENT "S-3A2N-ZD7P-KZKU-FPWKQ" // Endless loop while (true) { - if (Get_Current_Balance() < CONTRACT_MIN_BALANCE) { + if (getCurrentBalanceFx() < CONTRACT_MIN_BALANCE) { // Stops contracts before it hits end of balance halt; } else { - Send_To_Address_In_B(sendEachBlockNQT); + sendAmountFx(EACH_BLOCK, RECIPIENT); sleep SLP_BLOCKS; } } diff --git a/samples/get3random.smartc.c b/samples/get3random.smartc.c index 5109207..f2b3652 100644 --- a/samples/get3random.smartc.c +++ b/samples/get3random.smartc.c @@ -1,3 +1,9 @@ +/* DEPRECATION NOTICE: + * This contract may be not compatible with SmartC version greater or equal 2 + * because Signum Rainbow Hard Fork broke some compatibilities. Test before + * use or convert the API calls to new built-in functions. + */ + #program name Get3Random #program description When a message arrives, the program tries to parse a number\ from message. Number must be bigger or equal 5 and lower or equal 9999999. If\ diff --git a/src/__tests__/arithmetics.e.spec.ts b/src/__tests__/arithmetics.e.spec.ts index 77d0f0c..aef5e70 100644 --- a/src/__tests__/arithmetics.e.spec.ts +++ b/src/__tests__/arithmetics.e.spec.ts @@ -8,6 +8,13 @@ describe('Adress Of', () => { compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) + it('should compile: address of register', () => { + const code = 'long a, *b; b = &r1;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @b #0000000000000001\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) test('should throw: deferencing variable not pointer on left side', () => { expect(() => { const code = 'long a, b, c, d; *(a+1)=b;' diff --git a/src/__tests__/assembly.a.spec.ts b/src/__tests__/assembly.a.spec.ts index 8e2a4c1..1921111 100644 --- a/src/__tests__/assembly.a.spec.ts +++ b/src/__tests__/assembly.a.spec.ts @@ -17,6 +17,14 @@ describe('Assembly compilation:', () => { expect(result.ByteCode).toBe(MachineCode) expect(result.ByteData).toBe(MachineData) }) + it('should compile: all api functions (atv2)', () => { + const code = '^program name AllApiCodes\n^program description All Api Codes for AT version 2\n^program activationAmount 1000000000\n^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nFUN @a get_A1\nFUN @a get_A2\nFUN @a get_A3\nFUN @a get_A4\nFUN @b get_B1\nFUN @b get_B2\nFUN @b get_B3\nFUN @b get_B4\nFUN set_A1 $c\nFUN set_A2 $c\nFUN set_A3 $c\nFUN set_A4 $c\nFUN set_A1_A2 $a $b\nFUN set_A3_A4 $a $b\nFUN set_B1 $c\nFUN set_B2 $c\nFUN set_B3 $c\nFUN set_B4 $c\nFUN set_B1_B2 $a $b\nFUN set_B3_B4 $a $b\nFUN clear_A\nFUN clear_B\nFUN clear_A_B\nFUN copy_A_From_B\nFUN copy_B_From_A\nFUN @a check_A_Is_Zero\nFUN @b check_B_Is_Zero\nFUN @a check_A_equals_B\nFUN swap_A_and_B\nFUN OR_A_with_B\nFUN OR_B_with_A\nFUN AND_A_with_B\nFUN AND_B_with_A\nFUN XOR_A_with_B\nFUN XOR_B_with_A\nFUN add_A_to_B\nFUN add_B_to_A\nFUN sub_A_from_B\nFUN sub_B_from_A\nFUN mul_A_by_B\nFUN mul_B_by_A\nFUN div_A_by_B\nFUN div_B_by_A\nFUN MD5_A_to_B\nFUN @a check_MD5_A_with_B\nFUN HASH160_A_to_B\nFUN @a check_HASH160_A_with_B\nFUN SHA256_A_to_B\nFUN @a check_SHA256_A_with_B\nFUN @a get_Block_Timestamp\nFUN @a get_Creation_Timestamp\nFUN @a get_Last_Block_Timestamp\nFUN put_Last_Block_Hash_In_A\nFUN A_to_Tx_after_Timestamp $c\nFUN @a get_Type_for_Tx_in_A\nFUN @a get_Amount_for_Tx_in_A\nFUN @a get_Timestamp_for_Tx_in_A\nFUN @a get_Ticket_Id_for_Tx_in_A\nFUN message_from_Tx_in_A_to_B\nFUN B_to_Address_of_Tx_in_A\nFUN B_to_Address_of_Creator\nFUN @a get_Current_Balance\nFUN @a get_Previous_Balance\nFUN send_to_Address_in_B $c\nFUN send_All_to_Address_in_B\nFUN send_Old_to_Address_in_B\nFUN send_A_to_Address_in_B\nFUN @a add_Minutes_to_Timestamp $c $d\nFIN\n' + const MachineCode = '3500010300000035010103000000350201030000003503010300000035040104000000350501040000003506010400000035070104000000331001050000003311010500000033120105000000331301050000003414010300000004000000341501030000000400000033160105000000331701050000003318010500000033190105000000341a010300000004000000341b010300000004000000322001322101322201322301322401352501030000003526010400000035270103000000322801322901322a01322b01322c01322d01322e013240013241013242013243013244013245013246013247013200023501020300000032020235030203000000320402350502030000003500030300000035010303000000350203030000003203033304030500000035050303000000350603030000003507030300000035080303000000320903320a03320b0335000403000000350104030000003302040500000032030432040432050437060403000000050000000600000028' + const MachineData = '' + const result = new SmartC({ language: 'Assembly', sourceCode: code }).compile().getMachineCode() + expect(result.ByteCode).toBe(MachineCode) + expect(result.ByteData).toBe(MachineData) + }) it('should compile: rare opCodes ', () => { const code = 'FIZ $a\nSTZ $a\nERR :__error\nINC @a\nNOP\nNOP\n__error:\nDEC @a' const MachineCode = '260000000027000000002b1600000004000000007f7f0500000000' @@ -122,11 +130,27 @@ describe('Assembly compilation:', () => { expect(result.UserStackPages).toBe(10) expect(result.CodeStackPages).toBe(10) }) + it('should compile: reserved ^program directives for sc-simulator', () => { + const code = '^program creator 555\n^program contract 121314\nFIN\n' + const MachineCode = '28' + const MachineData = '' + const result = new SmartC({ language: 'Assembly', sourceCode: code }).compile().getMachineCode() + expect(result.ByteCode).toBe(MachineCode) + expect(result.ByteData).toBe(MachineData) + }) it('should compile: hashMachineCode test', () => { const code = '^declare var00\n^declare var01\n^declare var02\n^declare var03\n^declare var04\n^declare var05\n\nFUN set_B1 $var05\nFUN send_All_to_Address_in_B\nFIN' const result = new SmartC({ language: 'Assembly', sourceCode: code }).compile().getMachineCode() expect(result.MachineCodeHashId).toBe('17223659044509638052') }) + it('should compile: atv3 opCodes and apiCodes', () => { + const code = '^comment new opCodes SIP-37\nSLP\nPOW @base $exp\nMDV @x $y $den\n^comment new APICodes SIP-37\nFUN @ret Check_Sig_B_With_A\nFUN @ret Get_Code_Hash_Id\nFUN @ret Get_Activation_Fee\nFUN Put_Last_Block_GSig_In_A\n^comment new APICodes SIP-38\nFUN @ret Get_Map_Value_Keys_In_A\nFUN Set_Map_Value_Keys_In_A\n^comment new APICodes SIP-39\nFUN @ret Issue_Asset\nFUN Mint_Asset\nFUN Distribute_To_Asset_Holders\nFUN @ret Get_Asset_Holders_Count\nFUN @ret Get_Asset_Circulating\nFUN B_To_Assets_Of_Tx_In_A\n' + const MachineCode = '2a1900000000010000002c02000000030000000400000035060205000000350c0305000000350d0405000000320e043507040500000032080435090405000000320a04320b04350c0405000000350f0405000000320d03' + const MachineData = '' + const result = new SmartC({ language: 'Assembly', sourceCode: code }).compile().getMachineCode() + expect(result.ByteCode).toBe(MachineCode) + expect(result.ByteData).toBe(MachineData) + }) }) describe('Assembly wrong code', () => { diff --git a/src/__tests__/bugfixes.a.spec.ts b/src/__tests__/bugfixes.a.spec.ts index 588237d..cc6b00c 100644 --- a/src/__tests__/bugfixes.a.spec.ts +++ b/src/__tests__/bugfixes.a.spec.ts @@ -227,4 +227,46 @@ describe('Tests for bugfixes', () => { compiler.compile() }).toThrowError(/^At line/) }) + it('should compile: bug 29 Must not reuse assigned var if it is array and present at both sides', () => { + const code = '#pragma optimizationLevel 0\nlong message[3];\nmessage[1] = (message[1] & 0x0707070000) >> 16;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare message\n^const SET @message #0000000000000004\n^declare message_0\n^declare message_1\n^declare message_2\n\nSET @r0 #0000000707070000\nAND @r0 $message_1\nSET @r1 #0000000000000010\nSHR @r0 $r1\nSET @message_1 $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: bug 29 Must reuse assigned var if it is struct and present at both sides', () => { + const code = '#pragma optimizationLevel 0\nstruct TXINFO { long txId, timestamp, deadline, sender; } currentTX; currentTX.deadline = currentTX.txId >> 1;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare currentTX_txId\n^declare currentTX_timestamp\n^declare currentTX_deadline\n^declare currentTX_sender\n\nSET @currentTX_deadline $currentTX_txId\nSET @r0 #0000000000000001\nSHR @currentTX_deadline $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: bug 30 Internal functions not setting right return type', () => { + expect(() => { + const code = '#include APIFunctions\nlong a, *b; b = Get_A1();' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: bug 31 It was allowed to set array item outside range', () => { + expect(() => { + const code = 'long a[4], b; a[4] = 25;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: bug 32 Registers always registers even if not in use', () => { + const code = '#pragma optimizationLevel 0\nlong auxJ = 1; long auxTemp = 0; long squad;\n for (long auxI = 0; auxI < 3; auxI++) { r1 = squad & 0xFF; r1 &= 0xF; auxTemp += r1 * auxJ; auxJ *= 5; squad >>= 8; }' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare auxJ\n^declare auxTemp\n^declare squad\n^declare auxI\n\nSET @auxJ #0000000000000001\nCLR @auxTemp\nCLR @auxI\n__loop1_condition:\nSET @r0 #0000000000000003\nBGE $auxI $r0 :__loop1_break\n__loop1_start:\nSET @r0 #00000000000000ff\nAND @r0 $squad\nSET @r1 $r0\nSET @r0 #000000000000000f\nAND @r1 $r0\nMUL @r1 $auxJ\nADD @auxTemp $r1\nSET @r0 #0000000000000005\nMUL @auxJ $r0\nSET @r0 #0000000000000008\nSHR @squad $r0\n__loop1_continue:\nINC @auxI\nJMP :__loop1_condition\n__loop1_break:\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: bug 33 Adding verbose assembly breaks some optimizations', () => { + const code = '#pragma verboseAssembly\n #pragma optimizationLevel 2\n long txid, creator;\n while (txid != 0) {\n if (getSender(txid) != creator) {\n continue;\n }\n creator++;\n }\n creator++;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare txid\n^declare creator\n\n^comment line 4 while (txid != 0) {\n__loop1_continue:\nBZR $txid :__loop1_break\n^comment line 5 if (getSender(txid) != creator) {\nFUN set_A1 $txid\nFUN B_to_Address_of_Tx_in_A\nFUN @r0 get_B1\nBNE $r0 $creator :__loop1_continue\n^comment line 6 continue;\n^comment line 8 creator++;\nINC @creator\nJMP :__loop1_continue\n__loop1_break:\n^comment line 10 creator++;\nINC @creator\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) }) diff --git a/src/__tests__/castings.a.spec.ts b/src/__tests__/castings.a.spec.ts new file mode 100644 index 0000000..2fdda06 --- /dev/null +++ b/src/__tests__/castings.a.spec.ts @@ -0,0 +1,105 @@ +import { SmartC } from '../smartc' + +describe('Castings arithmetic', () => { + it('should compile: special verification on long <=> fixed', () => { + const code = '#pragma optimizationLevel 0\nfixed fa, fb; long la, lb;\n la = lb + (long)fa; fa = la + (fixed)lb;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n^declare fb\n^declare la\n^declare lb\n\nSET @la $fa\nDIV @la $f100000000\nADD @la $lb\nSET @fa $lb\nMUL @fa $f100000000\nSET @r0 $la\nMUL @r0 $f100000000\nADD @fa $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: from void to all', () => { + const code = ' #pragma optimizationLevel 0\n void *pv; long l, *pl; fixed f, *pf; struct TEST { long aa, bb; } s, *ps;\n l = (long)(); f = (fixed)(); pv = (void *)(); pl = (long *)(); pf = (fixed *)(); ps = (struct BB *)();' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare pv\n^declare l\n^declare pl\n^declare f\n^declare pf\n^declare s_aa\n^declare s_bb\n^declare ps\n\nCLR @l\nCLR @f\nCLR @pv\nCLR @pl\nCLR @pf\nCLR @ps\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: from all to void', () => { + const code = ' #pragma optimizationLevel 0\nvoid *pv; long l, *pl; fixed f, *pf; struct TEST { long aa, bb; } s, *ps;\n (void)(l+1); (void)(f+1.2);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare pv\n^declare l\n^declare pl\n^declare f\n^declare pf\n^declare s_aa\n^declare s_bb\n^declare ps\n\nSET @r0 $l\nINC @r0\nSET @r0 #0000000007270e00\nADD @r0 $f\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: from long to all', () => { + const code = '#pragma optimizationLevel 0\nvoid *pv;long l, *pl;fixed f, *pf;struct TEST { long aa, bb; } s, *ps;\n l = (long)l; f = (fixed)l; pv = (void *)l; pl = (long *)l; pf = (fixed *)l; ps = (struct BB *)l;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare pv\n^declare l\n^declare pl\n^declare f\n^declare pf\n^declare s_aa\n^declare s_bb\n^declare ps\n\nSET @f $l\nMUL @f $f100000000\nSET @pv $l\nSET @pl $l\nSET @pf $l\nSET @ps $l\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: from fixed to pointers', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\nvoid *pv;long l, *pl;fixed f, *pf;struct TEST { long aa, bb; } s, *ps;\n pv = (void *)f;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: from struct to all', () => { + const code = '#pragma optimizationLevel 0\nvoid *pv;long l, *pl;fixed f, *pf;struct TEST { long aa, bb; } s, *ps;\n pv = (void *)s; pl = (long *)s; pf = (fixed *)s; ps = (struct BB *)s;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare pv\n^declare l\n^declare pl\n^declare f\n^declare pf\n^declare s_aa\n^declare s_bb\n^declare ps\n\nSET @pv #0000000000000009\nSET @pl #0000000000000009\nSET @pf #0000000000000009\nSET @ps #0000000000000009\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: from struct to long', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\nvoid *pv;long l, *pl;fixed f, *pf;struct TEST { long aa, bb; } s, *ps;\n l = (long)s;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: from struct to fixed', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\nvoid *pv;long l, *pl;fixed f, *pf;struct TEST { long aa, bb; } s, *ps;\n f = (fixed)s;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: from pointer to all', () => { + const code = '#pragma optimizationLevel 0\nvoid *pv;long l, *pl;fixed f, *pf;struct TEST { long aa, bb; } s, *ps;\n l = (long)pl; pv = (void *)pl; pl = (long *)pl; pf = (fixed *)pl; ps = (struct BB *)pl;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare pv\n^declare l\n^declare pl\n^declare f\n^declare pf\n^declare s_aa\n^declare s_bb\n^declare ps\n\nSET @l $pl\nSET @pv $pl\nSET @pf $pl\nSET @ps $pl\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: from pointer to fixed', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\nvoid *pv;long l, *pl;fixed f, *pf;struct TEST { long aa, bb; } s, *ps;\n f = (fixed)pv;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: complex hack', () => { + const code = '#pragma optimizationLevel 0\nfixed fa, *pf; long la, *pl; la = *((long*)(&fa)); fa = *((fixed*)(&la));' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n^declare pf\n^declare la\n^declare pl\n\nSET @la $fa\nSET @fa $la\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: casting hack on returning function', () => { + const code = '#pragma optimizationLevel 0\n#include APIFunctions\nfixed a, b; a = b + (*(fixed *)(&Get_A1()));' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nFUN @r0 get_A1\nSET @a $b\nADD @a $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) + +describe('Castings logical', () => { + it('should compile: all permitted types', () => { + const code = '#pragma optimizationLevel 0\nlong la, lb, *pl; fixed fa, fb, *pf; void * pv;\n if (la > fb) la++; if (fa > lb) la++; if (fa >= fb) la++; if (la < pl) la++; if (la <= pf) la++; if (pv == pl) la++; if (pf != pl) la++;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare lb\n^declare pl\n^declare fa\n^declare fb\n^declare pf\n^declare pv\n\nSET @r0 $la\nMUL @r0 $f100000000\nBLE $r0 $fb :__if1_endif\n__if1_start:\nINC @la\n__if1_endif:\nSET @r0 $lb\nMUL @r0 $f100000000\nBLE $fa $r0 :__if2_endif\n__if2_start:\nINC @la\n__if2_endif:\nBLT $fa $fb :__if3_endif\n__if3_start:\nINC @la\n__if3_endif:\nBGE $la $pl :__if4_endif\n__if4_start:\nINC @la\n__if4_endif:\nBGT $la $pf :__if5_endif\n__if5_start:\nINC @la\n__if5_endif:\nBNE $pv $pl :__if6_endif\n__if6_start:\nINC @la\n__if6_endif:\nBEQ $pf $pl :__if7_endif\n__if7_start:\nINC @la\n__if7_endif:\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: pointer vs fixed', () => { + expect(() => { + const code = 'long la, lb, *pl; fixed fa, fb, *pf; void * pv;\n if (la > fb) la++; if (fa > pv) la++;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) +}) diff --git a/src/__tests__/fixed.a.spec.ts b/src/__tests__/fixed.a.spec.ts new file mode 100644 index 0000000..970a8d6 --- /dev/null +++ b/src/__tests__/fixed.a.spec.ts @@ -0,0 +1,340 @@ +import { SmartC } from '../smartc' + +describe('Assignment', () => { + it('should compile: = fixed/fixed', () => { + const code = '#pragma optimizationLevel 0\nfixed a, b; a=b;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @a $b\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: = fixed/long', () => { + const code = '#pragma optimizationLevel 0\nfixed a; long b; a=b;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @a $b\nMUL @a $f100000000\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: = long/fixed', () => { + const code = '#pragma optimizationLevel 0\nfixed a; long b; b=a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @b $a\nDIV @b $f100000000\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: = fixed/constantfixed', () => { + const code = '#pragma optimizationLevel 0\nfixed a, b; a=2.23;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @a #000000000d4ab5c0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: = fixed/constantlong', () => { + const code = '#pragma optimizationLevel 0\nfixed a; long b; a=55;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @a #0000000000000037\nMUL @a $f100000000\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: = long/constantfixed', () => { + const code = '#pragma optimizationLevel 0\nfixed a; long b; b=32.3;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @b #00000000c085e380\nDIV @b $f100000000\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) + +describe('SetOperator', () => { + it('should compile: += -= fixed/fixed fixed/long fixed/constantlong', () => { + const code = '#pragma optimizationLevel 0\nfixed fa, fb; long lc; fa+=fb; fa-=fb; fa+=lc; fa-=lc; fa+=2; fa-=2;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n^declare fb\n^declare lc\n\nADD @fa $fb\nSUB @fa $fb\nSET @r0 $lc\nMUL @r0 $f100000000\nADD @fa $r0\nSET @r0 $lc\nMUL @r0 $f100000000\nSUB @fa $r0\nSET @r0 #0000000000000002\nMUL @r0 $f100000000\nADD @fa $r0\nSET @r0 #0000000000000002\nMUL @r0 $f100000000\nSUB @fa $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: += -= long/fixed long/constantfixed', () => { + const code = '#pragma optimizationLevel 0\nfixed fa, fb; long lc; lc+=fa; lc-=fb; lc+=2.2; lc-=2.2;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n^declare fb\n^declare lc\n\nSET @r0 $fa\nDIV @r0 $f100000000\nADD @lc $r0\nSET @r0 $fb\nDIV @r0 $f100000000\nSUB @lc $r0\nSET @r0 #000000000d1cef00\nDIV @r0 $f100000000\nADD @lc $r0\nSET @r0 #000000000d1cef00\nDIV @r0 $f100000000\nSUB @lc $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: *= /= fixed/fixed fixed/long fixed/constantlong', () => { + const code = '#pragma optimizationLevel 0\nfixed fa, fb; long lc; fa*=fb; fa/=fb; fa*=lc; fa/=lc; fa*=2; fa/=2;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n^declare fb\n^declare lc\n\nMDV @fa $fb $f100000000\nMDV @fa $f100000000 $fb\nMUL @fa $lc\nDIV @fa $lc\nSET @r0 #0000000000000002\nMUL @fa $r0\nSET @r0 #0000000000000002\nDIV @fa $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: *= /= long/fixed long/constantfixed', () => { + const code = '#pragma optimizationLevel 0\nfixed fa, fb; long lc; lc*=fa; lc/=fb; lc*=2.2; lc/=2.2;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n^declare fb\n^declare lc\n\nSET @r0 $fa\nDIV @r0 $f100000000\nMUL @lc $r0\nSET @r0 $fb\nDIV @r0 $f100000000\nDIV @lc $r0\nSET @r0 #000000000d1cef00\nDIV @r0 $f100000000\nMUL @lc $r0\nSET @r0 #000000000d1cef00\nDIV @r0 $f100000000\nDIV @lc $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: %= ', () => { + expect(() => { + const code = 'fixed a; fixed b; a%=b;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: &= ', () => { + expect(() => { + const code = 'fixed a; fixed b; a&=b;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: |= ', () => { + expect(() => { + const code = 'fixed a; fixed b; a|=b;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: ^= ', () => { + expect(() => { + const code = 'fixed a; fixed b; a^=b;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: <<= >>= with long', () => { + const code = '#pragma optimizationLevel 0\nfixed a; long b; a>>=3; a<<=3; a>>=b; a<<=b;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @r0 #0000000000000003\nSHR @a $r0\nSET @r0 #0000000000000003\nSHL @a $r0\nSHR @a $b\nSHL @a $b\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: <<= ', () => { + expect(() => { + const code = 'fixed a; fixed b; a<<=b;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: >>= ', () => { + expect(() => { + const code = 'fixed a; fixed b; a>>=b;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) +}) + +describe('Operator', () => { + it('should compile: / ', () => { + const code = '#pragma optimizationLevel 0\n long la; fixed fa, fb, fresult; fresult = la / fa; fresult = fa / la; fresult = fa / fb;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare fa\n^declare fb\n^declare fresult\n\nSET @fresult $la\nMUL @fresult $f100000000\nMDV @fresult $f100000000 $fa\nSET @fresult $fa\nDIV @fresult $la\nSET @fresult $fa\nMDV @fresult $f100000000 $fb\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: % ', () => { + expect(() => { + const code = 'long la; fixed fa, fr; fr=fa%la;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: & ', () => { + expect(() => { + const code = 'long la; fixed fa, fr; fr=fa&la;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: | ', () => { + expect(() => { + const code = 'long la; fixed fa, fr; fr=fa|la;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: ^ ', () => { + expect(() => { + const code = 'long la; fixed fa, fr; fr=fa^la;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: << >> right usage', () => { + const code = 'long la; fixed fa, fb, fresult; fresult = fa << la; fresult = fa >> la; fb;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare fa\n^declare fb\n^declare fresult\n\nSET @fresult $fa\nSHL @fresult $la\nSET @fresult $fa\nSHR @fresult $la\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: >> wrong ', () => { + expect(() => { + const code = 'long la; fixed fa, fr; fr=la>>fa;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: << wrong ', () => { + expect(() => { + const code = 'long la; fixed fa, fr; fr=la< { + it('should compile: Assignment', () => { + const code = '#pragma optimizationLevel 0\nfixed fa; fa=2.5;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n\nSET @fa #000000000ee6b280\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) + +describe('UnaryOperator', () => { + it('should compile: !', () => { + const code = '#pragma optimizationLevel 0\nlong a; fixed b; a=!b;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nBNZ $b :__NOT_1_sF\n__NOT_1_sT:\nSET @a #0000000000000001\nJMP :__NOT_1_end\n__NOT_1_sF:\nCLR @a\n__NOT_1_end:\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: ~', () => { + const code = '#pragma optimizationLevel 0\nfixed a, b; a=~b;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @a $b\nNOT @a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) + +describe('SetUnaryOperator', () => { + it('should compile: ++ post', () => { + const code = 'fixed a, b; b=a++;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @b $a\nADD @a $f100000000\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: -- post', () => { + const code = 'fixed a, b; b=a--;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @b $a\nSUB @a $f100000000\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: ++ pre', () => { + const code = 'fixed a, b; b=++a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nADD @a $f100000000\nSET @b $a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: -- pre', () => { + const code = 'fixed a, b; b=--a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSUB @a $f100000000\nSET @b $a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) + +describe('CheckOperator Unary', () => { + it('should compile: - ', () => { + const code = 'fixed a, b; b=-a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nCLR @b\nSUB @b $a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: + ', () => { + const code = 'fixed a, b; b=+a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @b $a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: & ', () => { + const code = 'fixed a, *b; b=&a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @b #0000000000000004\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: * ', () => { + const code = 'fixed a, *b; a=*b;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @a $($b)\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: *() ', () => { + const code = 'fixed a, *b; a=*(b+1);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @a $b\nINC @a\nSET @a $($a)\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: *() ', () => { + const code = 'fixed a, *b; a=*(b+3);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @a #0000000000000003\nADD @a $b\nSET @a $($a)\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: *() ', () => { + const code = 'fixed a, *b;long la; a=*(b+la);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n^declare la\n\nSET @a $b\nADD @a $la\nSET @a $($a)\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: * on left side', () => { + const code = 'fixed a, *b; *b=a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @($b) $a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: *() on left side paren opt', () => { + const code = 'fixed a, *b; *(b + 1)=a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @r0 $b\nINC @r0\nSET @($r0) $a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: *() on left side paren not opt', () => { + const code = 'fixed a, *b; *(b + 3)=a;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @r0 #0000000000000003\nADD @r0 $b\nSET @($r0) $a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) + +describe('CheckOperator Binary', () => { + it('should compile: + -', () => { + const code = '#pragma optimizationLevel 0\nlong la; fixed fa, fb, fresult; fresult = la + fa; fresult = fa + la; fresult = la - fa; fresult = fa - la; fresult = fa + fb; fresult = fa - fb;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare fa\n^declare fb\n^declare fresult\n\nSET @fresult $la\nMUL @fresult $f100000000\nADD @fresult $fa\nSET @fresult $la\nMUL @fresult $f100000000\nADD @fresult $fa\nSET @fresult $la\nMUL @fresult $f100000000\nSUB @fresult $fa\nSET @fresult $fa\nSET @r0 $la\nMUL @r0 $f100000000\nSUB @fresult $r0\nSET @fresult $fa\nADD @fresult $fb\nSET @fresult $fa\nSUB @fresult $fb\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: *', () => { + const code = '#pragma optimizationLevel 0\n long la; fixed fa, fb, fresult; fresult = la * fa; fresult = fa * la; fresult = fa * fb;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare fa\n^declare fb\n^declare fresult\n\nSET @fresult $la\nMUL @fresult $f100000000\nMDV @fresult $fa $f100000000\nSET @fresult $fa\nMUL @fresult $la\nSET @fresult $fa\nMDV @fresult $fb $f100000000\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: & ', () => { + expect(() => { + const code = 'long la; fixed fa, fr; fr=la&fa;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) +}) diff --git a/src/__tests__/fixed.b.spec.ts b/src/__tests__/fixed.b.spec.ts new file mode 100644 index 0000000..18a007f --- /dev/null +++ b/src/__tests__/fixed.b.spec.ts @@ -0,0 +1,154 @@ +import { SmartC } from '../smartc' + +describe('Arrays', () => { + describe('Array assignment (left side)', () => { + it('should compile: Array long constant index', () => { + const code = '#pragma optimizationLevel 0\nfixed a[4]; long b; fixed c; a[3]=c;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^declare c\n\nSET @a_3 $c\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: array fixed constant index', () => { + expect(() => { + const code = 'fixed a[4]; long b; fixed c; a[3.0]=c;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: Array long variable index', () => { + const code = '#pragma optimizationLevel 0\nfixed a[4]; long b; fixed c; a[b]=c;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^declare c\n\nSET @($a + $b) $c\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: array fixed variable index', () => { + expect(() => { + const code = 'fixed a[4]; long b; fixed c; a[c]=c;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + }) + describe('Array assignment (right side)', () => { + it('should compile: Array long constant index', () => { + const code = '#pragma optimizationLevel 0\nfixed a[4]; long b; fixed c; c=a[3];' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^declare c\n\nSET @c $a_3\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: array fixed constant index', () => { + expect(() => { + const code = 'fixed a[4]; long b; fixed c; c=a[3.0];' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: Array long variable index', () => { + const code = '#pragma optimizationLevel 0\nfixed a[4]; long b; fixed c; c=a[b];' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^declare c\n\nSET @c $($a + $b)\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: array fixed variable index', () => { + expect(() => { + const code = 'fixed a[4]; long b; fixed c; c=a[c];' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + }) + describe('Array assignment (both sides)', () => { + it('should compile: Array long constant/constant index', () => { + const code = '#pragma optimizationLevel 0\nfixed a[4], b[4]; long c,d; a[2]=b[3];' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^const SET @b #000000000000000a\n^declare b_0\n^declare b_1\n^declare b_2\n^declare b_3\n^declare c\n^declare d\n\nSET @a_2 $b_3\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: Array long long/constant index', () => { + const code = '#pragma optimizationLevel 0\nfixed a[4], b[4]; long c,d; a[c]=b[3];' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^const SET @b #000000000000000a\n^declare b_0\n^declare b_1\n^declare b_2\n^declare b_3\n^declare c\n^declare d\n\nSET @($a + $c) $b_3\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: Array long constant/long index', () => { + const code = '#pragma optimizationLevel 0\nfixed a[4], b[4]; long c,d; a[3]=b[d];' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^const SET @b #000000000000000a\n^declare b_0\n^declare b_1\n^declare b_2\n^declare b_3\n^declare c\n^declare d\n\nSET @a_3 $($b + $d)\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: Array long long/long index', () => { + const code = '#pragma optimizationLevel 0\nfixed a[4], b[4]; long c,d; a[c]=b[d];' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^const SET @b #000000000000000a\n^declare b_0\n^declare b_1\n^declare b_2\n^declare b_3\n^declare c\n^declare d\n\nSET @r0 $($b + $d)\nSET @($a + $c) $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + }) + + describe('Multi dimensional Arrays assignment (left side)', () => { + it('should compile: constants', () => { + const code = 'fixed a[4][2]; long b, c; a[2][1]=4.0;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare a_4\n^declare a_5\n^declare a_6\n^declare a_7\n^declare b\n^declare c\n\nSET @a_5 #0000000017d78400\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: variable+constants', () => { + const code = 'fixed a[4][2]; long b, c; a[b][1]=4.0;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare a_4\n^declare a_5\n^declare a_6\n^declare a_7\n^declare b\n^declare c\n\nSET @r0 #0000000000000002\nMUL @r0 $b\nINC @r0\nSET @r1 #0000000017d78400\nSET @($a + $r0) $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: variable+zero', () => { + const code = 'fixed a[4][2]; long b, c; a[b][0]=4.0;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare a_4\n^declare a_5\n^declare a_6\n^declare a_7\n^declare b\n^declare c\n\nSET @r0 #0000000000000002\nMUL @r0 $b\nSET @r1 #0000000017d78400\nSET @($a + $r0) $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: zero+variable', () => { + const code = 'fixed a[4][2]; long b, c; a[0][b]=4.0;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare a_4\n^declare a_5\n^declare a_6\n^declare a_7\n^declare b\n^declare c\n\nSET @r0 #0000000017d78400\nSET @($a + $b) $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: constant+variable', () => { + const code = 'fixed a[4][2]; long b, c; a[3][b]=4.0;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare a_4\n^declare a_5\n^declare a_6\n^declare a_7\n^declare b\n^declare c\n\nSET @r0 $b\nSET @r1 #0000000000000006\nADD @r0 $r1\nSET @r1 #0000000017d78400\nSET @($a + $r0) $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: variable+variable', () => { + const code = 'fixed a[4][2]; long b, c; a[b][c]=4.0;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^const SET @a #0000000000000005\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare a_4\n^declare a_5\n^declare a_6\n^declare a_7\n^declare b\n^declare c\n\nSET @r0 #0000000000000002\nMUL @r0 $b\nADD @r0 $c\nSET @r1 #0000000017d78400\nSET @($a + $r0) $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + }) +}) + +describe('Detection of fixed numbers', () => { + it('should detect: fixed inside CodeDomain', () => { + const code = 'struct TXINFO { long txid; long sender; fixed amount; long message[4]; } currentTX; currentTX.sender++;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode().split('\n')).toContain('^declare f100000000') + }) + it('should detect: fixed inside CodeCave', () => { + const code = 'long a, b; a=(b+2.3)/5; b++;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode().split('\n')).toContain('^declare f100000000') + }) +}) diff --git a/src/__tests__/functions.a.spec.ts b/src/__tests__/functions.a.spec.ts index f125bcf..30d9ec7 100644 --- a/src/__tests__/functions.a.spec.ts +++ b/src/__tests__/functions.a.spec.ts @@ -9,14 +9,14 @@ describe('Special functions', () => { expect(compiler.getAssemblyCode()).toBe(assembly) }) it('should compile: main() with return at end', () => { - const code = '#pragma optimizationLevel 0\nlong a; void main(void) { a++; return; }' + const code = '#pragma optimizationLevel 0\nlong a; void main() { a++; return; }' const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nJMP :__fn_main\n\n__fn_main:\nPCS\nINC @a\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) it('should compile: main() with return at middle', () => { - const code = '#pragma optimizationLevel 0\nlong a; void main(void) { a++; return; a++; }' + const code = '#pragma optimizationLevel 0\nlong a; void main() { a++; return; a++; }' const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nJMP :__fn_main\n\n__fn_main:\nPCS\nINC @a\nFIN\nINC @a\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() @@ -30,7 +30,7 @@ describe('Special functions', () => { expect(compiler.getAssemblyCode()).toBe(assembly) }) it('should compile: catch() and no main function', () => { - const code = '#pragma optimizationLevel 0\nlong b, a = 0; while (true) { a++; } void catch(void) { long a++; }' + const code = '#pragma optimizationLevel 0\nlong b, a = 0; while (true) { a++; } void catch() { long a++; }' const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare b\n^declare a\n^declare catch_a\n\nERR :__fn_catch\nCLR @a\n__loop1_continue:\n__loop1_start:\nINC @a\nJMP :__loop1_continue\n__loop1_break:\nFIN\n\n__fn_catch:\nPCS\nINC @catch_a\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() @@ -330,55 +330,3 @@ void *ret(long *aa, void *bb) { aa++; return aa; }` }).toThrowError(/^At line/) }) }) - -describe('API functions', () => { - it('should compile: right use', () => { - const code = '#pragma optimizationLevel 0\n#include APIFunctions\nlong a;Set_A1(a);' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFUN set_A1 $a\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - test('should throw: missing argument', () => { - expect(() => { - const code = '#pragma optimizationLevel 0\n#include APIFunctions\nSet_A1();' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - }).toThrowError(/^At line/) - }) - it('should compile: Function with same name as API but not including it', () => { - const code = '#pragma optimizationLevel 0\nlong a=0; void Get_B1(void) { a++; }' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nCLR @a\nFIN\n\n__fn_Get_B1:\nINC @a\nRET\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - test('should throw: Function with same name as API and including it', () => { - expect(() => { - const code = '#pragma optimizationLevel 0\n#include APIFunctions\nlong a=0; void Get_B1(void) { a++; }' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - }).toThrowError(/^At line/) - }) - test('should throw: wrong variable types on API Function argument', () => { - expect(() => { - const code = '#pragma optimizationLevel 0\n#include APIFunctions\nlong * a;Set_A1(a);' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - }).toThrowError(/^At line/) - }) - it('should compile: Passing long inside struct (with offset)', () => { - const code = '#include APIFunctions\n#pragma optimizationLevel 0\nstruct KOMBI { long driver; long collector; long passenger; } ;struct KOMBI car[2]; long a;\nSet_A1(car[a].collector);' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare car\n^const SET @car #0000000000000004\n^declare car_0_driver\n^declare car_0_collector\n^declare car_0_passenger\n^declare car_1_driver\n^declare car_1_collector\n^declare car_1_passenger\n^declare a\n\nSET @r0 #0000000000000003\nMUL @r0 $a\nINC @r0\nSET @r1 $($car + $r0)\nFUN set_A1 $r1\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - test('should throw: Undeclared function when APIFunction is declared', () => { - expect(() => { - const code = '#pragma optimizationLevel 0\n#include APIFunctions\nlong b, a = 0; test();' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - }).toThrowError(/^At line/) - }) -}) diff --git a/src/__tests__/functions.b.spec.ts b/src/__tests__/functions.b.spec.ts new file mode 100644 index 0000000..30696f0 --- /dev/null +++ b/src/__tests__/functions.b.spec.ts @@ -0,0 +1,388 @@ +import { SmartC } from '../smartc' + +describe('API functions', () => { + it('should compile: right use', () => { + const code = '#pragma optimizationLevel 0\n#include APIFunctions\nlong a;Set_A1(a);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFUN set_A1 $a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: all API (atv2)', () => { + const code = '#include APIFunctions\nlong a, b, c, d;\n\n// Get/Set functions for “pseudo registers” \na = Get_A1();\na = Get_A2();\na = Get_A3();\na = Get_A4();\nb = Get_B1();\nb = Get_B2();\nb = Get_B3();\nb = Get_B4();\nSet_A1(c);\nSet_A2(c);\nSet_A3(c);\nSet_A4(c);\nSet_A1_A2(a, b);\nSet_A3_A4(a, b);\nSet_B1(c);\nSet_B2(c);\nSet_B3(c);\nSet_B4(c);\nSet_B1_B2(a, b);\nSet_B3_B4(a, b);\nClear_A();\nClear_B();\nClear_A_And_B();\nCopy_A_From_B();\nCopy_B_From_A();\na = Check_A_Is_Zero();\nb = Check_B_Is_Zero();\na = Check_A_Equals_B();\nSwap_A_and_B();\nOR_A_with_B();\nOR_B_with_A();\nAND_A_with_B();\nAND_B_with_A();\nXOR_A_with_B();\nXOR_B_with_A();\nAdd_A_To_B();\nAdd_B_To_A();\nSub_A_From_B();\nSub_B_From_A();\nMul_A_By_B();\nMul_B_By_A();\nDiv_A_By_B();\nDiv_B_By_A();\n\n// Functions that perform hash operations\nMD5_A_To_B();\na = Check_MD5_A_With_B();\nHASH160_A_To_B();\na = Check_HASH160_A_With_B();\nSHA256_A_To_B();\na = Check_SHA256_A_With_B();\n\n// Generic functions that get block and tx info\na = Get_Block_Timestamp();\na = Get_Creation_Timestamp();\na = Get_Last_Block_Timestamp();\nPut_Last_Block_Hash_In_A();\nA_To_Tx_After_Timestamp(c);\na = Get_Type_For_Tx_In_A();\na = Get_Amount_For_Tx_In_A();\na = Get_Timestamp_For_Tx_In_A();\na = Get_Random_Id_For_Tx_In_A();\nMessage_From_Tx_In_A_To_B();\nB_To_Address_Of_Tx_In_A();\nB_To_Address_Of_Creator();\n\n// Generic functions that check balances and perform ops\na = Get_Current_Balance();\na = Get_Previous_Balance();\nSend_To_Address_In_B(c);\nSend_All_To_Address_In_B();\nSend_Old_To_Address_In_B();\nSend_A_To_Address_In_B();\na = Add_Minutes_To_Timestamp(c, d);\n' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nFUN @a get_A1\nFUN @a get_A2\nFUN @a get_A3\nFUN @a get_A4\nFUN @b get_B1\nFUN @b get_B2\nFUN @b get_B3\nFUN @b get_B4\nFUN set_A1 $c\nFUN set_A2 $c\nFUN set_A3 $c\nFUN set_A4 $c\nFUN set_A1_A2 $a $b\nFUN set_A3_A4 $a $b\nFUN set_B1 $c\nFUN set_B2 $c\nFUN set_B3 $c\nFUN set_B4 $c\nFUN set_B1_B2 $a $b\nFUN set_B3_B4 $a $b\nFUN clear_A\nFUN clear_B\nFUN clear_A_B\nFUN copy_A_From_B\nFUN copy_B_From_A\nFUN @a check_A_Is_Zero\nFUN @b check_B_Is_Zero\nFUN @a check_A_equals_B\nFUN swap_A_and_B\nFUN OR_A_with_B\nFUN OR_B_with_A\nFUN AND_A_with_B\nFUN AND_B_with_A\nFUN XOR_A_with_B\nFUN XOR_B_with_A\nFUN add_A_to_B\nFUN add_B_to_A\nFUN sub_A_from_B\nFUN sub_B_from_A\nFUN mul_A_by_B\nFUN mul_B_by_A\nFUN div_A_by_B\nFUN div_B_by_A\nFUN MD5_A_to_B\nFUN @a check_MD5_A_with_B\nFUN HASH160_A_to_B\nFUN @a check_HASH160_A_with_B\nFUN SHA256_A_to_B\nFUN @a check_SHA256_A_with_B\nFUN @a get_Block_Timestamp\nFUN @a get_Creation_Timestamp\nFUN @a get_Last_Block_Timestamp\nFUN put_Last_Block_Hash_In_A\nFUN A_to_Tx_after_Timestamp $c\nFUN @a get_Type_for_Tx_in_A\nFUN @a get_Amount_for_Tx_in_A\nFUN @a get_Timestamp_for_Tx_in_A\nFUN @a get_Ticket_Id_for_Tx_in_A\nFUN message_from_Tx_in_A_to_B\nFUN B_to_Address_of_Tx_in_A\nFUN B_to_Address_of_Creator\nFUN @a get_Current_Balance\nFUN @a get_Previous_Balance\nFUN send_to_Address_in_B $c\nFUN send_All_to_Address_in_B\nFUN send_Old_to_Address_in_B\nFUN send_A_to_Address_in_B\nFUN @a add_Minutes_to_Timestamp $c $d\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: all new API (atv3)', () => { + const code = '#include APIFunctions\nlong a;\na = Check_Sig_B_With_A();\na = Get_Code_Hash_Id();\na = Get_Activation_Fee();\nPut_Last_Block_GSig_In_A();\nSet_Map_Value_Keys_In_A();\na = Get_Map_Value_Keys_In_A();\na = Issue_Asset();\nMint_Asset();\nDistribute_To_Asset_Holders();\na = Get_Asset_Holders_Count();\na = Get_Asset_Circulating();\nB_To_Assets_Of_Tx_In_A();\n' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFUN @a Check_Sig_B_With_A\nFUN @a Get_Code_Hash_Id\nFUN @a Get_Activation_Fee\nFUN Put_Last_Block_GSig_In_A\nFUN Set_Map_Value_Keys_In_A\nFUN @a Get_Map_Value_Keys_In_A\nFUN @a Issue_Asset\nFUN Mint_Asset\nFUN Distribute_To_Asset_Holders\nFUN @a Get_Asset_Holders_Count\nFUN @a Get_Asset_Circulating\nFUN B_To_Assets_Of_Tx_In_A\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: missing argument', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\n#include APIFunctions\nSet_A1();' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: Function with same name as API but not including it', () => { + const code = '#pragma optimizationLevel 0\nlong a=0; void Get_B1(void) { a++; }' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nCLR @a\nFIN\n\n__fn_Get_B1:\nINC @a\nRET\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: Function with same name as API and including it', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\n#include APIFunctions\nlong a=0; void Get_B1(void) { a++; }' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: wrong variable types on API Function argument', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\n#include APIFunctions\nlong * a;Set_A1(a);' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + it('should compile: Passing long inside struct (with offset)', () => { + const code = '#include APIFunctions\n#pragma optimizationLevel 0\nstruct KOMBI { long driver; long collector; long passenger; } ;struct KOMBI car[2]; long a;\nSet_A1(car[a].collector);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare car\n^const SET @car #0000000000000004\n^declare car_0_driver\n^declare car_0_collector\n^declare car_0_passenger\n^declare car_1_driver\n^declare car_1_collector\n^declare car_1_passenger\n^declare a\n\nSET @r0 #0000000000000003\nMUL @r0 $a\nINC @r0\nSET @r1 $($car + $r0)\nFUN set_A1 $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: Undeclared function when APIFunction is declared', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\n#include APIFunctions\nlong b, a = 0; test();' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) +}) + +describe('fixed API functions', () => { + it('should compile: right use', () => { + const code = '#pragma optimizationLevel 0\n#include fixedAPIFunctions\n fixed fa;F_Set_A1(fa);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n\nFUN set_A1 $fa\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: all fixed API', () => { + const code = '#include fixedAPIFunctions\nfixed fa; fa = F_Get_A1();\n fa = F_Get_A2();\n fa = F_Get_A3();\n fa = F_Get_A4();\n fa = F_Get_B1();\n fa = F_Get_B2();\n fa = F_Get_B3();\n fa = F_Get_B4();\n F_Set_A1(fa);\n F_Set_A2(fa);\n F_Set_A3(fa);\n F_Set_A4(fa);\n F_Set_B1(fa);\n F_Set_B2(fa);\n F_Set_B3(fa);\n F_Set_B4(2.33);\n fa = F_Get_Amount_For_Tx_In_A();\n fa = F_Get_Current_Balance();\n fa = F_Get_Previous_Balance();\n F_Send_To_Address_In_B(fa);\n fa = F_Get_Map_Value_Keys_In_A();\n fa = F_Get_Activation_Fee();\n ' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n\nFUN @fa get_A1\nFUN @fa get_A2\nFUN @fa get_A3\nFUN @fa get_A4\nFUN @fa get_B1\nFUN @fa get_B2\nFUN @fa get_B3\nFUN @fa get_B4\nFUN set_A1 $fa\nFUN set_A2 $fa\nFUN set_A3 $fa\nFUN set_A4 $fa\nFUN set_B1 $fa\nFUN set_B2 $fa\nFUN set_B3 $fa\nSET @r0 #000000000de34c40\nFUN set_B4 $r0\nFUN @fa get_Amount_for_Tx_in_A\nFUN @fa get_Current_Balance\nFUN @fa get_Previous_Balance\nFUN send_to_Address_in_B $fa\nFUN @fa Get_Map_Value_Keys_In_A\nFUN @fa Get_Activation_Fee\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: wrong argument (expect long, got fixed)', () => { + expect(() => { + const code = '#include APIFunctions\nfixed fa; Set_A1(fa);' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) + test('should throw: wrong argument (expected fixed, got long)', () => { + expect(() => { + const code = '#include APIFunctions\nlong a; F_Set_A1(a);' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) +}) + +describe('Built-in functions', () => { + it('should compile: mdv()', () => { + const code = '#pragma optimizationLevel 0\nlong a, b, c, d; a = mdv(b, c, d);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nMDV @a $c $d\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: mdv() (using flatmem)', () => { + const code = '#pragma optimizationLevel 0\nlong a, b, c, d; a = mdv(2, 4, 6);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @r0 #0000000000000002\nSET @a $r0\nSET @r1 #0000000000000004\nSET @r2 #0000000000000006\nMDV @a $r1 $r2\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: pow()', () => { + const code = '#pragma optimizationLevel 0\nlong a, b, c; a=pow(b,c);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nPOW @a $c\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: pow() (using flatmem)', () => { + const code = '#pragma optimizationLevel 0\nlong a, b, c; a=pow(2,4);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 #0000000000000002\nSET @a $r0\nSET @r1 #0000000000000004\nPOW @a $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: powf()', () => { + const code = '#pragma optimizationLevel 0\nlong a, b; fixed fc; a=powf(b,fc);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n^declare fc\n\nSET @a $b\nPOW @a $fc\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: memcopy()', () => { + const code = '#pragma optimizationLevel 0\nfixed fa, *pf; long la, *pl; memcopy(&fa, &la); memcopy(pf, &la); memcopy(&la, pf); memcopy(pf, pl); memcopy(pf+1, pl+4);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n^declare pf\n^declare la\n^declare pl\n\nSET @fa $la\nSET @($pf) $la\nSET @la $($pf)\nSET @r0 $($pl)\nSET @($pf) $r0\nSET @r0 $pf\nINC @r0\nSET @r1 #0000000000000004\nADD @r1 $pl\nSET @r2 $($r1)\nSET @($r0) $r2\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: bcltof() and bcftol()', () => { + const code = '#pragma optimizationLevel 0\n#include APIFunctions\n#include fixedAPIFunctions\n fixed fa; long la; fa = bcltof(Get_A1()+25); la = bcftol(F_Get_A1()+25.0);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare fa\n^declare la\n\nFUN @r0 get_A1\nSET @fa #0000000000000019\nADD @fa $r0\nFUN @r0 get_A1\nSET @la #000000009502f900\nADD @la $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getNextTx()', () => { + const code = '#pragma optimizationLevel 0\n long currTxId = getNextTx(); if (currTxId == 0) currTxId++;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare _counterTimestamp\n^declare currTxId\n\nFUN A_to_Tx_after_Timestamp $_counterTimestamp\nFUN @currTxId get_A1\nBZR $currTxId :__GNT_1\nFUN @_counterTimestamp get_Timestamp_for_Tx_in_A\n__GNT_1:\nBNZ $currTxId :__if2_endif\n__if2_start:\nINC @currTxId\n__if2_endif:\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getNextTx() inside a function', () => { + const code = '#pragma optimizationLevel 0\n getNextTxDetails(); void getNextTxDetails(void) { long tx = getNextTx(); }' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare _counterTimestamp\n^declare getNextTxDetails_tx\n\nJSR :__fn_getNextTxDetails\nFIN\n\n__fn_getNextTxDetails:\nFUN A_to_Tx_after_Timestamp $_counterTimestamp\nFUN @getNextTxDetails_tx get_A1\nBZR $getNextTxDetails_tx :__GNT_1\nFUN @_counterTimestamp get_Timestamp_for_Tx_in_A\n__GNT_1:\nRET\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getNextTx() inside a codecave', () => { + const code = '#pragma optimizationLevel 0\n long a, b, c; while ((a = getNextTx()) != 0) b++;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare _counterTimestamp\n^declare a\n^declare b\n^declare c\n\n__loop1_continue:\nFUN A_to_Tx_after_Timestamp $_counterTimestamp\nFUN @a get_A1\nBZR $a :__GNT_2\nFUN @_counterTimestamp get_Timestamp_for_Tx_in_A\n__GNT_2:\nBZR $a :__loop1_break\n__loop1_start:\nINC @b\nJMP :__loop1_continue\n__loop1_break:\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getNextTxFromBlockheight()', () => { + const code = '#pragma optimizationLevel 0\n long block; long currTxId = getNextTxFromBlockheight(100900); if (currTxId == 0) currTxId++; currTxId = getNextTxFromBlockheight(block);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare _counterTimestamp\n^declare block\n^declare currTxId\n\nSET @r0 #0000000000018a24\nSET @r1 #0000000000000020\nSHL @r0 $r1\nFUN A_to_Tx_after_Timestamp $r0\nFUN @currTxId get_A1\nBZR $currTxId :__GNT_1\nFUN @_counterTimestamp get_Timestamp_for_Tx_in_A\n__GNT_1:\nBNZ $currTxId :__if2_endif\n__if2_start:\nINC @currTxId\n__if2_endif:\nSET @r0 $block\nSET @r1 #0000000000000020\nSHL @r0 $r1\nFUN A_to_Tx_after_Timestamp $r0\nFUN @currTxId get_A1\nBZR $currTxId :__GNT_3\nFUN @_counterTimestamp get_Timestamp_for_Tx_in_A\n__GNT_3:\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getBlockheight()', () => { + const code = '#pragma optimizationLevel 0\n long block = getBlockheight(0xad);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare block\n\nSET @r0 #00000000000000ad\nFUN set_A1 $r0\nFUN @block get_Timestamp_for_Tx_in_A\nSET @r0 #0000000000000020\nSHR @block $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getCurrentBlockheight()', () => { + const code = '#pragma optimizationLevel 0\n long block = getCurrentBlockheight();' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare block\n\nFUN @block get_Block_Timestamp\nSET @r0 #0000000000000020\nSHR @block $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getAmount(); getAmountFx()', () => { + const code = '#pragma optimizationLevel 0\nlong la; fixed fa; la=getAmount(1234); fa=getAmountFx(0xfffe);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare fa\n\nSET @r0 #00000000000004d2\nCLR @r1\nFUN set_A1_A2 $r0 $r1\nFUN @la get_Amount_for_Tx_in_A\nSET @r0 #000000000000fffe\nCLR @r1\nFUN set_A1_A2 $r0 $r1\nFUN @fa get_Amount_for_Tx_in_A\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getSender()', () => { + const code = '#pragma optimizationLevel 0\n long a=getSender(1234);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #00000000000004d2\nFUN set_A1 $r0\nFUN B_to_Address_of_Tx_in_A\nFUN @a get_B1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getType()', () => { + const code = '#pragma optimizationLevel 0\n long a=getType(1234);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #00000000000004d2\nFUN set_A1 $r0\nFUN @a get_Type_for_Tx_in_A\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getCreator()', () => { + const code = '#pragma optimizationLevel 0\n long a=getCreator();' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nCLR @r0\nFUN set_B2 $r0\nFUN B_to_Address_of_Creator\nFUN @a get_B1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getCreatorOf()', () => { + const code = '#pragma optimizationLevel 0\n long a=getCreatorOf(1234);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #00000000000004d2\nFUN set_B2 $r0\nFUN B_to_Address_of_Creator\nFUN @a get_B1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getCodeHashOf()', () => { + const code = '#pragma optimizationLevel 0\n long a=getCodeHashOf(1234);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #00000000000004d2\nFUN set_B2 $r0\nFUN @a Get_Code_Hash_Id\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getWeakRandomNumber()', () => { + const code = '#pragma optimizationLevel 0\n long a=getWeakRandomNumber();' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFUN Put_Last_Block_GSig_In_A\nFUN @a get_A2\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getActivationOf(); getActivationOfFx', () => { + const code = '#pragma optimizationLevel 0\n long a=getActivationOf(1234); fixed b=getActivationOfFx(1234);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @r0 #00000000000004d2\nFUN set_B2 $r0\nFUN @a Get_Activation_Fee\nSET @r0 #00000000000004d2\nFUN set_B2 $r0\nFUN @b Get_Activation_Fee\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getCurrentBalance(); getCurrentBalanceFx() ', () => { + const code = '#pragma optimizationLevel 0\n long a=getCurrentBalance(); fixed b=getCurrentBalanceFx();' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nCLR @r0\nFUN set_B2 $r0\nFUN @a get_Current_Balance\nCLR @r0\nFUN set_B2 $r0\nFUN @b get_Current_Balance\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: readMessage()', () => { + const code = '#pragma optimizationLevel 0\n long buffer[4], *bufPtr; readMessage(0xdede, 1, buffer); readMessage(0xfafa, 0, bufPtr); readMessage(0xfefe, 0, bufPtr+1);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare buffer\n^const SET @buffer #0000000000000004\n^declare buffer_0\n^declare buffer_1\n^declare buffer_2\n^declare buffer_3\n^declare bufPtr\n\nSET @r0 #000000000000dede\nSET @r1 #0000000000000001\nFUN set_A1_A2 $r0 $r1\nFUN message_from_Tx_in_A_to_B\nFUN @buffer_0 get_B1\nFUN @buffer_1 get_B2\nFUN @buffer_2 get_B3\nFUN @buffer_3 get_B4\nSET @r0 #000000000000fafa\nCLR @r1\nFUN set_A1_A2 $r0 $r1\nFUN message_from_Tx_in_A_to_B\nSET @r0 $bufPtr\nFUN @r1 get_B1\nSET @($r0) $r1\nFUN @r1 get_B2\nINC @r0\nSET @($r0) $r1\nFUN @r1 get_B3\nINC @r0\nSET @($r0) $r1\nFUN @r1 get_B4\nINC @r0\nSET @($r0) $r1\nSET @r0 $bufPtr\nINC @r0\nSET @r1 #000000000000fefe\nCLR @r2\nFUN set_A1_A2 $r1 $r2\nFUN message_from_Tx_in_A_to_B\nFUN @r1 get_B1\nSET @($r0) $r1\nFUN @r1 get_B2\nINC @r0\nSET @($r0) $r1\nFUN @r1 get_B3\nINC @r0\nSET @($r0) $r1\nFUN @r1 get_B4\nINC @r0\nSET @($r0) $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: sendMessage()', () => { + const code = '#pragma optimizationLevel 0\n#pragma verboseAssembly\nlong a, b, msg[4], *msgPtr;\nsendMessage(msg, getCreator());\nsendMessage(&msg[0], getCreator());\nsendMessage(&msg[a], getCreator());\nsendMessage(msgPtr, getCreator());\nsendMessage(&a, getCreator());' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare msg\n^const SET @msg #0000000000000006\n^declare msg_0\n^declare msg_1\n^declare msg_2\n^declare msg_3\n^declare msgPtr\n\n^comment line 4 sendMessage(msg, getCreator());\nCLR @r1\nFUN set_B2 $r1\nFUN B_to_Address_of_Creator\nFUN @r0 get_B1\nFUN set_B1 $r0\nFUN set_A1_A2 $msg_0 $msg_1\nFUN set_A3_A4 $msg_2 $msg_3\nFUN send_A_to_Address_in_B\n^comment line 5 sendMessage(&msg[0], getCreator());\nCLR @r1\nFUN set_B2 $r1\nFUN B_to_Address_of_Creator\nFUN @r0 get_B1\nFUN set_B1 $r0\nFUN set_A1_A2 $msg_0 $msg_1\nFUN set_A3_A4 $msg_2 $msg_3\nFUN send_A_to_Address_in_B\n^comment line 6 sendMessage(&msg[a], getCreator());\nSET @r0 $msg\nADD @r0 $a\nCLR @r2\nFUN set_B2 $r2\nFUN B_to_Address_of_Creator\nFUN @r1 get_B1\nFUN set_B1 $r1\nSET @r1 $($r0)\nINC @r0\nSET @r2 $($r0)\nFUN set_A1_A2 $r1 $r2\nINC @r0\nSET @r1 $($r0)\nINC @r0\nSET @r2 $($r0)\nFUN set_A3_A4 $r1 $r2\nFUN send_A_to_Address_in_B\n^comment line 7 sendMessage(msgPtr, getCreator());\nCLR @r1\nFUN set_B2 $r1\nFUN B_to_Address_of_Creator\nFUN @r0 get_B1\nFUN set_B1 $r0\nSET @r0 $msgPtr\nSET @r1 $($r0)\nINC @r0\nSET @r2 $($r0)\nFUN set_A1_A2 $r1 $r2\nINC @r0\nSET @r1 $($r0)\nINC @r0\nSET @r2 $($r0)\nFUN set_A3_A4 $r1 $r2\nFUN send_A_to_Address_in_B\n^comment line 8 sendMessage(&a, getCreator());\nCLR @r1\nFUN set_B2 $r1\nFUN B_to_Address_of_Creator\nFUN @r0 get_B1\nFUN set_B1 $r0\nFUN set_A1_A2 $a $b\nFUN set_A3_A4 $msg $msg_0\nFUN send_A_to_Address_in_B\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: sendAmountAndMessage()', () => { + const code = '#pragma optimizationLevel 0\n #pragma verboseAssembly\n long a, b, msg[4], *msgPtr;\n sendAmountAndMessage(1200, msg, 0xdede);\n sendAmountAndMessage(1200, msgPtr, 0xdede);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare msg\n^const SET @msg #0000000000000006\n^declare msg_0\n^declare msg_1\n^declare msg_2\n^declare msg_3\n^declare msgPtr\n\n^comment line 4 sendAmountAndMessage(1200, msg, 0xdede);\nSET @r0 #00000000000004b0\nSET @r1 #000000000000dede\nCLR @r2\nFUN set_B1_B2 $r1 $r2\nFUN send_to_Address_in_B $r0\nFUN set_A1_A2 $msg_0 $msg_1\nFUN set_A3_A4 $msg_2 $msg_3\nFUN send_A_to_Address_in_B\n^comment line 5 sendAmountAndMessage(1200, msgPtr, 0xdede);\nSET @r0 #00000000000004b0\nSET @r1 #000000000000dede\nCLR @r2\nFUN set_B1_B2 $r1 $r2\nFUN send_to_Address_in_B $r0\nSET @r0 $msgPtr\nSET @r1 $($r0)\nINC @r0\nSET @r2 $($r0)\nFUN set_A1_A2 $r1 $r2\nINC @r0\nSET @r1 $($r0)\nINC @r0\nSET @r2 $($r0)\nFUN set_A3_A4 $r1 $r2\nFUN send_A_to_Address_in_B\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: sendAmount(); sendAmountFx()', () => { + const code = '#pragma optimizationLevel 0\n long a; fixed b; sendAmount(1_0000, 0xdede); sendAmountFx(0.2, 0xfafa);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nSET @r0 #0000000000002710\nSET @r1 #000000000000dede\nCLR @r2\nFUN set_B1_B2 $r1 $r2\nFUN send_to_Address_in_B $r0\nSET @r0 #0000000001312d00\nSET @r1 #000000000000fafa\nCLR @r2\nFUN set_B1_B2 $r1 $r2\nFUN send_to_Address_in_B $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: sendBalance()()', () => { + const code = '#pragma optimizationLevel 0\n sendBalance(0xdede);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n\nSET @r0 #000000000000dede\nFUN set_B1 $r0\nFUN send_All_to_Address_in_B\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getMapValue(); getMapValueFx()', () => { + const code = '#pragma optimizationLevel 0\n long a = getMapValue(0, 1); fixed b = getMapValueFx(2, 3);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nCLR @r0\nSET @r1 #0000000000000001\nCLR @r2\nFUN set_A1_A2 $r0 $r1\nFUN set_A3 $r2\nFUN @a Get_Map_Value_Keys_In_A\nSET @r0 #0000000000000002\nSET @r1 #0000000000000003\nCLR @r2\nFUN set_A1_A2 $r0 $r1\nFUN set_A3 $r2\nFUN @b Get_Map_Value_Keys_In_A\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getExtMapValue(); getExtMapValueFx()', () => { + const code = '#pragma optimizationLevel 0\n long a = getExtMapValue(0, 1, 0xdede); fixed b = getExtMapValueFx(2, 3, 0xfafa);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare a\n^declare b\n\nCLR @r0\nSET @r1 #0000000000000001\nSET @r2 #000000000000dede\nFUN set_A1_A2 $r0 $r1\nFUN set_A3 $r2\nFUN @a Get_Map_Value_Keys_In_A\nSET @r0 #0000000000000002\nSET @r1 #0000000000000003\nSET @r2 #000000000000fafa\nFUN set_A1_A2 $r0 $r1\nFUN set_A3 $r2\nFUN @b Get_Map_Value_Keys_In_A\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: setMapValue(); setMapValueFx()', () => { + const code = '#pragma optimizationLevel 0\n setMapValue(0, 1, 1_0000); setMapValueFx(2, 3, 0.2222);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n\nCLR @r0\nSET @r1 #0000000000000001\nSET @r2 #0000000000002710\nFUN set_A1_A2 $r0 $r1\nFUN set_A4 $r2\nFUN Set_Map_Value_Keys_In_A\nSET @r0 #0000000000000002\nSET @r1 #0000000000000003\nSET @r2 #0000000001530ce0\nFUN set_A1_A2 $r0 $r1\nFUN set_A4 $r2\nFUN Set_Map_Value_Keys_In_A\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: issueAsset()', () => { + const code = '#pragma optimizationLevel 0\n long asset = issueAsset("ABCDEFGH", "IJ", 4);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare asset\n\nSET @r0 #4847464544434241\nSET @r1 #0000000000004a49\nSET @r2 #0000000000000004\nFUN set_A1_A2 $r0 $r1\nFUN set_B1 $r2\nFUN @asset Issue_Asset\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: mintAsset()', () => { + const code = '#pragma optimizationLevel 0\n mintAsset(1_0000, 0xa5531);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n\nSET @r0 #0000000000002710\nSET @r1 #00000000000a5531\nFUN set_B1_B2 $r0 $r1\nFUN Mint_Asset\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: sendQuantity()', () => { + const code = '#pragma optimizationLevel 0\n sendQuantity(1_000, 0xa5531, 0xdede);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n\nSET @r0 #00000000000003e8\nSET @r1 #00000000000a5531\nSET @r2 #000000000000dede\nFUN set_B1_B2 $r2 $r1\nFUN send_to_Address_in_B $r0\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: sendQuantityAndAmount(); sendQuantityAndAmountFx()', () => { + const code = '#pragma optimizationLevel 0\n sendQuantityAndAmount(1_000, 0xa5531, 22, 0xdede); sendQuantityAndAmountFx(1_000, 0xa5531, .02, 0xdede); ' + const assembly = '^declare r0\n^declare r1\n^declare r2\n\nSET @r0 #000000000000dede\nSET @r1 #00000000000a5531\nFUN set_B1_B2 $r0 $r1\nSET @r0 #0000000000000016\nSET @r1 #00000000000003e8\nFUN set_B3 $r0\nFUN send_to_Address_in_B $r1\nSET @r0 #000000000000dede\nSET @r1 #00000000000a5531\nFUN set_B1_B2 $r0 $r1\nSET @r0 #00000000001e8480\nSET @r1 #00000000000003e8\nFUN set_B3 $r0\nFUN send_to_Address_in_B $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getAssetBalance()', () => { + const code = '#pragma optimizationLevel 0\n long a = getAssetBalance(0xa5531);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #00000000000a5531\nFUN set_B2 $r0\nFUN @a get_Current_Balance\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: checkSignature()', () => { + const code = '#pragma optimizationLevel 0\n #pragma maxAuxVars 6\n long msg2, msg3, msg4, txId, page, creator; long result = checkSignature(msg2, msg3, msg4, txId, page, creator); asm { ^comment break } result = checkSignature("msg2", "msg3", "msg4", "txId", "page", "creator");' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare r3\n^declare r4\n^declare r5\n^declare msg2\n^declare msg3\n^declare msg4\n^declare txId\n^declare page\n^declare creator\n^declare result\n\nFUN set_A1_A2 $txId $page\nFUN set_A3 $creator\nFUN set_B2 $msg2\nFUN set_B3_B4 $msg3 $msg4\nFUN @result Check_Sig_B_With_A\n^comment break\nSET @r0 #0000000064497874\nSET @r1 #0000000065676170\nFUN set_A1_A2 $r0 $r1\nSET @r0 #00726f7461657263\nSET @r1 #000000003267736d\nFUN set_A3 $r0\nFUN set_B2 $r1\nSET @r0 #000000003367736d\nSET @r1 #000000003467736d\nFUN set_B3_B4 $r0 $r1\nFUN @result Check_Sig_B_With_A\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: distributeToHolders()', () => { + const code = '#pragma optimizationLevel 0\n #pragma maxAuxVars 5\n long holdersAssetMinQuantity, holdersAsset, amountToDistribute, assetToDistribute, quantityToDistribute; distributeToHolders(holdersAssetMinQuantity, holdersAsset, amountToDistribute, assetToDistribute, quantityToDistribute); asm { ^comment break } distributeToHoldersFx(1, 2, 3.3, 4, 5);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare r3\n^declare r4\n^declare holdersAssetMinQuantity\n^declare holdersAsset\n^declare amountToDistribute\n^declare assetToDistribute\n^declare quantityToDistribute\n\nFUN set_B1_B2 $holdersAssetMinQuantity $holdersAsset\nFUN set_A1 $amountToDistribute\nFUN set_A3_A4 $assetToDistribute $quantityToDistribute\nFUN Distribute_To_Asset_Holders\n^comment break\nSET @r0 #0000000000000001\nSET @r1 #0000000000000002\nFUN set_B1_B2 $r0 $r1\nSET @r0 #0000000013ab6680\nFUN set_A1 $r0\nSET @r0 #0000000000000004\nSET @r1 #0000000000000005\nFUN set_A3_A4 $r0 $r1\nFUN Distribute_To_Asset_Holders\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getAssetHoldersCount()', () => { + const code = '#pragma optimizationLevel 0\n long block = getAssetHoldersCount(1000, 0xa55e1);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare block\n\nSET @r0 #00000000000003e8\nSET @r1 #00000000000a55e1\nFUN set_B1_B2 $r0 $r1\nFUN @block Get_Asset_Holders_Count\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: readAssets()', () => { + const code = '#pragma optimizationLevel 0\n long txId, assets[4], *assetsPtr; readAssets(txId, assets); asm { ^comment b} readAssets(25, assets); asm { ^comment b} readAssets(25, assetsPtr); asm { ^comment c} readAssets(txId, assetsPtr + 1);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare txId\n^declare assets\n^const SET @assets #0000000000000005\n^declare assets_0\n^declare assets_1\n^declare assets_2\n^declare assets_3\n^declare assetsPtr\n\nFUN set_A1 $txId\nFUN B_To_Assets_Of_Tx_In_A\nFUN @assets_0 get_B1\nFUN @assets_1 get_B2\nFUN @assets_2 get_B3\nFUN @assets_3 get_B4\n^comment b\nSET @r0 #0000000000000019\nFUN set_A1 $r0\nFUN B_To_Assets_Of_Tx_In_A\nFUN @assets_0 get_B1\nFUN @assets_1 get_B2\nFUN @assets_2 get_B3\nFUN @assets_3 get_B4\n^comment b\nSET @r0 #0000000000000019\nFUN set_A1 $r0\nFUN B_To_Assets_Of_Tx_In_A\nSET @r0 $assetsPtr\nFUN @r1 get_B1\nSET @($r0) $r1\nFUN @r1 get_B2\nINC @r0\nSET @($r0) $r1\nFUN @r1 get_B3\nINC @r0\nSET @($r0) $r1\nFUN @r1 get_B4\nINC @r0\nSET @($r0) $r1\n^comment c\nSET @r0 $assetsPtr\nINC @r0\nFUN set_A1 $txId\nFUN B_To_Assets_Of_Tx_In_A\nFUN @r1 get_B1\nSET @($r0) $r1\nFUN @r1 get_B2\nINC @r0\nSET @($r0) $r1\nFUN @r1 get_B3\nINC @r0\nSET @($r0) $r1\nFUN @r1 get_B4\nINC @r0\nSET @($r0) $r1\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getQuantity()', () => { + const code = '#pragma optimizationLevel 0\n long txId, asset; long qty = getQuantity(txId, asset); asm { ^comment b} qty = getQuantity(1234, 0xA55E1);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare txId\n^declare asset\n^declare qty\n\nFUN set_A1_A2 $txId $asset\nFUN @qty get_Amount_for_Tx_in_A\n^comment b\nSET @r0 #00000000000004d2\nSET @r1 #00000000000a55e1\nFUN set_A1_A2 $r0 $r1\nFUN @qty get_Amount_for_Tx_in_A\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: getAssetCirculating()', () => { + const code = '#pragma optimizationLevel 0\n long a = getAssetCirculating(0xa55e1);' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #00000000000a55e1\nFUN set_A2 $r0\nFUN @a Get_Asset_Circulating\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) diff --git a/src/__tests__/keywords.a.spec.ts b/src/__tests__/keywords.a.spec.ts index 7879ebf..8c29b2f 100644 --- a/src/__tests__/keywords.a.spec.ts +++ b/src/__tests__/keywords.a.spec.ts @@ -148,6 +148,13 @@ describe('Keywords right usage', () => { compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) + it('should compile: sleep (no arguments)', () => { + const code = '#pragma optimizationLevel 0\nlong a, b; a++; sleep;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nINC @a\nSLP\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) it('should compile: sleep', () => { const code = '#pragma optimizationLevel 0\nlong a, b; a++; sleep 1;' const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nINC @a\nSET @r0 #0000000000000001\nSLP $r0\nFIN\n' diff --git a/src/__tests__/keywords.b.spec.ts b/src/__tests__/keywords.b.spec.ts index d27c359..4f0bc91 100644 --- a/src/__tests__/keywords.b.spec.ts +++ b/src/__tests__/keywords.b.spec.ts @@ -50,13 +50,6 @@ describe('Keywords wrong usage', () => { compiler.compile() }).toThrowError(/^At line/) }) - test('should throw: sleep without argument', () => { - expect(() => { - const code = 'long a; sleep; a++;' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - }).toThrowError(/^At line/) - }) test('should throw: const without arguments', () => { expect(() => { const code = 'long a; const ;' diff --git a/src/__tests__/macros.a.spec.ts b/src/__tests__/macros.a.spec.ts index 800b23f..a8b20e8 100644 --- a/src/__tests__/macros.a.spec.ts +++ b/src/__tests__/macros.a.spec.ts @@ -41,6 +41,13 @@ describe('#program', () => { compiler.compile() }).toThrowError(/^At line/) }) + test('should throw: forbidden char in name', () => { + expect(() => { + const code = '#program name test2 d\n long a; a++;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) test('should throw: description bigger than 1000 chars', () => { expect(() => { const code = '#program description Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam efficitur mollis mauris eu pretium. Vivamus ut nisl eget elit aliquam finibus eget a ex. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vivamus vel neque risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse potenti. Sed eget lacinia lorem, et luctus orci. Praesent ut lorem pretium, iaculis dui eu, convallis sem. Ut lorem metus, eleifend eu velit in, volutpat ullamcorper sem. Donec hendrerit ornare posuere. Curabitur vitae lacus non dolor lacinia mollis. Sed ex felis, fringilla ac fringilla id, lobortis condimentum mi. Vestibulum nec orci vel lectus pulvinar imperdiet sit amet id nulla. Morbi quis orci tristique, pharetra libero pharetra, fermentum nunc. Nulla vestibulum felis risus, at cursus leo blandit ut. Praesent interdum commodo ex, sed vehicula sem luctus eu. Ut sed diam quis lectus lobortis maximus. Etiam hendrerit tincidunt ligula nec efficitur. Donec pulvinar mauris ac integer.\nlong a; a++;' @@ -55,12 +62,15 @@ describe('#program', () => { compiler.compile() }).toThrowError(/^At line/) }) - test('should throw: forbiden activation amount in hex', () => { + it('should compile: activation amount in hex', () => { expect(() => { const code = '#program activationAmount 0xff\n long a; a++;' + const assembly = '^program activationAmount 500000000\n^declare r0\n^declare r1\n^declare r2\n\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() - }).toThrowError(/^At line/) + expect(compiler.getAssemblyCode()).toBe(assembly) + expect(compiler.getMachineCode().PActivationAmount).toBe('255') + }) }) it('should compile: allow _ in activationAmount', () => { const code = '#program activationAmount 5_0000_0000' @@ -77,6 +87,13 @@ describe('#program', () => { compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) + it('should compile: creator and contract', () => { + const code = '#pragma optimizationLevel 0\n#program creator 10 \n#program contract 9223372036854775808 \nlong a; void test(void) { a++; return; a++; }' + const assembly = '^program creator 10\n^program contract 9223372036854775808\n^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFIN\n\n__fn_test:\nINC @a\nRET\nINC @a\nRET\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) it('should compile: codeStackPages userStackPages ', () => { const code = '#pragma optimizationLevel 0\n#program codeStackPages 0 \n#program userStackPages 5\n long a; void test(long aa) { a++; return; a++; }' const assembly = '^program userStackPages 5\n^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare test_aa\n\nFIN\n\n__fn_test:\nPOP @test_aa\nINC @a\nRET\nINC @a\nRET\n' @@ -105,12 +122,47 @@ describe('#program', () => { compiler.compile() }).toThrowError(/^At line/) }) + it('should compile: codeHashId', () => { + const code = '#pragma optimizationLevel 0\n#program codeHashId 0 \n long a; a++;' + const assembly = '^program codeHashId 16984156175653688123\n^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: codeHashId right', () => { + const code = '#pragma optimizationLevel 0\n#program codeHashId 16984156175653688123 \n long a; a++;' + const assembly = '^program codeHashId 16984156175653688123\n^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: codeHashId not on first line', () => { + const code = '#program name GetATCodeHashId\n#program codeHashId 0 \nlong a;\na++;' + const assembly = '^program name GetATCodeHashId\n^program codeHashId 16984156175653688123\n^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + test('should throw: codeHashId wrong', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\n#program codeHashId 1 \n long a; a++;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^assembler\(\)/) + }) + test('should throw: codeHashId wrong', () => { + expect(() => { + const code = '#pragma optimizationLevel 0\n#program codeHashId 0x237a33 \n long a; a++;' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + }).toThrowError(/^At line/) + }) }) describe('#pragma', () => { - it('should compile: outputSourceLineNumber', () => { - const code = '#pragma outputSourceLineNumber\nlong a=5;\nif (a){\nwhile (a<5) {\n a--;\n }\n a--;\n}\n#pragma optimizationLevel 0\n' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\n^comment line 2\nSET @a #0000000000000005\n^comment line 3\nBZR $a :__if1_endif\n__if1_start:\n^comment line 4\n__loop2_continue:\nSET @r0 #0000000000000005\nBGE $a $r0 :__loop2_break\n__loop2_start:\n^comment line 5\nDEC @a\nJMP :__loop2_continue\n__loop2_break:\n^comment line 7\nDEC @a\n__if1_endif:\nFIN\n' + it('should compile: verboseAssembly', () => { + const code = '#pragma verboseAssembly\nlong a=5;\nif (a){\nwhile (a<5) {\n a--;\n }\n a--;\n}\n#pragma optimizationLevel 0\n' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\n^comment line 2 long a=5;\nSET @a #0000000000000005\n^comment line 3 if (a){\nBZR $a :__if1_endif\n__if1_start:\n^comment line 4 while (a<5) {\n__loop2_continue:\nSET @r0 #0000000000000005\nBGE $a $r0 :__loop2_break\n__loop2_start:\n^comment line 5 a--;\nDEC @a\nJMP :__loop2_continue\n__loop2_break:\n^comment line 7 a--;\nDEC @a\n__if1_endif:\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) @@ -136,37 +188,9 @@ describe('#pragma', () => { compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) - it('should compile: enableRandom', () => { - const code = '#pragma optimizationLevel 0\n#pragma maxAuxVars 1\n#pragma enableRandom true\nlong a; if (a) a++;' - const assembly = /^\^declare r0\n\^declare a\n\nBZR \$a :__if\w{5}_endif\n__if\w{5}_start:\nINC @a\n__if\w{5}_endif:\nFIN\n$/g - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toMatch(assembly) - }) - it('should compile: enableRandom (with getJumpID in AuxVars)', () => { - const code = '#pragma optimizationLevel 0\n#pragma maxAuxVars 1\n#pragma enableRandom true\nlong a, b; b = !a;' - const assembly = /^\^declare r0\n\^declare a\n\^declare b\n\nBNZ \$a :__NOT_(\w{5})_sF\n__NOT_\1_sT:\nSET @b #0000000000000001\nJMP :__NOT_\1_end\n__NOT_\1_sF:\nCLR @b\n__NOT_\1_end:\nFIN\n$/g - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toMatch(assembly) - }) - it('should compile: enableLineLabels', () => { - const code = '#pragma enableLineLabels true\nlong a;\nif (a) a++;\n#pragma optimizationLevel 0' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nBZR $a :__if3_1_endif\n__if3_1_start:\nINC @a\n__if3_1_endif:\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: enableLineLabels (getJumpId in AuxVars)', () => { - const code = '#pragma enableLineLabels true\n long a, b;\n a = !b;\n#pragma optimizationLevel 0\n' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBNZ $b :__NOT_3_1_sF\n__NOT_3_1_sT:\nSET @a #0000000000000001\nJMP :__NOT_3_1_end\n__NOT_3_1_sF:\nCLR @a\n__NOT_3_1_end:\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: outputSourceLineNumber', () => { - const code = '#pragma outputSourceLineNumber true\n long a;\n if (a) a++;\n a++;\n#pragma optimizationLevel 0\n' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\n^comment line 3\nBZR $a :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\n^comment line 4\nINC @a\nFIN\n' + it('should compile: verboseAssembly', () => { + const code = '#pragma verboseAssembly true\n long a;\n if (a) a++;\n a++;\n#pragma optimizationLevel 0\n' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\n^comment line 3 if (a) a++;\nBZR $a :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\n^comment line 4 a++;\nINC @a\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) @@ -208,7 +232,7 @@ describe('#pragma', () => { }) test('should throw: wrong boolean value', () => { expect(() => { - const code = '#pragma enableLineLabels 10\nlong a;\nif (a) a++;' + const code = '#pragma APIFunctions 10\nlong a;\nif (a) a++;' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() }).toThrowError(/^At line/) @@ -237,3 +261,12 @@ describe('#include and misc', () => { }).toThrowError(/^At line/) }) }) +describe('#define macro() ()', () => { + it('should compile: macro returning value', () => { + const code = '#pragma optimizationLevel 0\n#include APIFunctions\n#define getCreator() (Clear_A(), B_To_Address_Of_Creator(), Get_B1())\n long a; if (getCreator() == 0) a++;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFUN clear_A\nFUN B_to_Address_of_Creator\nFUN @r0 get_B1\nBNZ $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) diff --git a/src/__tests__/macros.b.spec.ts b/src/__tests__/macros.b.spec.ts deleted file mode 100644 index 477eb3f..0000000 --- a/src/__tests__/macros.b.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { SmartC } from '../smartc' - -describe('#pragma warningToError false', () => { - it('should compile: regular variable as pointer + warningToError false', () => { - const code = '#pragma warningToError false\nlong *pa, *pb, va, vb;\n pa=vb; pa=*pb; *pa=pb; *pa=&pb; *pa=&vb; va=pb; va=&pb; va=&vb;' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare pa\n^declare pb\n^declare va\n^declare vb\n\nSET @pa $vb\nSET @pa $($pb)\nSET @($pa) $pb\nSET @r0 #0000000000000004\nSET @($pa) $r0\nSET @r0 #0000000000000006\nSET @($pa) $r0\nSET @va $pb\nSET @va #0000000000000004\nSET @va #0000000000000006\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: function argument', () => { - const code = '#pragma warningToError false\n long a, *b; test(a); void test(long *arg) {}' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare test_arg\n\nPSH $a\nJSR :__fn_test\nFIN\n\n__fn_test:\nPOP @test_arg\nRET\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: function return', () => { - const code = '#pragma warningToError false\nlong a, *b; b = test(); long test(void) { return 5; }' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nJSR :__fn_test\nPOP @b\nFIN\n\n__fn_test:\nSET @r0 #0000000000000005\nPSH $r0\nRET\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: function return 2', () => { - const code = '#pragma warningToError false\n long a, *b; b = test(); long test(void) { return b; }' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nJSR :__fn_test\nPOP @b\nFIN\n\n__fn_test:\nPSH $b\nRET\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: API function argument', () => { - const code = '#pragma warningToError false\n#include APIFunctions\nlong a, *b; Set_A1(b);' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nFUN set_A1 $b\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: API function return', () => { - const code = '#pragma warningToError false\n#include APIFunctions\nlong a, *b; b = Get_A1();' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nFUN @b get_A1\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: Address of an register', () => { - const code = '#pragma warningToError false\nlong a, *b; b = &r1;' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @b #0000000000000001\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - it('should compile: Using regular variable as pointer', () => { - const code = '#pragma warningToError false\nlong a, b; a=*(b+1);' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $b\nINC @a\nSET @a $($a)\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) - test('should throw: forbidden char in name', () => { - expect(() => { - const code = '#program name test2 d\n long a; a++;' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - }).toThrowError(/^At line/) - }) -}) diff --git a/src/__tests__/misc.a.spec.ts b/src/__tests__/misc.a.spec.ts index 4e360f1..f1fe2bf 100644 --- a/src/__tests__/misc.a.spec.ts +++ b/src/__tests__/misc.a.spec.ts @@ -210,3 +210,51 @@ describe('Wrong code to check error safeguards', () => { }).toThrowError(/^At line/) }) }) + +describe('Warnings', () => { + it('should compile with warning: right side of operator', () => { + const code = 'long la, lb; fixed fa, fb; fa = fb - la;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare lb\n^declare fa\n^declare fb\n\nSET @fa $fb\nSET @r0 $la\nMUL @r0 $f100000000\nSUB @fa $r0\nFIN\n' + const warnings = "Warning: at line 1. Implicit type casting conversion on right side of operator '-'." + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + expect(compiler.getMachineCode().Warnings).toBe(warnings) + }) + it('should compile with warning: left side of operator', () => { + const code = 'long la, lb; fixed fa, fb; fa = la - fb;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare lb\n^declare fa\n^declare fb\n\nSET @fa $la\nMUL @fa $f100000000\nSUB @fa $fb\nFIN\n' + const warnings = "Warning: at line 1. Implicit type casting conversion on left side of operator '-'." + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + expect(compiler.getMachineCode().Warnings).toBe(warnings) + }) + it('should compile with warning: rigth side of comparision', () => { + const code = 'long la, lb; fixed fa, fb; if(fa < lb) la++;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare lb\n^declare fa\n^declare fb\n\nSET @r0 $lb\nMUL @r0 $f100000000\nBLT $fa $r0 :__opt_1\nFIN\n__opt_1:\nINC @la\nFIN\n' + const warnings = "Warning: at line 1. Implicit type casting conversion on right side of comparision '<'." + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + expect(compiler.getMachineCode().Warnings).toBe(warnings) + }) + it('should compile with warning: left side of comparision', () => { + const code = 'long la, lb; fixed fa, fb; if(la < fb) la++;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare lb\n^declare fa\n^declare fb\n\nSET @r0 $la\nMUL @r0 $f100000000\nBLT $r0 $fb :__opt_1\nFIN\n__opt_1:\nINC @la\nFIN\n' + const warnings = "Warning: at line 1. Implicit type casting conversion on left side of comparision '<'." + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + expect(compiler.getMachineCode().Warnings).toBe(warnings) + }) + it('should compile with warning: right side of assignment', () => { + const code = 'long la, lb; fixed fa, fb; la = fb;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare la\n^declare lb\n^declare fa\n^declare fb\n\nSET @la $fb\nDIV @la $f100000000\nFIN\n' + const warnings = "Warning: at line 1. Implicit type casting conversion on right side of assignment '='." + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + expect(compiler.getMachineCode().Warnings).toBe(warnings) + }) +}) diff --git a/src/__tests__/optimizations.a.spec.ts b/src/__tests__/optimizations.a.spec.ts index 3914e5b..fb8550a 100644 --- a/src/__tests__/optimizations.a.spec.ts +++ b/src/__tests__/optimizations.a.spec.ts @@ -23,6 +23,13 @@ describe('Optimizations level zero', () => { compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) + it('should compile: Optimization with const fX variables', () => { + const code = '#pragma optimizationLevel 0\nconst fixed f20000000 = .2; fixed fb; long a= fb * .2;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare f100000000\n^const SET @f100000000 #0000000005f5e100\n^declare f20000000\n^declare fb\n^declare a\n\n^const SET @f20000000 #0000000001312d00\nSET @a $f20000000\nMDV @a $fb $f100000000\nDIV @a $f100000000\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) it('should compile: Specifc operator optimization with WRONG const n2 variables', () => { const code = '#pragma optimizationLevel 0\nlong a , b; const long n2 = 20; a=b+2;' const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare n2\n\n^const SET @n2 #0000000000000014\nSET @a $b\nINC @a\nINC @a\nFIN\n' @@ -148,26 +155,59 @@ describe('Optimizations level 2', () => { compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) + it('should compile: mul+div to mdv', () => { + const code = '#pragma optimizationLevel 2\n long a, b, c, d; a = b * c / d;' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nMDV @a $c $d\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: get_B1 + set_B1', () => { + const code = '#pragma optimizationLevel 2\n sendBalance(getCreator());' + const assembly = '^declare r0\n^declare r1\n^declare r2\n\nCLR @r1\nFUN set_B2 $r1\nFUN B_to_Address_of_Creator\nFUN send_All_to_Address_in_B\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) + it('should compile: psh+pop same register', () => { + const code = '#pragma optimizationLevel 2\n asm {INC @a\nPSH $r0\nPOP @r0\nPSH $a\nPOP @a}' + const assembly = '^declare r0\n^declare r1\n^declare r2\n\nINC @a\nPSH $a\nPOP @a\nFIN\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) }) /* Please note that these optimizations are not well tested. Code generated by smartc mostly have this safely coded during AST processing. */ describe('Optimizations level 3', () => { + it('should compile: simple test', () => { + const code = '#pragma optimizationLevel 3\nlong txid; while ((txid = getNextTx()) != 0) { if (getSender(txid) != 0xfefe) continue; }' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare _counterTimestamp\n^declare txid\n\n__loop1_continue:\nFUN A_to_Tx_after_Timestamp $_counterTimestamp\nFUN @txid get_A1\nBZR $txid :__GNT_2\nFUN @_counterTimestamp get_Timestamp_for_Tx_in_A\n__GNT_2:\nBNZ $txid :__opt_1\nFIN\n__opt_1:\nFUN B_to_Address_of_Tx_in_A\nFUN @r0 get_B1\nSET @r1 #000000000000fefe\nBNE $r0 $r1 :__loop1_continue\nJMP :__loop1_continue\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) +}) + +/* Please note that these optimizations are not well tested. Code generated by smartc +mostly have this safely coded during AST processing. */ +describe('Optimizations level 4', () => { it('should optimize: pop+set = pop', () => { - const code = 'SET @r0 #000000000000655a\nPOP @r0\nSET @pcar $r0\nFIN\n' - const assembly = 'SET @r0 #000000000000655a\nPOP @pcar\nFIN\n' - const result = optimizer(3, code, []) + const code = '^declare r0\n^declare pcar\nSET @r0 #000000000000655a\nPOP @r0\nSET @pcar $r0\nFIN\n' + const assembly = '^declare r0\n^declare pcar\nSET @r0 #000000000000655a\nPOP @pcar\nFIN\n' + const result = optimizer(4, code, []) expect(result).toBe(assembly) }) it('should compile: clear+pointer = pointer', () => { - const code = '#pragma optimizationLevel 3\nlong a, d[2]; a=0; d[a]=5; d[1]=d[a];' + const code = '#pragma optimizationLevel 4\nlong a, d[2]; a=0; d[a]=5; d[1]=d[a];' const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare d\n^const SET @d #0000000000000005\n^declare d_0\n^declare d_1\n\nSET @r0 #0000000000000005\nSET @($d) $r0\nSET @d_1 $($d)\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) it('should compile: set+pointer = pointer with old var', () => { - const code = '#pragma optimizationLevel 3\nlong a, b, d[2]; a=b; d[a]=5; d[1]=d[a];' + const code = '#pragma optimizationLevel 4\nlong a, b, d[2]; a=b; d[a]=5; d[1]=d[a];' const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare d\n^const SET @d #0000000000000006\n^declare d_0\n^declare d_1\n\nSET @r0 #0000000000000005\nSET @($d + $b) $r0\nSET @d_1 $($d + $b)\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() diff --git a/src/__tests__/pointers.a.spec.ts b/src/__tests__/pointers.a.spec.ts index 2601cb4..c74b9fa 100644 --- a/src/__tests__/pointers.a.spec.ts +++ b/src/__tests__/pointers.a.spec.ts @@ -102,13 +102,6 @@ describe('Pointer/Array assignment', () => { compiler.compile() }).toThrowError(/^At line/) }) - it('should compile: Address of array to regular variable (disregard warning)', () => { - const code = '#pragma warningToError false\nlong a[4], *b, c; c=&a; c=&a[0]; c=&c;' - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^const SET @a #0000000000000004\n^declare a_0\n^declare a_1\n^declare a_2\n^declare a_3\n^declare b\n^declare c\n\nSET @c #0000000000000003\nSET @c #0000000000000004\nSET @c #0000000000000009\nFIN\n' - const compiler = new SmartC({ language: 'C', sourceCode: code }) - compiler.compile() - expect(compiler.getAssemblyCode()).toBe(assembly) - }) it('should compile: Operation with pointer before deferencing', () => { const code = 'long *a, b, c;\n*(a+1)=b; *(a+30)=b; *(a+c)=b;\nb=*(a+1); b=*(a+30); b=*(a+c);' const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $a\nINC @r0\nSET @($r0) $b\nSET @r0 #000000000000001e\nADD @r0 $a\nSET @($r0) $b\nSET @r0 $a\nADD @r0 $c\nSET @($r0) $b\nSET @b $a\nINC @b\nSET @b $($b)\nSET @b #000000000000001e\nADD @b $a\nSET @b $($b)\nSET @b $a\nADD @b $c\nSET @b $($b)\nFIN\n' diff --git a/src/__tests__/structs.b.spec.ts b/src/__tests__/structs.b.spec.ts index dc010f4..5400240 100644 --- a/src/__tests__/structs.b.spec.ts +++ b/src/__tests__/structs.b.spec.ts @@ -17,7 +17,7 @@ a=pcar->collector; *c=pcar->collector; d[1]=pcar->collector; d[a]=pcar->collector;` - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare car_driver\n^declare car_collector\n^declare car_passenger\n^declare pcar\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #000000000000000b\n^declare d_0\n^declare d_1\n\nSET @pcar #0000000000000003\nSET @r0 #000000000000655a\nSET @r1 #0000000000000002\nSET @($pcar + $r1) $r0\nSET @($pcar) $a\nSET @r0 $($c)\nSET @($pcar) $r0\nSET @($pcar) $d_1\nSET @r0 $($d + $a)\nSET @($pcar) $r0\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @($pcar) $r0\nSET @r0 #0000000000000001\nSET @a $($pcar + $r0)\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @($c) $r0\nSET @r0 #0000000000000001\nSET @d_1 $($pcar + $r0)\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @($d + $a) $r0\nFIN\n' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare car_driver\n^declare car_collector\n^declare car_passenger\n^declare pcar\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #000000000000000b\n^declare d_0\n^declare d_1\n\nSET @pcar #0000000000000003\nSET @r0 #000000000000655a\nSET @r1 #0000000000000002\nSET @($pcar + $r1) $r0\nSET @($pcar) $a\nSET @r0 $($c)\nSET @($pcar) $r0\nSET @($pcar) $d_1\nSET @r0 $($d + $a)\nSET @($pcar) $r0\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @($pcar) $r0\nSET @a #0000000000000001\nSET @a $($pcar + $a)\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @($c) $r0\nSET @d_1 #0000000000000001\nSET @d_1 $($pcar + $d_1)\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @($d + $a) $r0\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) @@ -39,7 +39,7 @@ a=pcar->passenger[2]; *c=pcar->passenger[2]; d[1]=pcar->passenger[2]; d[a]=pcar->passenger[2];` - const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare car_driver\n^declare car_collector\n^declare car_passenger\n^const SET @car_passenger #0000000000000006\n^declare car_passenger_0\n^declare car_passenger_1\n^declare car_passenger_2\n^declare car_passenger_3\n^declare pcar\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #000000000000000f\n^declare d_0\n^declare d_1\n\nSET @pcar #0000000000000003\nSET @r0 #000000000000655a\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r0 #0000000000000005\nSET @($pcar + $r0) $a\nSET @r0 $($c)\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r0 #0000000000000005\nSET @($pcar + $r0) $d_1\nSET @r0 $($d + $a)\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r1 #0000000000000004\nSET @r0 $($pcar + $r1)\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r0 #0000000000000005\nSET @a $($pcar + $r0)\nSET @r1 #0000000000000005\nSET @r0 $($pcar + $r1)\nSET @($c) $r0\nSET @r0 #0000000000000005\nSET @d_1 $($pcar + $r0)\nSET @r1 #0000000000000005\nSET @r0 $($pcar + $r1)\nSET @($d + $a) $r0\nFIN\n' + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare car_driver\n^declare car_collector\n^declare car_passenger\n^const SET @car_passenger #0000000000000006\n^declare car_passenger_0\n^declare car_passenger_1\n^declare car_passenger_2\n^declare car_passenger_3\n^declare pcar\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #000000000000000f\n^declare d_0\n^declare d_1\n\nSET @pcar #0000000000000003\nSET @r0 #000000000000655a\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r0 #0000000000000005\nSET @($pcar + $r0) $a\nSET @r0 $($c)\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r0 #0000000000000005\nSET @($pcar + $r0) $d_1\nSET @r0 $($d + $a)\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @r1 #0000000000000004\nSET @r0 $($pcar + $r1)\nSET @r1 #0000000000000005\nSET @($pcar + $r1) $r0\nSET @a #0000000000000005\nSET @a $($pcar + $a)\nSET @r1 #0000000000000005\nSET @r0 $($pcar + $r1)\nSET @($c) $r0\nSET @d_1 #0000000000000005\nSET @d_1 $($pcar + $d_1)\nSET @r1 #0000000000000005\nSET @r0 $($pcar + $r1)\nSET @($d + $a) $r0\nFIN\n' const compiler = new SmartC({ language: 'C', sourceCode: code }) compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) @@ -184,6 +184,22 @@ if (a<=pcar->collector) { b--; }` compiler.compile() expect(compiler.getAssemblyCode()).toBe(assembly) }) + it('should compile: struct declaration inside function', () => { + const code = `search(2); +struct PLAYER * search(long playerAddress) { + struct PLAYER { + long address, balance, VDLS; + } *playerPtr, players[2]; + long nPlayers; + struct PLAYER * foundPlayer; + nPlayers = sizeof(struct PLAYER); + return NULL; +}` + const assembly = '^declare r0\n^declare r1\n^declare r2\n^declare search_playerAddress\n^declare search_playerPtr\n^declare search_players\n^const SET @search_players #0000000000000006\n^declare search_players_0_address\n^declare search_players_0_balance\n^declare search_players_0_VDLS\n^declare search_players_1_address\n^declare search_players_1_balance\n^declare search_players_1_VDLS\n^declare search_nPlayers\n^declare search_foundPlayer\n\nSET @r0 #0000000000000002\nPSH $r0\nJSR :__fn_search\nPOP @r0\nFIN\n\n__fn_search:\nPOP @search_playerAddress\nSET @search_nPlayers #0000000000000003\nCLR @r0\nPSH $r0\nRET\n' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getAssemblyCode()).toBe(assembly) + }) }) describe('structs (pointer / arrays) wrong usage', () => { diff --git a/src/__tests__/warnings.a.spec.ts b/src/__tests__/warnings.a.spec.ts new file mode 100644 index 0000000..629394d --- /dev/null +++ b/src/__tests__/warnings.a.spec.ts @@ -0,0 +1,11 @@ +import { SmartC } from '../smartc' + +describe('Warnings', () => { + it('should not warn: add or sub pointers with longs values (struct_ptr)', () => { + const code = 'search(2);\nstruct PLAYER { long address, balance, VDLS; } *playerPtr, players[2];\nstruct PLAYER * search(long playerAddress) {\nlong nPlayers;\n struct PLAYER * foundPlayer;\n foundPlayer = &players[0];\n for (long auxI = 0; auxI < nPlayers; auxI++) {\n if (foundPlayer->address == playerAddress) {\n return foundPlayer;\n }\n foundPlayer += (sizeof(struct PLAYER));\n foundPlayer = foundPlayer + (sizeof(struct PLAYER));\n foundPlayer++;\n }\n return NULL;\n}' + const warnings = '' + const compiler = new SmartC({ language: 'C', sourceCode: code }) + compiler.compile() + expect(compiler.getMachineCode().Warnings).toBe(warnings) + }) +}) diff --git a/src/assembler/assembler.ts b/src/assembler/assembler.ts index 247e836..34ae45a 100644 --- a/src/assembler/assembler.ts +++ b/src/assembler/assembler.ts @@ -57,6 +57,14 @@ type ASM_OBJECT = { PUserStackPages: number /** Selected size for code stack */ PCodeStackPages: number + /** Previous calculates codeHashId. If zero, it is ignored */ + PCodeHashId: string + /** Line of codeHashId preprocessor instruction */ + PCodeHashIdLine: number + /** Modified assembly source code */ + assembledCode: string + /** Calculated codeHashId for this run */ + codeHashId: string /** hexstring of compiled program */ bytecode: string /** hexstring for memory starting values */ @@ -122,6 +130,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { { opCode: 0x16, name: 'MOD_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*MOD\s+@(\w+)\s+\$(\w+)\s*$/ }, { opCode: 0x17, name: 'SHL_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SHL\s+@(\w+)\s+\$(\w+)\s*$/ }, { opCode: 0x18, name: 'SHR_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SHR\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x19, name: 'POW_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*POW\s+@(\w+)\s+\$(\w+)\s*$/ }, // POW @var $var { opCode: 0x1a, name: 'JMP_ADR', size: 5, argsType: ['J'], regex: /^\s*JMP\s+:(\w+)\s*$/ }, { opCode: 0x1b, name: 'BZR_DAT', size: 6, argsType: ['I', 'B'], regex: /^\s*BZR\s+\$(\w+)\s+:(\w+)\s*$/ }, { opCode: 0x1e, name: 'BNZ_DAT', size: 6, argsType: ['I', 'B'], regex: /^\s*BNZ\s+\$(\w+)\s+:(\w+)\s*$/ }, @@ -136,7 +145,9 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { { opCode: 0x27, name: 'STZ_DAT', size: 5, argsType: ['I'], regex: /^\s*STZ\s+\$(\w+)\s*$/ }, { opCode: 0x28, name: 'FIN_IMD', size: 1, argsType: [], regex: /^\s*FIN\s*$/ }, { opCode: 0x29, name: 'STP_IMD', size: 1, argsType: [], regex: /^\s*STP\s*$/ }, + { opCode: 0x2a, name: 'SLP_IMD', size: 1, argsType: [], regex: /^\s*SLP\s*$/ }, { opCode: 0x2b, name: 'ERR_ADR', size: 5, argsType: ['J'], regex: /^\s*ERR\s+:(\w+)\s*$/ }, + { opCode: 0x2c, name: 'MDV_DAT', size: 13, argsType: ['I', 'I', 'I'], regex: /^\s*MDV\s+@(\w+)\s+\$(\w+)\s+\$(\w+)\s*$/ }, // MDV @var $var $var { opCode: 0x30, name: 'SET_PCS', size: 1, argsType: [], regex: /^\s*PCS\s*$/ }, { opCode: 0x32, name: 'EXT_FUN', size: 3, argsType: ['F'], regex: /^\s*FUN\s+(\w+)\s*$/ }, { opCode: 0x33, name: 'EXT_FUN_DAT', size: 7, argsType: ['F', 'I'], regex: /^\s*FUN\s+(\w+)\s+\$(\w+)\s*$/ }, @@ -196,6 +207,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { { name: 'check_HASH160_A_with_B', apiCode: 0x0203, opCode: 0x35 }, { name: 'SHA256_A_to_B', apiCode: 0x0204, opCode: 0x32 }, { name: 'check_SHA256_A_with_B', apiCode: 0x0205, opCode: 0x35 }, + { name: 'Check_Sig_B_With_A', apiCode: 0x0206, opCode: 0x35 }, { name: 'get_Block_Timestamp', apiCode: 0x0300, opCode: 0x35 }, { name: 'get_Creation_Timestamp', apiCode: 0x0301, opCode: 0x35 }, { name: 'get_Last_Block_Timestamp', apiCode: 0x0302, opCode: 0x35 }, @@ -208,13 +220,24 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { { name: 'message_from_Tx_in_A_to_B', apiCode: 0x0309, opCode: 0x32 }, { name: 'B_to_Address_of_Tx_in_A', apiCode: 0x030a, opCode: 0x32 }, { name: 'B_to_Address_of_Creator', apiCode: 0x030b, opCode: 0x32 }, + { name: 'Get_Code_Hash_Id', apiCode: 0x030c, opCode: 0x35 }, + { name: 'B_To_Assets_Of_Tx_In_A', apiCode: 0x030d, opCode: 0x32 }, { name: 'get_Current_Balance', apiCode: 0x0400, opCode: 0x35 }, { name: 'get_Previous_Balance', apiCode: 0x0401, opCode: 0x35 }, { name: 'send_to_Address_in_B', apiCode: 0x0402, opCode: 0x33 }, { name: 'send_All_to_Address_in_B', apiCode: 0x0403, opCode: 0x32 }, { name: 'send_Old_to_Address_in_B', apiCode: 0x0404, opCode: 0x32 }, { name: 'send_A_to_Address_in_B', apiCode: 0x0405, opCode: 0x32 }, - { name: 'add_Minutes_to_Timestamp', apiCode: 0x0406, opCode: 0x37 } + { name: 'add_Minutes_to_Timestamp', apiCode: 0x0406, opCode: 0x37 }, + { name: 'Get_Map_Value_Keys_In_A', apiCode: 0x0407, opCode: 0x35 }, + { name: 'Set_Map_Value_Keys_In_A', apiCode: 0x0408, opCode: 0x32 }, + { name: 'Issue_Asset', apiCode: 0x0409, opCode: 0x35 }, + { name: 'Mint_Asset', apiCode: 0x040a, opCode: 0x32 }, + { name: 'Distribute_To_Asset_Holders', apiCode: 0x040b, opCode: 0x32 }, + { name: 'Get_Asset_Holders_Count', apiCode: 0x040c, opCode: 0x35 }, + { name: 'Get_Activation_Fee', apiCode: 0x040d, opCode: 0x35 }, + { name: 'Put_Last_Block_GSig_In_A', apiCode: 0x040e, opCode: 0x32 }, + { name: 'Get_Asset_Circulating', apiCode: 0x040f, opCode: 0x35 } ] const AsmObj: ASM_OBJECT = { memory: [], @@ -225,6 +248,10 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { PActivationAmount: '', PUserStackPages: 0, PCodeStackPages: 0, + PCodeHashId: '', + PCodeHashIdLine: -1, + assembledCode: '', + codeHashId: '', bytecode: '', bytedata: '' } @@ -234,12 +261,11 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { // process line by line const line = assemblyCode.split('\n') // first pass, fill address, opcodes, apicodes, constants - line.forEach((codeLine, idx) => { + AsmObj.code = line.map((codeLine, idx) => { for (const CurrRule of opCodeTable) { const parts = CurrRule.regex.exec(codeLine) if (parts !== null) { - process(parts, CurrRule) - return + return process(parts, CurrRule, idx) } } throw new Error(`assembler() error #1. No rule found to process line ${idx}: "${codeLine}".`) @@ -262,6 +288,16 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { // last pass, join all contents in little endian notation (code and data) AsmObj.code.forEach(finishHim) AsmObj.bytedata = fatality(AsmObj.memory) + // codeHashId calculation and checks + AsmObj.codeHashId = hashMachineCode(AsmObj.bytecode) + if (AsmObj.PCodeHashId === '0') { + AsmObj.PCodeHashId = AsmObj.codeHashId + line[AsmObj.PCodeHashIdLine] = `^program codeHashId ${AsmObj.codeHashId}` + } + if (AsmObj.PCodeHashId !== '' && AsmObj.PCodeHashId !== AsmObj.codeHashId) { + throw new Error(`assembler() error #8. This compilation did not produce expected machine code hash id. Maybe the source code was changed or the program need to be compiled in another version of SmartC compiler. Code hash id expected: ${AsmObj.PCodeHashId}, generated: ${AsmObj.codeHashId}.`) + } + AsmObj.assembledCode = line.join('\n') return buildRetObj() } @@ -275,30 +311,29 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { } /** Process one matched instruction */ - function process (parts: RegExpExecArray, Instruction: OPCODE_RULE) { + function process (parts: RegExpExecArray, Instruction: OPCODE_RULE, line: number): CODE_INSTRUCTION { // Create a new object const CodeObj = genCodeInstr() CodeObj.source = parts[0] switch (Instruction.opCode) { case 0xF0: // blank line - return + return CodeObj case 0xF1: // label: CodeObj.station = parts[1] - AsmObj.code.push(CodeObj) - return + return CodeObj case 0xF2: // '^comment user_comment' - return + return CodeObj case 0xF3: // '^declare varName' getMemoryAddress(parts[1]) - return + return CodeObj case 0xF4: // '^const SET @(varName) #(hex_content)' AsmObj.memory[getMemoryAddress(parts[1])].value = BigInt('0x' + parts[2]) - return + return CodeObj case 0xF5: // '^program type information' switch (parts[1]) { @@ -317,10 +352,18 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { case 'codeStackPages': AsmObj.PCodeStackPages = Number(parts[2].trim()) break + case 'codeHashId': + AsmObj.PCodeHashId = parts[2].trim() + AsmObj.PCodeHashIdLine = line + break + case 'creator': + case 'contract': + // Reserved for use in SC-Simulator. Do nothing here + break default: throw new Error(`assembler() error #7. Unknow '^program' directive: '${parts[1]}'`) } - return + return CodeObj } CodeObj.size = Instruction.size CodeObj.instructionValues.push({ type: 'O', value: BigInt(Instruction.opCode) }) @@ -367,7 +410,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { throw new Error('Internal error.') } } - AsmObj.code.push(CodeObj) + return CodeObj } /** Returns a skeleton code instruction object */ @@ -491,18 +534,20 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT { } const datapages = Math.ceil(AsmObj.memory.length / 32) const codepages = Math.ceil(AsmObj.bytecode.length / (32 * 16)) - const minimumfee = (cspages + uspages + datapages + codepages) * 7350000 + const minimumfee = (cspages + uspages + datapages + codepages) * 10000000 return { + Warnings: '', DataPages: datapages, CodeStackPages: cspages, UserStackPages: uspages, CodePages: codepages, MinimumFeeNQT: minimumfee.toString(10), ByteCode: AsmObj.bytecode, - MachineCodeHashId: hashMachineCode(AsmObj.bytecode), + MachineCodeHashId: AsmObj.codeHashId, ByteData: AsmObj.bytedata, Memory: AsmObj.memory.map(Obj => Obj.name), Labels: AsmObj.labels, + AssemblyCode: AsmObj.assembledCode, PName: AsmObj.PName, PDescription: AsmObj.PDescription, PActivationAmount: AsmObj.PActivationAmount diff --git a/src/codeGenerator/__tests__/utils.spec.ts b/src/codeGenerator/__tests__/utils.spec.ts index 777a01d..2faa904 100644 --- a/src/codeGenerator/__tests__/utils.spec.ts +++ b/src/codeGenerator/__tests__/utils.spec.ts @@ -1,57 +1,150 @@ import utils from '../utils' -describe('codeGenerator utils functions', () => { - it('should calculate: addHexContents', () => { - expect(utils.addHexContents( - '000a000', 'aaa0aaa' - )).toBe('000000000aaaaaaa') - expect(utils.addHexContents( - 139840123, 93364483 - )).toBe('000000000de66b7e') - expect(utils.addHexContents( - 'fffffffffffffff6', 20 - )).toBe('000000000000000a') - }) - it('should calculate: subHexContents', () => { - expect(utils.subHexContents( - '0000', 'a' - )).toBe('fffffffffffffff6') - expect(utils.subHexContents( - 20, 10 - )).toBe('000000000000000a') - }) - it('should calculate: mulHexContents', () => { - expect(utils.mulHexContents( - '0000', 'a' - )).toBe('0000000000000000') - expect(utils.mulHexContents( - 20, 10 - )).toBe('00000000000000c8') - expect(utils.mulHexContents( - '89528e28c0542dba', 4 - )).toBe('254a38a30150b6e8') - }) - it('should calculate: divHexContents', () => { - expect(utils.divHexContents( - 11, 2 - )).toBe('0000000000000005') - expect(utils.divHexContents( - 0, 10 - )).toBe('0000000000000000') - }) - test('should throw: divHexContents', () => { - expect(() => { - utils.divHexContents(11, undefined) - }).toThrowError('Division by zero') +describe('codeGenerator utils functions (long)', () => { + it('should calculate: addConstants long, long', () => { + expect(utils.addConstants( + { value: '000a000', declaration: 'long' }, + { value: 'aaa0aaa', declaration: 'long' } + )).toStrictEqual({ value: 0xaaaaaaan, declaration: 'long' }) + expect(utils.addConstants( + { value: 139840123, declaration: 'long' }, + { value: 93364483, declaration: 'long' } + )).toStrictEqual({ value: 0xde66b7en, declaration: 'long' }) + expect(utils.addConstants( + { value: 'fffffffffffffff6', declaration: 'long' }, + { value: 20, declaration: 'long' } + )).toStrictEqual({ value: 0xan, declaration: 'long' }) + }) + it('should calculate: subConstants long, long', () => { + expect(utils.subConstants( + { value: '0000', declaration: 'long' }, + { value: 'a', declaration: 'long' } + )).toStrictEqual({ value: 0xfffffffffffffff6n, declaration: 'long' }) + expect(utils.subConstants( + { value: 20, declaration: 'long' }, + { value: 10, declaration: 'long' } + )).toStrictEqual({ value: 0xan, declaration: 'long' }) + }) + it('should calculate: mulHexContents long, long', () => { + expect(utils.mulConstants( + { value: '0000', declaration: 'long' }, + { value: 'a', declaration: 'long' } + )).toStrictEqual({ value: 0n, declaration: 'long' }) + expect(utils.mulConstants( + { value: 20, declaration: 'long' }, + { value: 10, declaration: 'long' } + )).toStrictEqual({ value: 0xc8n, declaration: 'long' }) + expect(utils.mulConstants( + { value: '89528e28c0542dba', declaration: 'long' }, + { value: 4, declaration: 'long' } + )).toStrictEqual({ value: 0x254a38a30150b6e8n, declaration: 'long' }) }) - test('should throw: divHexContents', () => { + it('should calculate: divHexContents long, long', () => { + expect(utils.divConstants( + { value: 11, declaration: 'long' }, + { value: 2, declaration: 'long' } + )).toStrictEqual({ value: 0x5n, declaration: 'long' }) + expect(utils.divConstants( + { value: 0, declaration: 'long' }, + { value: 10, declaration: 'long' } + )).toStrictEqual({ value: 0n, declaration: 'long' }) + }) + test('should throw: divHexContents long, long', () => { expect(() => { - utils.divHexContents(11, undefined) + utils.divConstants( + { value: 11, declaration: 'long' }, + { value: '', declaration: 'long' } + ) }).toThrowError('Division by zero') }) - test('should throw: Fractional number in createConstantMemObj', () => { + it('should create: createConstantMemObj Number', () => { + const MemObj = utils.createConstantMemObj(32) + expect(MemObj.hexContent).toBe('0000000000000020') + expect(MemObj.declaration).toBe('long') + expect(MemObj.size).toBe(1) + }) + it('should create: createConstantMemObj Bigint', () => { + const MemObj = utils.createConstantMemObj(320n) + expect(MemObj.hexContent).toBe('0000000000000140') + expect(MemObj.declaration).toBe('long') + }) + it('should create: createConstantMemObj Bigint overflow', () => { + const MemObj = utils.createConstantMemObj(18446744073709551616n) + expect(MemObj.hexContent).toBe('00000000000000010000000000000000') + expect(MemObj.declaration).toBe('long') + expect(MemObj.size).toBe(2) + }) + test('should throw: createConstantMemObj Fixed', () => { expect(() => { utils.createConstantMemObj(1.5) - }).toThrowError('Only integer numbers in createConstantMemObj().') + }).toThrowError('Internal error') + }) + it('should create: createConstantMemObj String', () => { + const MemObj = utils.createConstantMemObj('feff') + expect(MemObj.hexContent).toBe('000000000000feff') + expect(MemObj.declaration).toBe('long') + }) +}) + +describe('codeGenerator utils functions (mixed long/fixed)', () => { + it('should calculate: addConstants (mixed)', () => { + expect(utils.addConstants( + { value: '000a000', declaration: 'long' }, + { value: 'aaa0aaa', declaration: 'fixed' } + )).toStrictEqual({ value: 0x3B9B74A0AAAn, declaration: 'fixed' }) + expect(utils.addConstants( + { value: 139840123, declaration: 'fixed' }, + { value: 93364483, declaration: 'fixed' } + )).toStrictEqual({ value: 0xde66b7en, declaration: 'fixed' }) + expect(utils.addConstants( + { value: 0x3b9aca01, declaration: 'fixed' }, + { value: 'fffffffffffffff6', declaration: 'long' } + )).toStrictEqual({ value: 0x1n, declaration: 'fixed' }) + }) + it('should calculate: subConstants (mixed)', () => { + expect(utils.subConstants( + { value: '0000', declaration: 'long' }, + { value: 'a', declaration: 'fixed' } + )).toStrictEqual({ value: 0xfffffffffffffff6n, declaration: 'fixed' }) + expect(utils.subConstants( + { value: 200000002, declaration: 'fixed' }, + { value: 1, declaration: 'long' } + )).toStrictEqual({ value: 100000002n, declaration: 'fixed' }) + expect(utils.subConstants( + { value: 300000003, declaration: 'fixed' }, + { value: 200000002, declaration: 'fixed' } + )).toStrictEqual({ value: 100000001n, declaration: 'fixed' }) + }) + it('should calculate: mulHexContents (mixed)', () => { + expect(utils.mulConstants( + { value: '0000', declaration: 'fixed' }, + { value: 'a', declaration: 'long' } + )).toStrictEqual({ value: 0n, declaration: 'fixed' }) + expect(utils.mulConstants( + { value: 20, declaration: 'long' }, + { value: 10, declaration: 'fixed' } + )).toStrictEqual({ value: 0xc8n, declaration: 'fixed' }) + expect(utils.mulConstants( + { value: 300000003, declaration: 'fixed' }, + { value: 200000002, declaration: 'fixed' } + )).toStrictEqual({ value: 600000012n, declaration: 'fixed' }) + }) + it('should calculate: divHexContents (mixed)', () => { + expect(utils.divConstants( + { value: 11, declaration: 'fixed' }, + { value: 2, declaration: 'long' } + )).toStrictEqual({ value: 0x5n, declaration: 'fixed' }) + expect(utils.divConstants( + { value: 0, declaration: 'long' }, + { value: 10, declaration: 'fixed' } + )).toStrictEqual({ value: 0n, declaration: 'fixed' }) + expect(utils.divConstants( + { value: 1, declaration: 'long' }, + { value: 10000000, declaration: 'fixed' } + )).toStrictEqual({ value: 1000000000n, declaration: 'fixed' }) + expect(utils.divConstants( + { value: 200000002, declaration: 'fixed' }, + { value: 300030000, declaration: 'fixed' } + )).toStrictEqual({ value: 66660001n, declaration: 'fixed' }) }) }) diff --git a/src/codeGenerator/assemblyProcessor/assignmentToAsm.ts b/src/codeGenerator/assemblyProcessor/assignmentToAsm.ts index c2e4579..7772e04 100644 --- a/src/codeGenerator/assemblyProcessor/assignmentToAsm.ts +++ b/src/codeGenerator/assemblyProcessor/assignmentToAsm.ts @@ -16,6 +16,7 @@ export default function assignmentToAsm ( switch (Left.type) { case 'register': case 'long': + case 'fixed': case 'structRef': return leftRegularToAsm() case 'array': @@ -37,6 +38,7 @@ export default function assignmentToAsm ( return leftRegularOffsetUndefinedAndRightConstantToAsm() case 'register': case 'long': + case 'fixed': case 'structRef': return leftRegularOffsetUndefinedAndRightRegularToAsm() case 'array': @@ -58,6 +60,20 @@ export default function assignmentToAsm ( /** Left type is 'register', 'long' or ''structRef', with offset undefined. Right type is 'constant'. * Create assembly instruction. */ function leftRegularOffsetUndefinedAndRightConstantToAsm () : string { + let newVarName: string + switch (Right.Offset?.type) { + case undefined: + return leftRegularOffsetUndefinedAndRightConstantOffsetUndefinedToAsm() + case 'constant': + Right.hexContent = assertNotUndefined(Right.hexContent) + newVarName = auxVars.getMemoryObjectByLocation(utils.addHexSimple(Right.Offset.value, Right.hexContent), operationLine).asmName + return `SET @${Left.asmName} $${newVarName}\n` + case 'variable': + throw new Error('Not implemented.') + } + } + + function leftRegularOffsetUndefinedAndRightConstantOffsetUndefinedToAsm () : string { Right.hexContent = assertNotUndefined(Right.hexContent) if (Right.hexContent.length > 17) { throw new Error(`At line: ${operationLine}.` + @@ -66,9 +82,12 @@ export default function assignmentToAsm ( if (Right.hexContent === '0000000000000000') { return `CLR @${Left.asmName}\n` } - const findOpt = auxVars.memory.find(MEM => { - return MEM.asmName === `n${Number('0x' + Right.hexContent)}` && MEM.hexContent === Right.hexContent - }) + let optVarName = 'n' + if (Right.declaration === 'fixed') { + optVarName = 'f' + } + optVarName += Number('0x' + Right.hexContent).toString(10) + const findOpt = auxVars.memory.find(MEM => MEM.asmName === optVarName && MEM.hexContent === Right.hexContent) if (findOpt) { return `SET @${Left.asmName} $${findOpt.asmName}\n` } @@ -93,15 +112,13 @@ export default function assignmentToAsm ( /** Left type is 'register', 'long' or 'structRef', with offset undefined. Right type is 'register', 'long', or * 'structRef' with offset undefined. Create assembly instruction. */ function leftRegularOffsetUndefinedAndRightRegularOffsetUndefinedToAsm (): string { - if ((Left.declaration === Right.declaration) || - (Left.declaration === 'void_ptr' && Right.declaration.includes('ptr')) || - (Left.declaration.includes('ptr') && Right.declaration === 'void_ptr')) { - if (Left.address === Right.address) { - return '' - } - return `SET @${Left.asmName} $${Right.asmName}\n` + if (Left.declaration !== Right.declaration) { + throw new Error(`Internal error at line: ${operationLine}.`) + } + if (Left.address === Right.address) { + return '' } - throw new Error(`Internal error at line: ${operationLine}.`) + return `SET @${Left.asmName} $${Right.asmName}\n` } /** Left type is 'register', 'long' or 'structRef', with offset undefined. Right type is 'register', 'long' or @@ -125,7 +142,7 @@ export default function assignmentToAsm ( return `SET @${Left.asmName} $${Right.asmName}\n` } if (Right.Offset.type === 'constant') { - const memLoc = utils.addHexContents(Right.hexContent, Right.Offset.value) + const memLoc = utils.addHexSimple(Right.hexContent, Right.Offset.value) const RightMem = auxVars.getMemoryObjectByLocation(memLoc, operationLine) return `SET @${Left.asmName} $${RightMem.asmName}\n` } @@ -185,7 +202,7 @@ export default function assignmentToAsm ( const paddedLong = assertNotUndefined(Right.hexContent).padStart(arraySize * 16, '0') let assemblyCode = '' for (let i = 0; i < arraySize; i++) { - const newLeft = auxVars.getMemoryObjectByLocation(utils.addHexContents(Left.hexContent, i), operationLine) + const newLeft = auxVars.getMemoryObjectByLocation(utils.addHexSimple(Left.hexContent, i), operationLine) const newRight = utils.createConstantMemObj( paddedLong.slice(16 * (arraySize - i - 1), 16 * (arraySize - i)) ) diff --git a/src/codeGenerator/assemblyProcessor/createInstruction.ts b/src/codeGenerator/assemblyProcessor/createInstruction.ts index 07e209a..75162c6 100644 --- a/src/codeGenerator/assemblyProcessor/createInstruction.ts +++ b/src/codeGenerator/assemblyProcessor/createInstruction.ts @@ -1,6 +1,6 @@ import { assertExpression, assertNotUndefined } from '../../repository/repository' -import { MEMORY_SLOT, TOKEN } from '../../typings/syntaxTypes' -import { FLATTEN_MEMORY_RETURN_OBJECT, GENCODE_AUXVARS } from '../codeGeneratorTypes' +import { MEMORY_SLOT, OFFSET_MODIFIER_CONSTANT, TOKEN } from '../../typings/syntaxTypes' +import { FLATTEN_MEMORY_RETURN_OBJECT, GENCODE_AUXVARS, GENCODE_SOLVED_OBJECT } from '../codeGeneratorTypes' import utils from '../utils' import assignmentToAsm from './assignmentToAsm' @@ -59,11 +59,38 @@ export function createSimpleInstruction (instruction: string, param1: string = ' return `${param1}:\n` case 'Function': return `JSR :__fn_${param1}\n` + case 'LongToFixed': + return `MUL @${param1} $f100000000\n` + case 'FixedToLong': + return `DIV @${param1} $f100000000\n` default: throw new Error(`Unknow simple instruction: ${instruction}`) } } +export function toRegister ( + AuxVars: GENCODE_AUXVARS, InSolved: GENCODE_SOLVED_OBJECT, line: number +) : GENCODE_SOLVED_OBJECT { + const retObj = flattenMemory(AuxVars, InSolved.SolvedMem, line) + + if (!AuxVars.isTemp(retObj.FlatMem.address)) { + const inType = utils.getDeclarationFromMemory(InSolved.SolvedMem) + const TmpMemObj = AuxVars.getNewRegister(line) + retObj.asmCode += `SET @${TmpMemObj.asmName} $${retObj.FlatMem.asmName}\n` + utils.setMemoryDeclaration(TmpMemObj, inType) + AuxVars.freeRegister(retObj.FlatMem.address) + AuxVars.freeRegister(InSolved.SolvedMem.address) + retObj.FlatMem = TmpMemObj + } + if (retObj.isNew === true) { + AuxVars.freeRegister(InSolved.SolvedMem.address) + } + return { + SolvedMem: retObj.FlatMem, + asmCode: InSolved.asmCode + retObj.asmCode + } +} + /** Create assembly code for one api function call */ export function createAPICallInstruction ( AstAuxVars: GENCODE_AUXVARS, ApiToken: TOKEN, RetMem: MEMORY_SLOT, argsMem: MEMORY_SLOT[] @@ -87,6 +114,483 @@ export function createAPICallInstruction ( return assemblyCode + '\n' } +/** Create assembly code for built-in functions */ +export function createBuiltInInstruction ( + AstAuxVars: GENCODE_AUXVARS, BuiltInToken: TOKEN, RetMem: MEMORY_SLOT, argsMem: MEMORY_SLOT[] +) : string { + let memcopyType : 'SIMPLE' | 'MIXED_LEFT' | 'MIXED_RIGHT' | 'COMPLEX' + let AuxRegister: MEMORY_SLOT + let AuxRegisterA: MEMORY_SLOT + let AuxRegisterB: MEMORY_SLOT + let newJump: string + let auxFlatMem: FLATTEN_MEMORY_RETURN_OBJECT + let assemblyCode = '' + + switch (BuiltInToken.value) { + case 'checkSignature': + case 'distributeToHolders': + case 'distributeToHoldersFx': + case 'sendQuantityAndAmount': + case 'sendQuantityAndAmountFx': + return createBuiltIn4ArgsPlusInstruction(AstAuxVars, BuiltInToken, RetMem, argsMem) + } + + const tempArgsMem = argsMem.map((VarObj) => flattenMemory(AstAuxVars, VarObj, BuiltInToken.line)) + switch (BuiltInToken.value) { + case 'pow': + case 'powf': + assemblyCode = tempArgsMem[0].asmCode + + `SET @${RetMem.asmName} $${tempArgsMem[0].FlatMem.asmName}\n` + + tempArgsMem[1].asmCode + + `POW @${RetMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + break + case 'mdv': + assemblyCode = tempArgsMem[0].asmCode + + `SET @${RetMem.asmName} $${tempArgsMem[0].FlatMem.asmName}\n` + + tempArgsMem[1].asmCode + + tempArgsMem[2].asmCode + + `MDV @${RetMem.asmName} $${tempArgsMem[1].FlatMem.asmName} $${tempArgsMem[2].FlatMem.asmName}\n` + break + case 'memcopy': + memcopyType = 'COMPLEX' + if (argsMem[0].type === 'constant' || argsMem[1].type === 'constant') { + if (argsMem[0].type === 'constant' && argsMem[1].type === 'constant') { + memcopyType = 'SIMPLE' + } else if (argsMem[0].type === 'constant') { + memcopyType = 'MIXED_LEFT' + } else { + memcopyType = 'MIXED_RIGHT' + } + } + switch (memcopyType) { + case 'SIMPLE': + argsMem[0].hexContent = assertNotUndefined(argsMem[0].hexContent) + argsMem[1].hexContent = assertNotUndefined(argsMem[1].hexContent) + assemblyCode = `SET @${AstAuxVars.getMemoryObjectByLocation(argsMem[0].hexContent).asmName} $${AstAuxVars.getMemoryObjectByLocation(argsMem[1].hexContent).asmName}\n` + break + case 'MIXED_LEFT': + argsMem[0].hexContent = assertNotUndefined(argsMem[0].hexContent) + assemblyCode = tempArgsMem[1].asmCode + + `SET @${AstAuxVars.getMemoryObjectByLocation(argsMem[0].hexContent).asmName} $($${tempArgsMem[1].FlatMem.asmName})\n` + break + case 'MIXED_RIGHT': + argsMem[1].hexContent = assertNotUndefined(argsMem[1].hexContent) + assemblyCode = tempArgsMem[0].asmCode + + `SET @($${tempArgsMem[0].FlatMem.asmName}) $${AstAuxVars.getMemoryObjectByLocation(argsMem[1].hexContent).asmName}\n` + break + default: + // 'COMPLEX' + AuxRegister = AstAuxVars.getNewRegister() + assemblyCode = tempArgsMem[1].asmCode + + tempArgsMem[0].asmCode + + `SET @${AuxRegister.asmName} $($${tempArgsMem[1].FlatMem.asmName})\n` + + `SET @($${tempArgsMem[0].FlatMem.asmName}) $${AuxRegister.asmName}\n` + AstAuxVars.freeRegister(AuxRegister.address) + break + } + break + case 'getNextTx': + newJump = '__GNT_' + AstAuxVars.getNewJumpID(BuiltInToken.line) + assemblyCode = 'FUN A_to_Tx_after_Timestamp $_counterTimestamp\n' + + `FUN @${RetMem.asmName} get_A1\n` + + `BZR $${RetMem.asmName} :${newJump}\n` + + 'FUN @_counterTimestamp get_Timestamp_for_Tx_in_A\n' + + `${newJump}:\n` + break + case 'getNextTxFromBlockheight': + newJump = '__GNT_' + AstAuxVars.getNewJumpID(BuiltInToken.line) + assemblyCode = tempArgsMem[0].asmCode + if (AstAuxVars.isTemp(tempArgsMem[0].FlatMem.address)) { + AuxRegister = tempArgsMem[0].FlatMem + } else { + AuxRegister = AstAuxVars.getNewRegister() + assemblyCode += `SET @${AuxRegister.asmName} $${tempArgsMem[0].FlatMem.asmName}\n` + } + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(32), BuiltInToken.line) + assemblyCode += auxFlatMem.asmCode + + `SHL @${AuxRegister.asmName} $${auxFlatMem.FlatMem.asmName}\n` + + `FUN A_to_Tx_after_Timestamp $${AuxRegister.asmName}\n` + + `FUN @${RetMem.asmName} get_A1\n` + + `BZR $${RetMem.asmName} :${newJump}\n` + + 'FUN @_counterTimestamp get_Timestamp_for_Tx_in_A\n' + + `${newJump}:\n` + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + AstAuxVars.freeRegister(AuxRegister.address) + break + case 'getBlockheight': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_A1 $${tempArgsMem[0].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} get_Timestamp_for_Tx_in_A\n` + AstAuxVars.freeRegister(tempArgsMem[0].FlatMem.address) + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(32), BuiltInToken.line) + assemblyCode += auxFlatMem.asmCode + + `SHR @${RetMem.asmName} $${auxFlatMem.FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + break + case 'getCurrentBlockheight': + assemblyCode = `FUN @${RetMem.asmName} get_Block_Timestamp\n` + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(32), BuiltInToken.line) + assemblyCode += auxFlatMem.asmCode + + `SHR @${RetMem.asmName} $${auxFlatMem.FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + break + case 'getAmount': + case 'getAmountFx': + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(0n), BuiltInToken.line) + assemblyCode = tempArgsMem[0].asmCode + auxFlatMem.asmCode + + `FUN set_A1_A2 $${tempArgsMem[0].FlatMem.asmName} $${auxFlatMem.FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} get_Amount_for_Tx_in_A\n` + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + break + case 'getSender': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_A1 $${tempArgsMem[0].FlatMem.asmName}\n` + + 'FUN B_to_Address_of_Tx_in_A\n' + + `FUN @${RetMem.asmName} get_B1\n` + break + case 'getType': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_A1 $${tempArgsMem[0].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} get_Type_for_Tx_in_A\n` + break + case 'getCreator': + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(0n), BuiltInToken.line) + assemblyCode = auxFlatMem.asmCode + + `FUN set_B2 $${auxFlatMem.FlatMem.asmName}\n` + + 'FUN B_to_Address_of_Creator\n' + + `FUN @${RetMem.asmName} get_B1\n` + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + break + case 'getCreatorOf': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_B2 $${tempArgsMem[0].FlatMem.asmName}\n` + + 'FUN B_to_Address_of_Creator\n' + + `FUN @${RetMem.asmName} get_B1\n` + break + case 'getCodeHashOf': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_B2 $${tempArgsMem[0].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} Get_Code_Hash_Id\n` + break + case 'getWeakRandomNumber': + assemblyCode = 'FUN Put_Last_Block_GSig_In_A\n' + + `FUN @${RetMem.asmName} get_A2\n` + break + case 'getActivationOf': + case 'getActivationOfFx': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_B2 $${tempArgsMem[0].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} Get_Activation_Fee\n` + break + case 'getCurrentBalance': + case 'getCurrentBalanceFx': + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(0n), BuiltInToken.line) + assemblyCode = auxFlatMem.asmCode + + `FUN set_B2 $${auxFlatMem.FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} get_Current_Balance\n` + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + break + case 'readMessage': + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + + `FUN set_A1_A2 $${tempArgsMem[0].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + 'FUN message_from_Tx_in_A_to_B\n' + AstAuxVars.freeRegister(tempArgsMem[0].FlatMem.address) + AstAuxVars.freeRegister(tempArgsMem[1].FlatMem.address) + if (argsMem[2].type === 'constant' || (argsMem[2].type === 'array' && argsMem[2].Offset === undefined)) { + argsMem[2].hexContent = assertNotUndefined(argsMem[2].hexContent) + const m1 = AstAuxVars.getMemoryObjectByLocation(argsMem[2].hexContent).asmName + const m2 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(argsMem[2].hexContent, 1)).asmName + const m3 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(argsMem[2].hexContent, 2)).asmName + const m4 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(argsMem[2].hexContent, 3)).asmName + assemblyCode += + `FUN @${m1} get_B1\n` + + `FUN @${m2} get_B2\n` + + `FUN @${m3} get_B3\n` + + `FUN @${m4} get_B4\n` + break + } + assemblyCode += tempArgsMem[2].asmCode + if (AstAuxVars.isTemp(tempArgsMem[2].FlatMem.address)) { + AuxRegister = tempArgsMem[2].FlatMem + } else { + AuxRegister = AstAuxVars.getNewRegister() + assemblyCode += `SET @${AuxRegister.asmName} $${tempArgsMem[2].FlatMem.asmName}\n` + } + AuxRegisterA = AstAuxVars.getNewRegister() + assemblyCode += + `FUN @${AuxRegisterA.asmName} get_B1\n` + + `SET @($${AuxRegister.asmName}) $${AuxRegisterA.asmName}\n` + + `FUN @${AuxRegisterA.asmName} get_B2\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @($${AuxRegister.asmName}) $${AuxRegisterA.asmName}\n` + + `FUN @${AuxRegisterA.asmName} get_B3\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @($${AuxRegister.asmName}) $${AuxRegisterA.asmName}\n` + + `FUN @${AuxRegisterA.asmName} get_B4\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @($${AuxRegister.asmName}) $${AuxRegisterA.asmName}\n` + AstAuxVars.freeRegister(AuxRegister.address) + AstAuxVars.freeRegister(AuxRegisterA.address) + break + case 'sendMessage': + case 'sendAmountAndMessage': + case 'sendAmountAndMessageFx': { + let amountArg = 0 + let messageArg = 1 + let recipientArg = 2 + if (BuiltInToken.value === 'sendMessage') { + amountArg = -1 + messageArg = 0 + recipientArg = 1 + } + if (amountArg !== -1) { + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(0n), BuiltInToken.line) + assemblyCode = tempArgsMem[amountArg].asmCode + tempArgsMem[recipientArg].asmCode + auxFlatMem.asmCode + + `FUN set_B1_B2 $${tempArgsMem[recipientArg].FlatMem.asmName} $${auxFlatMem.FlatMem.asmName}\n` + + `FUN send_to_Address_in_B $${tempArgsMem[amountArg].FlatMem.asmName}\n` + AstAuxVars.freeRegister(tempArgsMem[amountArg].FlatMem.address) + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + } else { + assemblyCode = tempArgsMem[recipientArg].asmCode + + `FUN set_B1 $${tempArgsMem[recipientArg].FlatMem.asmName}\n` + } + AstAuxVars.freeRegister(tempArgsMem[recipientArg].FlatMem.address) + if (argsMem[messageArg].type === 'constant' || (argsMem[messageArg].type === 'array' && argsMem[messageArg].Offset === undefined)) { + const theHexContent = assertNotUndefined(argsMem[messageArg].hexContent) + const m1 = AstAuxVars.getMemoryObjectByLocation(theHexContent).asmName + const m2 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(theHexContent, 1)).asmName + const m3 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(theHexContent, 2)).asmName + const m4 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(theHexContent, 3)).asmName + assemblyCode += + `FUN set_A1_A2 $${m1} $${m2}\n` + + `FUN set_A3_A4 $${m3} $${m4}\n` + + 'FUN send_A_to_Address_in_B\n' + break + } + assemblyCode += tempArgsMem[messageArg].asmCode + if (AstAuxVars.isTemp(tempArgsMem[messageArg].FlatMem.address)) { + AuxRegister = tempArgsMem[messageArg].FlatMem + } else { + AuxRegister = AstAuxVars.getNewRegister() + assemblyCode += `SET @${AuxRegister.asmName} $${tempArgsMem[messageArg].FlatMem.asmName}\n` + } + AuxRegisterA = AstAuxVars.getNewRegister() + AuxRegisterB = AstAuxVars.getNewRegister() + assemblyCode += + `SET @${AuxRegisterA.asmName} $($${AuxRegister.asmName})\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @${AuxRegisterB.asmName} $($${AuxRegister.asmName})\n` + + `FUN set_A1_A2 $${AuxRegisterA.asmName} $${AuxRegisterB.asmName}\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @${AuxRegisterA.asmName} $($${AuxRegister.asmName})\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @${AuxRegisterB.asmName} $($${AuxRegister.asmName})\n` + + `FUN set_A3_A4 $${AuxRegisterA.asmName} $${AuxRegisterB.asmName}\n` + + 'FUN send_A_to_Address_in_B\n' + AstAuxVars.freeRegister(AuxRegister.address) + AstAuxVars.freeRegister(AuxRegisterA.address) + AstAuxVars.freeRegister(AuxRegisterB.address) + break + } + case 'sendAmount': + case 'sendAmountFx': + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(0n), BuiltInToken.line) + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + auxFlatMem.asmCode + + `FUN set_B1_B2 $${tempArgsMem[1].FlatMem.asmName} $${auxFlatMem.FlatMem.asmName}\n` + + `FUN send_to_Address_in_B $${tempArgsMem[0].FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + break + case 'sendBalance': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_B1 $${tempArgsMem[0].FlatMem.asmName}\n` + + 'FUN send_All_to_Address_in_B\n' + break + case 'getMapValue': + case 'getMapValueFx': + auxFlatMem = flattenMemory(AstAuxVars, utils.createConstantMemObj(0n), BuiltInToken.line) + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + auxFlatMem.asmCode + + `FUN set_A1_A2 $${tempArgsMem[0].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + `FUN set_A3 $${auxFlatMem.FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} Get_Map_Value_Keys_In_A\n` + AstAuxVars.freeRegister(auxFlatMem.FlatMem.address) + break + case 'getExtMapValue': + case 'getExtMapValueFx': + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + tempArgsMem[2].asmCode + + `FUN set_A1_A2 $${tempArgsMem[0].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + `FUN set_A3 $${tempArgsMem[2].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} Get_Map_Value_Keys_In_A\n` + break + case 'setMapValue': + case 'setMapValueFx': + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + tempArgsMem[2].asmCode + + `FUN set_A1_A2 $${tempArgsMem[0].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + `FUN set_A4 $${tempArgsMem[2].FlatMem.asmName}\n` + + 'FUN Set_Map_Value_Keys_In_A\n' + break + case 'issueAsset': + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + tempArgsMem[2].asmCode + + `FUN set_A1_A2 $${tempArgsMem[0].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + `FUN set_B1 $${tempArgsMem[2].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} Issue_Asset\n` + break + case 'mintAsset': + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + + `FUN set_B1_B2 $${tempArgsMem[0].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + 'FUN Mint_Asset\n' + break + case 'sendQuantity': + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + tempArgsMem[2].asmCode + + `FUN set_B1_B2 $${tempArgsMem[2].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + `FUN send_to_Address_in_B $${tempArgsMem[0].FlatMem.asmName}\n` + break + case 'getAssetBalance': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_B2 $${tempArgsMem[0].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} get_Current_Balance\n` + break + case 'getAssetHoldersCount': + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + + `FUN set_B1_B2 $${tempArgsMem[0].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} Get_Asset_Holders_Count\n` + break + case 'readAssets': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_A1 $${tempArgsMem[0].FlatMem.asmName}\n` + + 'FUN B_To_Assets_Of_Tx_In_A\n' + if (argsMem[1].type === 'constant' || (argsMem[1].type === 'array' && argsMem[1].Offset === undefined)) { + argsMem[1].hexContent = assertNotUndefined(argsMem[1].hexContent) + const m1 = AstAuxVars.getMemoryObjectByLocation(argsMem[1].hexContent).asmName + const m2 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(argsMem[1].hexContent, 1)).asmName + const m3 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(argsMem[1].hexContent, 2)).asmName + const m4 = AstAuxVars.getMemoryObjectByLocation(utils.addHexSimple(argsMem[1].hexContent, 3)).asmName + assemblyCode += + `FUN @${m1} get_B1\n` + + `FUN @${m2} get_B2\n` + + `FUN @${m3} get_B3\n` + + `FUN @${m4} get_B4\n` + break + } + AstAuxVars.freeRegister(tempArgsMem[0].FlatMem.address) + assemblyCode += tempArgsMem[1].asmCode + if (AstAuxVars.isTemp(tempArgsMem[1].FlatMem.address)) { + AuxRegister = tempArgsMem[1].FlatMem + } else { + AuxRegister = AstAuxVars.getNewRegister() + assemblyCode += `SET @${AuxRegister.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + } + AuxRegisterA = AstAuxVars.getNewRegister() + assemblyCode += + `FUN @${AuxRegisterA.asmName} get_B1\n` + + `SET @($${AuxRegister.asmName}) $${AuxRegisterA.asmName}\n` + + `FUN @${AuxRegisterA.asmName} get_B2\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @($${AuxRegister.asmName}) $${AuxRegisterA.asmName}\n` + + `FUN @${AuxRegisterA.asmName} get_B3\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @($${AuxRegister.asmName}) $${AuxRegisterA.asmName}\n` + + `FUN @${AuxRegisterA.asmName} get_B4\n` + + `INC @${AuxRegister.asmName}\n` + + `SET @($${AuxRegister.asmName}) $${AuxRegisterA.asmName}\n` + AstAuxVars.freeRegister(AuxRegister.address) + AstAuxVars.freeRegister(AuxRegisterA.address) + break + case 'getQuantity': + assemblyCode = tempArgsMem[0].asmCode + tempArgsMem[1].asmCode + + `FUN set_A1_A2 $${tempArgsMem[0].FlatMem.asmName} $${tempArgsMem[1].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} get_Amount_for_Tx_in_A\n` + break + case 'getAssetCirculating': + assemblyCode = tempArgsMem[0].asmCode + + `FUN set_A2 $${tempArgsMem[0].FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} Get_Asset_Circulating\n` + break + default: + throw new Error(`Internal error at line: ${BuiltInToken.line}. Built-in function not implemented.`) + } + tempArgsMem.forEach(tmpArg => { + if (tmpArg.isNew) { + AstAuxVars.freeRegister(tmpArg.FlatMem.address) + } + }) + return assemblyCode +} + +/** Create assembly code for built-in functions */ +export function createBuiltIn4ArgsPlusInstruction ( + AstAuxVars: GENCODE_AUXVARS, BuiltInToken: TOKEN, RetMem: MEMORY_SLOT, argsMem: MEMORY_SLOT[] +) : string { + let auxFlatMemA: FLATTEN_MEMORY_RETURN_OBJECT + let auxFlatMemB: FLATTEN_MEMORY_RETURN_OBJECT + let assemblyCode = '' + + switch (BuiltInToken.value) { + case 'checkSignature': + auxFlatMemA = flattenMemory(AstAuxVars, argsMem[3], BuiltInToken.line) + auxFlatMemB = flattenMemory(AstAuxVars, argsMem[4], BuiltInToken.line) + assemblyCode += auxFlatMemA.asmCode + auxFlatMemB.asmCode + + `FUN set_A1_A2 $${auxFlatMemA.FlatMem.asmName} $${auxFlatMemB.FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMemA.FlatMem.address) + AstAuxVars.freeRegister(auxFlatMemB.FlatMem.address) + auxFlatMemA = flattenMemory(AstAuxVars, argsMem[5], BuiltInToken.line) + auxFlatMemB = flattenMemory(AstAuxVars, argsMem[0], BuiltInToken.line) + assemblyCode += auxFlatMemA.asmCode + auxFlatMemB.asmCode + + `FUN set_A3 $${auxFlatMemA.FlatMem.asmName}\n` + + `FUN set_B2 $${auxFlatMemB.FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMemA.FlatMem.address) + AstAuxVars.freeRegister(auxFlatMemB.FlatMem.address) + + auxFlatMemA = flattenMemory(AstAuxVars, argsMem[1], BuiltInToken.line) + auxFlatMemB = flattenMemory(AstAuxVars, argsMem[2], BuiltInToken.line) + assemblyCode += auxFlatMemA.asmCode + auxFlatMemB.asmCode + + `FUN set_B3_B4 $${auxFlatMemA.FlatMem.asmName} $${auxFlatMemB.FlatMem.asmName}\n` + + `FUN @${RetMem.asmName} Check_Sig_B_With_A\n` + AstAuxVars.freeRegister(auxFlatMemA.FlatMem.address) + AstAuxVars.freeRegister(auxFlatMemB.FlatMem.address) + return assemblyCode + case 'distributeToHolders': + case 'distributeToHoldersFx': + auxFlatMemA = flattenMemory(AstAuxVars, argsMem[0], BuiltInToken.line) + auxFlatMemB = flattenMemory(AstAuxVars, argsMem[1], BuiltInToken.line) + assemblyCode += auxFlatMemA.asmCode + auxFlatMemB.asmCode + + `FUN set_B1_B2 $${auxFlatMemA.FlatMem.asmName} $${auxFlatMemB.FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMemA.FlatMem.address) + AstAuxVars.freeRegister(auxFlatMemB.FlatMem.address) + + auxFlatMemA = flattenMemory(AstAuxVars, argsMem[2], BuiltInToken.line) + assemblyCode += auxFlatMemA.asmCode + + `FUN set_A1 $${auxFlatMemA.FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMemA.FlatMem.address) + + auxFlatMemA = flattenMemory(AstAuxVars, argsMem[3], BuiltInToken.line) + auxFlatMemB = flattenMemory(AstAuxVars, argsMem[4], BuiltInToken.line) + assemblyCode += auxFlatMemA.asmCode + auxFlatMemB.asmCode + + `FUN set_A3_A4 $${auxFlatMemA.FlatMem.asmName} $${auxFlatMemB.FlatMem.asmName}\n` + + 'FUN Distribute_To_Asset_Holders\n' + AstAuxVars.freeRegister(auxFlatMemA.FlatMem.address) + AstAuxVars.freeRegister(auxFlatMemB.FlatMem.address) + return assemblyCode + case 'sendQuantityAndAmount': + case 'sendQuantityAndAmountFx': + auxFlatMemA = flattenMemory(AstAuxVars, argsMem[3], BuiltInToken.line) + auxFlatMemB = flattenMemory(AstAuxVars, argsMem[1], BuiltInToken.line) + assemblyCode += auxFlatMemA.asmCode + auxFlatMemB.asmCode + + `FUN set_B1_B2 $${auxFlatMemA.FlatMem.asmName} $${auxFlatMemB.FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMemA.FlatMem.address) + AstAuxVars.freeRegister(auxFlatMemB.FlatMem.address) + auxFlatMemA = flattenMemory(AstAuxVars, argsMem[2], BuiltInToken.line) + auxFlatMemB = flattenMemory(AstAuxVars, argsMem[0], BuiltInToken.line) + assemblyCode += auxFlatMemA.asmCode + auxFlatMemB.asmCode + + `FUN set_B3 $${auxFlatMemA.FlatMem.asmName}\n` + + `FUN send_to_Address_in_B $${auxFlatMemB.FlatMem.asmName}\n` + AstAuxVars.freeRegister(auxFlatMemA.FlatMem.address) + AstAuxVars.freeRegister(auxFlatMemB.FlatMem.address) + return assemblyCode + default: + throw new Error('Internal error') + } +} + /** * From ParamMemObj create an memory object suitable for assembly operations (a regular long variable). * Do do rely in createInstruction, all hardwork done internally. Returns also instructions maybe needed for @@ -100,7 +604,15 @@ export function flattenMemory ( function flattenMemoryMain (): FLATTEN_MEMORY_RETURN_OBJECT { let retInstructions = '' if (StuffedMemory.type === 'constant') { - return flattenConstant(StuffedMemory.hexContent) + switch (StuffedMemory.Offset?.type) { + case undefined: + return flattenConstant(StuffedMemory.hexContent) + case 'constant': + return flattenConstantWithOffsetConstant(StuffedMemory.Offset) + default: + // 'variable' + throw new Error('Not implemented') + } } if (StuffedMemory.Offset === undefined) { return { FlatMem: StuffedMemory, asmCode: '', isNew: false } @@ -135,7 +647,7 @@ export function flattenMemory ( case 'array': // Looks like an array but can be converted to regular variable RetObj = AuxVars.getMemoryObjectByLocation( - utils.addHexContents(StuffedMemory.hexContent, StuffedMemory.Offset.value), line + utils.addHexSimple(StuffedMemory.hexContent, StuffedMemory.Offset.value), line ) AuxVars.freeRegister(StuffedMemory.address) return { FlatMem: RetObj, asmCode: retInstructions, isNew: true } @@ -144,6 +656,14 @@ export function flattenMemory ( } } + function flattenConstantWithOffsetConstant (ConstOffset: OFFSET_MODIFIER_CONSTANT) : FLATTEN_MEMORY_RETURN_OBJECT { + const deferencedVar = AuxVars.getMemoryObjectByLocation(utils.addHexSimple(ConstOffset.value, StuffedMemory.hexContent), line) + if (AuxVars.isTemp(deferencedVar.address)) { + deferencedVar.declaration = paramDec + } + return { FlatMem: deferencedVar, asmCode: '', isNew: false } + } + function flattenConstant (hexParam: string|number|undefined): FLATTEN_MEMORY_RETURN_OBJECT { hexParam = assertNotUndefined(hexParam) let hexString: string @@ -154,8 +674,12 @@ export function flattenMemory ( } hexString = hexString.padStart(16, '0') assertExpression(hexString.length <= 16) + let prefix = 'n' + if (paramDec === 'fixed') { + prefix = 'f' + } const OptMem = AuxVars.memory.find(MEM => { - return MEM.asmName === 'n' + Number('0x' + hexString) && MEM.hexContent === hexString + return MEM.asmName === prefix + Number('0x' + hexString) && MEM.hexContent === hexString }) if (OptMem) { return { FlatMem: OptMem, asmCode: '', isNew: false } @@ -220,6 +744,18 @@ export function createInstruction ( /** Create instruction for SetUnaryOperator `++`, `--`. Create instruction for Unary operator `~` and `+`. */ function unaryOperatorToAsm (OperatorToken: TOKEN, Variable: MEMORY_SLOT): string { + if (Variable.declaration === 'fixed') { + switch (OperatorToken.value) { + case '++': + return `ADD @${Variable.asmName} $f100000000\n` + case '--': + return `SUB @${Variable.asmName} $f100000000\n` + case '~': + return `NOT @${Variable.asmName}\n` + default: + throw new Error(`Internal error at line: ${OperatorToken.line}.`) + } + } switch (OperatorToken.value) { case '++': return `INC @${Variable.asmName}\n` diff --git a/src/codeGenerator/assemblyProcessor/keywordToAsm.ts b/src/codeGenerator/assemblyProcessor/keywordToAsm.ts index db98d8a..2f17a9b 100644 --- a/src/codeGenerator/assemblyProcessor/keywordToAsm.ts +++ b/src/codeGenerator/assemblyProcessor/keywordToAsm.ts @@ -34,7 +34,9 @@ export default function keywordToAsm ( } return TmpMemObj.asmCode case 'sleep': - FlatMem = assertNotUndefined(FlatMem) + if (FlatMem === undefined) { + return 'SLP\n' + } TmpMemObj = flattenMemory(AuxVars, assertNotUndefined(FlatMem), OperatorToken.line) TmpMemObj.asmCode += `SLP $${TmpMemObj.FlatMem.asmName}\n` AuxVars.freeRegister(FlatMem.address) diff --git a/src/codeGenerator/assemblyProcessor/operatorToAsm.ts b/src/codeGenerator/assemblyProcessor/operatorToAsm.ts index 58b6f9f..dc72368 100644 --- a/src/codeGenerator/assemblyProcessor/operatorToAsm.ts +++ b/src/codeGenerator/assemblyProcessor/operatorToAsm.ts @@ -14,9 +14,77 @@ export default function operatorToAsm ( const FlatLeft = flattenMemory(AuxVars, LeftMem, OperatorToken.line) const FlatRight = flattenMemory(AuxVars, RightMem, OperatorToken.line) - function operatorToAsmMain () { + function operatorToAsmMain () : string { assertExpression(LeftMem.type !== 'constant') - let assemblyCode = '' + + switch (FlatLeft.FlatMem.declaration) { + case 'fixed': + return leftFixedToAsm() + default: + if (FlatRight.FlatMem.declaration === 'fixed') { + throw new Error('Internal error') + } + return leftRegularRightRegularToAsm() + } + } + + function leftFixedToAsm () : string { + switch (FlatRight.FlatMem.declaration) { + case 'fixed': + return leftFixedRightFixedToAsm() + default: + return leftFixedRightRegularToAsm() + } + } + + function leftFixedRightFixedToAsm () : string { + switch (OperatorToken.value) { + case '+': + case '+=': + case '-': + case '-=': + return returnThisCode(chooseOperator(OperatorToken.value) + + ` @${FlatLeft.FlatMem.asmName} $${FlatRight.FlatMem.asmName}\n`) + case '*': + case '*=': + return returnThisCode(`MDV @${FlatLeft.FlatMem.asmName} $${FlatRight.FlatMem.asmName} $f100000000\n`) + case '/': + case '/=': + return returnThisCode(`MDV @${FlatLeft.FlatMem.asmName} $f100000000 $${FlatRight.FlatMem.asmName}\n`) + default: + // % & | ^ << >> + throw new Error('Internal error') + } + } + + function leftFixedRightRegularToAsm () : string { + switch (OperatorToken.value) { + case '/': + case '/=': + case '*': + case '*=': + case '>>': + case '>>=': + case '<<': + case '<<=': + return returnThisCode(chooseOperator(OperatorToken.value) + + ` @${FlatLeft.FlatMem.asmName} $${FlatRight.FlatMem.asmName}\n`) + default: + // + - % & | ^ + throw new Error('Internal error') + } + } + + function leftRegularRightRegularToAsm () : string { + const optimized = tryOptimization() + if (optimized !== undefined) { + return optimized + } + return returnThisCode(chooseOperator(OperatorToken.value) + + ` @${FlatLeft.FlatMem.asmName} $${FlatRight.FlatMem.asmName}\n`) + } + + function tryOptimization () : string | undefined { if (RightMem.type === 'constant') { const optimizationResult = testOptimizations() if (optimizationResult === undefined) { @@ -29,17 +97,7 @@ export default function operatorToAsm ( return FlatLeft.asmCode + optimizationResult } } - // No optimization was possible, do regular operations - assemblyCode = chooseOperator(OperatorToken.value) + - ` @${FlatLeft.FlatMem.asmName} $${FlatRight.FlatMem.asmName}\n` - AuxVars.freeRegister(FlatRight.FlatMem.address) - if (FlatLeft.isNew === true) { - assemblyCode += createInstruction(AuxVars, utils.genAssignmentToken(OperatorToken.line), LeftMem, FlatLeft.FlatMem) - AuxVars.freeRegister(FlatLeft.FlatMem.address) - } - return FlatLeft.asmCode + FlatRight.asmCode + assemblyCode } - /** Check and do optimization on a constant right side. * Returns undefined if optimization is do nothing * Returns empty string if no optimization was found @@ -114,6 +172,15 @@ export default function operatorToAsm ( return '' } + function returnThisCode (asm : string) : string { + AuxVars.freeRegister(FlatRight.FlatMem.address) + if (FlatLeft.isNew === true) { + asm += createInstruction(AuxVars, utils.genAssignmentToken(OperatorToken.line), LeftMem, FlatLeft.FlatMem) + AuxVars.freeRegister(FlatLeft.FlatMem.address) + } + return FlatLeft.asmCode + FlatRight.asmCode + asm + } + return operatorToAsmMain() } diff --git a/src/codeGenerator/assemblyProcessor/optimizer.ts b/src/codeGenerator/assemblyProcessor/optimizer.ts index 00e851c..11e6e0c 100644 --- a/src/codeGenerator/assemblyProcessor/optimizer.ts +++ b/src/codeGenerator/assemblyProcessor/optimizer.ts @@ -1,10 +1,13 @@ +import { CONTRACT } from './optimizerVM/index' + /** * Optimize assembly code with peephole strategy. * @param O Optimization level: * - 0: No optimization * - 1: Very basic optimization, just remove silly/unused code * - 2: Safely changes and delete code for smarter outcome - * - 3: Dangerous deep optimizations. Must be checked by developer + * - 3: Use final optimizations tracing variable contents (beta) + * - 4: Dangerous deep optimizations. Must be checked by developer * @param assemblyCode Input assembly * @param labels Array with labels (they will not be removed) * @returns Assembly code processed @@ -29,10 +32,13 @@ export default function optimizer (O: number, assemblyCode: string, labels: stri codeLines.forEach(jumpJumpOpt) codeLines.forEach(swapBranches) codeLines.forEach(notOpt) + codeLines.forEach(getSetSuper) + codeLines.forEach(pushPopRegister) codeLines.forEach(popPushRegister) + codeLines.forEach(mdvOpt) codeLines = codeLines.flatMap(branchOpt) } - if (O >= 3) { + if (O >= 4) { codeLines.forEach(operatorSetSwap) codeLines.forEach(pushOpt) codeLines.forEach(setOperatorSwap) @@ -41,7 +47,17 @@ export default function optimizer (O: number, assemblyCode: string, labels: stri codeLines.forEach(pointerZeroOpt) } } while (optimizedLines !== 0) - return codeLines.join('\n') + const partialOptimized = codeLines.join('\n') + if (O >= 3) { + // silent optimization + const OptVM = new CONTRACT(codeLines) + try { + return OptVM.optimize().join('\n') + } catch (error) { + return partialOptimized + } + } + return partialOptimized } // Collect jumps information @@ -87,20 +103,14 @@ export default function optimizer (O: number, assemblyCode: string, labels: stri if (jmpto === null) { return } - let i = index - while (++i < array.length - 1) { - const nextLabel = /^\s*(\w+):\s*$/.exec(array[i]) - if (nextLabel === null) { - if (isUntouchable(array[i])) { - continue - } - break - } - if (jmpto[1] === nextLabel[1]) { - array[index] = 'DELETE' - optimizedLines++ - return - } + const ip = getNextInstruction(index) + const nextLabel = /^\s*(\w+):\s*$/.exec(array[ip]) + if (nextLabel === null) { + return + } + if (jmpto[1] === nextLabel[1]) { + array[index] = 'DELETE' + optimizedLines++ } } @@ -158,18 +168,20 @@ export default function optimizer (O: number, assemblyCode: string, labels: stri if (branchdat === null) { return } - const label = /^\s*(\w+):\s*$/.exec(array[index + 2]) - if (label === null || branchdat[4] !== label[1]) { + const ip = getNextInstruction(index) + const jmpto = /^\s*JMP\s+:(\w+)\s*$/.exec(array[ip]) + if (jmpto === null) { return } - const jmpto = /^\s*JMP\s+:(\w+)\s*$/.exec(array[index + 1]) - if (jmpto === null) { + const ipp = getNextInstruction(ip) + const label = /^\s*(\w+):\s*$/.exec(array[ipp]) + if (label === null || branchdat[4] !== label[1]) { return } array[index] = value .replace(branchdat[1], inverseBranch(branchdat[1])) .replace(branchdat[4], jmpto[1]) - array[index + 1] = 'DELETE' + array[ip] = 'DELETE' optimizedLines++ } @@ -234,6 +246,28 @@ export default function optimizer (O: number, assemblyCode: string, labels: stri } } + /** Optimizes MUL + DIV to MDV + * ``` + * MUL @a $c -> DELETE + * DIV @a $d -> MDV @a $c $d + * ``` */ + function mdvOpt (value: string, index: number, array: string[]) : void { + const muldat = /^\s*MUL\s+@(\w+)\s+\$(\w+)\s*$/.exec(value) + if (muldat === null) { + return + } + const ip = getNextInstruction(index) + const divdat = /^\s*DIV\s+@(\w+)\s+\$(\w+)\s*$/.exec(array[ip]) + if (divdat === null) { + return + } + if (muldat[1] === divdat[1]) { + array[index] = `MDV @${muldat[1]} $${muldat[2]} $${divdat[2]}` + array[ip] = 'DELETE' + optimizedLines++ + } + } + /** Optimizes the meaningless SET * ``` * SET @var $var -> DELETE @@ -247,6 +281,50 @@ export default function optimizer (O: number, assemblyCode: string, labels: stri optimizedLines++ } + /** Optimizes get and set Superregisters in sequence with registers + * ``` + * FUN @r0 get_B1 -> DELETE + * FUN set_B1 $r0 -> DELETE + * ``` */ + function getSetSuper (value: string, index: number, array: string[]) : void { + const getSuper = /^\s*FUN\s+@(r\d+)\s+(get_[AB][1-4])\s*$/.exec(value) + if (getSuper === null) { + return + } + const ip = getNextInstruction(index) + const setSuper = /^\s*FUN\s+(set_[AB][1-4])\s+\$(r\d+)\s*$/.exec(array[ip]) + if (setSuper === null) { + return + } + if (getSuper[1] === setSuper[2] && getSuper[2].slice(1) === setSuper[1].slice(1)) { + array[index] = 'DELETE' + array[ip] = 'DELETE' + optimizedLines++ + } + } + + /** Optimizes register poping and pushing in sequence + * ``` + * PSH $r0 -> DELETE + * POP @r0 -> DELETE + * ``` */ + function pushPopRegister (value: string, index: number, array: string[]) : void { + const pshdat = /^\s*PSH\s+\$(r\d+)\s*$/.exec(value) + if (pshdat === null) { + return + } + const ip = getNextInstruction(index) + const popdat = /^\s*POP\s+@(r\d)\s*$/.exec(array[ip]) + if (popdat === null) { + return + } + if (pshdat[1] === popdat[1]) { + array[index] = 'DELETE' + array[ip] = 'DELETE' + optimizedLines++ + } + } + /** Optimizes register poping and pushing in sequence * ``` * POP @r0 -> DELETE @@ -257,13 +335,14 @@ export default function optimizer (O: number, assemblyCode: string, labels: stri if (popdat === null) { return } - const pshslpdat = /^\s*PSH\s+\$(r\d+)\s*$/.exec(array[index + 1]) + const ip = getNextInstruction(index) + const pshslpdat = /^\s*PSH\s+\$(r\d+)\s*$/.exec(array[ip]) if (pshslpdat === null) { return } if (pshslpdat[1] === popdat[1]) { array[index] = 'DELETE' - array[index + 1] = 'DELETE' + array[ip] = 'DELETE' optimizedLines++ } } @@ -307,6 +386,17 @@ export default function optimizer (O: number, assemblyCode: string, labels: stri return false } + /** Starting from line, returns the next line that is a valid instruction. + * Valid instructions are labels or codes. + */ + function getNextInstruction (line: number) : number { + line++ + while (isUntouchable(codeLines[line])) { + line++ + } + return line + } + function inverseBranch (branchOperator: string) : string { switch (branchOperator) { case 'BGT': return 'BLE' diff --git a/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.a.spec.ts b/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.a.spec.ts new file mode 100644 index 0000000..54dd3e5 --- /dev/null +++ b/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.a.spec.ts @@ -0,0 +1,257 @@ +import { CONTRACT } from '../index' + +describe('optimzeVM:', () => { + it('should optimize: all api T_EXT_FUN', () => { + /* this is source code for test: + #include APIFunctions\n#pragma verboseAssembly\n// #pragma optimizationLevel 3\n\nlong testID, a, b;\n\nswitch (testID) {\ncase 1: // Clear_A\n Clear_A();\n Set_A2(0);\n Clear_A();\n break;\ncase 2: // Clear_B\n Clear_B();\n Set_B4(0);\n Clear_B();\ncase 3: // Clear_A_And_B\n Set_A1_A2(0, 0); Set_A3_A4(0, 0); Clear_B();\n Clear_A_And_B();\n Clear_B();\n break;\ncase 4: // Swap_A_and_B\n Clear_A_And_B();\n a++;\n Swap_A_and_B();\n a++;\n Set_B4(0);\n Set_A3(0);\n break;\ncase 5: // metaDoNothing\n Clear_A_And_B();\n Send_All_To_Address_In_B();\n Clear_A_And_B();\n break;\ncase 6: // metaUnknowSuperRegisterA\n Set_A3(a); Set_B3(a);\n Put_Last_Block_GSig_In_A();\n Set_A3(a); Set_B3(a);\n break;\ncase 7: // metaUnknowSuperRegisterB\n Set_A3(a); Set_B3(a);\n OR_B_with_A();\n Set_A3(a); Set_B3(a);\n break;\ncase 8: // metaUB1ZB2ZB3ZB4\n Set_B1_B2(a, 0);\n B_To_Address_Of_Tx_In_A();\n Set_B1(a); Set_B2(0); Set_B3(0); Set_B4(0);\n break;\n}\n + */ + const code = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 7 switch (testID) {', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_0', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000003', + 'BEQ $testID $r0 :__switch1_2', + 'SET @r0 #0000000000000004', + 'BEQ $testID $r0 :__switch1_3', + 'SET @r0 #0000000000000005', + 'BEQ $testID $r0 :__switch1_4', + 'SET @r0 #0000000000000006', + 'BEQ $testID $r0 :__switch1_5', + 'SET @r0 #0000000000000007', + 'BEQ $testID $r0 :__switch1_6', + 'SET @r0 #0000000000000008', + 'BEQ $testID $r0 :__switch1_7', + 'FIN', + '^comment line 8 case 1: // Clear_A', + '__switch1_0:', + '^comment line 9 Clear_A();', + 'FUN clear_A', + '^comment line 10 Set_A2(0);', + 'CLR @r0', + 'FUN set_A2 $r0', + '^comment line 11 Clear_A();', + 'FUN clear_A', + '^comment line 12 break;', + 'FIN', + '^comment line 13 case 2: // Clear_B', + '__switch1_1:', + '^comment line 14 Clear_B();', + 'FUN clear_B', + '^comment line 15 Set_B4(0);', + 'CLR @r0', + 'FUN set_B4 $r0', + '^comment line 16 Clear_B();', + 'FUN clear_B', + '^comment line 17 case 3: // Clear_A_And_B', + '__switch1_2:', + '^comment line 18 Set_A1_A2(0', + 'CLR @r0', + 'CLR @r1', + 'FUN set_A1_A2 $r0 $r1', + 'CLR @r0', + 'CLR @r1', + 'FUN set_A3_A4 $r0 $r1', + 'FUN clear_B', + '^comment line 19 Clear_A_And_B();', + 'FUN clear_A_B', + '^comment line 20 Clear_B();', + 'FUN clear_B', + '^comment line 21 break;', + 'FIN', + '^comment line 22 case 4: // Swap_A_and_B', + '__switch1_3:', + '^comment line 23 Clear_A_And_B();', + 'FUN clear_A_B', + '^comment line 24 a++;', + 'INC @a', + '^comment line 25 Swap_A_and_B();', + 'FUN swap_A_and_B', + '^comment line 26 a++;', + 'INC @a', + '^comment line 27 Set_B4(0);', + 'CLR @r0', + 'FUN set_B4 $r0', + '^comment line 28 Set_A3(0);', + 'CLR @r0', + 'FUN set_A3 $r0', + '^comment line 29 break;', + 'FIN', + '^comment line 30 case 5: // metaDoNothing', + '__switch1_4:', + '^comment line 31 Clear_A_And_B();', + 'FUN clear_A_B', + '^comment line 32 Send_All_To_Address_In_B();', + 'FUN send_All_to_Address_in_B', + '^comment line 33 Clear_A_And_B();', + 'FUN clear_A_B', + '^comment line 34 break;', + 'FIN', + '^comment line 35 case 6: // metaUnknowSuperRegisterA', + '__switch1_5:', + '^comment line 36 Set_A3(a); Set_B3(a);', + 'FUN set_A3 $a', + 'FUN set_B3 $a', + '^comment line 37 Put_Last_Block_GSig_In_A();', + 'FUN Put_Last_Block_GSig_In_A', + '^comment line 38 Set_A3(a); Set_B3(a);', + 'FUN set_A3 $a', + 'FUN set_B3 $a', + '^comment line 39 break;', + 'FIN', + '^comment line 40 case 7: // metaUnknowSuperRegisterB', + '__switch1_6:', + '^comment line 41 Set_A3(a); Set_B3(a);', + 'FUN set_A3 $a', + 'FUN set_B3 $a', + '^comment line 42 OR_B_with_A();', + 'FUN OR_B_with_A', + '^comment line 43 Set_A3(a); Set_B3(a);', + 'FUN set_A3 $a', + 'FUN set_B3 $a', + '^comment line 44 break;', + 'FIN', + '^comment line 45 case 8: // metaUB1ZB2ZB3ZB4', + '__switch1_7:', + '^comment line 46 Set_B1_B2(a, 0);', + 'CLR @r0', + 'FUN set_B1_B2 $a $r0', + '^comment line 47 B_To_Address_Of_Tx_In_A();', + 'FUN B_to_Address_of_Tx_in_A', + '^comment line 48 Set_B1(a); Set_B2(0); Set_B3(0); Set_B4(0);', + 'FUN set_B1 $a', + 'CLR @r0', + 'FUN set_B2 $r0', + 'CLR @r0', + 'FUN set_B3 $r0', + 'CLR @r0', + 'FUN set_B4 $r0', + '^comment line 49 break;', + 'FIN' + ] + const optCode = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 7 switch (testID) {', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_0', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000003', + 'BEQ $testID $r0 :__switch1_2', + 'SET @r0 #0000000000000004', + 'BEQ $testID $r0 :__switch1_3', + 'SET @r0 #0000000000000005', + 'BEQ $testID $r0 :__switch1_4', + 'SET @r0 #0000000000000006', + 'BEQ $testID $r0 :__switch1_5', + 'SET @r0 #0000000000000007', + 'BEQ $testID $r0 :__switch1_6', + 'SET @r0 #0000000000000008', + 'BEQ $testID $r0 :__switch1_7', + 'FIN', + '^comment line 8 case 1: // Clear_A', + '__switch1_0:', + '^comment line 9 Clear_A();', + 'FUN clear_A', + '^comment line 10 Set_A2(0);', + 'CLR @r0', + '^comment line 11 Clear_A();', + '^comment line 12 break;', + 'FIN', + '^comment line 13 case 2: // Clear_B', + '__switch1_1:', + '^comment line 14 Clear_B();', + 'FUN clear_B', + '^comment line 15 Set_B4(0);', + 'CLR @r0', + '^comment line 16 Clear_B();', + '^comment line 17 case 3: // Clear_A_And_B', + '__switch1_2:', + '^comment line 18 Set_A1_A2(0', + 'CLR @r0', + 'CLR @r1', + 'FUN set_A1_A2 $r0 $r1', + 'FUN set_A3_A4 $r0 $r1', + 'FUN clear_B', + '^comment line 19 Clear_A_And_B();', + '^comment line 20 Clear_B();', + '^comment line 21 break;', + 'FIN', + '^comment line 22 case 4: // Swap_A_and_B', + '__switch1_3:', + '^comment line 23 Clear_A_And_B();', + 'FUN clear_A_B', + '^comment line 24 a++;', + 'INC @a', + '^comment line 25 Swap_A_and_B();', + 'FUN swap_A_and_B', + '^comment line 26 a++;', + 'INC @a', + '^comment line 27 Set_B4(0);', + 'CLR @r0', + 'FUN set_B4 $r0', + '^comment line 28 Set_A3(0);', + 'FUN set_A3 $r0', + '^comment line 29 break;', + 'FIN', + '^comment line 30 case 5: // metaDoNothing', + '__switch1_4:', + '^comment line 31 Clear_A_And_B();', + 'FUN clear_A_B', + '^comment line 32 Send_All_To_Address_In_B();', + 'FUN send_All_to_Address_in_B', + '^comment line 33 Clear_A_And_B();', + '^comment line 34 break;', + 'FIN', + '^comment line 35 case 6: // metaUnknowSuperRegisterA', + '__switch1_5:', + '^comment line 36 Set_A3(a); Set_B3(a);', + 'FUN set_A3 $a', + 'FUN set_B3 $a', + '^comment line 37 Put_Last_Block_GSig_In_A();', + 'FUN Put_Last_Block_GSig_In_A', + '^comment line 38 Set_A3(a); Set_B3(a);', + 'FUN set_A3 $a', + '^comment line 39 break;', + 'FIN', + '^comment line 40 case 7: // metaUnknowSuperRegisterB', + '__switch1_6:', + '^comment line 41 Set_A3(a); Set_B3(a);', + 'FUN set_A3 $a', + 'FUN set_B3 $a', + '^comment line 42 OR_B_with_A();', + 'FUN OR_B_with_A', + '^comment line 43 Set_A3(a); Set_B3(a);', + 'FUN set_B3 $a', + '^comment line 44 break;', + 'FIN', + '^comment line 45 case 8: // metaUB1ZB2ZB3ZB4', + '__switch1_7:', + '^comment line 46 Set_B1_B2(a, 0);', + 'CLR @r0', + 'FUN set_B1_B2 $a $r0', + '^comment line 47 B_To_Address_Of_Tx_In_A();', + 'FUN B_to_Address_of_Tx_in_A', + '^comment line 48 Set_B1(a); Set_B2(0); Set_B3(0); Set_B4(0);', + 'FUN set_B1 $a', + '^comment line 49 break;', + 'FIN' + ] + const result = new CONTRACT(code).optimize() + expect(result).toEqual(optCode) + }) +}) diff --git a/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.b.spec.ts b/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.b.spec.ts new file mode 100644 index 0000000..50e7ba4 --- /dev/null +++ b/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.b.spec.ts @@ -0,0 +1,255 @@ +import { CONTRACT } from '../index' + +describe('optimzeVM:', () => { + it('should optimize: all api EXT_FUN_DAT', () => { + /* this is source code for test: + #include APIFunctions\n#pragma verboseAssembly\n// #pragma optimizationLevel 3\n\nlong testID, a, b;\n\nswitch (testID) {\ncase 0: // Same content\n Set_A1(25);\n a=25;\n Set_A1(a);\n break;\ncase 1: // Same unknown var\n Set_A2(a);\n b++;\n Set_A2(a);\n break;\ncase 2: // Operation destroys same\n Set_A3(a);\n a++;\n Set_A3(a);\n break;\ncase 3: // Operation destroys same\n Set_A4(a);\n a^=b;\n Set_A4(a);\n break;\ncase 4: // Same content\n Set_B1(25);\n a=25;\n Set_B1(a);\n break;\ncase 5: // Same unknown var\n Set_B2(a);\n b++;\n Set_B2(a);\n break;\ncase 6: // Operation destroys same\n Set_B3(a);\n a++;\n Set_B3(a);\n break;\ncase 7: // Operation destroys same\n Set_B4(a);\n a|=b;\n Set_B4(a);\n break;\ncase 8: // A_to_Tx_after_Timestamp\n Set_A1_A2(a, 0);\n A_To_Tx_After_Timestamp(b);\n Set_A1(a); Set_A2(0); Set_A3(0); Set_A4(0);\n break;\n\n}\n + */ + const code = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 7 switch (testID) {', + 'BZR $testID :__switch1_0', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_2', + 'SET @r0 #0000000000000003', + 'BEQ $testID $r0 :__switch1_3', + 'SET @r0 #0000000000000004', + 'BEQ $testID $r0 :__switch1_4', + 'SET @r0 #0000000000000005', + 'BEQ $testID $r0 :__switch1_5', + 'SET @r0 #0000000000000006', + 'BEQ $testID $r0 :__switch1_6', + 'SET @r0 #0000000000000007', + 'BEQ $testID $r0 :__switch1_7', + 'SET @r0 #0000000000000008', + 'BEQ $testID $r0 :__switch1_8', + 'FIN', + '^comment line 8 case 0: // Same content', + '__switch1_0:', + '^comment line 9 Set_A1(25);', + 'SET @r0 #0000000000000019', + 'FUN set_A1 $r0', + '^comment line 10 a=25;', + 'SET @a #0000000000000019', + '^comment line 11 Set_A1(a);', + 'FUN set_A1 $a', + '^comment line 12 break;', + 'FIN', + '^comment line 13 case 1: // Same unknown var', + '__switch1_1:', + '^comment line 14 Set_A2(a);', + 'FUN set_A2 $a', + '^comment line 15 b++;', + 'INC @b', + '^comment line 16 Set_A2(a);', + 'FUN set_A2 $a', + '^comment line 17 break;', + 'FIN', + '^comment line 18 case 2: // Operation destroys same', + '__switch1_2:', + '^comment line 19 Set_A3(a);', + 'FUN set_A3 $a', + '^comment line 20 a++;', + 'INC @a', + '^comment line 21 Set_A3(a);', + 'FUN set_A3 $a', + '^comment line 22 break;', + 'FIN', + '^comment line 23 case 3: // Operation destroys same', + '__switch1_3:', + '^comment line 24 Set_A4(a);', + 'FUN set_A4 $a', + '^comment line 25 a^=b;', + 'XOR @a $b', + '^comment line 26 Set_A4(a);', + 'FUN set_A4 $a', + '^comment line 27 break;', + 'FIN', + '^comment line 28 case 4: // Same content', + '__switch1_4:', + '^comment line 29 Set_B1(25);', + 'SET @r0 #0000000000000019', + 'FUN set_B1 $r0', + '^comment line 30 a=25;', + 'SET @a #0000000000000019', + '^comment line 31 Set_B1(a);', + 'FUN set_B1 $a', + '^comment line 32 break;', + 'FIN', + '^comment line 33 case 5: // Same unknown var', + '__switch1_5:', + '^comment line 34 Set_B2(a);', + 'FUN set_B2 $a', + '^comment line 35 b++;', + 'INC @b', + '^comment line 36 Set_B2(a);', + 'FUN set_B2 $a', + '^comment line 37 break;', + 'FIN', + '^comment line 38 case 6: // Operation destroys same', + '__switch1_6:', + '^comment line 39 Set_B3(a);', + 'FUN set_B3 $a', + '^comment line 40 a++;', + 'INC @a', + '^comment line 41 Set_B3(a);', + 'FUN set_B3 $a', + '^comment line 42 break;', + 'FIN', + '^comment line 43 case 7: // Operation destroys same', + '__switch1_7:', + '^comment line 44 Set_B4(a);', + 'FUN set_B4 $a', + '^comment line 45 a|=b;', + 'BOR @a $b', + '^comment line 46 Set_B4(a);', + 'FUN set_B4 $a', + '^comment line 47 break;', + 'FIN', + '^comment line 48 case 8: // A_to_Tx_after_Timestamp', + '__switch1_8:', + '^comment line 49 Set_A1_A2(a, 0);', + 'CLR @r0', + 'FUN set_A1_A2 $a $r0', + '^comment line 50 A_to_Tx_after_Timestamp(b);', + 'FUN A_to_Tx_after_Timestamp $b', + '^comment line 51 Set_A1(a); Set_A2(0); Set_A3(0); Set_A4(0);', + 'FUN set_A1 $a', + 'CLR @r0', + 'FUN set_A2 $r0', + 'CLR @r0', + 'FUN set_A3 $r0', + 'CLR @r0', + 'FUN set_A4 $r0', + '^comment line 52 break;', + 'FIN' + ] + const optCode = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 7 switch (testID) {', + 'BZR $testID :__switch1_0', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_2', + 'SET @r0 #0000000000000003', + 'BEQ $testID $r0 :__switch1_3', + 'SET @r0 #0000000000000004', + 'BEQ $testID $r0 :__switch1_4', + 'SET @r0 #0000000000000005', + 'BEQ $testID $r0 :__switch1_5', + 'SET @r0 #0000000000000006', + 'BEQ $testID $r0 :__switch1_6', + 'SET @r0 #0000000000000007', + 'BEQ $testID $r0 :__switch1_7', + 'SET @r0 #0000000000000008', + 'BEQ $testID $r0 :__switch1_8', + 'FIN', + '^comment line 8 case 0: // Same content', + '__switch1_0:', + '^comment line 9 Set_A1(25);', + 'SET @r0 #0000000000000019', + 'FUN set_A1 $r0', + '^comment line 10 a=25;', + 'SET @a #0000000000000019', + '^comment line 11 Set_A1(a);', + '^comment line 12 break;', + 'FIN', + '^comment line 13 case 1: // Same unknown var', + '__switch1_1:', + '^comment line 14 Set_A2(a);', + 'FUN set_A2 $a', + '^comment line 15 b++;', + 'INC @b', + '^comment line 16 Set_A2(a);', + '^comment line 17 break;', + 'FIN', + '^comment line 18 case 2: // Operation destroys same', + '__switch1_2:', + '^comment line 19 Set_A3(a);', + 'FUN set_A3 $a', + '^comment line 20 a++;', + 'INC @a', + '^comment line 21 Set_A3(a);', + 'FUN set_A3 $a', + '^comment line 22 break;', + 'FIN', + '^comment line 23 case 3: // Operation destroys same', + '__switch1_3:', + '^comment line 24 Set_A4(a);', + 'FUN set_A4 $a', + '^comment line 25 a^=b;', + 'XOR @a $b', + '^comment line 26 Set_A4(a);', + 'FUN set_A4 $a', + '^comment line 27 break;', + 'FIN', + '^comment line 28 case 4: // Same content', + '__switch1_4:', + '^comment line 29 Set_B1(25);', + 'SET @r0 #0000000000000019', + 'FUN set_B1 $r0', + '^comment line 30 a=25;', + 'SET @a #0000000000000019', + '^comment line 31 Set_B1(a);', + '^comment line 32 break;', + 'FIN', + '^comment line 33 case 5: // Same unknown var', + '__switch1_5:', + '^comment line 34 Set_B2(a);', + 'FUN set_B2 $a', + '^comment line 35 b++;', + 'INC @b', + '^comment line 36 Set_B2(a);', + '^comment line 37 break;', + 'FIN', + '^comment line 38 case 6: // Operation destroys same', + '__switch1_6:', + '^comment line 39 Set_B3(a);', + 'FUN set_B3 $a', + '^comment line 40 a++;', + 'INC @a', + '^comment line 41 Set_B3(a);', + 'FUN set_B3 $a', + '^comment line 42 break;', + 'FIN', + '^comment line 43 case 7: // Operation destroys same', + '__switch1_7:', + '^comment line 44 Set_B4(a);', + 'FUN set_B4 $a', + '^comment line 45 a|=b;', + 'BOR @a $b', + '^comment line 46 Set_B4(a);', + 'FUN set_B4 $a', + '^comment line 47 break;', + 'FIN', + '^comment line 48 case 8: // A_to_Tx_after_Timestamp', + '__switch1_8:', + '^comment line 49 Set_A1_A2(a, 0);', + 'CLR @r0', + 'FUN set_A1_A2 $a $r0', + '^comment line 50 A_to_Tx_after_Timestamp(b);', + 'FUN A_to_Tx_after_Timestamp $b', + '^comment line 51 Set_A1(a); Set_A2(0); Set_A3(0); Set_A4(0);', + 'FUN set_A1 $a', + '^comment line 52 break;', + 'FIN' + ] + const result = new CONTRACT(code).optimize() + expect(result).toEqual(optCode) + }) +}) diff --git a/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.c.spec.ts b/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.c.spec.ts new file mode 100644 index 0000000..5c8a1cf --- /dev/null +++ b/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.c.spec.ts @@ -0,0 +1,326 @@ +import { CONTRACT } from '../index' + +describe('optimzeVM:', () => { + it('should optimize: all api EXT_FUN_DAT_2', () => { + /* this is source code for test: + #include APIFunctions\n#pragma verboseAssembly\n// #pragma optimizationLevel 3\n\nlong testID, a, b;\n\nswitch (testID) {\ncase 0: // Same content\n Set_A1_A2(25, 13);\n a=25;\n b=13;\n Set_A1_A2(a, b);\n break;\ncase 1: // Same var\n Set_A3_A4(a, b);\n Set_A3_A4(a, b);\n break;\ncase 2: // content+var\n Set_B1_B2(25, b);\n a=25;\n Set_B1_B2(a, b);\n break;\ncase 3: // var+content\n Set_B3_B4(a, 13);\n b=13;\n Set_B3_B4(a, b);\n break;\n}\n + */ + const code = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 7 switch (testID) {', + 'BZR $testID :__switch1_0', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_2', + 'SET @r0 #0000000000000003', + 'BEQ $testID $r0 :__switch1_3', + 'FIN', + '^comment line 8 case 0: // Same content', + '__switch1_0:', + '^comment line 9 Set_A1_A2(25, 13);', + 'SET @r0 #0000000000000019', + 'SET @r1 #000000000000000d', + 'FUN set_A1_A2 $r0 $r1', + '^comment line 10 a=25;', + 'SET @a #0000000000000019', + '^comment line 11 b=13;', + 'SET @b #000000000000000d', + '^comment line 12 Set_A1_A2(a, b);', + 'FUN set_A1_A2 $a $b', + '^comment line 13 break;', + 'FIN', + '^comment line 14 case 1: // Same var', + '__switch1_1:', + '^comment line 15 Set_A3_A4(a, b);', + 'FUN set_A3_A4 $a $b', + '^comment line 16 Set_A3_A4(a, b);', + 'FUN set_A3_A4 $a $b', + '^comment line 17 break;', + 'FIN', + '^comment line 18 case 2: // content+var', + '__switch1_2:', + '^comment line 19 Set_B1_B2(25, b);', + 'SET @r0 #0000000000000019', + 'FUN set_B1_B2 $r0 $b', + '^comment line 20 a=25;', + 'SET @a #0000000000000019', + '^comment line 21 Set_B1_B2(a, b);', + 'FUN set_B1_B2 $a $b', + '^comment line 22 break;', + 'FIN', + '^comment line 23 case 3: // var+content', + '__switch1_3:', + '^comment line 24 Set_B3_B4(a, 13);', + 'SET @r0 #000000000000000d', + 'FUN set_B3_B4 $a $r0', + '^comment line 25 b=13;', + 'SET @b #000000000000000d', + '^comment line 26 Set_B3_B4(a, b);', + 'FUN set_B3_B4 $a $b', + '^comment line 27 break;', + 'FIN' + ] + const optCode = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 7 switch (testID) {', + 'BZR $testID :__switch1_0', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_2', + 'SET @r0 #0000000000000003', + 'BEQ $testID $r0 :__switch1_3', + 'FIN', + '^comment line 8 case 0: // Same content', + '__switch1_0:', + '^comment line 9 Set_A1_A2(25, 13);', + 'SET @r0 #0000000000000019', + 'SET @r1 #000000000000000d', + 'FUN set_A1_A2 $r0 $r1', + '^comment line 10 a=25;', + 'SET @a #0000000000000019', + '^comment line 11 b=13;', + 'SET @b #000000000000000d', + '^comment line 12 Set_A1_A2(a, b);', + '^comment line 13 break;', + 'FIN', + '^comment line 14 case 1: // Same var', + '__switch1_1:', + '^comment line 15 Set_A3_A4(a, b);', + 'FUN set_A3_A4 $a $b', + '^comment line 16 Set_A3_A4(a, b);', + '^comment line 17 break;', + 'FIN', + '^comment line 18 case 2: // content+var', + '__switch1_2:', + '^comment line 19 Set_B1_B2(25, b);', + 'SET @r0 #0000000000000019', + 'FUN set_B1_B2 $r0 $b', + '^comment line 20 a=25;', + 'SET @a #0000000000000019', + '^comment line 21 Set_B1_B2(a, b);', + '^comment line 22 break;', + 'FIN', + '^comment line 23 case 3: // var+content', + '__switch1_3:', + '^comment line 24 Set_B3_B4(a, 13);', + 'SET @r0 #000000000000000d', + 'FUN set_B3_B4 $a $r0', + '^comment line 25 b=13;', + 'SET @b #000000000000000d', + '^comment line 26 Set_B3_B4(a, b);', + '^comment line 27 break;', + 'FIN' + ] + const result = new CONTRACT(code).optimize() + expect(result).toEqual(optCode) + }) + it('should optimize: EXT_FUN_RET Get_[AB1-4]', () => { + /* this is source code for test: + #include APIFunctions\n#pragma verboseAssembly\n// #pragma optimizationLevel 3\n\nlong testID, a, b;\n\nswitch (testID) {\ncase 0: // Same content\n Set_A1_A2(1, 5);\n Set_A3_A4(2, 6);\n a=1;\n a=Get_A1();\n a=5;\n a=Get_A2();\n a=2;\n a=Get_A3();\n a=6;\n a=Get_A4();\n break;\ncase 1: // Same var\n Set_B1_B2(a, b);\n Set_B3_B4(a, b);\n a=Get_B1();\n b=Get_B2();\n a=Get_B3();\n b=Get_B4();\n break;\ncase 2: // get+set\n a=Get_B1();\n b++;\n Set_B1(a);\n break;\n}\n + */ + const code = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 7 switch (testID) {', + 'BZR $testID :__switch1_0', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_2', + 'FIN', + '^comment line 8 case 0: // Same content', + '__switch1_0:', + '^comment line 9 Set_A1_A2(1, 5);', + 'SET @r0 #0000000000000001', + 'SET @r1 #0000000000000005', + 'FUN set_A1_A2 $r0 $r1', + '^comment line 10 Set_A3_A4(2, 6);', + 'SET @r0 #0000000000000002', + 'SET @r1 #0000000000000006', + 'FUN set_A3_A4 $r0 $r1', + '^comment line 11 a=1;', + 'SET @a #0000000000000001', + '^comment line 12 a=Get_A1();', + 'FUN @a get_A1', + '^comment line 13 a=5;', + 'SET @a #0000000000000005', + '^comment line 14 a=Get_A2();', + 'FUN @a get_A2', + '^comment line 15 a=2;', + 'SET @a #0000000000000002', + '^comment line 16 a=Get_A3();', + 'FUN @a get_A3', + '^comment line 17 a=6;', + 'SET @a #0000000000000006', + '^comment line 18 a=Get_A4();', + 'FUN @a get_A4', + '^comment line 19 break;', + 'FIN', + '^comment line 20 case 1: // Same var', + '__switch1_1:', + '^comment line 21 Set_B1_B2(a, b);', + 'FUN set_B1_B2 $a $b', + '^comment line 22 Set_B3_B4(a, b);', + 'FUN set_B3_B4 $a $b', + '^comment line 23 a=Get_B1();', + 'FUN @a get_B1', + '^comment line 24 b=Get_B2();', + 'FUN @b get_B2', + '^comment line 25 a=Get_B3();', + 'FUN @a get_B3', + '^comment line 26 b=Get_B4();', + 'FUN @b get_B4', + '^comment line 27 break;', + 'FIN', + '^comment line 28 case 2: // get+set', + '__switch1_2:', + '^comment line 29 a=Get_B1();', + 'FUN @a get_B1', + '^comment line 30 b++;', + 'INC @b', + '^comment line 31 Set_B1(a);', + 'FUN set_B1 $a', + '^comment line 32 break;', + 'FIN' + ] + const optCode = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 7 switch (testID) {', + 'BZR $testID :__switch1_0', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_2', + 'FIN', + '^comment line 8 case 0: // Same content', + '__switch1_0:', + '^comment line 9 Set_A1_A2(1, 5);', + 'SET @r0 #0000000000000001', + 'SET @r1 #0000000000000005', + 'FUN set_A1_A2 $r0 $r1', + '^comment line 10 Set_A3_A4(2, 6);', + 'SET @r0 #0000000000000002', + 'SET @r1 #0000000000000006', + 'FUN set_A3_A4 $r0 $r1', + '^comment line 11 a=1;', + 'SET @a #0000000000000001', + '^comment line 12 a=Get_A1();', + '^comment line 13 a=5;', + 'SET @a #0000000000000005', + '^comment line 14 a=Get_A2();', + '^comment line 15 a=2;', + 'SET @a #0000000000000002', + '^comment line 16 a=Get_A3();', + '^comment line 17 a=6;', + 'SET @a #0000000000000006', + '^comment line 18 a=Get_A4();', + '^comment line 19 break;', + 'FIN', + '^comment line 20 case 1: // Same var', + '__switch1_1:', + '^comment line 21 Set_B1_B2(a, b);', + 'FUN set_B1_B2 $a $b', + '^comment line 22 Set_B3_B4(a, b);', + 'FUN set_B3_B4 $a $b', + '^comment line 23 a=Get_B1();', + '^comment line 24 b=Get_B2();', + '^comment line 25 a=Get_B3();', + '^comment line 26 b=Get_B4();', + '^comment line 27 break;', + 'FIN', + '^comment line 28 case 2: // get+set', + '__switch1_2:', + '^comment line 29 a=Get_B1();', + 'FUN @a get_B1', + '^comment line 30 b++;', + 'INC @b', + '^comment line 31 Set_B1(a);', + '^comment line 32 break;', + 'FIN' + ] + const result = new CONTRACT(code).optimize() + expect(result).toEqual(optCode) + }) + it('should optimize: Remaining EXT_FUN_RET and EXT_FUN_RET_DAT_2 ', () => { + const code = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 6 if (a) {', + 'BZR $a :__if1_endif', + '^comment line 7 Set_B1(a);', + 'FUN set_B1 $a', + '^comment line 8 a=Get_Activation_Fee();', + 'FUN @a Get_Activation_Fee', + '^comment line 9 Set_B1(a);', + 'FUN set_B1 $a', + '__if1_endif:', + '^comment line 12 Set_B1_B2(b, a);', + 'FUN set_B1_B2 $b $a', + '^comment line 13 a=Add_Minutes_To_Timestamp(testID, b);', + 'FUN @a add_Minutes_to_Timestamp $testID $b', + '^comment line 14 Set_B1(b);', + 'FUN set_B1 $b', + '^comment line 15 Set_B2(a);', + 'FUN set_B2 $a', + 'FIN' + ] + const optCode = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare testID', + '^declare a', + '^declare b', + '', + '^comment line 6 if (a) {', + 'BZR $a :__if1_endif', + '^comment line 7 Set_B1(a);', + 'FUN set_B1 $a', + '^comment line 8 a=Get_Activation_Fee();', + 'FUN @a Get_Activation_Fee', + '^comment line 9 Set_B1(a);', + 'FUN set_B1 $a', + '__if1_endif:', + '^comment line 12 Set_B1_B2(b, a);', + 'FUN set_B1_B2 $b $a', + '^comment line 13 a=Add_Minutes_To_Timestamp(testID, b);', + 'FUN @a add_Minutes_to_Timestamp $testID $b', + '^comment line 14 Set_B1(b);', + '^comment line 15 Set_B2(a);', + 'FUN set_B2 $a', + 'FIN' + ] + const result = new CONTRACT(code).optimize() + expect(result).toEqual(optCode) + }) +}) diff --git a/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.d.spec.ts b/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.d.spec.ts new file mode 100644 index 0000000..fd227a4 --- /dev/null +++ b/src/codeGenerator/assemblyProcessor/optimizerVM/__tests__/optimizerVM.d.spec.ts @@ -0,0 +1,204 @@ +import { CONTRACT } from '../index' + +describe('optimzeVM:', () => { + it('should optimize: cpu opcodes (1)', () => { + /* this is source code for test: + #include APIFunctions\n#pragma verboseAssembly\n#pragma optimizationLevel 3\n\nconst long n3 = 3;\n\nlong testID, a, b, c, d;\n\nswitch (testID) {\ncase 0: // label __GNT\n b=testID;\n a=getNextTx();\n b=testID;\n break;\ncase 1: // label _opt\n b=testID;\n if (b) {\n b=testID;\n }\n break;\ncase 2: // SET_VAL\n b=9;\n b=9; //optimize same content\n break;\ncase 3: // SET_DAT\n b=9; a=9;\n b=a; //optimize same content\n c=d;\n d=c; //optimize same var\n break;\ncase 4: // CLR\n a=0;\n asm { SET @b #0000000000000000 }\n b=0; //optimize same content\n a=b; //optimize same content\n break;\ncase 5: // INC DEC\n a=0;\n b=10;\n a++; b--;\n a=1; //optimize same content\n b=9; //optimize same content\n break;\n} + */ + const code = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare _counterTimestamp', + '^declare n3', + '^declare testID', + '^declare a', + '^declare b', + '^declare c', + '^declare d', + '', + '^comment line 5 const long n3 = 3;', + '^const SET @n3 #0000000000000003', + '^comment line 9 switch (testID) {', + 'BZR $testID :__switch1_0', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_2', + 'BEQ $testID $n3 :__switch1_3', + 'SET @r0 #0000000000000004', + 'BEQ $testID $r0 :__switch1_4', + 'SET @r0 #0000000000000005', + 'BEQ $testID $r0 :__switch1_5', + 'FIN', + '^comment line 10 case 0: // label __GNT', + '__switch1_0:', + '^comment line 11 b=testID;', + 'SET @b $testID', + '^comment line 12 a=getNextTx();', + 'FUN A_to_Tx_after_Timestamp $_counterTimestamp', + 'FUN @a get_A1', + 'BZR $a :__GNT_2', + 'FUN @_counterTimestamp get_Timestamp_for_Tx_in_A', + '__GNT_2:', + '^comment line 13 b=testID;', + 'SET @b $testID', + '^comment line 14 break;', + 'FIN', + '^comment line 15 case 1: // label _opt', + '__switch1_1:', + '^comment line 16 b=testID;', + 'SET @b $testID', + '^comment line 17 if (b) {', + 'BNZ $b :__opt_1', + 'FIN', + '__opt_1:', + '^comment line 18 b=testID;', + 'SET @b $testID', + '^comment line 20 break;', + 'FIN', + '^comment line 21 case 2: // SET_VAL', + '__switch1_2:', + '^comment line 22 b=9;', + 'SET @b #0000000000000009', + '^comment line 23 b=9; //optimize same content', + 'SET @b #0000000000000009', + '^comment line 24 break;', + 'FIN', + '^comment line 25 case 3: // SET_DAT', + '__switch1_3:', + '^comment line 26 b=9; a=9;', + 'SET @b #0000000000000009', + 'SET @a #0000000000000009', + '^comment line 27 b=a; //optimize same content', + 'SET @b $a', + '^comment line 28 c=d;', + 'SET @c $d', + '^comment line 29 d=c; //optimize same var', + 'SET @d $c', + '^comment line 30 break;', + 'FIN', + '^comment line 31 case 4: // CLR', + '__switch1_4:', + '^comment line 32 a=0;', + 'CLR @a', + '^comment line 33 asm { SET @b #0000000000000000 }', + 'SET @b #0000000000000000', + '^comment line 34 b=0; //optimize same content', + 'CLR @b', + '^comment line 35 a=b; //optimize same content', + 'SET @a $b', + '^comment line 36 break;', + 'FIN', + '^comment line 37 case 5: // INC DEC', + '__switch1_5:', + '^comment line 38 a=0;', + 'CLR @a', + '^comment line 39 b=10;', + 'SET @b #000000000000000a', + '^comment line 40 a++; b--;', + 'INC @a', + 'DEC @b', + '^comment line 41 a=1; //optimize same content', + 'SET @a #0000000000000001', + '^comment line 42 b=9; //optimize same content', + 'SET @b #0000000000000009', + '^comment line 43 break;', + 'FIN' + ] + const optCode = [ + '^declare r0', + '^declare r1', + '^declare r2', + '^declare _counterTimestamp', + '^declare n3', + '^declare testID', + '^declare a', + '^declare b', + '^declare c', + '^declare d', + '', + '^comment line 5 const long n3 = 3;', + '^const SET @n3 #0000000000000003', + '^comment line 9 switch (testID) {', + 'BZR $testID :__switch1_0', + 'SET @r0 #0000000000000001', + 'BEQ $testID $r0 :__switch1_1', + 'SET @r0 #0000000000000002', + 'BEQ $testID $r0 :__switch1_2', + 'BEQ $testID $n3 :__switch1_3', + 'SET @r0 #0000000000000004', + 'BEQ $testID $r0 :__switch1_4', + 'SET @r0 #0000000000000005', + 'BEQ $testID $r0 :__switch1_5', + 'FIN', + '^comment line 10 case 0: // label __GNT', + '__switch1_0:', + '^comment line 11 b=testID;', + 'SET @b $testID', + '^comment line 12 a=getNextTx();', + 'FUN A_to_Tx_after_Timestamp $_counterTimestamp', + 'FUN @a get_A1', + 'BZR $a :__GNT_2', + 'FUN @_counterTimestamp get_Timestamp_for_Tx_in_A', + '__GNT_2:', + '^comment line 13 b=testID;', + '^comment line 14 break;', + 'FIN', + '^comment line 15 case 1: // label _opt', + '__switch1_1:', + '^comment line 16 b=testID;', + 'SET @b $testID', + '^comment line 17 if (b) {', + 'BNZ $b :__opt_1', + 'FIN', + '__opt_1:', + '^comment line 18 b=testID;', + '^comment line 20 break;', + 'FIN', + '^comment line 21 case 2: // SET_VAL', + '__switch1_2:', + '^comment line 22 b=9;', + 'SET @b #0000000000000009', + '^comment line 23 b=9; //optimize same content', + '^comment line 24 break;', + 'FIN', + '^comment line 25 case 3: // SET_DAT', + '__switch1_3:', + '^comment line 26 b=9; a=9;', + 'SET @b #0000000000000009', + 'SET @a #0000000000000009', + '^comment line 27 b=a; //optimize same content', + '^comment line 28 c=d;', + 'SET @c $d', + '^comment line 29 d=c; //optimize same var', + '^comment line 30 break;', + 'FIN', + '^comment line 31 case 4: // CLR', + '__switch1_4:', + '^comment line 32 a=0;', + 'CLR @a', + '^comment line 33 asm { SET @b #0000000000000000 }', + 'SET @b #0000000000000000', + '^comment line 34 b=0; //optimize same content', + '^comment line 35 a=b; //optimize same content', + '^comment line 36 break;', + 'FIN', + '^comment line 37 case 5: // INC DEC', + '__switch1_5:', + '^comment line 38 a=0;', + 'CLR @a', + '^comment line 39 b=10;', + 'SET @b #000000000000000a', + '^comment line 40 a++; b--;', + 'INC @a', + 'DEC @b', + '^comment line 41 a=1; //optimize same content', + '^comment line 42 b=9; //optimize same content', + '^comment line 43 break;', + 'FIN' + ] + const result = new CONTRACT(code).optimize() + expect(result).toEqual(optCode) + }) +}) diff --git a/src/codeGenerator/assemblyProcessor/optimizerVM/api.ts b/src/codeGenerator/assemblyProcessor/optimizerVM/api.ts new file mode 100644 index 0000000..a987239 --- /dev/null +++ b/src/codeGenerator/assemblyProcessor/optimizerVM/api.ts @@ -0,0 +1,525 @@ +import { CONTRACT, MemoryObj, unknownValue } from './index' + +interface T_EXT { + funName: string +} +interface T_EXT_FUN extends T_EXT{ + execute(ContractState: CONTRACT): boolean|null +} +interface T_EXT_FUN_DAT extends T_EXT{ + execute(ContractState: CONTRACT, value: MemoryObj): boolean|null +} +interface T_EXT_FUN_DAT_2 extends T_EXT{ + execute(ContractState: CONTRACT, value1: MemoryObj, value2: MemoryObj): boolean|null +} +interface T_EXT_FUN_RET extends T_EXT{ + execute(ContractState: CONTRACT, RetVar: MemoryObj): boolean|null +} +interface T_EXT_FUN_RET_DAT_2 extends T_EXT{ + execute(ContractState: CONTRACT, RetVar: MemoryObj, value1: MemoryObj, value2: MemoryObj): boolean|null +} + +function metaUnknownSuperRegisterA (ContractState: CONTRACT) : boolean { + ContractState.unknownSuperRegisterA() + return true +} +function metaUnknownSuperRegisterB (ContractState: CONTRACT) : boolean { + ContractState.unknownSuperRegisterB() + return true +} +export function metaDoNothing () : boolean { + return true +} +function metaUB1ZB2ZB3ZB4 (ContractState: CONTRACT) : boolean { + const B1 = ContractState.getMemoryByName('B1') + const B2 = ContractState.getMemoryByName('B2') + const B3 = ContractState.getMemoryByName('B3') + const B4 = ContractState.getMemoryByName('B4') + ContractState.unknownAndRevoke(B1) + ContractState.zeroAndRevoke(B2) + ContractState.zeroAndRevoke(B3) + ContractState.zeroAndRevoke(B4) + return true +} +function metaSetAndRevokeVariable (ContractState: CONTRACT, ContentVar: MemoryObj, variableName: string) : boolean|null { + const superReg = ContractState.getMemoryByName(variableName) + if (superReg.shadow === ContentVar.varName) { + // Optimize setting same variable content + return null + } + if (superReg.value !== unknownValue && superReg.value === ContentVar.value) { + // Optimize setting same constant content + return null + } + ContractState.setAndRevoke(superReg, ContentVar) + return true +} +function metaDoubleSetSuperregister (ContractState: CONTRACT, ContentVar1: MemoryObj, ContentVar2: MemoryObj, reg1: string, reg2: string) : boolean|null { + const superReg1 = ContractState.getMemoryByName(reg1) + const superReg2 = ContractState.getMemoryByName(reg2) + if ((superReg1.shadow === ContentVar1.varName || (superReg1.value !== unknownValue && superReg1.value === ContentVar1.value)) && + (superReg2.shadow === ContentVar2.varName || (superReg2.value !== unknownValue && superReg2.value === ContentVar2.value))) { + // Optimize setting same variable/constant content + return null + } + ContractState.setAndRevoke(superReg1, ContentVar1) + ContractState.setAndRevoke(superReg2, ContentVar2) + return true +} +function metaUnknowAndRevokeVariable (ContractState: CONTRACT, RetVar: MemoryObj) : boolean { + ContractState.unknownAndRevoke(RetVar) + return true +} +function metaGetSuperregister (ContractState: CONTRACT, RetVar: MemoryObj, reg: string) : boolean|null { + const superReg = ContractState.getMemoryByName(reg) + if (superReg.shadow === RetVar.varName || (superReg.value !== unknownValue && superReg.value === RetVar.value)) { + // Optimize same content + return null + } + ContractState.unknownAndRevoke(RetVar) + RetVar.shadow = superReg.varName + superReg.shadow = RetVar.varName + return true +} + +export class API_MICROCODE { + static EXT_FUN: T_EXT_FUN[] = [ + { + funName: 'clear_A', + execute (ContractState) { + const A1 = ContractState.getMemoryByName('A1') + const A2 = ContractState.getMemoryByName('A2') + const A3 = ContractState.getMemoryByName('A3') + const A4 = ContractState.getMemoryByName('A4') + if (A1.value === 0n && A2.value === 0n && A3.value === 0n && A4.value === 0n) { + // Optimize: already all zero + return null + } + ContractState.zeroAndRevoke(A1) + ContractState.zeroAndRevoke(A2) + ContractState.zeroAndRevoke(A3) + ContractState.zeroAndRevoke(A4) + return true + } + }, + { + funName: 'clear_B', + execute (ContractState) { + const B1 = ContractState.getMemoryByName('B1') + const B2 = ContractState.getMemoryByName('B2') + const B3 = ContractState.getMemoryByName('B3') + const B4 = ContractState.getMemoryByName('B4') + if (B1.value === 0n && B2.value === 0n && B3.value === 0n && B4.value === 0n) { + // Optimize: already all zero + return null + } + ContractState.zeroAndRevoke(B1) + ContractState.zeroAndRevoke(B2) + ContractState.zeroAndRevoke(B3) + ContractState.zeroAndRevoke(B4) + return true + } + }, + { + funName: 'clear_A_B', + execute (ContractState) { + const A1 = ContractState.getMemoryByName('A1') + const A2 = ContractState.getMemoryByName('A2') + const A3 = ContractState.getMemoryByName('A3') + const A4 = ContractState.getMemoryByName('A4') + const B1 = ContractState.getMemoryByName('B1') + const B2 = ContractState.getMemoryByName('B2') + const B3 = ContractState.getMemoryByName('B3') + const B4 = ContractState.getMemoryByName('B4') + if (A1.value === 0n && A2.value === 0n && A3.value === 0n && A4.value === 0n && + B1.value === 0n && B2.value === 0n && B3.value === 0n && B4.value === 0n) { + // Optimize: already all zero + return null + } + ContractState.zeroAndRevoke(A1) + ContractState.zeroAndRevoke(A2) + ContractState.zeroAndRevoke(A3) + ContractState.zeroAndRevoke(A4) + ContractState.zeroAndRevoke(B1) + ContractState.zeroAndRevoke(B2) + ContractState.zeroAndRevoke(B3) + ContractState.zeroAndRevoke(B4) + return true + } + }, + { + funName: 'copy_A_From_B', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'copy_B_From_A', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'swap_A_and_B', + execute (ContractState) { + ContractState.unknownSuperRegisterA() + ContractState.unknownSuperRegisterB() + return true + } + }, + { + funName: 'OR_A_with_B', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'OR_B_with_A', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'AND_A_with_B', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'AND_B_with_A', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'XOR_A_with_B', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'XOR_B_with_A', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'add_A_to_B', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'add_B_to_A', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'sub_A_from_B', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'sub_B_from_A', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'mul_A_by_B', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'mul_B_by_A', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'div_A_by_B', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'div_B_by_A', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'MD5_A_to_B', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'HASH160_A_to_B', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'SHA256_A_to_B', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'put_Last_Block_Hash_In_A', + execute: metaUnknownSuperRegisterA + }, + { + funName: 'message_from_Tx_in_A_to_B', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'B_to_Address_of_Tx_in_A', + execute: metaUB1ZB2ZB3ZB4 + }, + { + funName: 'B_to_Address_of_Creator', + execute: metaUB1ZB2ZB3ZB4 + }, + { + funName: 'B_To_Assets_Of_Tx_In_A', + execute: metaUnknownSuperRegisterB + }, + { + funName: 'send_All_to_Address_in_B', + execute: metaDoNothing + }, + { + funName: 'send_Old_to_Address_in_B', + execute: metaDoNothing + }, + { + funName: 'send_A_to_Address_in_B', + execute: metaDoNothing + }, + { + funName: 'Set_Map_Value_Keys_In_A', + execute: metaDoNothing + }, + { + funName: 'Mint_Asset', + execute: metaDoNothing + }, + { + funName: 'Distribute_To_Asset_Holders', + execute: metaDoNothing + }, + { + funName: 'Put_Last_Block_GSig_In_A', + execute: metaUnknownSuperRegisterA + } + ] + + static EXT_FUN_DAT: T_EXT_FUN_DAT[] = [ + { + funName: 'set_A1', + execute (ContractState, ContentVar) { + return metaSetAndRevokeVariable(ContractState, ContentVar, 'A1') + } + }, + { + funName: 'set_A2', + execute (ContractState, ContentVar) { + return metaSetAndRevokeVariable(ContractState, ContentVar, 'A2') + } + }, + { + funName: 'set_A3', + execute (ContractState, ContentVar) { + return metaSetAndRevokeVariable(ContractState, ContentVar, 'A3') + } + }, + { + funName: 'set_A4', + execute (ContractState, ContentVar) { + return metaSetAndRevokeVariable(ContractState, ContentVar, 'A4') + } + }, + { + funName: 'set_B1', + execute (ContractState, ContentVar) { + return metaSetAndRevokeVariable(ContractState, ContentVar, 'B1') + } + }, + { + funName: 'set_B2', + execute (ContractState, ContentVar) { + return metaSetAndRevokeVariable(ContractState, ContentVar, 'B2') + } + }, + { + funName: 'set_B3', + execute (ContractState, ContentVar) { + return metaSetAndRevokeVariable(ContractState, ContentVar, 'B3') + } + }, + { + funName: 'set_B4', + execute (ContractState, ContentVar) { + return metaSetAndRevokeVariable(ContractState, ContentVar, 'B4') + } + }, + { + funName: 'A_to_Tx_after_Timestamp', + execute (ContractState, value) { + const A1 = ContractState.getMemoryByName('A1') + const A2 = ContractState.getMemoryByName('A2') + const A3 = ContractState.getMemoryByName('A3') + const A4 = ContractState.getMemoryByName('A4') + ContractState.unknownAndRevoke(A1) + ContractState.zeroAndRevoke(A2) + ContractState.zeroAndRevoke(A3) + ContractState.zeroAndRevoke(A4) + return true + } + }, + { + funName: 'send_to_Address_in_B', + execute: metaDoNothing + } + ] + + static EXT_FUN_DAT_2: T_EXT_FUN_DAT_2[] = [ + { + funName: 'set_A1_A2', + execute (ContractState, ContentVar1, ContentVar2) { + return metaDoubleSetSuperregister(ContractState, ContentVar1, ContentVar2, 'A1', 'A2') + } + }, + { + funName: 'set_A3_A4', + execute (ContractState, ContentVar1, ContentVar2) { + return metaDoubleSetSuperregister(ContractState, ContentVar1, ContentVar2, 'A3', 'A4') + } + }, + { + funName: 'set_B1_B2', + execute (ContractState, ContentVar1, ContentVar2) { + return metaDoubleSetSuperregister(ContractState, ContentVar1, ContentVar2, 'B1', 'B2') + } + }, + { + funName: 'set_B3_B4', + execute (ContractState, ContentVar1, ContentVar2) { + return metaDoubleSetSuperregister(ContractState, ContentVar1, ContentVar2, 'B3', 'B4') + } + } + ] + + static EXT_FUN_RET: T_EXT_FUN_RET[] = [ + { + funName: 'get_A1', + execute (ContractState, RetVar) { + return metaGetSuperregister(ContractState, RetVar, 'A1') + } + }, + { + funName: 'get_A2', + execute (ContractState, RetVar) { + return metaGetSuperregister(ContractState, RetVar, 'A2') + } + }, + { + funName: 'get_A3', + execute (ContractState, RetVar) { + return metaGetSuperregister(ContractState, RetVar, 'A3') + } + }, + { + funName: 'get_A4', + execute (ContractState, RetVar) { + return metaGetSuperregister(ContractState, RetVar, 'A4') + } + }, + { + funName: 'get_B1', + execute (ContractState, RetVar) { + return metaGetSuperregister(ContractState, RetVar, 'B1') + } + }, + { + funName: 'get_B2', + execute (ContractState, RetVar) { + return metaGetSuperregister(ContractState, RetVar, 'B2') + } + }, + { + funName: 'get_B3', + execute (ContractState, RetVar) { + return metaGetSuperregister(ContractState, RetVar, 'B3') + } + }, + { + funName: 'get_B4', + execute (ContractState, RetVar) { + return metaGetSuperregister(ContractState, RetVar, 'B4') + } + }, + { + funName: 'check_A_Is_Zero', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'check_B_Is_Zero', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'check_A_equals_B', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'check_MD5_A_with_B', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'check_HASH160_A_with_B', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'check_SHA256_A_with_B', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'Check_Sig_B_With_A', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Block_Timestamp', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Creation_Timestamp', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Last_Block_Timestamp', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Type_for_Tx_in_A', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Amount_for_Tx_in_A', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Timestamp_for_Tx_in_A', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Ticket_Id_for_Tx_in_A', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Current_Balance', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'get_Previous_Balance', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'Get_Code_Hash_Id', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'Get_Map_Value_Keys_In_A', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'Issue_Asset', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'Get_Asset_Holders_Count', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'Get_Activation_Fee', + execute: metaUnknowAndRevokeVariable + }, + { + funName: 'Get_Asset_Circulating', + execute: metaUnknowAndRevokeVariable + } + ] + + static EXT_FUN_RET_DAT_2: T_EXT_FUN_RET_DAT_2[] = [ + { + funName: 'add_Minutes_to_Timestamp', + execute: metaUnknowAndRevokeVariable + } + ] +} diff --git a/src/codeGenerator/assemblyProcessor/optimizerVM/cpu.ts b/src/codeGenerator/assemblyProcessor/optimizerVM/cpu.ts new file mode 100644 index 0000000..d77d09f --- /dev/null +++ b/src/codeGenerator/assemblyProcessor/optimizerVM/cpu.ts @@ -0,0 +1,485 @@ +import { Constants, CONTRACT, unknownValue, utils } from './index' +import { API_MICROCODE, metaDoNothing } from './api' + +interface CPU_MICROCODE { + name: string + regex: RegExp + execute (ContractState: CONTRACT, regexParts: RegExpExecArray) : boolean|null +} + +function metaUnknowAndRevokeVariableAtOne (ContractState: CONTRACT, regexParts: RegExpExecArray) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + ContractState.unknownAndRevoke(variable1) + return true +} +function metaReset (ContractState: CONTRACT) : boolean|null { + ContractState.unknownMemory(false, false) + return true +} + +export class CPU { + static cpuMicrocode: CPU_MICROCODE[] = [ + { + name: 'blank', + regex: /^\s*$/, + execute: metaDoNothing + }, + { + name: 'label', + regex: /^\s*(\w+):\s*$/, + execute (ContractState, regexParts) { + if (regexParts[1].startsWith('__opt_') || regexParts[1].startsWith('__GNT_')) { + // Optimization with __opt just swap to RET or FIN, so no destruction inside this branch + // Same with __GNT_ that is a simple loop of getNextTx(). + return false + } + return metaReset(ContractState) + } + }, + { + name: 'comment', + regex: /^\s*\^comment\s+(.*)/, + execute: metaDoNothing + }, + { + name: 'declare', + regex: /^\s*\^declare\s+(\w+)\s*$/, + execute (ContractState, regexParts) { + if (ContractState.Memory.find(mem => mem.varName === regexParts[1]) === undefined) { + let value = unknownValue + if (/^n\d+$/.test(regexParts[1])) { + value = BigInt(regexParts[1].substring(1)) + } + ContractState.Memory.push({ varName: regexParts[1], value, shadow: '' }) + } + return false + } + }, + { + name: 'const', + regex: /^\s*\^const\s+SET\s+@(\w+)\s+#([\da-f]{16})\b\s*$/, + execute: metaDoNothing + }, + { + name: 'program', + regex: /^\s*\^program\s+(\w+)\s+([\s\S]+)$/, + execute: metaDoNothing + }, + { + name: 'SET_VAL', + regex: /^\s*SET\s+@(\w+)\s+#([\da-f]{16})\b\s*$/, + execute (ContractState, regexParts) { + const variable = ContractState.getMemoryByName(regexParts[1]) + const val = BigInt('0x' + regexParts[2]) + if (variable.value !== unknownValue && variable.value === val) { + // Optimize set val same content + return null + } + variable.value = BigInt('0x' + regexParts[2]) + variable.shadow = '' + ContractState.revokeShadow(variable.varName) + return true + } + }, + { + name: 'SET_DAT', + regex: /^\s*SET\s+@(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + const variable2 = ContractState.getMemoryByName(regexParts[2]) + if (variable1.value !== unknownValue && variable1.value === variable2.value) { + // Optimize: same content + return null + } + if (variable1.shadow === variable2.varName) { + // Optimize: Same variable + return null + } + ContractState.setAndRevoke(variable1, variable2) + return true + } + }, + { + name: 'CLR', + regex: /^\s*CLR\s+@(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + if (variable1.value === 0n) { + // Optimize setting zero to something with zero + return null + } + ContractState.zeroAndRevoke(variable1) + return true + } + }, + { + name: 'INC', + regex: /^\s*INC\s+@(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + if (variable1.value !== unknownValue) { + variable1.value = (variable1.value + 1n) % 18446744073709551616n + } + variable1.shadow = '' + ContractState.revokeShadow(variable1.varName) + return true + } + }, + { + name: 'DEC', + regex: /^\s*DEC\s+@(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + if (variable1.value !== unknownValue) { + if (variable1.value === 0n) { + variable1.value = Constants.minus1 + } else { + variable1.value = variable1.value - 1n + } + } + variable1.shadow = '' + ContractState.revokeShadow(variable1.varName) + return true + } + }, + { + name: 'ADD', + regex: /^\s*ADD\s+@(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + const variable2 = ContractState.getMemoryByName(regexParts[2]) + if (variable2.value === 0n) { + // Optimize adding zero + return null + } + if (variable1.value !== unknownValue && variable2.value !== unknownValue) { + variable1.value = (variable1.value + variable2.value) % Constants.pow2to64 + } + variable1.shadow = '' + ContractState.revokeShadow(variable1.varName) + return true + } + }, + { + name: 'SUB', + regex: /^\s*SUB\s+@(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + const variable2 = ContractState.getMemoryByName(regexParts[2]) + if (variable2.value === 0n) { + // Optimize subtracting zero + return null + } + if (variable1.value !== unknownValue && variable2.value !== unknownValue) { + variable1.value = (Constants.pow2to64 + variable1.value - variable2.value) % Constants.pow2to64 + } + variable1.shadow = '' + ContractState.revokeShadow(variable1.varName) + return true + } + }, + { + name: 'MUL', + regex: /^\s*MUL\s+@(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + const variable2 = ContractState.getMemoryByName(regexParts[2]) + if (variable2.value === 1n) { + // Optimize multiply by one + return null + } + if (variable1.value !== unknownValue && variable2.value !== unknownValue) { + variable1.value = (variable1.value * variable2.value) % Constants.pow2to64 + } + variable1.shadow = '' + ContractState.revokeShadow(variable1.varName) + return true + } + }, + { + name: 'DIV', + regex: /^\s*DIV\s+@(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + const variable2 = ContractState.getMemoryByName(regexParts[2]) + if (variable2.value === 1n) { + // Optimize divide by one + return null + } + if (variable1.value !== unknownValue && variable2.value !== unknownValue) { + const val1 = utils.unsigned2signed(variable1.value) + const val2 = utils.unsigned2signed(variable2.value) + + variable1.value = utils.signed2unsigned(val1 / val2) + } + variable1.shadow = '' + ContractState.revokeShadow(variable1.varName) + return true + } + }, + { + name: 'BOR / AND / XOR', + regex: /^\s*(?:BOR|AND|XOR)\s+@(\w+)\s+\$(\w+)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'NOT', + regex: /^\s*NOT\s+@(\w+)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'SET_IND', + regex: /^\s*SET\s+@(\w+)\s+\$\(\$(\w+)\)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'SET_IDX', + regex: /^\s*SET\s+@(\w+)\s+\$\(\$(\w+)\s*\+\s*\$(\w+)\)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'PSH', + regex: /^\s*PSH\s+\$(\w+)\s*$/, + execute: metaDoNothing + }, + { + name: 'POP', + regex: /^\s*POP\s+@(\w+)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'JMP_SUB', + regex: /^\s*JSR\s+:(\w+)\s*$/, + execute: metaReset + }, + { + name: 'RET_SUB', + regex: /^\s*RET\s*$/, + execute: metaDoNothing + }, + { + name: 'IND_DAT', + regex: /^\s*SET\s+@\(\$(\w+)\)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const variable1 = ContractState.getMemoryByName(regexParts[1]) + const variable2 = ContractState.getMemoryByName(regexParts[2]) + const addr = Number(variable1.value) + if (variable1.value !== unknownValue) { + ContractState.Memory[addr].value = variable2.value + ContractState.Memory[addr].shadow = variable2.varName + return true + } + // It is not possible to know which variable was updated. Unknow to all memory variable. + ContractState.unknownMemory(true, true) + return true + } + }, + { + name: 'IDX_DAT', + regex: /^\s*SET\s+@\(\$(\w+)\s*\+\s*\$(\w+)\)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + // It is not possible to know which variable was updated. Unknow to all memory variable. + ContractState.unknownMemory(true, true) + return true + } + }, + { + name: 'MOD_DAT', + regex: /^\s*MOD\s+@(\w+)\s+\$(\w+)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'SHL / SHR', + regex: /^\s*(?:SHL|SHR)\s+@(\w+)\s+\$(\w+)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'POW_DAT', + regex: /^\s*POW\s+@(\w+)\s+\$(\w+)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'JMP_ADR', + regex: /^\s*JMP\s+:(\w+)\s*$/, + execute: metaDoNothing + }, + { + name: 'BZR / BNZ', + regex: /^\s*(BZR|BNZ)\s+\$(\w+)\s+:(\w+)\s*$/, + execute: metaDoNothing + }, + { + name: 'BGT / BLT / BGE / BLE / BEQ / BNE', + regex: /^\s*(BGT|BLT|BGE|BLE|BEQ|BNE)\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/, + execute: metaDoNothing + }, + { + name: 'SLP_DAT', + regex: /^\s*SLP\s+\$(\w+)\s*$/, + execute: metaDoNothing + }, + { + name: 'SLP_IMD', + regex: /^\s*SLP\s*$/, + execute: metaDoNothing + }, + { + name: 'FIZ_DAT', + regex: /^\s*FIZ\s+\$(\w+)\s*$/, + execute: metaDoNothing + }, + { + name: 'STZ_DAT', + regex: /^\s*STZ\s+\$(\w+)\s*$/, + execute: metaDoNothing + }, + { + name: 'FIN_IMD', + regex: /^\s*FIN\s*$/, + execute: metaDoNothing + }, + { + name: 'STP_IMD', + regex: /^\s*STP\s*$/, + execute: metaDoNothing + }, + { + name: 'ERR_ADR', + regex: /^\s*ERR\s+:(\w+)\s*$/, + execute: metaDoNothing + }, + { + name: 'SET_PCS', + regex: /^\s*PCS\s*$/, + execute: metaReset + }, + { + name: 'MDV_DAT', + regex: /^\s*MDV\s+@(\w+)\s+\$(\w+)\s+\$(\w+)\s*$/, + execute: metaUnknowAndRevokeVariableAtOne + }, + { + name: 'EXT_FUN', + regex: /^\s*FUN\s+(\w+)\s*$/, + execute (ContractState, regexParts) { + const Api = API_MICROCODE.EXT_FUN.find(Obj => Obj.funName === regexParts[1]) + if (Api === undefined) { + throw new Error(`Unknow API Function ${regexParts[1]}`) + } + return Api.execute(ContractState) + } + }, + { + name: 'EXT_FUN_DAT', + regex: /^\s*FUN\s+(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const Api = API_MICROCODE.EXT_FUN_DAT.find(Obj => Obj.funName === regexParts[1]) + if (Api === undefined) { + throw new Error(`Unknow API Function ${regexParts[1]}`) + } + const variable1 = ContractState.getMemoryByName(regexParts[2]) + return Api.execute(ContractState, variable1) + } + }, + { + name: 'EXT_FUN_DAT_2', + regex: /^\s*FUN\s+(\w+)\s+\$(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const Api = API_MICROCODE.EXT_FUN_DAT_2.find(Obj => Obj.funName === regexParts[1]) + if (Api === undefined) { + throw new Error(`Unknow API Function ${regexParts[1]}`) + } + const variable1 = ContractState.getMemoryByName(regexParts[2]) + const variable2 = ContractState.getMemoryByName(regexParts[3]) + return Api.execute(ContractState, variable1, variable2) + } + }, + { + name: 'EXT_FUN_RET', + regex: /^\s*FUN\s+@(\w+)\s+(\w+)\s*$/, + execute (ContractState, regexParts) { + const Api = API_MICROCODE.EXT_FUN_RET.find(Obj => Obj.funName === regexParts[2]) + if (Api === undefined) { + throw new Error(`Unknow API Function ${regexParts[1]}`) + } + const retVar = ContractState.getMemoryByName(regexParts[1]) + return Api.execute(ContractState, retVar) + } + }, + { + name: 'EXT_FUN_RET_DAT', + regex: /^\s*FUN\s+@(\w+)\s+(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + throw new Error(`Unknow API Function ${regexParts[2]}`) + } + }, + { + name: 'EXT_FUN_RET_DAT_2', + regex: /^\s*FUN\s+@(\w+)\s+(\w+)\s+\$(\w+)\s+\$(\w+)\s*$/, + execute (ContractState, regexParts) { + const Api = API_MICROCODE.EXT_FUN_RET_DAT_2.find(Obj => Obj.funName === regexParts[2]) + if (Api === undefined) { + throw new Error(`Unknow API Function ${regexParts[2]}`) + } + const retVar = ContractState.getMemoryByName(regexParts[1]) + const variable1 = ContractState.getMemoryByName(regexParts[3]) + const variable2 = ContractState.getMemoryByName(regexParts[4]) + return Api.execute(ContractState, retVar, variable1, variable2) + } + }, + { + name: 'NOP', + regex: /^\s*NOP\s*$/, + execute: metaDoNothing + } + ] + + /** Process one line of assembly code. + * @returns true or false for no optimization + * null if line can be optimized (deleted) */ + static cpu (ContractState: CONTRACT, line: number) { + const currLine = ContractState.asmCodeArr[line] + + const InstructionObj = this.cpuMicrocode.find(currCode => { + if (currCode.regex.exec(currLine) === null) { + return false + } + return true + }) + + if (InstructionObj === undefined) { + throw new Error(`Wrong line of code: '${currLine}'`) + } + const currParts = InstructionObj.regex.exec(currLine) + if (currParts === null) { + throw new Error(`Wrong line of code: '${currLine}'`) + } + + return InstructionObj.execute(ContractState, currParts) + } + + /** + * Loop all lines colecting assembly directives + * + * @param {CONTRACT} ContractState - Contract to execute function + * */ + static cpuDeploy (ContractState: CONTRACT) { + let lastExecResult: RegExpExecArray | null, currCode: CPU_MICROCODE | undefined + + ContractState.asmCodeArr.forEach(line => { + if (/^\s*\^.*/.exec(line) !== null) { + // visit all compiler directives to deploy contract + currCode = CPU.cpuMicrocode.find(instr => { + lastExecResult = instr.regex.exec(line) + if (lastExecResult === null) { + return false + } + return true + }) + if (currCode !== undefined && lastExecResult !== null) { + currCode.execute(ContractState, lastExecResult) + } + } + }) + } +} diff --git a/src/codeGenerator/assemblyProcessor/optimizerVM/index.ts b/src/codeGenerator/assemblyProcessor/optimizerVM/index.ts new file mode 100644 index 0000000..d207782 --- /dev/null +++ b/src/codeGenerator/assemblyProcessor/optimizerVM/index.ts @@ -0,0 +1,163 @@ +import { CPU } from './cpu' + +export const unknownValue = -1n + +// TODO: Add stack inspection. It will be needed to save the line of the PUSH, +// so optimizing POP also delete the respective PUSH. +/** + * Object for memory entries + * + * @member {string} varName Variable name defined in assembly + * @member {bigint} value Variable value (64-bit unsigned) + * @member {string} shadow If content is the same as other variable, + * this will have the other variable name. + */ +export interface MemoryObj { + varName: string + value: bigint + shadow: string +} + +export const Constants = { + // do not change these + minus1: 18446744073709551615n, // -1 in 64-bit unsigned + pow2to64: 18446744073709551616n, // 2^64 + maxPositive: 9223372036854775807n, // (2^63)-1 + numberMaxPositive: 9223372036854775000 +} + +export const utils = { + unsigned2signed (unsigned: bigint): bigint { + unsigned %= 1n << 64n + if (unsigned >= (1n << 63n)) { + return unsigned - (1n << 64n) + } + return unsigned + }, + + signed2unsigned (signed: bigint): bigint { + signed %= 18446744073709551616n + if (signed < 0) { + return signed + 18446744073709551616n + } + return signed + } +} + +export class CONTRACT { + Memory: MemoryObj[] + asmCodeArr: string[] + + constructor (asmCode: string[]) { + this.Memory = [] + this.asmCodeArr = asmCode + CPU.cpuDeploy(this) + this.Memory.push( + { varName: 'A1', value: unknownValue, shadow: '' }, + { varName: 'A2', value: unknownValue, shadow: '' }, + { varName: 'A3', value: unknownValue, shadow: '' }, + { varName: 'A4', value: unknownValue, shadow: '' }, + { varName: 'B1', value: unknownValue, shadow: '' }, + { varName: 'B2', value: unknownValue, shadow: '' }, + { varName: 'B3', value: unknownValue, shadow: '' }, + { varName: 'B4', value: unknownValue, shadow: '' }) + } + + unknownMemory (keepRegisters: boolean, keepSuperRegisters: boolean) : void { + this.Memory.forEach((Mem) => { + if (keepRegisters && /^r\d$/.test(Mem.varName)) { + return + } + if (keepSuperRegisters && /^[AB][1234]$/.test(Mem.varName)) { + return + } + if (/^n\d+$/.test(Mem.varName)) { + Mem.value = BigInt(Mem.varName.substring(1)) + Mem.shadow = '' + return + } + Mem.value = unknownValue + Mem.shadow = '' + }) + } + + unknownSuperRegisterA () : void { + this.Memory.forEach((Mem) => { + if (/^[A][1234]$/.test(Mem.varName)) { + Mem.value = unknownValue + Mem.shadow = '' + return + } + if (/^[A][1234]$/.test(Mem.shadow)) { + Mem.shadow = '' + } + }) + } + + unknownSuperRegisterB () : void { + this.Memory.forEach((Mem) => { + if (/^[B][1234]$/.test(Mem.varName)) { + Mem.value = unknownValue + Mem.shadow = '' + return + } + if (/^[B][1234]$/.test(Mem.shadow)) { + Mem.shadow = '' + } + }) + } + + getMemoryByName (name: string): MemoryObj { + const RetObj = this.Memory.find(Mem => Mem.varName === name) + if (RetObj === undefined) { + throw new Error(`optimizerVM: getMemoryByName: Variable '${name}' not declared.`) + } + return RetObj + } + + revokeShadow (changedVar: string) : void { + this.Memory.forEach((Mem) => { + if (Mem.shadow === changedVar) { + Mem.shadow = '' + } + }) + } + + unknownAndRevoke (Var: MemoryObj) : void { + Var.value = unknownValue + Var.shadow = '' + this.revokeShadow(Var.varName) + } + + setAndRevoke (AssignedVar: MemoryObj, Variable: MemoryObj) : void { + AssignedVar.value = Variable.value + AssignedVar.shadow = Variable.varName + this.revokeShadow(AssignedVar.varName) + Variable.shadow = AssignedVar.varName + } + + zeroAndRevoke (Var: MemoryObj) : void { + if (Var.value !== 0n) { + Var.value = 0n + Var.shadow = '' + this.revokeShadow(Var.varName) + } + } + + /** + * Runs only one instruction (step into) + * + * @return string indicating error/status. Empty string on success. + */ + optimize (): string[] { + let cpuExitCode: boolean|null + + this.asmCodeArr.forEach((code, line) => { + cpuExitCode = CPU.cpu(this, line) + if (cpuExitCode === null) { + this.asmCodeArr[line] = 'DELETE' + } + }) + return this.asmCodeArr.filter(code => code !== 'DELETE') + } +} diff --git a/src/codeGenerator/assemblyProcessor/typeCastingToAsm.ts b/src/codeGenerator/assemblyProcessor/typeCastingToAsm.ts new file mode 100644 index 0000000..f56a063 --- /dev/null +++ b/src/codeGenerator/assemblyProcessor/typeCastingToAsm.ts @@ -0,0 +1,119 @@ +import { DECLARATION_TYPES } from '../../typings/syntaxTypes' +import { GENCODE_AUXVARS, GENCODE_SOLVED_OBJECT } from '../codeGeneratorTypes' +import utils from '../utils' +import { createSimpleInstruction, toRegister } from './createInstruction' + +export function typeCasting ( + AuxVars: GENCODE_AUXVARS, InSolved: GENCODE_SOLVED_OBJECT, toType: DECLARATION_TYPES, line: number +) : GENCODE_SOLVED_OBJECT { + const fromType = utils.getDeclarationFromMemory(InSolved.SolvedMem) + if (fromType === toType) { + return InSolved + } + + function typeCastingMain () : GENCODE_SOLVED_OBJECT { + switch (toType) { + case 'void': + // From anything to void + AuxVars.freeRegister(InSolved.SolvedMem.address) + InSolved.SolvedMem = utils.createVoidMemObj() + return InSolved + case 'long': + return toLong() + case 'fixed': + return toFixed() + case 'void_ptr': + case 'long_ptr': + case 'fixed_ptr': + case 'struct_ptr': + return toPointer() + case 'struct': + default: + throw new Error('Internal error') + } + } + + function toFixed () : GENCODE_SOLVED_OBJECT { + switch (fromType) { + case 'long': + // From long to fixed + InSolved = toRegister(AuxVars, InSolved, line) + InSolved.asmCode += createSimpleInstruction('LongToFixed', InSolved.SolvedMem.asmName) + utils.setMemoryDeclaration(InSolved.SolvedMem, 'fixed') + return InSolved + case 'void': + // From void to fixed + InSolved.SolvedMem = AuxVars.getNewRegister(line) + InSolved.asmCode += `CLR @${InSolved.SolvedMem.asmName}\n` + utils.setMemoryDeclaration(InSolved.SolvedMem, 'fixed') + return InSolved + case 'struct': + case 'void_ptr': + case 'long_ptr': + case 'fixed_ptr': + case 'struct_ptr': + throw new Error(`At line: ${line}. It is not possible to cast ${fromType} to fixed number.`) + default: + throw new Error('Internal error') + } + } + + function toLong () : GENCODE_SOLVED_OBJECT { + switch (fromType) { + case 'void': + // From void to long + InSolved.SolvedMem = AuxVars.getNewRegister(line) + InSolved.asmCode += `CLR @${InSolved.SolvedMem.asmName}\n` + utils.setMemoryDeclaration(InSolved.SolvedMem, 'long') + return InSolved + case 'fixed': + InSolved = toRegister(AuxVars, InSolved, line) + InSolved.asmCode += createSimpleInstruction('FixedToLong', InSolved.SolvedMem.asmName) + utils.setMemoryDeclaration(InSolved.SolvedMem, 'long') + return InSolved + case 'struct': + throw new Error(`At line: ${line}. It is not possible to cast ${fromType} to long number.`) + case 'void_ptr': + case 'long_ptr': + case 'fixed_ptr': + case 'struct_ptr': + utils.setMemoryDeclaration(InSolved.SolvedMem, 'long') + return InSolved + default: + throw new Error('Internal error') + } + } + + function toPointer () : GENCODE_SOLVED_OBJECT { + switch (fromType) { + case 'void': + // From void to pointer (NULL) + InSolved.SolvedMem = utils.createConstantMemObj(0) + utils.setMemoryDeclaration(InSolved.SolvedMem, toType) + return InSolved + case 'long': + // From long to any pointer + utils.setMemoryDeclaration(InSolved.SolvedMem, toType) + return InSolved + case 'fixed': + // From fixed to any pointer + throw new Error(`At line: ${line}. It is not possible to cast ${fromType} to a pointer type.`) + case 'struct': + // From struct to pointer (address of first value in struct) + InSolved.SolvedMem = utils.createConstantMemObj(InSolved.SolvedMem.hexContent) + utils.setMemoryDeclaration(InSolved.SolvedMem, toType) + return InSolved + case 'void_ptr': + case 'long_ptr': + case 'fixed_ptr': + case 'struct_ptr': + // From any pointer to any pointer + utils.setMemoryDeclaration(InSolved.SolvedMem, toType) + return InSolved + default: + throw new Error('Internal error') + } + } + + return typeCastingMain() +} diff --git a/src/codeGenerator/astProcessor/binaryAsnProcessor.ts b/src/codeGenerator/astProcessor/binaryAsnProcessor.ts index 721a99b..7eb604f 100644 --- a/src/codeGenerator/astProcessor/binaryAsnProcessor.ts +++ b/src/codeGenerator/astProcessor/binaryAsnProcessor.ts @@ -1,7 +1,8 @@ import { deepCopy } from '../../repository/repository' import { CONTRACT } from '../../typings/contractTypes' import { MEMORY_SLOT, DECLARATION_TYPES, BINARY_ASN } from '../../typings/syntaxTypes' -import { createSimpleInstruction, createInstruction, setConstAsmCode } from '../assemblyProcessor/createInstruction' +import { createSimpleInstruction, createInstruction, setConstAsmCode, toRegister } from '../assemblyProcessor/createInstruction' +import { typeCasting } from '../assemblyProcessor/typeCastingToAsm' import { GENCODE_AUXVARS, GENCODE_ARGS, GENCODE_SOLVED_OBJECT } from '../codeGeneratorTypes' import utils from '../utils' import genCode from './genCode' @@ -50,11 +51,9 @@ export default function binaryAsnProcessor ( jumpFalse: ScopeInfo.jumpFalse, jumpTrue: ScopeInfo.jumpTrue }) - LGenObj.asmCode += RGenObj.asmCode - LGenObj.asmCode += AuxVars.getPostOperations() - // Note: RGenObj always have MemObj, because jumpTarget is undefined. - AuxVars.freeRegister(RGenObj.SolvedMem.address) - return LGenObj + RGenObj.asmCode = LGenObj.asmCode + RGenObj.asmCode + AuxVars.getPostOperations() + AuxVars.freeRegister(LGenObj.SolvedMem.address) + return RGenObj } function operatorProc () : GENCODE_SOLVED_OBJECT { @@ -65,7 +64,6 @@ export default function binaryAsnProcessor ( jumpFalse: ScopeInfo.jumpFalse, jumpTrue: ScopeInfo.jumpTrue }) - assemblyCode += LGenObj.asmCode let RGenObj = genCode(Program, AuxVars, { RemAST: CurrentNode.Right, logicalOp: false, @@ -73,7 +71,7 @@ export default function binaryAsnProcessor ( jumpFalse: ScopeInfo.jumpFalse, jumpTrue: ScopeInfo.jumpTrue }) - assemblyCode += RGenObj.asmCode + LGenObj.asmCode += RGenObj.asmCode // Error handling if (LGenObj.SolvedMem.type === 'void' || RGenObj.SolvedMem.type === 'void') { throw new Error(`At line: ${CurrentNode.Operation.line}. Can not make operations with void values.`) @@ -85,57 +83,61 @@ export default function binaryAsnProcessor ( CurrentNode.Operation.value ) if (PossibleRetObj) { - return { SolvedMem: PossibleRetObj, asmCode: assemblyCode } + return { SolvedMem: PossibleRetObj, asmCode: LGenObj.asmCode } } // Optimizations 2 if (OperatorOptBySwap(LGenObj.SolvedMem, RGenObj.SolvedMem, CurrentNode.Operation.value)) { const Temp = RGenObj RGenObj = LGenObj LGenObj = Temp + LGenObj.asmCode = RGenObj.asmCode } + const leftDeclaration = utils.getDeclarationFromMemory(LGenObj.SolvedMem) + const rightDeclaration = utils.getDeclarationFromMemory(RGenObj.SolvedMem) // Prepare return object - let TmpMemObj: MEMORY_SLOT - if (LGenObj.SolvedMem.type !== 'register') { - TmpMemObj = AuxVars.getNewRegister() - TmpMemObj.declaration = utils.getDeclarationFromMemory(LGenObj.SolvedMem) - assemblyCode += createInstruction(AuxVars, utils.genAssignmentToken(CurrentNode.Operation.line), TmpMemObj, LGenObj.SolvedMem) - AuxVars.freeRegister(LGenObj.SolvedMem.address) - } else { - TmpMemObj = LGenObj.SolvedMem - } - // Pointer verifications 1 - if (utils.getDeclarationFromMemory(RGenObj.SolvedMem).includes('_ptr') && - !TmpMemObj.declaration.includes('_ptr')) { - // Operation with pointers - TmpMemObj.declaration += '_ptr' - } - // Pointer verifications 2 - if (TmpMemObj.declaration.includes('_ptr')) { - if (CurrentNode.Operation.value !== '+' && CurrentNode.Operation.value !== '-') { - throw new Error(`At line: ${CurrentNode.Operation.line}. ` + - "Operation not allowed on pointers. Only '+', '-', '++' and '--' are.") + LGenObj = toRegister(AuxVars, LGenObj, CurrentNode.Operation.line) + // implicit type casting tests and operations + if (isPointerAddOrSub(CurrentNode.Operation.value, leftDeclaration, rightDeclaration)) { + utils.setMemoryDeclaration(LGenObj.SolvedMem, rightDeclaration) + } + const castSide = implicitTypeCastingTest(CurrentNode.Operation.value, leftDeclaration, rightDeclaration) + switch (castSide) { + case 'left': + if (!(leftDeclaration.endsWith('_ptr') && rightDeclaration.endsWith('_ptr'))) { + AuxVars.warnings.push(`Warning: at line ${CurrentNode.Operation.line}. Implicit type casting conversion on left side of operator '${CurrentNode.Operation.value}'.`) } + LGenObj = typeCasting(AuxVars, LGenObj, rightDeclaration, CurrentNode.Operation.line) + break + case 'right': { + if (!(leftDeclaration.endsWith('_ptr') && rightDeclaration.endsWith('_ptr'))) { + AuxVars.warnings.push(`Warning: at line ${CurrentNode.Operation.line}. Implicit type casting conversion on right side of operator '${CurrentNode.Operation.value}'.`) + } + const oldAsm = RGenObj.asmCode + RGenObj = typeCasting(AuxVars, RGenObj, leftDeclaration, CurrentNode.Operation.line) + // append only the diff + LGenObj.asmCode += RGenObj.asmCode.slice(oldAsm.length) + } } // Create instruction - assemblyCode += createInstruction(AuxVars, CurrentNode.Operation, TmpMemObj, RGenObj.SolvedMem) + LGenObj.asmCode += createInstruction(AuxVars, CurrentNode.Operation, LGenObj.SolvedMem, RGenObj.SolvedMem) // Handle logical operation if (ScopeInfo.logicalOp === true) { - assemblyCode += createInstruction( + LGenObj.asmCode += createInstruction( AuxVars, utils.genNotEqualToken(), - TmpMemObj, + LGenObj.SolvedMem, utils.createConstantMemObj(0), ScopeInfo.revLogic, ScopeInfo.jumpFalse, ScopeInfo.jumpTrue ) AuxVars.freeRegister(RGenObj.SolvedMem.address) - AuxVars.freeRegister(TmpMemObj.address) - return { SolvedMem: utils.createVoidMemObj(), asmCode: assemblyCode } + AuxVars.freeRegister(LGenObj.SolvedMem.address) + return { SolvedMem: utils.createVoidMemObj(), asmCode: LGenObj.asmCode } } // Return arithmetic result AuxVars.freeRegister(RGenObj.SolvedMem.address) - return { SolvedMem: TmpMemObj, asmCode: assemblyCode } + return LGenObj } function assignmentProc () : GENCODE_SOLVED_OBJECT { @@ -149,7 +151,6 @@ export default function binaryAsnProcessor ( jumpFalse: ScopeInfo.jumpFalse, jumpTrue: ScopeInfo.jumpTrue }) - assemblyCode += LGenObj.asmCode AuxVars.isLeftSideOfAssignment = false assignmentLeftSideErrorTests(LGenObj.SolvedMem) // Clear isDeclaration before right side evaluation. @@ -161,13 +162,16 @@ export default function binaryAsnProcessor ( // If it is an array item we know, change to the item if (LGenObj.SolvedMem.type === 'array' && LGenObj.SolvedMem.Offset?.type === 'constant') { + if (LGenObj.SolvedMem.ArrayItem !== undefined && LGenObj.SolvedMem.ArrayItem.totalSize <= LGenObj.SolvedMem.Offset.value + 1) { + throw new Error(`At line ${CurrentNode.Operation.line}. ` + + 'Array index is outside array size.') + } LGenObj.SolvedMem = AuxVars.getMemoryObjectByLocation( - utils.addHexContents(LGenObj.SolvedMem.hexContent, LGenObj.SolvedMem.Offset.value) + utils.addHexSimple(LGenObj.SolvedMem.hexContent, LGenObj.SolvedMem.Offset.value) ) } // Get right side gencode object - const RGenObj = assignmentRightSideSolver(LGenObj.SolvedMem) - assemblyCode += RGenObj.asmCode + let RGenObj = assignmentRightSideSolver(LGenObj.SolvedMem) // Restore isDeclaration value AuxVars.isDeclaration = savedDeclaration // Error check for Right side @@ -175,26 +179,32 @@ export default function binaryAsnProcessor ( throw new Error(`At line: ${CurrentNode.Operation.line}. ` + `Invalid right value for '${CurrentNode.Operation.type}'. Possible void value.`) } - // Check declaration types - assignmentDeclarationTests( - LGenObj.SolvedMem, - RGenObj.SolvedMem, - CurrentNode.Operation.value, - CurrentNode.Operation.line - ) + // Implicit type casting on assignment + const lDecl = utils.getDeclarationFromMemory(LGenObj.SolvedMem) + const rDecl = utils.getDeclarationFromMemory(RGenObj.SolvedMem) + const castSide = implicitTypeCastingTest(CurrentNode.Operation.value, lDecl, rDecl) + switch (castSide) { + case 'left': + throw new Error('Internal error') + case 'right': + if (!(lDecl.endsWith('_ptr') && rDecl.endsWith('_ptr'))) { + AuxVars.warnings.push(`Warning: at line ${CurrentNode.Operation.line}. Implicit type casting conversion on right side of assignment '='.`) + } + RGenObj = typeCasting(AuxVars, RGenObj, lDecl, CurrentNode.Operation.line) + } // Create instruction - assemblyCode += createInstruction(AuxVars, CurrentNode.Operation, LGenObj.SolvedMem, RGenObj.SolvedMem) + LGenObj.asmCode += RGenObj.asmCode + createInstruction(AuxVars, CurrentNode.Operation, LGenObj.SolvedMem, RGenObj.SolvedMem) // Process use of 'const' keyword if (AuxVars.isConstSentence === true) { return assignmentConstSolver( LGenObj.SolvedMem, RGenObj.SolvedMem, - assemblyCode, + LGenObj.asmCode, CurrentNode.Operation.line ) } AuxVars.freeRegister(RGenObj.SolvedMem.address) - return { SolvedMem: LGenObj.SolvedMem, asmCode: assemblyCode } + return LGenObj } function assignmentStartErrorTests () : void { @@ -232,10 +242,11 @@ export default function binaryAsnProcessor ( } } + /** Checks if left side can be reused and solve right side of assigment */ function assignmentRightSideSolver (Left: MEMORY_SLOT) : GENCODE_SOLVED_OBJECT { if (CurrentNode.Operation.type !== 'Assignment' || Program.Config.reuseAssignedVar === false || - Left.type !== 'long' || + (Left.type !== 'long' && Left.type !== 'fixed') || Left.Offset !== undefined || !utils.findVarNameInAst(Left.name, CurrentNode.Right)) { // Can not reuse assigned var. @@ -250,7 +261,7 @@ export default function binaryAsnProcessor ( const registerInitialState = deepCopy(AuxVars.registerInfo) const NewRegister: MEMORY_SLOT = deepCopy(Left) NewRegister.type = 'register' - NewRegister.declaration = 'long' + NewRegister.declaration = Left.declaration AuxVars.registerInfo.unshift({ inUse: false, Template: NewRegister @@ -262,10 +273,10 @@ export default function binaryAsnProcessor ( jumpFalse: ScopeInfo.jumpFalse, jumpTrue: ScopeInfo.jumpTrue }) - AuxVars.registerInfo.shift() const registerFinalState = deepCopy(AuxVars.registerInfo) // if returning var is not the reused one, put it in that returning location and solve right side again if (RetGenObj.SolvedMem.address !== Left.address && AuxVars.isTemp(RetGenObj.SolvedMem.address)) { + AuxVars.registerInfo.shift() const index = RetGenObj.SolvedMem.address + 1 AuxVars.registerInfo = registerInitialState AuxVars.registerInfo.splice(index, 0, { inUse: false, Template: NewRegister }) @@ -289,22 +300,145 @@ export default function binaryAsnProcessor ( return RetGenObj } - function assignmentDeclarationTests ( - Left: MEMORY_SLOT, Right: MEMORY_SLOT, operVal: string, line: number - ) : void { - if (utils.isNotValidDeclarationOp(utils.getDeclarationFromMemory(Left), Right)) { - const lDecl = utils.getDeclarationFromMemory(Left) - const rDecl = utils.getDeclarationFromMemory(Right) - // Allow SetOperator and pointer operation - if (!(lDecl === rDecl + '_ptr' && (operVal === '+=' || operVal === '-='))) { - if (Program.Config.warningToError) { - throw new Error(`At line: ${line}.` + - ' Warning: Left and right values does not match.' + - ` Values are: '${Left.declaration}' and '${Right.declaration}'.`) - } - // Override declaration protection rules - Left.declaration = Right.declaration + /** Tests if implicit type casting is needed and also checks valid operations for binary operators. + * @returns the side needed to be changed + * @throws Error if operation is not allowed */ + function implicitTypeCastingTest (operVal: string, lDecl: DECLARATION_TYPES, rDecl: DECLARATION_TYPES) : 'left' | 'right' | 'none' { + if (lDecl === rDecl) { + if (lDecl === 'fixed') { + return fixedFixedImplicitTC(operVal) + } + return 'none' + } + const fixedRet = fixedLongImplicitTC(operVal, lDecl, rDecl) + if (fixedRet !== 'notFixedLong') { + return fixedRet + } + const pointerRet = remainingImplicitTC(operVal, lDecl, rDecl) + return pointerRet + } + + function fixedLongImplicitTC (operVal: string, lDecl: DECLARATION_TYPES, rDecl: DECLARATION_TYPES) : 'left' | 'right' | 'none' | 'notFixedLong' { + let fixedSide: 'left' | 'right' | 'none' = 'none' + if (lDecl === 'long' && rDecl === 'fixed') { + fixedSide = 'right' + } + if (lDecl === 'fixed' && rDecl === 'long') { + fixedSide = 'left' + } + if (fixedSide === 'none') { + return 'notFixedLong' + } + // now there is only one fixed and one long + switch (operVal) { + case '=': + case '+=': + case '-=': + return 'right' + case '+': + case '-': + return fixedSide === 'left' ? 'right' : 'left' + case '/': + case '*': + return fixedSide === 'left' ? 'none' : 'left' + case '%': + case '&': + case '|': + case '^': + case '%=': + case '&=': + case '|=': + case '^=': + throw new Error(`At line ${CurrentNode.Operation.line}. Cannot use operator ${operVal} with fixed type numbers.`) + case '>>': + case '<<': + case '>>=': + case '<<=': + if (fixedSide === 'right') { + throw new Error(`At line ${CurrentNode.Operation.line}. Cannot use operator ${operVal} with fixed type numbers on right side.`) + } + return 'none' + case '/=': + case '*=': + return fixedSide === 'left' ? 'none' : 'right' + case '==': + case '!=': + case '<=': + case '>=': + case '<': + case '>': + return fixedSide === 'left' ? 'right' : 'left' + default: + throw new Error('Internal error') + } + } + + function fixedFixedImplicitTC (operVal: string) : 'left' | 'right' | 'none' { + switch (operVal) { + case '%': + case '&': + case '|': + case '^': + case '%=': + case '&=': + case '|=': + case '^=': + case '>>': + case '<<': + throw new Error(`At line ${CurrentNode.Operation.line}. Cannot use operator ${operVal} with fixed type numbers.`) + case '>>=': + case '<<=': + throw new Error(`At line ${CurrentNode.Operation.line}. Cannot use operator ${operVal} with fixed type numbers on right side.`) + default: + return 'none' + } + } + + function remainingImplicitTC (operVal: string, lDecl: DECLARATION_TYPES, rDecl: DECLARATION_TYPES) : 'left' | 'right' | 'none' { + switch (operVal) { + case '=': + if ((lDecl === 'void_ptr' && rDecl.includes('_ptr')) || + (lDecl.includes('_ptr') && rDecl === 'void_ptr')) { + return 'right' + } + if (lDecl.includes('ptr') && rDecl === 'long' && AuxVars.hasVoidArray) { + return 'none' + } + throw new Error(`At line ${CurrentNode.Operation.line}. Left and right side of assigment does not match. Types are '${lDecl}' and '${rDecl}'.`) + case '+=': + case '-=': + if (lDecl.endsWith('_ptr') && rDecl === 'long') { + return 'none' + } + throw new Error(`At line ${CurrentNode.Operation.line}. Left and right side of ${operVal} does not match. Types are '${lDecl}' and '${rDecl}'.`) + case '+': + case '-': + if (lDecl.includes('_ptr') && rDecl === 'long') { + return 'none' } + if (rDecl.includes('_ptr') && lDecl === 'long') { + return 'none' + } + throw new Error(`At line ${CurrentNode.Operation.line}. Left and right side of ${operVal} does not match. Types are '${lDecl}' and '${rDecl}'.`) + case '==': + case '!=': + case '<=': + case '>=': + case '<': + case '>': + if (lDecl.includes('_ptr') && rDecl.includes('_ptr')) { + return 'right' + } + if (lDecl.includes('_ptr') && rDecl === 'long') { + return 'right' + } + if (lDecl === 'long' && rDecl.includes('_ptr')) { + return 'left' + } + throw new Error(`At line ${CurrentNode.Operation.line}. Left and right side of ${operVal} does not match. Types are '${lDecl}' and '${rDecl}'.`) + default: + // / * % & | ^ %= &= |= ^= >> << >>= <<= /= *= + throw new Error(`At line ${CurrentNode.Operation.line}. Left and right side of ${operVal} does not match. Types are '${lDecl}' and '${rDecl}'.`) } } @@ -428,18 +562,33 @@ export default function binaryAsnProcessor ( } function defaultLogicalOpProc () : GENCODE_SOLVED_OBJECT { - const LGenObj = genCode(Program, AuxVars, { + let LGenObj = genCode(Program, AuxVars, { RemAST: CurrentNode.Left, logicalOp: false, revLogic: ScopeInfo.revLogic }) // ScopeInfo.jumpFalse and ScopeInfo.jumpTrue must be undefined to evaluate expressions - assemblyCode += LGenObj.asmCode - const RGenObj = genCode(Program, AuxVars, { + let RGenObj = genCode(Program, AuxVars, { RemAST: CurrentNode.Right, logicalOp: false, revLogic: ScopeInfo.revLogic }) // ScopeInfo.jumpFalse and ScopeInfo.jumpTrue must be undefined to evaluate expressions - assemblyCode += RGenObj.asmCode + const leftDeclaration = utils.getDeclarationFromMemory(LGenObj.SolvedMem) + const rightDeclaration = utils.getDeclarationFromMemory(RGenObj.SolvedMem) + const castSide = implicitTypeCastingTest(CurrentNode.Operation.value, leftDeclaration, rightDeclaration) + switch (castSide) { + case 'left': + if (!(leftDeclaration.endsWith('_ptr') && rightDeclaration.endsWith('_ptr'))) { + AuxVars.warnings.push(`Warning: at line ${CurrentNode.Operation.line}. Implicit type casting conversion on left side of comparision '${CurrentNode.Operation.value}'.`) + } + LGenObj = typeCasting(AuxVars, LGenObj, rightDeclaration, CurrentNode.Operation.line) + break + case 'right': + if (!(leftDeclaration.endsWith('_ptr') && rightDeclaration.endsWith('_ptr'))) { + AuxVars.warnings.push(`Warning: at line ${CurrentNode.Operation.line}. Implicit type casting conversion on right side of comparision '${CurrentNode.Operation.value}'.`) + } + RGenObj = typeCasting(AuxVars, RGenObj, leftDeclaration, CurrentNode.Operation.line) + } + assemblyCode = LGenObj.asmCode + RGenObj.asmCode assemblyCode += createInstruction( AuxVars, CurrentNode.Operation, @@ -459,15 +608,17 @@ export default function binaryAsnProcessor ( ) : MEMORY_SLOT | undefined { // If left and right side are constants, do the math now for basic operations if (Left.type === 'constant' && Right.type === 'constant') { + const LeftConstant = utils.memoryToConstantContent(Left) + const RightConstant = utils.memoryToConstantContent(Right) switch (operatorVal) { case '+': - return utils.createConstantMemObj(utils.addHexContents(Left.hexContent, Right.hexContent)) + return utils.createConstantMemObjWithDeclaration(utils.addConstants(LeftConstant, RightConstant)) case '*': - return utils.createConstantMemObj(utils.mulHexContents(Left.hexContent, Right.hexContent)) + return utils.createConstantMemObjWithDeclaration(utils.mulConstants(LeftConstant, RightConstant)) case '/': - return utils.createConstantMemObj(utils.divHexContents(Left.hexContent, Right.hexContent)) + return utils.createConstantMemObjWithDeclaration(utils.divConstants(LeftConstant, RightConstant)) case '-': - return utils.createConstantMemObj(utils.subHexContents(Left.hexContent, Right.hexContent)) + return utils.createConstantMemObjWithDeclaration(utils.subConstants(LeftConstant, RightConstant)) } } } @@ -485,6 +636,9 @@ export default function binaryAsnProcessor ( default: return false } + if (Left.declaration === 'fixed' && Right.declaration === 'long') { + return operatorVal === '+' + } // Try optimization if left side is constant (only commutativa operations!) if (checkOperatorOptimization(operatorVal, Left)) { return true @@ -494,7 +648,7 @@ export default function binaryAsnProcessor ( return true } // Try optimization if right side is constant, but do not mess if already optimized - if (Right.type === 'constant' && !checkOperatorOptimization(operatorVal, Right)) { + if (Right.type === 'constant' && Right.Offset === undefined && !checkOperatorOptimization(operatorVal, Right)) { return true } return false @@ -518,5 +672,14 @@ export default function binaryAsnProcessor ( return false } + /** Checks if its a pointer addition or subtractions. + * @returns true if it is needed to change the LEFT SIDE declaration to avoid type error */ + function isPointerAddOrSub (operator : string, lDecl : DECLARATION_TYPES, rDecl : DECLARATION_TYPES) : boolean { + if ((operator === '+' || operator === '-') && lDecl === 'long' && rDecl.endsWith('_ptr')) { + return true + } + return false + } + return binaryAsnProcessorMain() } diff --git a/src/codeGenerator/astProcessor/endAsnProcessor.ts b/src/codeGenerator/astProcessor/endAsnProcessor.ts index dc46818..8f8227b 100644 --- a/src/codeGenerator/astProcessor/endAsnProcessor.ts +++ b/src/codeGenerator/astProcessor/endAsnProcessor.ts @@ -44,9 +44,10 @@ export default function endAsnProcessor ( } return { SolvedMem: utils.createVoidMemObj(), asmCode: '' } } - const RetMemObj = utils.createConstantMemObj() - RetMemObj.size = CurrentNode.Token.value.length / 16 - RetMemObj.hexContent = CurrentNode.Token.value + const RetMemObj = utils.createConstantMemObj(CurrentNode.Token.value) + if (CurrentNode.Token.extValue === 'fixed') { + RetMemObj.declaration = 'fixed' + } return { SolvedMem: RetMemObj, asmCode: '' } } @@ -88,6 +89,7 @@ export default function endAsnProcessor ( case 'asm': case 'exit': case 'halt': + case 'sleep': return { SolvedMem: utils.createVoidMemObj(), asmCode: createInstruction(AuxVars, CurrentNode.Token) @@ -105,11 +107,20 @@ export default function endAsnProcessor ( } } case 'struct': { - const StructTypeDefinition = Program.typesDefinitions.find( + let StructTypeDefinition = Program.typesDefinitions.find( Obj => Obj.type === 'struct' && Obj.name === CurrentNode.Token.extValue ) as STRUCT_TYPE_DEFINITION | undefined + if (StructTypeDefinition === undefined && AuxVars.CurrentFunction !== undefined) { + StructTypeDefinition = Program.typesDefinitions.find( + Obj => Obj.type === 'struct' && Obj.name === AuxVars.CurrentFunction?.name + '_' + CurrentNode.Token.extValue + ) as STRUCT_TYPE_DEFINITION | undefined + } + if (StructTypeDefinition === undefined) { + throw new Error(`At line: ${CurrentNode.Token.line}. ` + + `Struct type definition for '${CurrentNode.Token.extValue}' not found.`) + } return { - SolvedMem: assertNotUndefined(StructTypeDefinition).MemoryTemplate, + SolvedMem: StructTypeDefinition.MemoryTemplate, asmCode: '' } } diff --git a/src/codeGenerator/astProcessor/functionSolver.ts b/src/codeGenerator/astProcessor/functionSolver.ts index 8c285d6..7926607 100644 --- a/src/codeGenerator/astProcessor/functionSolver.ts +++ b/src/codeGenerator/astProcessor/functionSolver.ts @@ -2,7 +2,7 @@ import { assertExpression, assertNotUndefined } from '../../repository/repositor import { CONTRACT, SC_FUNCTION } from '../../typings/contractTypes' import { LOOKUP_ASN, AST, MEMORY_SLOT } from '../../typings/syntaxTypes' import { - createSimpleInstruction, createInstruction, createAPICallInstruction + createSimpleInstruction, createInstruction, createAPICallInstruction, createBuiltInInstruction } from '../assemblyProcessor/createInstruction' import { GENCODE_AUXVARS, GENCODE_ARGS, GENCODE_SOLVED_OBJECT } from '../codeGeneratorTypes' import utils from '../utils' @@ -19,12 +19,16 @@ export default function functionSolver ( const fnName = assertNotUndefined(CurrentNode.Token.extValue) const FnToCall = Program.functions.find(val => val.name === fnName) const ApiToCall = Program.Global.APIFunctions.find(val => val.name === fnName) + const BuiltInToCall = Program.Global.BuiltInFunctions.find(val => val.name === fnName) const subSentences = utils.splitASTOnDelimiters(assertNotUndefined(CurrentNode.FunctionArgs)) if (FnToCall) { return userFunctionSolver(FnToCall, subSentences) } + if (BuiltInToCall) { + return internalFunctionSolver('builtin', BuiltInToCall, subSentences) + } if (ApiToCall) { - return apiFunctionSolver(ApiToCall, subSentences) + return internalFunctionSolver('api', ApiToCall, subSentences) } throw new Error(`At line: ${CurrentNode.Token.line}. Function '${fnName}' not declared.`) } @@ -64,13 +68,9 @@ export default function functionSolver ( }) const fnArg = FunctionToCall.argsMemObj[i] if (utils.isNotValidDeclarationOp(fnArg.declaration, ArgGenObj.SolvedMem)) { - if (Program.Config.warningToError) { - throw new Error(`At line: ${CurrentNode.Token.line}.` + - ` Warning: Function parameter type is different from variable: '${fnArg.declaration}'` + - ` and '${ArgGenObj.SolvedMem.declaration}'.`) - } - // Override declaration protection rules - utils.setMemoryDeclaration(ArgGenObj.SolvedMem, fnArg.declaration) + throw new Error(`At line: ${CurrentNode.Token.line}.` + + ` Type of function argument #${i + 1} is different from variable: ` + + ` Expecting '${fnArg.declaration}', got '${ArgGenObj.SolvedMem.declaration}'.`) } if (ArgGenObj.SolvedMem.size !== 1 && ArgGenObj.SolvedMem.Offset === undefined) { throw new Error(`At line: ${CurrentNode.Token.line}.` + @@ -109,38 +109,35 @@ export default function functionSolver ( return { SolvedMem: FnRetObj, asmCode: returnAssemblyCode } } - function apiFunctionSolver (ApiToCall: SC_FUNCTION, rawArgs: AST[]) : GENCODE_SOLVED_OBJECT { + function internalFunctionSolver (type: 'builtin' | 'api', ifnToCall: SC_FUNCTION, rawArgs: AST[]) : GENCODE_SOLVED_OBJECT { let FnRetObj: MEMORY_SLOT const processedArgs: MEMORY_SLOT [] = [] let returnAssemblyCode = '' - if (ApiToCall.declaration === 'void') { + if (ifnToCall.declaration === 'void' || ifnToCall.name === 'bcftol' || ifnToCall.name === 'bcltof') { FnRetObj = utils.createVoidMemObj() } else { FnRetObj = AuxVars.getNewRegister() // reserve tempvar for return type + FnRetObj.declaration = ifnToCall.declaration } if (rawArgs[0].type === 'nullASN') { rawArgs.pop() } - if (rawArgs.length !== ApiToCall.argsMemObj.length) { + if (rawArgs.length !== ifnToCall.argsMemObj.length) { throw new Error(`At line: ${CurrentNode.Token.line}.` + - ` Wrong number of arguments for function '${ApiToCall.name}'.` + - ` It must have '${ApiToCall.argsMemObj.length}' args.`) + ` Wrong number of arguments for function '${ifnToCall.name}'.` + + ` It must have '${ifnToCall.argsMemObj.length}' args.`) } - rawArgs.forEach(RawSentence => { + rawArgs.forEach((RawSentence, idx) => { const ArgGenObj = genCode(Program, AuxVars, { RemAST: RawSentence, logicalOp: false, revLogic: false }) returnAssemblyCode += ArgGenObj.asmCode - if (utils.getDeclarationFromMemory(ArgGenObj.SolvedMem) !== 'long') { - if (Program.Config.warningToError) { - throw new Error(`At line: ${CurrentNode.Token.line}.` + - ' Warning: API Function parameter type is different from variable: ' + - ` 'long' and '${ArgGenObj.SolvedMem.declaration}'.`) - } - // Override declaration protection rules - utils.setMemoryDeclaration(ArgGenObj.SolvedMem, 'long') + if (utils.isNotValidDeclarationOp(ifnToCall.argsMemObj[idx].declaration, ArgGenObj.SolvedMem)) { + throw new Error(`At line: ${CurrentNode.Token.line}.` + + ` Type of API Function argument #${idx + 1} is different from variable. ` + + ` Expecting '${ifnToCall.argsMemObj[idx].declaration}', got '${ArgGenObj.SolvedMem.declaration}'.`) } if (ArgGenObj.SolvedMem.size !== 1 && ArgGenObj.SolvedMem.Offset === undefined) { throw new Error(`At line: ${CurrentNode.Token.line}.` + @@ -148,12 +145,25 @@ export default function functionSolver ( } processedArgs.push(ArgGenObj.SolvedMem) }) - returnAssemblyCode += createAPICallInstruction( - AuxVars, - utils.genAPICallToken(CurrentNode.Token.line, ApiToCall.asmName), - FnRetObj, - processedArgs - ) + if (type === 'api') { + returnAssemblyCode += createAPICallInstruction( + AuxVars, + utils.genAPICallToken(CurrentNode.Token.line, ifnToCall.asmName), + FnRetObj, + processedArgs + ) + } else { + if (ifnToCall.name === 'bcftol' || ifnToCall.name === 'bcltof') { + utils.setMemoryDeclaration(processedArgs[0], ifnToCall.declaration) + return { SolvedMem: processedArgs[0], asmCode: returnAssemblyCode } + } + returnAssemblyCode += createBuiltInInstruction( + AuxVars, + utils.genBuiltInToken(CurrentNode.Token.line, ifnToCall.asmName), + FnRetObj, + processedArgs + ) + } processedArgs.forEach(varnm => AuxVars.freeRegister(varnm.address)) return { SolvedMem: FnRetObj, asmCode: returnAssemblyCode } } diff --git a/src/codeGenerator/astProcessor/lookupAsnProcessor.ts b/src/codeGenerator/astProcessor/lookupAsnProcessor.ts index f99bf01..bf44d4c 100644 --- a/src/codeGenerator/astProcessor/lookupAsnProcessor.ts +++ b/src/codeGenerator/astProcessor/lookupAsnProcessor.ts @@ -295,6 +295,10 @@ export default function lookupAsnProcessor ( if (Previous.SolvedMem.ArrayItem === undefined) { throw new Error('Internal error.') } + if (utils.getDeclarationFromMemory(Param) === 'fixed') { + throw new Error(`At line ${CurrentNode.Token.line}. ` + + 'Array index cannot be fixed type.') + } switch (paramType) { case 'constant': Previous.SolvedMem.Offset = { diff --git a/src/codeGenerator/astProcessor/setupGenCode.ts b/src/codeGenerator/astProcessor/setupGenCode.ts index 5114314..43f6c97 100644 --- a/src/codeGenerator/astProcessor/setupGenCode.ts +++ b/src/codeGenerator/astProcessor/setupGenCode.ts @@ -18,6 +18,7 @@ export default function setupGenCode ( isLeftSideOfAssignment: false, isConstSentence: false, hasVoidArray: false, + warnings: [], isTemp: auxvarsIsTemp, getNewRegister: auxvarsGetNewRegister, freeRegister: auxvarsFreeRegister, @@ -47,6 +48,7 @@ export default function setupGenCode ( validateReturnedVariable(CodeGenInfo.InitialAST, code.SolvedMem) code.asmCode += AuxVars.postOperations Globals.jumpId = AuxVars.jumpId + Globals.Program.warnings.push(...AuxVars.warnings) // Check throw conditions that were out-of-scope const analysyCode = code.asmCode.split('\n') code.asmCode = analysyCode.map(line => { @@ -62,14 +64,13 @@ export default function setupGenCode ( } function validateReturnedVariable (InitAST: AST, RetObj: MEMORY_SLOT) { - if (Globals.Program.Config.warningToError && - CodeGenInfo.initialJumpTarget === undefined && + if (CodeGenInfo.initialJumpTarget === undefined && RetObj.type === 'register') { if ((InitAST.type === 'unaryASN' && InitAST.Operation.value !== '*') || (InitAST.type === 'binaryASN' && (InitAST.Operation.type === 'Comparision' || InitAST.Operation.type === 'Operator'))) { throw new Error(`At line: ${InitAST.Operation.line}. ` + - 'Warning: Operation returning a value that is not being used.') + 'Operation returning a value that is not being used. Use casting to (void) to avoid this error.') } } } @@ -77,17 +78,18 @@ export default function setupGenCode ( function auxvarsIsTemp (loc: number) : boolean { if (loc === -1) return false const id = AuxVars.registerInfo.find(OBJ => OBJ.Template.address === loc) - if (id?.inUse === true) { - return true + if (id === undefined) { + return false } - return false + return true } function auxvarsGetNewRegister (line: number = sentenceLine): MEMORY_SLOT { const id = AuxVars.registerInfo.find(OBJ => OBJ.inUse === false) if (id === undefined) { throw new Error(`At line: ${line}. ` + - "No more registers available. Try to reduce nested operations or increase 'maxAuxVars'.") + 'No more registers available. ' + + `Increase the number with '#pragma maxAuxVars ${Globals.Program.Config.maxAuxVars + 1}' or try to reduce nested operations.`) } id.inUse = true return deepCopy(id.Template) @@ -131,12 +133,12 @@ export default function setupGenCode ( return deepCopy(MemFound) } - function auxvarsGetMemoryObjectByLocation (loc: number|string, line: number = sentenceLine): MEMORY_SLOT { + function auxvarsGetMemoryObjectByLocation (loc: number|bigint|string, line: number = sentenceLine): MEMORY_SLOT { let addr:number - if (typeof loc === 'number') { - addr = loc - } else { - addr = parseInt(loc, 16) + switch (typeof loc) { + case 'number': addr = loc; break + case 'string': addr = parseInt(loc, 16); break + default: addr = Number(loc) } const FoundMemory = AuxVars.memory.find(obj => obj.address === addr) if (FoundMemory === undefined) { @@ -147,15 +149,8 @@ export default function setupGenCode ( function auxvarsGetNewJumpID (line: number) : string { // This code shall be equal GlobalCodeVars.getNewJumpID() - let id = '' - if (Globals.Program.Config.enableLineLabels) { - id += line + '_' - } - if (Globals.Program.Config.enableRandom === true) { - return id + Math.random().toString(36).substr(2, 5) - } AuxVars.jumpId++ - return id + AuxVars.jumpId.toString(36) + return AuxVars.jumpId.toString(36) } return setupGenCodeMain() diff --git a/src/codeGenerator/astProcessor/unaryAsnProcessor.ts b/src/codeGenerator/astProcessor/unaryAsnProcessor.ts index 0821f0b..4e925f6 100644 --- a/src/codeGenerator/astProcessor/unaryAsnProcessor.ts +++ b/src/codeGenerator/astProcessor/unaryAsnProcessor.ts @@ -2,6 +2,7 @@ import { deepCopy } from '../../repository/repository' import { CONTRACT } from '../../typings/contractTypes' import { MEMORY_SLOT, DECLARATION_TYPES, UNARY_ASN } from '../../typings/syntaxTypes' import { createSimpleInstruction, createInstruction } from '../assemblyProcessor/createInstruction' +import { typeCasting } from '../assemblyProcessor/typeCastingToAsm' import { GENCODE_AUXVARS, GENCODE_ARGS, GENCODE_SOLVED_OBJECT } from '../codeGeneratorTypes' import utils from '../utils' import genCode from './genCode' @@ -18,6 +19,8 @@ export default function unaryAsnProcessor ( return UnaryOperatorProcessor() case 'Keyword': return unaryKeywordProcessor() + case 'CodeCave': + return CodeCaveProcessor() default: throw new Error(`Internal error at line: ${CurrentNode.Operation.line}.`) } @@ -113,16 +116,13 @@ export default function unaryAsnProcessor ( } const declar = utils.getDeclarationFromMemory(CGenObj.SolvedMem) if (declar.includes('_ptr') === false) { - if (Program.Config.warningToError) { - if (CurrentNode.Center.type === 'endASN' || CurrentNode.Center.type === 'lookupASN') { - throw new Error(`At line: ${CurrentNode.Operation.line}.` + - ` Warning: Trying to read/set content of variable ${CurrentNode.Center.Token.value}` + - ' that is not declared as pointer.') - } + if (CurrentNode.Center.type === 'endASN' || CurrentNode.Center.type === 'lookupASN') { throw new Error(`At line: ${CurrentNode.Operation.line}.` + - ' Warning: Trying to read/set content of a value that is not declared as pointer.') + ` Trying to read/set content of variable ${CurrentNode.Center.Token.value}` + + ' that is not declared as pointer.') } - utils.setMemoryDeclaration(CGenObj.SolvedMem, (declar + '_ptr') as DECLARATION_TYPES) + throw new Error(`At line: ${CurrentNode.Operation.line}.` + + ' Trying to read/set content of a value that is not declared as pointer.') } if (CGenObj.SolvedMem.Offset) { // Double deference: deference and continue @@ -138,7 +138,7 @@ export default function unaryAsnProcessor ( CGenObj.SolvedMem.Offset = { type: 'constant', value: 0, - declaration: 'long' + declaration: declar.slice(0, -4) as DECLARATION_TYPES } if (ScopeInfo.logicalOp === true) { CGenObj.asmCode += createInstruction( @@ -160,7 +160,10 @@ export default function unaryAsnProcessor ( let { SolvedMem: CGenObj, asmCode } = traverseNotLogical() if (CGenObj.type === 'constant') { return { - SolvedMem: utils.createConstantMemObj(utils.subHexContents(0, CGenObj.hexContent)), + SolvedMem: utils.createConstantMemObjWithDeclaration(utils.subConstants( + { value: 0, declaration: 'long' }, + utils.memoryToConstantContent(CGenObj) + )), asmCode: asmCode } } @@ -230,11 +233,8 @@ export default function unaryAsnProcessor ( throw new Error(`At line: ${CurrentNode.Operation.line}. ` + 'Trying to get address of void value.') case 'register': - if (Program.Config.warningToError) { - throw new Error(`At line: ${CurrentNode.Operation.line}. ` + - 'Warning: Returning address of a register.') - } TmpMemObj = utils.createConstantMemObj(RetMem.address) + TmpMemObj.declaration = RetMem.declaration break case 'constant': throw new Error(`At line: ${CurrentNode.Operation.line}. ` + @@ -243,7 +243,7 @@ export default function unaryAsnProcessor ( if (RetMem.Offset !== undefined) { if (RetMem.Offset.type === 'constant') { TmpMemObj = utils.createConstantMemObj( - utils.addHexContents(RetMem.hexContent, RetMem.Offset.value) + utils.addHexSimple(RetMem.hexContent, RetMem.Offset.value) ) TmpMemObj.declaration = RetMem.declaration break @@ -276,8 +276,9 @@ export default function unaryAsnProcessor ( TmpMemObj.declaration = 'struct_ptr' break case 'long': + case 'fixed': TmpMemObj = utils.createConstantMemObj(RetMem.address) - TmpMemObj.declaration = 'long' + TmpMemObj.declaration = RetMem.type break default: throw new Error(`Internal error at line ${CurrentNode.Operation.line}.`) @@ -291,6 +292,7 @@ export default function unaryAsnProcessor ( function unaryKeywordProcessor () : GENCODE_SOLVED_OBJECT { switch (CurrentNode.Operation.value) { case 'long': + case 'fixed': case 'void': AuxVars.isDeclaration = CurrentNode.Operation.value return traverseNotLogical() @@ -331,14 +333,10 @@ export default function unaryAsnProcessor ( const CGenObj = traverseNotLogical() CGenObj.asmCode += AuxVars.getPostOperations() if (utils.isNotValidDeclarationOp(AuxVars.CurrentFunction.declaration, CGenObj.SolvedMem)) { - if (Program.Config.warningToError) { - throw new Error(`At line: ${CurrentNode.Operation.line}.` + - ` Warning: Function ${AuxVars.CurrentFunction.name} must return` + + throw new Error(`At line: ${CurrentNode.Operation.line}.` + + ` Function ${AuxVars.CurrentFunction.name} must return` + ` '${AuxVars.CurrentFunction.declaration}' value,` + ` but it is returning '${CGenObj.SolvedMem.declaration}'.`) - } - // Override declaration protection rules - utils.setMemoryDeclaration(CGenObj.SolvedMem, AuxVars.CurrentFunction.declaration) } CGenObj.asmCode += createInstruction(AuxVars, CurrentNode.Operation, CGenObj.SolvedMem) AuxVars.freeRegister(CGenObj.SolvedMem.address) @@ -376,5 +374,17 @@ export default function unaryAsnProcessor ( return { SolvedMem: utils.createConstantMemObj(size), asmCode: CGenObj.asmCode } } + function CodeCaveProcessor () : GENCODE_SOLVED_OBJECT { + if (CurrentNode.Operation.declaration === '' || CurrentNode.Operation.declaration === undefined) { + throw new Error('Internal error.') + } + const CGenObj = traverseNotLogical() + const cDecl = utils.getDeclarationFromMemory(CGenObj.SolvedMem) + if (cDecl === CurrentNode.Operation.declaration) { + return CGenObj + } + return typeCasting(AuxVars, CGenObj, CurrentNode.Operation.declaration, CurrentNode.Operation.line) + } + return unaryAsnProcessorMain() } diff --git a/src/codeGenerator/codeGenerator.ts b/src/codeGenerator/codeGenerator.ts index d32c41e..d3bb304 100644 --- a/src/codeGenerator/codeGenerator.ts +++ b/src/codeGenerator/codeGenerator.ts @@ -17,20 +17,14 @@ export default function codeGenerator (Program: CONTRACT) { latestLoopId: [], jumpId: 0, assemblyCode: '', + errors: '', + warnings: '', currFunctionIndex: -1, currSourceLine: 0, getNewJumpID: function (line: number) { // Any changes here, also change function auxvarsGetNewJumpID - let id = '' - if (this.Program.Config.enableLineLabels) { - id += line + '_' - } - if (this.Program.Config.enableRandom === true) { - return id + Math.random().toString(36).substr(2, 5) - } - this.jumpId++ - return id + this.jumpId.toString(36) + return this.jumpId.toString(36) }, getLatestLoopID: function () { // error check must be in code! @@ -78,6 +72,10 @@ export default function codeGenerator (Program: CONTRACT) { } functionTailGenerator() }) + // Inspect if there were errros and throw now + if (GlobalCodeVars.errors.length !== 0) { + throw new Error(GlobalCodeVars.errors) + } return optimizer( Program.Config.optimizationLevel, GlobalCodeVars.assemblyCode, @@ -86,10 +84,10 @@ export default function codeGenerator (Program: CONTRACT) { } function writeAsmLine (lineContent: string, sourceCodeLine: number = 0) { - if (Program.Config.outputSourceLineNumber === true && + if (Program.Config.verboseAssembly === true && sourceCodeLine !== 0 && sourceCodeLine !== GlobalCodeVars.currSourceLine) { - GlobalCodeVars.assemblyCode += `^comment line ${sourceCodeLine}\n` + GlobalCodeVars.assemblyCode += `^comment line ${sourceCodeLine} ${Program.sourceLines[sourceCodeLine - 1]}\n` GlobalCodeVars.currSourceLine = sourceCodeLine } GlobalCodeVars.assemblyCode += lineContent + '\n' @@ -99,15 +97,19 @@ export default function codeGenerator (Program: CONTRACT) { if (lines.length === 0) { return } - if (Program.Config.outputSourceLineNumber === true && + if (Program.Config.verboseAssembly === true && sourceCodeLine !== 0 && sourceCodeLine !== GlobalCodeVars.currSourceLine) { - GlobalCodeVars.assemblyCode += `^comment line ${sourceCodeLine}\n` + GlobalCodeVars.assemblyCode += `^comment line ${sourceCodeLine} ${Program.sourceLines[sourceCodeLine - 1]}\n` GlobalCodeVars.currSourceLine = sourceCodeLine } GlobalCodeVars.assemblyCode += lines } + function addError (erroMessage: string) { + GlobalCodeVars.errors += erroMessage + '\n' + } + /** Add content of macro 'program' information to assembly code */ function configDeclarationGenerator () { if (Program.Config.PName !== '') { @@ -119,12 +121,21 @@ export default function codeGenerator (Program: CONTRACT) { if (Program.Config.PActivationAmount !== '') { writeAsmLine('^program activationAmount ' + Program.Config.PActivationAmount) } + if (Program.Config.PCreator !== '') { + writeAsmLine(`^program creator ${Program.Config.PCreator}`) + } + if (Program.Config.PContract !== '') { + writeAsmLine(`^program contract ${Program.Config.PContract}`) + } if (Program.Config.PUserStackPages !== 0) { writeAsmLine(`^program userStackPages ${Program.Config.PUserStackPages}`) } if (Program.Config.PCodeStackPages !== 0) { writeAsmLine(`^program codeStackPages ${Program.Config.PCodeStackPages}`) } + if (Program.Config.PCodeHashId !== '') { + writeAsmLine(`^program codeHashId ${Program.Config.PCodeHashId}`) + } } /** Handles variables declarations to assembly code. */ @@ -182,10 +193,19 @@ export default function codeGenerator (Program: CONTRACT) { let assemblyCode: string switch (Sentence.type) { case 'phrase': - writeAsmCode( - setupGenCode(GlobalCodeVars, { InitialAST: Sentence.CodeAST }, Sentence.line), - Sentence.line - ) + try { + writeAsmCode( + setupGenCode(GlobalCodeVars, { InitialAST: Sentence.CodeAST }, Sentence.line), + Sentence.line + ) + } catch (err) { + if (err instanceof Error) { + addError(err.message) + break + } + // Fatal error + throw err + } break case 'ifEndif': sentenceID = '__if' + GlobalCodeVars.getNewJumpID(Sentence.line) diff --git a/src/codeGenerator/codeGeneratorTypes.ts b/src/codeGenerator/codeGeneratorTypes.ts index 546915f..d2724e8 100644 --- a/src/codeGenerator/codeGeneratorTypes.ts +++ b/src/codeGenerator/codeGeneratorTypes.ts @@ -10,6 +10,10 @@ export type GLOBAL_AUXVARS = { jumpId: number /** Assembly code being created */ assemblyCode: string + /** Errors found */ + errors: string + /** Warnings found */ + warnings: string /** Current function being processed */ currFunctionIndex: number /** Line counter for source code */ @@ -64,6 +68,8 @@ export type GENCODE_AUXVARS = { isConstSentence: boolean /** Flag to inform lower level AST that there are an void array assignment */ hasVoidArray: boolean + /** Warnings found */ + warnings: string[] /** Verifies if a variable at loc address is register or temporary reused var */ isTemp(loc: number): boolean /** Get a new register variable */ @@ -84,7 +90,7 @@ export type GENCODE_AUXVARS = { * Object can be global or local function scope. * if not found, throws exception with line number. */ - getMemoryObjectByLocation (loc: number|string, line?: number): MEMORY_SLOT + getMemoryObjectByLocation (loc: number|bigint|string, line?: number): MEMORY_SLOT /** Get a new jump id according to current Configs (genCode scope) */ getNewJumpID(currLine: number): string } diff --git a/src/codeGenerator/utils.ts b/src/codeGenerator/utils.ts index 5f4faae..cb9e918 100644 --- a/src/codeGenerator/utils.ts +++ b/src/codeGenerator/utils.ts @@ -1,5 +1,5 @@ -import { assertNotUndefined } from '../repository/repository' -import { MEMORY_SLOT, TOKEN, AST, DECLARATION_TYPES, LOOKUP_ASN, BINARY_ASN, END_ASN, EXCEPTION_ASN, NULL_ASN, UNARY_ASN, SWITCH_ASN } from '../typings/syntaxTypes' +import { assertExpression, assertNotUndefined } from '../repository/repository' +import { MEMORY_SLOT, TOKEN, AST, DECLARATION_TYPES, LOOKUP_ASN, BINARY_ASN, END_ASN, EXCEPTION_ASN, NULL_ASN, UNARY_ASN, SWITCH_ASN, CONSTANT_CONTENT, HEX_CONTENT } from '../typings/syntaxTypes' type OBJECT_ASN_TYPE = T extends 'binaryASN' ? BINARY_ASN : T extends 'unaryASN' ? UNARY_ASN : @@ -10,35 +10,46 @@ T extends 'exceptionASN' ? EXCEPTION_ASN : T extends 'switchASN' ? SWITCH_ASN : never; -type HEXCONTENTS = number | string | undefined - /** * Simple functions that do not depend external variables. */ export default { /** Creates a constant Memory Object */ - createConstantMemObj (value: number | string = ''): MEMORY_SLOT { + createConstantMemObj (value?: number | bigint | string): MEMORY_SLOT { let param: string - if (typeof (value) === 'number') { - if (value % 1 !== 0) { - throw new Error('Only integer numbers in createConstantMemObj().') - } - param = value.toString(16).padStart(16, '0').slice(-16) - } else { - param = value.padStart(16, '0').slice(-16) + switch (typeof (value)) { + case 'number': + if (value % 1 !== 0) throw new Error('Internal error') + param = value.toString(16) + break + case 'bigint': + param = value.toString(16) + break + case 'string': + param = value + break + default: + param = '' } + param = param.padStart((Math.floor((param.length - 1) / 16) + 1) * 16, '0') return { address: -1, name: '', asmName: '', type: 'constant', scope: '', - size: 1, + size: param.length / 16, declaration: 'long', isDeclared: true, hexContent: param } }, + /** Creates a constant Memory Object with a given declaration */ + createConstantMemObjWithDeclaration (input: CONSTANT_CONTENT): MEMORY_SLOT { + const retObj = this.createConstantMemObj(input.value) + retObj.declaration = input.declaration + return retObj + }, /** Creates a constant Memory Object */ createVoidMemObj (): MEMORY_SLOT { return { @@ -55,6 +66,9 @@ export default { genMulToken (line: number = -1): TOKEN { return { type: 'Operator', precedence: 3, value: '*', line: line } }, + genDivToken (line: number = -1): TOKEN { + return { type: 'Operator', precedence: 3, value: '/', line: line } + }, genAddToken (line: number = -1): TOKEN { return { type: 'Operator', precedence: 4, value: '+', line: line } }, @@ -76,45 +90,136 @@ export default { genAPICallToken (line: number, name?: string): TOKEN { return { type: 'APICall', precedence: 0, value: assertNotUndefined(name), line: line } }, + genBuiltInToken (line: number, name?: string): TOKEN { + return { type: 'BuiltInCall', precedence: 0, value: assertNotUndefined(name), line: line } + }, genPushToken (line: number): TOKEN { return { type: 'Push', precedence: 12, value: '', line: line } }, - mulHexContents (param1: HEXCONTENTS, param2: HEXCONTENTS) { - const n1 = this.HexContentsToBigint(param1) - const n2 = this.HexContentsToBigint(param2) - return (n1 * n2).toString(16).padStart(16, '0').slice(-16) + memoryToConstantContent (MemObj: MEMORY_SLOT) : CONSTANT_CONTENT { + assertExpression(MemObj.type === 'constant') + return { + value: MemObj.hexContent ?? '', + declaration: MemObj.declaration === 'fixed' ? 'fixed' : 'long' + } + }, + mulConstants (param1: CONSTANT_CONTENT, param2: CONSTANT_CONTENT) : CONSTANT_CONTENT { + const n1 = this.HexContentToBigint(param1.value) + const n2 = this.HexContentToBigint(param2.value) + switch (param1.declaration + param2.declaration) { + case 'fixedfixed': + return { + value: ((n1 * n2) / 100000000n) % 18446744073709551616n, + declaration: 'fixed' + } + case 'fixedlong': + case 'longfixed': + return { + value: (n1 * n2) % 18446744073709551616n, + declaration: 'fixed' + } + default: + return { + value: (n1 * n2) % 18446744073709551616n, + declaration: 'long' + } + } }, - divHexContents (param1: HEXCONTENTS, param2: HEXCONTENTS) { - const n1 = this.HexContentsToBigint(param1) - const n2 = this.HexContentsToBigint(param2) + divConstants (param1: CONSTANT_CONTENT, param2: CONSTANT_CONTENT) : CONSTANT_CONTENT { + const n1 = this.HexContentToBigint(param1.value) + const n2 = this.HexContentToBigint(param2.value) if (n2 === 0n) { throw new Error('Division by zero') } - return (n1 / n2).toString(16).padStart(16, '0').slice(-16) + switch (param1.declaration + param2.declaration) { + case 'fixedfixed': + return { + value: ((n1 * 100000000n) / n2) % 18446744073709551616n, + declaration: 'fixed' + } + case 'fixedlong': + return { + value: n1 / n2, + declaration: 'fixed' + } + case 'longfixed': + return { + value: ((n1 * 100000000n * 100000000n) / n2), + declaration: 'fixed' + } + default: + // longlong': + return { + value: n1 / n2, + declaration: 'long' + } + } }, - addHexContents (param1: HEXCONTENTS, param2: HEXCONTENTS) { - const n1 = this.HexContentsToBigint(param1) - const n2 = this.HexContentsToBigint(param2) + addHexSimple (param1: HEX_CONTENT = '', param2: HEX_CONTENT = '') { + const n1 = this.HexContentToBigint(param1) + const n2 = this.HexContentToBigint(param2) return (n1 + n2).toString(16).padStart(16, '0').slice(-16) }, - subHexContents (param1: HEXCONTENTS, param2: HEXCONTENTS) { - const n1 = this.HexContentsToBigint(param1) - const n2 = this.HexContentsToBigint(param2) - let sub = n1 - n2 - if (sub < 0) { - sub += 18446744073709551616n + addConstants (param1: CONSTANT_CONTENT, param2: CONSTANT_CONTENT) : CONSTANT_CONTENT { + const n1 = this.HexContentToBigint(param1.value) + const n2 = this.HexContentToBigint(param2.value) + switch (param1.declaration + param2.declaration) { + case 'fixedlong': + return { + value: (n1 + (n2 * 100000000n)) % 18446744073709551616n, + declaration: 'fixed' + } + case 'longfixed': + return { + value: ((n1 * 100000000n) + n2) % 18446744073709551616n, + declaration: 'fixed' + } + default: + // 'fixedfixed' or 'longlong': + return { + value: (n1 + n2) % 18446744073709551616n, + declaration: param1.declaration + } } - return sub.toString(16).padStart(16, '0').slice(-16) }, - /** Converts a hex string or number to bigint */ - HexContentsToBigint (arg: HEXCONTENTS) : bigint { - if (typeof arg === 'undefined') { - return 0n + subConstants (param1: CONSTANT_CONTENT, param2: CONSTANT_CONTENT) { + const n1 = this.HexContentToBigint(param1.value) + const n2 = this.HexContentToBigint(param2.value) + let value: bigint + let declaration: 'fixed'|'long' + switch (param1.declaration + param2.declaration) { + case 'fixedfixed': + case 'longlong': + value = n1 - n2 + declaration = param1.declaration + break + case 'fixedlong': + value = n1 - (n2 * 100000000n) + declaration = 'fixed' + break + default: + // 'longfixed': + value = (n1 * 100000000n) - n2 + declaration = 'fixed' + } + if (value < 0) { + value += 18446744073709551616n } - if (typeof arg === 'number') { + return { value, declaration } + }, + /** Converts a hex string or number to bigint NO FIXED NUMBER! */ + HexContentToBigint (arg: HEX_CONTENT | undefined) : bigint { + switch (typeof (arg)) { + case 'number': return BigInt(arg) + case 'bigint': + return arg + case 'string': + if (arg === '') return 0n + return BigInt('0x' + arg) + default: + return 0n } - return BigInt('0x' + arg) }, /** Splits an AST into array of AST based on delimiters */ splitASTOnDelimiters (Obj: AST) { @@ -149,11 +254,11 @@ export default { return false } if (desiredDeclaration === 'void_ptr' && - (memoDeclaration === 'long_ptr' || memoDeclaration === 'struct_ptr')) { + memoDeclaration.includes('_ptr')) { return false } if (memoDeclaration === 'void_ptr' && - (desiredDeclaration === 'long_ptr' || desiredDeclaration === 'struct_ptr')) { + desiredDeclaration.includes('_ptr')) { return false } if (desiredDeclaration.includes('_ptr') && MemoObj.type === 'constant') { @@ -211,6 +316,12 @@ export default { } } function lookupAsn (InspAst: LOOKUP_ASN): boolean { + const parts = vname.split('_') + const idx = parts.findIndex(part => part === InspAst.Token.value) + if (idx !== -1 && /^\d+$/.test(parts[idx + 1])) { + // Inpect next part. If is number, it is an array. Cancel reuseAssignedVar + return false + } const CanReuse = InspAst.modifiers.find(CurrentModifier => { if (CurrentModifier.type === 'Array') { if (recursiveFind(CurrentModifier.Center) === false) { diff --git a/src/parser/parser.ts b/src/parser/parser.ts index dcf6237..cc97497 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -1,5 +1,5 @@ import { PRE_TOKEN, TOKEN, TOKEN_TYPES } from '../typings/syntaxTypes' -import { stringToHexstring, ReedSalomonAddressDecode } from '../repository/repository' +import { stringToHexstring, ReedSalomonAddressDecode, parseDecimalNumber } from '../repository/repository' type TOKEN_SPEC = { sequence: string[] @@ -79,9 +79,10 @@ export default function parser (preTokens: PRE_TOKEN[]): TOKEN[] { sequence: ['numberDec'], action (tokenID): TOKEN { const PreTkn = preTokens[tokenID] - let val = BigInt(PreTkn.value.replace(/_/g, '')).toString(16) - val = val.padStart((Math.floor((val.length - 1) / 16) + 1) * 16, '0') - return { type: 'Constant', precedence: 0, value: val, line: PreTkn.line } + const Parsed = parseDecimalNumber(PreTkn.value, PreTkn.line) + const valString = Parsed.value.toString(16) + const paddedValString = valString.padStart((Math.floor((valString.length - 1) / 16) + 1) * 16, '0') + return { type: 'Constant', precedence: 0, value: paddedValString, line: PreTkn.line, extValue: Parsed.declaration } } }, { @@ -90,7 +91,7 @@ export default function parser (preTokens: PRE_TOKEN[]): TOKEN[] { const PreTkn = preTokens[tokenID] let val = PreTkn.value.replace(/_/g, '').toLowerCase() val = val.padStart((Math.floor((val.length - 1) / 16) + 1) * 16, '0') - return { type: 'Constant', precedence: 0, value: val, line: PreTkn.line } + return { type: 'Constant', precedence: 0, value: val, line: PreTkn.line, extValue: 'long' } } }, { diff --git a/src/preprocessor/__tests__/preprocessor.spec.ts b/src/preprocessor/__tests__/preprocessor.spec.ts index 9fd16e4..9c757ea 100644 --- a/src/preprocessor/__tests__/preprocessor.spec.ts +++ b/src/preprocessor/__tests__/preprocessor.spec.ts @@ -32,6 +32,27 @@ describe('preprocessor right tests', () => { expect(preprocessor(code)).toBe(result) }) + it('#define macro (simple)', () => { + const code = '#define DEF(top, bottom) (((top) << 8) | (bottom))\nlong a,b,c;\na = DEF(b, c);\n' + const result = '\nlong a,b,c;\na = (((b) << 8) | (c));\n' + expect(preprocessor(code)).toBe(result) + }) + it('#define macro (empty argument)', () => { + const code = '#define DEF() (Get_Current_Timestamp( ) >> 32)\nlong a;\na = DEF() ;\n' + const result = '\nlong a;\na = (Get_Current_Timestamp( ) >> 32) ;\n' + expect(preprocessor(code)).toBe(result) + }) + it('#define macro (complex)', () => { + const code = '#define DEF(top, bottom) (((top) << 8) | (bottom))\nlong a,b,c;\na = DEF(mdv(a,b, c), c) + DEF(22, 25);\n' + const result = '\nlong a,b,c;\na = (((mdv(a,b, c)) << 8) | (c)) + (((22) << 8) | (25));\n' + expect(preprocessor(code)).toBe(result) + }) + it('#define macro (with define constant)', () => { + const code = '#define ONE n1\n#define DEF(top, bottom) (((top) << 8) | (bottom + ONE))\n#undef ONE\nlong a,b,c;\na = DEF(mdv(a,b, c), c);\n' + const result = '\n\n\nlong a,b,c;\na = (((mdv(a,b, c)) << 8) | (c + n1));\n' + expect(preprocessor(code)).toBe(result) + }) + it('#ifdef test', () => { const code = '#define debug\n#ifdef debug\n#pragma maxAuxVars 1\n#endif\nlong a; a++;' const result = '\n\n#pragma maxAuxVars 1\n\nlong a; a++;' @@ -139,4 +160,40 @@ describe('preprocessor wrong code', () => { preprocessor(code) }).toThrowError(/^At line/) }) + test('wrong number of macro arguments', () => { + expect(() => { + const code = '#define DEF(top) ((top) << 8))\nlong a,b,c,n1;\na = DEF(b, c);' + preprocessor(code) + }).toThrowError(/^At line/) + }) + test('error parsing macro arguments', () => { + expect(() => { + const code = '#define DEF(top) ((top) << 8)\nlong a,b,c,n1;\na = DEF(b();' + preprocessor(code) + }).toThrowError(/^At line/) + }) + test('macro redefinition', () => { + expect(() => { + const code = '#define DEF(top) ((top) << 8)\n#define DEF(top, bottom) ((top) << 8) | (bottom))\nlong a,b,c,n1;\na = DEF(b);' + preprocessor(code) + }).toThrowError(/^At line/) + }) + test('macro wrong numbers of arguments (empty)', () => { + expect(() => { + const code = '#define getCurrentBlock( ) (Get_Block_Timestamp() >> 32)\n long a; a = getCurrentBlock( s );' + preprocessor(code) + }).toThrowError(/^At line/) + }) + test('macro wrong numbers of arguments (empty, something)', () => { + expect(() => { + const code = '#define getCurrentBlock( , a ) (Get_Block_Timestamp(a) >> 32)\n long ll; ll = getCurrentBlock( ll , ll );' + preprocessor(code) + }).toThrowError(/^At line/) + }) + test('macro wrong numbers of arguments (something, empty)', () => { + expect(() => { + const code = '#define getCurrentBlock( a) (Get_Block_Timestamp(a) >> 32)\n long ll; ll = getCurrentBlock( ll , );' + preprocessor(code) + }).toThrowError(/^At line/) + }) }) diff --git a/src/preprocessor/preprocessor.ts b/src/preprocessor/preprocessor.ts index 471b654..e0831a5 100644 --- a/src/preprocessor/preprocessor.ts +++ b/src/preprocessor/preprocessor.ts @@ -2,7 +2,7 @@ import { assertNotEqual } from '../repository/repository' type PREPROCESSOR_RULE = { regex: RegExp - type: 'DEFINE_NULL' | 'DEFINE_VAL' | 'UNDEF' | 'IFDEF' | 'IFNDEF' | 'ELSE' | 'ENDIF' | 'MATCHES_ALL' + type: 'DEFINE_NULL' | 'DEFINE_MACRO' | 'DEFINE_VAL' | 'UNDEF' | 'IFDEF' | 'IFNDEF' | 'ELSE' | 'ENDIF' | 'MATCHES_REMAINING' } type IF_INFO = { active: boolean @@ -10,7 +10,9 @@ type IF_INFO = { } type REPLACEMENTS = { cname: string + regex: RegExp value: string + macro?: string } type PROCESSED_RULE = { Code: PREPROCESSOR_RULE @@ -26,19 +28,20 @@ export default function preprocessor (sourcecode: string) : string { const preprocessorCodes: PREPROCESSOR_RULE[] = [ // Regex order is important! { regex: /^\s*#\s*define\s+(\w+)\s*$/, type: 'DEFINE_NULL' }, + { regex: /^\s*#\s*define\s+(\w+)\s*(\([^)]*\))\s*(\(.+\))\s*$/, type: 'DEFINE_MACRO' }, { regex: /^\s*#\s*define\s+(\w+\b)(.+)$/, type: 'DEFINE_VAL' }, { regex: /^\s*#\s*undef\s+(\w+)\s*$/, type: 'UNDEF' }, { regex: /^\s*#\s*ifdef\s+(\w+)\s*$/, type: 'IFDEF' }, { regex: /^\s*#\s*ifndef\s+(\w+)\s*$/, type: 'IFNDEF' }, { regex: /^\s*#\s*else\s*$/, type: 'ELSE' }, { regex: /^\s*#\s*endif\s*$/, type: 'ENDIF' }, - { regex: /^[\s\S]*$/, type: 'MATCHES_ALL' } + { regex: /^[\s\S]*$/, type: 'MATCHES_REMAINING' } ] let preprocessorReplacements: REPLACEMENTS[] = [ - { cname: 'true', value: '1' }, - { cname: 'false', value: '0' }, - { cname: 'NULL', value: '0' }, - { cname: 'SMARTC', value: '' } + { cname: 'true', regex: /\btrue\b/, value: '1' }, + { cname: 'false', regex: /\bfalse\b/, value: '0' }, + { cname: 'NULL', regex: /\bNULL\b/, value: '(void *)(0)' }, + { cname: 'SMARTC', regex: /\bSMARTC\b/, value: '' } ] const ifActive: IF_INFO[] = [{ active: true, flipped: false }] let currentIfLevel = 0 @@ -101,12 +104,80 @@ export default function preprocessor (sourcecode: string) : string { throw new Error('Internal error.') } - function replaceDefines (codeline: string) : string { + function replaceDefines (codeline: string, lineNo: number) : string { + let retLine = codeline preprocessorReplacements.forEach((Replacement) => { - const rep = new RegExp('\\b' + Replacement.cname + '\\b', 'g') - codeline = codeline.replace(rep, Replacement.value) + if (Replacement.macro) { + while (true) { + const foundCname = Replacement.regex.exec(retLine) + if (foundCname === null) { + return + } + let replaced = Replacement.macro + const currExtArgs = extractArgs(retLine, foundCname.index + 1, lineNo) + const origExtArgs = extractArgs(Replacement.value, 0, lineNo) + if (origExtArgs.argArray.length !== currExtArgs.argArray.length) { + throw new Error(`At line: ${lineNo + 1}. ` + + `Wrong number of arguments for macro '${Replacement.cname}'. ` + + `Expected ${origExtArgs.argArray.length}, got ${currExtArgs.argArray.length}.`) + } + for (let currArg = 0; currArg < origExtArgs.argArray.length; currArg++) { + replaced = replaced.replace(new RegExp(`\\b${origExtArgs.argArray[currArg]}\\b`, 'g'), currExtArgs.argArray[currArg]) + } + retLine = retLine.slice(0, foundCname.index) + replaced + retLine.slice(currExtArgs.endPosition) + } + } + retLine = retLine.replace(Replacement.regex, Replacement.value) }) - return codeline + return retLine + } + + function extractArgs (fnArgString: string, needle: number, line: number): { argArray: string[], endPosition: number} { + const argArray : string [] = [] + let currArg: string = '' + let pLevel = 0 + let started = false + for (;;needle++) { + const currChar = fnArgString.charAt(needle) + if (currChar === '') { + throw new Error(`At line: ${line + 1}. Unmatched parenthesis or unexpected end of line.`) + } + if (currChar === '(') { + pLevel++ + if (pLevel === 1) { + started = true + continue + } + } + if (!started) continue + if (currChar === ')') { + pLevel-- + if (pLevel === 0) { + const endArg = currArg.trim() + if (endArg.length === 0 && argArray.length !== 0) { + throw new Error(`At line: ${line + 1}. Found empty argument on macro declaration.`) + } + if (endArg.length !== 0) { + argArray.push(currArg.trim()) + } + break + } + } + if (currChar === ',' && pLevel === 1) { + const newArg = currArg.trim() + if (newArg.length === 0) { + throw new Error(`At line: ${line + 1}. Found empty argument on macro declaration.`) + } + argArray.push(currArg.trim()) + currArg = '' + continue + } + currArg += currChar + } + return { + argArray, + endPosition: needle + 1 + } } function processLine (currentLine: string, lineNo: number) : string { @@ -114,12 +185,12 @@ export default function preprocessor (sourcecode: string) : string { const IfTemplateObj = { active: true, flipped: false } let idx: number if (currentIfLevel < 0) { - throw new Error(`At line: ${lineNo}. Unmatched '#endif' directive.`) + throw new Error(`At line: ${lineNo + 1}. Unmatched '#endif' directive.`) } assertNotEqual(ifActive.length, 0, 'Internal error') const LastIfInfo = ifActive[ifActive.length - 1] const lineActive = ifActive[currentIfLevel].active - // Process rules that does not depend on lineActive + // Process rules that depend on lineActive switch (PrepRule.Code.type) { case 'IFDEF': currentIfLevel += lineActive ? 1 : 0 @@ -154,11 +225,16 @@ export default function preprocessor (sourcecode: string) : string { if (lineActive === false) { return '' } + // Process rules that does not depend on lineActive switch (PrepRule.Code.type) { case 'DEFINE_NULL': idx = preprocessorReplacements.findIndex(Obj => Obj.cname === PrepRule.parts[1]) if (idx === -1) { - preprocessorReplacements.push({ cname: PrepRule.parts[1], value: '' }) + preprocessorReplacements.push({ + cname: PrepRule.parts[1], + regex: new RegExp('\\b' + PrepRule.parts[1] + '\\b', 'g'), + value: '' + }) return '' } preprocessorReplacements[idx].value = '' @@ -168,17 +244,30 @@ export default function preprocessor (sourcecode: string) : string { if (idx === -1) { preprocessorReplacements.push({ cname: PrepRule.parts[1], - value: replaceDefines(PrepRule.parts[2]).trim() + regex: new RegExp('\\b' + PrepRule.parts[1] + '\\b', 'g'), + value: replaceDefines(PrepRule.parts[2], lineNo).trim() }) return '' } - preprocessorReplacements[idx].value = PrepRule.parts[2].trim() + preprocessorReplacements[idx].value = replaceDefines(PrepRule.parts[2], lineNo).trim() + return '' + case 'DEFINE_MACRO': + idx = preprocessorReplacements.findIndex(Obj => Obj.cname === PrepRule.parts[1]) + if (idx !== -1) { + throw new Error(`At line: ${lineNo + 1}. Cannot redefine macro '${PrepRule.parts[1]}'.`) + } + preprocessorReplacements.push({ + cname: PrepRule.parts[1], + regex: new RegExp(`\\b${PrepRule.parts[1]}\\s*\\(`, 'g'), + value: replaceDefines(PrepRule.parts[2], lineNo), + macro: replaceDefines(PrepRule.parts[3], lineNo) + }) return '' case 'UNDEF': preprocessorReplacements = preprocessorReplacements.filter(obj => obj.cname !== PrepRule.parts[1]) return '' - case 'MATCHES_ALL': - return replaceDefines(currentLine) + case 'MATCHES_REMAINING': + return replaceDefines(currentLine, lineNo) default: // Never reached code. throw new Error('Internal error.') diff --git a/src/repository/__tests__/repository.spec.ts b/src/repository/__tests__/repository.spec.ts index 5f68357..a1de5f1 100644 --- a/src/repository/__tests__/repository.spec.ts +++ b/src/repository/__tests__/repository.spec.ts @@ -1,4 +1,4 @@ -import { stringToHexstring, ReedSalomonAddressDecode, assertNotUndefined, assertNotEqual, assertExpression, deepCopy } from '../repository' +import { stringToHexstring, ReedSalomonAddressDecode, assertNotUndefined, assertNotEqual, assertExpression, deepCopy, parseDecimalNumber } from '../repository' describe('Strings to hexstring', () => { it('should convert: simple string ( <= 0x7f)', () => { @@ -181,3 +181,16 @@ describe('assert/deepcopy functions', () => { expect(data).not.toBe(copy) }) }) + +describe('parseDecimal error', () => { + test('should throw: two decimal points', () => { + expect(() => { + parseDecimalNumber('2.234.33', -1) + }).toThrowError(/^At line/) + }) + test('should throw: more than 8 decimals', () => { + expect(() => { + parseDecimalNumber('2.123456789', -1) + }).toThrowError(/^At line/) + }) +}) diff --git a/src/repository/repository.ts b/src/repository/repository.ts index 0503693..0808ca0 100644 --- a/src/repository/repository.ts +++ b/src/repository/repository.ts @@ -2,6 +2,8 @@ // Using them with regular error messages will lead to condition not beeing checked // in coverage report. +import { CONSTANT_CONTENT, DECLARATION_TYPES } from '../typings/syntaxTypes' + /** * Ensure the value is not undefined * @param argument Anything @@ -186,3 +188,48 @@ export function ReedSalomonAddressDecode (RSString: string, currLine: number) : return run() } + +/** Parse a string supposed to be a decimal (no negative) + * @returns Object with value and type + * @throws Error if string is not valid + */ +export function parseDecimalNumber (strNum: string, line: number): CONSTANT_CONTENT { + strNum = strNum.replace(/_/g, '') + let value: bigint + let type : 'long'|'fixed' + if (strNum.includes('.')) { + const parts = strNum.split('.') + if (parts.length !== 2) { + throw new Error(`At line ${line}. ` + + ' Found more than one decimal point in number.') + } + if (parts[1].length > 8) { + throw new Error(`At line ${line}. ` + + 'Fixed numbers cannot have more than 8 digits as decimal fraction.') + } + value = BigInt(parts[0]) * 100000000n + BigInt(parts[1].padEnd(8, '0')) + type = 'fixed' + } else { + value = BigInt(strNum) + type = 'long' + } + return { + value, + declaration: type + } +} + +export function isDeclarationType (str: string) : str is DECLARATION_TYPES { + switch (str) { + case 'void': + case 'long': + case 'fixed': + case 'struct': + case 'void_ptr': + case 'long_ptr': + case 'fixed_ptr': + case 'struct_ptr': + return true + } + return false +} diff --git a/src/shaper/memoryProcessor.ts b/src/shaper/memoryProcessor.ts index 0219e01..0c666b0 100644 --- a/src/shaper/memoryProcessor.ts +++ b/src/shaper/memoryProcessor.ts @@ -30,8 +30,9 @@ export default function memoryProcessor ( while (phraseCode[tokenCounter]?.type === 'Keyword') { switch (phraseCode[tokenCounter].value) { case 'long': + case 'fixed': case 'void': - retMem.push(...longOrVoidProcessControl(phraseCode[tokenCounter].value as 'long'|'void')) + retMem.push(...lfvProcessControl(phraseCode[tokenCounter].value as 'long'|'fixed'|'void')) break case 'struct': retMem.push(...structProcessControl()) @@ -43,9 +44,9 @@ export default function memoryProcessor ( return retMem } - /** From Code containing long/void declaration, return an array of memory objects. + /** From Code containing long/fixed/void declaration, return an array of memory objects. * Handle regular variables, arrays and pointers. This is control flow */ - function longOrVoidProcessControl (definition: 'long'|'void') : MEMORY_SLOT[] { + function lfvProcessControl (definition: 'long'|'fixed'|'void') : MEMORY_SLOT[] { const retMemory : MEMORY_SLOT[] = [] const keywordIndex = tokenCounter let valid = true @@ -66,7 +67,7 @@ export default function memoryProcessor ( tokenCounter++ break } - retMemory.push(...longOrVoidToMemoryObject(definition)) + retMemory.push(...lfvToMemoryObject(definition)) valid = false tokenCounter++ break @@ -80,60 +81,60 @@ export default function memoryProcessor ( /** Return an array of memory objects. Handle regular variables, arrays and pointers. * This is the actual processing code. */ - function longOrVoidToMemoryObject (definition: 'long'|'void') : MEMORY_SLOT[] { - const LongTD = getTypeDefinitionTemplate('long') - const isLovPointer = isItPointer() + function lfvToMemoryObject (definition: 'long'|'fixed'|'void') : MEMORY_SLOT[] { + const definitionTD = getTypeDefinitionTemplate(definition) + const isPointer = isItPointer() const startingTokenCounter = tokenCounter - const lovDimensions = getArrayDimensions() + const dimensions = getArrayDimensions() // tokenCounter was advanced by structArrDimensions.length // prepare lovHeader - const LovHeader = deepCopy(LongTD.MemoryTemplate) - LovHeader.name = phraseCode[startingTokenCounter].value - LovHeader.asmName = AuxVars.currentPrefix + phraseCode[startingTokenCounter].value - LovHeader.scope = AuxVars.currentScopeName + const header = deepCopy(definitionTD.MemoryTemplate) + header.name = phraseCode[startingTokenCounter].value + header.asmName = AuxVars.currentPrefix + phraseCode[startingTokenCounter].value + header.scope = AuxVars.currentScopeName if (definition === 'void') { - if (isLovPointer === false) { + if (isPointer === false) { throw new Error(`At line: ${phraseCode[startingTokenCounter].line}.` + ' Can not declare variables as void.') } - LovHeader.declaration = 'void_ptr' - } else { // phraseCode[keywordIndex].value === 'long' - if (isLovPointer) { - LovHeader.declaration += '_ptr' + header.declaration = 'void_ptr' + } else { // phraseCode[keywordIndex].value === 'long' | 'fixed' + if (isPointer) { + header.declaration += '_ptr' } } - LovHeader.isDeclared = AuxVars.isFunctionArgument + header.isDeclared = AuxVars.isFunctionArgument // If is not an array, just send the header - if (lovDimensions.length === 0) { - return [LovHeader] + if (dimensions.length === 0) { + return [header] } // But if it IS an array, update header - LovHeader.type = 'array' - LovHeader.typeDefinition = structPrefix + LovHeader.asmName - LovHeader.ArrayItem = { + header.type = 'array' + header.typeDefinition = structPrefix + header.asmName + header.ArrayItem = { type: 'long', - declaration: LovHeader.declaration, - typeDefinition: structPrefix + LovHeader.asmName, + declaration: header.declaration, + typeDefinition: structPrefix + header.asmName, totalSize: 0 } - if (isLovPointer === false) { - LovHeader.declaration += '_ptr' + if (isPointer === false) { + header.declaration += '_ptr' } - LovHeader.ArrayItem.totalSize = 1 + lovDimensions.reduce(function (total, num) { + header.ArrayItem.totalSize = 1 + dimensions.reduce(function (total, num) { return total * num }, 1) // Push items into memory - const retArrMem = [LovHeader] - for (let i = 1; i < LovHeader.ArrayItem.totalSize; i++) { - const Mem2 = deepCopy(LongTD.MemoryTemplate) - Mem2.name = `${LovHeader.name}_${i - 1}` - Mem2.asmName = `${LovHeader.asmName}_${i - 1}` + const retArrMem = [header] + for (let i = 1; i < header.ArrayItem.totalSize; i++) { + const Mem2 = deepCopy(definitionTD.MemoryTemplate) + Mem2.name = `${header.name}_${i - 1}` + Mem2.asmName = `${header.asmName}_${i - 1}` Mem2.scope = AuxVars.currentScopeName - Mem2.declaration = LovHeader.ArrayItem.declaration + Mem2.declaration = header.ArrayItem.declaration retArrMem.push(Mem2) } // create array type definition - programTD.push(createArrayTypeDefinition(LovHeader, lovDimensions)) + programTD.push(createArrayTypeDefinition(header, dimensions)) return retArrMem } diff --git a/src/shaper/shaper.ts b/src/shaper/shaper.ts index 2b19298..b2fd9ba 100644 --- a/src/shaper/shaper.ts +++ b/src/shaper/shaper.ts @@ -4,8 +4,10 @@ import { MEMORY_SLOT, REGISTER_TYPE_DEFINITION } from '../typings/syntaxTypes' -import { assertNotUndefined, deepCopy } from '../repository/repository' -import { APITableTemplate, getMemoryTemplate, getTypeDefinitionTemplate } from './templates' +import { assertNotUndefined, deepCopy, parseDecimalNumber } from '../repository/repository' +import { + APITableTemplate, getMemoryTemplate, getTypeDefinitionTemplate, BuiltInTemplate, fixedBaseTemplate, fixedAPITableTemplate, autoCounterTemplate +} from './templates' import sentencesProcessor from './sentencesProcessor' import memoryProcessor from './memoryProcessor' import { SHAPER_AUXVARS } from './shaperTypes' @@ -29,15 +31,28 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { function shapeMain () : void { splitCode() Program.Global.macros.forEach(processMacroControl) - checkCompilerVersion() - Program.typesDefinitions = [getTypeDefinitionTemplate('register'), getTypeDefinitionTemplate('long')] + Program.typesDefinitions = [ + getTypeDefinitionTemplate('register'), + getTypeDefinitionTemplate('long'), + getTypeDefinitionTemplate('fixed') + ] Program.memory.push(...addRegistersInMemory(Program.Config.maxAuxVars)) Program.memory.push(...addConstantsInMemory(Program.Config.maxConstVars)) + if (Program.Config.fixedAPIFunctions || fixedDetected(tokenAST)) { + Program.memory.push(fixedBaseTemplate) + } + if (autoCounterDetected(tokenAST)) { + Program.memory.push(autoCounterTemplate) + } processGlobalCode() Program.functions.forEach(processFunctionCodeAndArguments) if (Program.Config.APIFunctions) { Program.Global.APIFunctions = APITableTemplate.slice() } + if (Program.Config.fixedAPIFunctions) { + Program.Global.APIFunctions.push(...fixedAPITableTemplate) + } + Program.Global.BuiltInFunctions = BuiltInTemplate.slice() validateFunctions() validateMemory() consolidateMemory() @@ -140,6 +155,11 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { usedBoolVal = true break } + if (Token.property === 'fixedAPIFunctions') { + Program.Config.fixedAPIFunctions = boolVal + usedBoolVal = true + break + } throw new Error(`At line: ${Token.line}.` + ` Unknow macro property '#${Token.type} ${Token.property}'.` + " Do you mean 'APIFunctions'? Check valid values on Help page") @@ -162,7 +182,7 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { const num = parseInt(MacroToken.value) switch (MacroToken.property) { case 'maxAuxVars': - if (num >= 1 && num <= 10) { + if (num >= 0 && num <= 10) { Program.Config.maxAuxVars = num return false } @@ -176,26 +196,17 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { case 'reuseAssignedVar': Program.Config.reuseAssignedVar = bool return true - case 'enableRandom': - Program.Config.enableRandom = bool - return true - case 'enableLineLabels': - Program.Config.enableLineLabels = bool - return true case 'optimizationLevel': - if (num >= 0 && num <= 3) { + if (num >= 0 && num <= 4) { Program.Config.optimizationLevel = num return false } throw new Error(`At line: ${MacroToken.line}. Value out of permitted range 0..3.`) case 'version': - Program.Config.sourcecodeVersion = MacroToken.value + // Nothing to do. 'version' is a reminder for programmers. return false - case 'warningToError': - Program.Config.warningToError = bool - return true - case 'outputSourceLineNumber': - Program.Config.outputSourceLineNumber = bool + case 'verboseAssembly': + Program.Config.verboseAssembly = bool return true default: throw new Error(`At line: ${MacroToken.line}.` + @@ -222,11 +233,14 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { Program.Config.PDescription = MacroToken.value return case 'activationAmount': - if (/^[0-9_]{1,20}$/.test(MacroToken.value)) { - Program.Config.PActivationAmount = MacroToken.value.replace(/_/g, '') - return - } - throw new Error(`At line: ${MacroToken.line}. Program activation must be only numbers or '_'.`) + Program.Config.PActivationAmount = parseDecimalNumber(MacroToken.value, MacroToken.line).value.toString(10) + return + case 'creator': + Program.Config.PCreator = parseDecimalNumber(MacroToken.value, MacroToken.line).value.toString(10) + return + case 'contract': + Program.Config.PContract = parseDecimalNumber(MacroToken.value, MacroToken.line).value.toString(10) + return case 'userStackPages': if (/^\d\s*$|^10\s*$/.test(MacroToken.value)) { Program.Config.PUserStackPages = Number(MacroToken.value) @@ -241,6 +255,16 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { } throw new Error(`At line: ${MacroToken.line}.` + ' Program code stack pages must be a number between 0 and 10, included.') + case 'codeHashId': + if (/^\d+\s*$/.test(MacroToken.value)) { + Program.Config.PCodeHashId = MacroToken.value.trim() + return + } + throw new Error(`At line: ${MacroToken.line}.` + + ' Program code hash id must be a decimal number. Use 0 to let compiler fill the value at assembly output.') + case 'compilerVersion': + // Nothing to do. compilerVersion is a reminder for programmers. + break default: throw new Error(`At line: ${MacroToken.line}.` + ` Unknow macro property: '#${MacroToken.type} ${MacroToken.property}'.` + @@ -248,36 +272,6 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { } } - /** Checks sourcecodeVersion and compiler current version. - * @throws {Error} if not pass rules checks. - */ - function checkCompilerVersion () : void { - try { - if (process.env.JEST === 'true') { - return - } - } catch (err) { - // On browser, continue - } - // Running on browser OR Runing on node, but it is not jest - if (Program.Config.sourcecodeVersion === '') { - if (!Program.Config.compilerVersion.includes('dev')) { - throw new Error('Compiler version not set.' + - ' Pin current compiler version in your program' + - ` adding '#pragma version ${Program.Config.compilerVersion}' to code.`) - } - Program.Config.sourcecodeVersion = Program.Config.compilerVersion - } - if (Program.Config.sourcecodeVersion !== Program.Config.compilerVersion) { - if (Program.Config.sourcecodeVersion !== 'dev') { - throw new Error(`This compiler is version '${Program.Config.compilerVersion}'.` + - ` File needs a compiler version '${Program.Config.sourcecodeVersion}'.` + - " Update '#pragma version' macro or run another SmartC version.") - } - Program.Config.sourcecodeVersion = Program.Config.compilerVersion - } - } - function addRegistersInMemory (howMany: number) : MEMORY_SLOT[] { const RegisterTD = getRegisterTypeDefinition() const retObj: MEMORY_SLOT[] = [] @@ -310,6 +304,35 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { return retObj } + /** Detects if fixed point calculations will be needed */ + function fixedDetected (tokenTrain: TOKEN[] | undefined) : boolean { + if (tokenTrain === undefined) return false + return !!tokenTrain.find((Tkn) => { + if ((Tkn.type === 'Keyword' && Tkn.value === 'fixed') || + (Tkn.type === 'Constant' && Tkn.extValue === 'fixed')) { + return true + } + if (Tkn.type === 'CodeDomain' || Tkn.type === 'CodeCave') { + return fixedDetected(Tkn.params) + } + return false + }) + } + + /** Detects if hidden variable for timestamp loop will be needed */ + function autoCounterDetected (tokenTrain: TOKEN[] | undefined) : boolean { + if (tokenTrain === undefined) return false + return !!tokenTrain.find((Tkn) => { + if (Tkn.type === 'Variable' && (Tkn.value === 'getNextTx' || Tkn.value === 'getNextTxFromBlockheight')) { + return true + } + if (Tkn.type === 'CodeDomain' || Tkn.type === 'CodeCave') { + return autoCounterDetected(Tkn.params) + } + return false + }) + } + /** Process global code, transforming them into global sentences properties */ function processGlobalCode () : void { AuxVars.currentScopeName = '' @@ -394,12 +417,6 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { * into argsMemObj and sentences properties */ function processFunctionCodeAndArguments (CurrentFunction: SC_FUNCTION, fnNum: number) { CurrentFunction.sentences = sentencesProcessor(AuxVars, CurrentFunction.code) - let expectVoid = false - if (CurrentFunction.arguments?.length === 1 && - CurrentFunction.arguments[0].type === 'Keyword' && - CurrentFunction.arguments[0].value === 'void') { - expectVoid = true - } AuxVars.currentScopeName = CurrentFunction.name AuxVars.currentPrefix = AuxVars.currentScopeName + '_' AuxVars.isFunctionArgument = true @@ -409,10 +426,6 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { `Wrong arguments for function '${CurrentFunction.name}'.`) } CurrentFunction.argsMemObj = memoryProcessor(Program.typesDefinitions, AuxVars, sentence[0].code) - if (CurrentFunction.argsMemObj.length === 0 && expectVoid === false) { - throw new Error(`At line: ${CurrentFunction.line}.` + - ` No variables in arguments for function '${CurrentFunction.name}'. Do you mean 'void'?`) - } Program.memory = Program.memory.concat(CurrentFunction.argsMemObj) AuxVars.isFunctionArgument = false delete Program.functions[fnNum].arguments @@ -454,6 +467,12 @@ export default function shaper (Program: CONTRACT, tokenAST: TOKEN[]): void { ` Found second definition for function '${Program.functions[j].name}'.`) } } + for (j = 0; j < Program.Global.BuiltInFunctions.length; j++) { + if (Program.functions[i].name === Program.Global.BuiltInFunctions[j].name) { + throw new Error(`At line: ${Program.functions[i].line}.` + + ` Function '${Program.functions[i].name}' has same name of one built-in Functions.`) + } + } if (Program.Config.APIFunctions === true) { for (j = 0; j < Program.Global.APIFunctions.length; j++) { if (Program.functions[i].name === Program.Global.APIFunctions[j].name || diff --git a/src/shaper/templates.ts b/src/shaper/templates.ts index 076fff9..f25aa2c 100644 --- a/src/shaper/templates.ts +++ b/src/shaper/templates.ts @@ -1,17 +1,18 @@ import { SC_FUNCTION } from '../typings/contractTypes' import { - REGISTER_TYPE_DEFINITION, LONG_TYPE_DEFINITION, STRUCT_TYPE_DEFINITION, MEMORY_BASE_TYPES, MEMORY_SLOT + REGISTER_TYPE_DEFINITION, LONG_TYPE_DEFINITION, STRUCT_TYPE_DEFINITION, MEMORY_BASE_TYPES, MEMORY_SLOT, FIXED_TYPE_DEFINITION } from '../typings/syntaxTypes' type ObjectTypeDefinition = T extends 'register' ? REGISTER_TYPE_DEFINITION : T extends 'long' ? LONG_TYPE_DEFINITION : + T extends 'fixed' ? LONG_TYPE_DEFINITION : T extends 'struct' ? STRUCT_TYPE_DEFINITION : never; -export function getTypeDefinitionTemplate ( +export function getTypeDefinitionTemplate ( templateType: T ) : ObjectTypeDefinition { - let RetObj: REGISTER_TYPE_DEFINITION | LONG_TYPE_DEFINITION | STRUCT_TYPE_DEFINITION + let RetObj: REGISTER_TYPE_DEFINITION | LONG_TYPE_DEFINITION | STRUCT_TYPE_DEFINITION | FIXED_TYPE_DEFINITION switch (templateType) { case 'register': RetObj = { @@ -24,6 +25,7 @@ export function getTypeDefinitionTemplate RetObj.MemoryTemplate.isDeclared = true break case 'long': + case 'void': RetObj = { type: 'long', name: '', @@ -32,6 +34,15 @@ export function getTypeDefinitionTemplate RetObj.MemoryTemplate.declaration = 'long' RetObj.MemoryTemplate.size = 1 break + case 'fixed': + RetObj = { + type: 'fixed', + name: '', + MemoryTemplate: getMemoryTemplate('fixed') + } + RetObj.MemoryTemplate.declaration = 'fixed' + RetObj.MemoryTemplate.size = 1 + break case 'struct': RetObj = { name: '', @@ -49,6 +60,413 @@ export function getTypeDefinitionTemplate return RetObj as ObjectTypeDefinition } +const longArg : MEMORY_SLOT = { + address: -1, + name: 'dummy', + asmName: 'dummy_dummy', + type: 'long', + scope: 'dummy', + declaration: 'long', + size: 1, + isDeclared: true +} + +const longPtrArg : MEMORY_SLOT = { + address: -1, + name: 'dummy', + asmName: 'dummy_dummy', + type: 'long', + scope: 'dummy', + declaration: 'long_ptr', + size: 1, + isDeclared: true +} + +const fixedArg : MEMORY_SLOT = { + address: -1, + name: 'dummy', + asmName: 'dummy_dummy', + type: 'fixed', + scope: 'dummy', + declaration: 'fixed', + size: 1, + isDeclared: true +} + +export const autoCounterTemplate : MEMORY_SLOT = { + address: -1, + asmName: '_counterTimestamp', + declaration: 'long', + isDeclared: true, + name: '_counterTimestamp', + scope: '', + size: 1, + type: 'long' +} + +export const BuiltInTemplate: SC_FUNCTION[] = [ + /* Note here: asmName and name shall be the same */ + { + argsMemObj: [longArg, longArg], + asmName: 'pow', + declaration: 'long', + sentences: [], + name: 'pow' + }, + { + argsMemObj: [longArg, longArg, longArg], + asmName: 'mdv', + declaration: 'long', + sentences: [], + name: 'mdv' + }, + { + argsMemObj: [longArg, fixedArg], + asmName: 'powf', + declaration: 'long', + sentences: [], + name: 'powf' + }, + { + argsMemObj: [ + { + address: -1, + name: 'addr1', + asmName: 'memcopy_addr1', + type: 'long', + scope: 'memcopy', + declaration: 'void_ptr', + size: 1, + isDeclared: true + }, + { + address: -1, + name: 'addr2', + asmName: 'memcopy_addr2', + type: 'long', + scope: 'memcopy', + declaration: 'void_ptr', + size: 1, + isDeclared: true + } + ], + asmName: 'memcopy', + declaration: 'void', + sentences: [], + name: 'memcopy' + }, + { + argsMemObj: [fixedArg], + asmName: 'bcftol', + declaration: 'long', + sentences: [], + name: 'bcftol' + }, + { + argsMemObj: [longArg], + asmName: 'bcltof', + declaration: 'fixed', + sentences: [], + name: 'bcltof' + }, + { + argsMemObj: [], + asmName: 'getNextTx', + declaration: 'long', + sentences: [], + name: 'getNextTx' + }, + { + argsMemObj: [longArg], + asmName: 'getNextTxFromBlockheight', + declaration: 'long', + sentences: [], + name: 'getNextTxFromBlockheight' + }, + { + argsMemObj: [longArg], + asmName: 'getBlockheight', + declaration: 'long', + sentences: [], + name: 'getBlockheight' + }, + { + argsMemObj: [], + asmName: 'getCurrentBlockheight', + declaration: 'long', + sentences: [], + name: 'getCurrentBlockheight' + }, + { + argsMemObj: [longArg], + asmName: 'getAmount', + declaration: 'long', + sentences: [], + name: 'getAmount' + }, + { + argsMemObj: [longArg], + asmName: 'getSender', + declaration: 'long', + sentences: [], + name: 'getSender' + }, + { + argsMemObj: [longArg], + asmName: 'getType', + declaration: 'long', + sentences: [], + name: 'getType' + }, + { + argsMemObj: [], + asmName: 'getCreator', + declaration: 'long', + sentences: [], + name: 'getCreator' + }, + { + argsMemObj: [longArg], + asmName: 'getCreatorOf', + declaration: 'long', + sentences: [], + name: 'getCreatorOf' + }, + { + argsMemObj: [longArg], + asmName: 'getCodeHashOf', + declaration: 'long', + sentences: [], + name: 'getCodeHashOf' + }, + { + argsMemObj: [], + asmName: 'getWeakRandomNumber', + declaration: 'long', + sentences: [], + name: 'getWeakRandomNumber' + }, + { + argsMemObj: [longArg], + asmName: 'getActivationOf', + declaration: 'long', + sentences: [], + name: 'getActivationOf' + }, + { + argsMemObj: [], + asmName: 'getCurrentBalance', + declaration: 'long', + sentences: [], + name: 'getCurrentBalance' + }, + { + argsMemObj: [longArg, longArg, longPtrArg], + asmName: 'readMessage', + declaration: 'void', + sentences: [], + name: 'readMessage' + }, + { + argsMemObj: [longPtrArg, longArg], + asmName: 'sendMessage', + declaration: 'void', + sentences: [], + name: 'sendMessage' + }, + { + argsMemObj: [longArg, longPtrArg, longArg], + asmName: 'sendAmountAndMessage', + declaration: 'void', + sentences: [], + name: 'sendAmountAndMessage' + }, + { + argsMemObj: [longArg, longArg], + asmName: 'sendAmount', + declaration: 'void', + sentences: [], + name: 'sendAmount' + }, + { + argsMemObj: [longArg], + asmName: 'sendBalance', + declaration: 'void', + sentences: [], + name: 'sendBalance' + }, + { + argsMemObj: [longArg, longArg], + asmName: 'getMapValue', + declaration: 'long', + sentences: [], + name: 'getMapValue' + }, + { + argsMemObj: [longArg, longArg, longArg], + asmName: 'getExtMapValue', + declaration: 'long', + sentences: [], + name: 'getExtMapValue' + }, + { + argsMemObj: [longArg, longArg, longArg], + asmName: 'setMapValue', + declaration: 'void', + sentences: [], + name: 'setMapValue' + }, + { + argsMemObj: [longArg, longArg, longArg], + asmName: 'issueAsset', + declaration: 'long', + sentences: [], + name: 'issueAsset' + }, + { + argsMemObj: [longArg, longArg], + asmName: 'mintAsset', + declaration: 'void', + sentences: [], + name: 'mintAsset' + }, + { + argsMemObj: [longArg, longArg, longArg], + asmName: 'sendQuantity', + declaration: 'void', + sentences: [], + name: 'sendQuantity' + }, + { + argsMemObj: [longArg, longArg, longArg, longArg], + asmName: 'sendQuantityAndAmount', + declaration: 'void', + sentences: [], + name: 'sendQuantityAndAmount' + }, + { + argsMemObj: [longArg], + asmName: 'getAssetBalance', + declaration: 'long', + sentences: [], + name: 'getAssetBalance' + }, + { + argsMemObj: [longArg, longArg, longArg, longArg, longArg, longArg], + asmName: 'checkSignature', + declaration: 'long', + sentences: [], + name: 'checkSignature' + }, + { + argsMemObj: [longArg, longArg, longArg, longArg, longArg], + asmName: 'distributeToHolders', + declaration: 'void', + sentences: [], + name: 'distributeToHolders' + }, + { + argsMemObj: [longArg, longArg], + asmName: 'getAssetHoldersCount', + declaration: 'long', + sentences: [], + name: 'getAssetHoldersCount' + }, + { + argsMemObj: [longArg, longPtrArg], + asmName: 'readAssets', + declaration: 'void', + sentences: [], + name: 'readAssets' + }, + { + argsMemObj: [longArg, longArg], + asmName: 'getQuantity', + declaration: 'long', + sentences: [], + name: 'getQuantity' + }, + { + argsMemObj: [longArg], + asmName: 'getAssetCirculating', + declaration: 'long', + sentences: [], + name: 'getAssetCirculating' + }, + + /* fixed number versions */ + { + argsMemObj: [longArg], + asmName: 'getAmountFx', + declaration: 'fixed', + sentences: [], + name: 'getAmountFx' + }, + { + argsMemObj: [longArg], + asmName: 'getActivationOfFx', + declaration: 'fixed', + sentences: [], + name: 'getActivationOfFx' + }, + { + argsMemObj: [], + asmName: 'getCurrentBalanceFx', + declaration: 'fixed', + sentences: [], + name: 'getCurrentBalanceFx' + }, + { + argsMemObj: [fixedArg, longPtrArg, longArg], + asmName: 'sendAmountAndMessageFx', + declaration: 'void', + sentences: [], + name: 'sendAmountAndMessageFx' + }, + { + argsMemObj: [fixedArg, longArg], + asmName: 'sendAmountFx', + declaration: 'void', + sentences: [], + name: 'sendAmountFx' + }, + { + argsMemObj: [longArg, longArg], + asmName: 'getMapValueFx', + declaration: 'fixed', + sentences: [], + name: 'getMapValueFx' + }, + { + argsMemObj: [longArg, longArg, longArg], + asmName: 'getExtMapValueFx', + declaration: 'fixed', + sentences: [], + name: 'getExtMapValueFx' + }, + { + argsMemObj: [longArg, longArg, fixedArg], + asmName: 'setMapValueFx', + declaration: 'void', + sentences: [], + name: 'setMapValueFx' + }, + { + argsMemObj: [longArg, longArg, fixedArg, longArg], + asmName: 'sendQuantityAndAmountFx', + declaration: 'void', + sentences: [], + name: 'sendQuantityAndAmountFx' + }, + { + argsMemObj: [longArg, longArg, fixedArg, longArg, longArg], + asmName: 'distributeToHoldersFx', + declaration: 'void', + sentences: [], + name: 'distributeToHoldersFx' + } +] + export function getMemoryTemplate (memType: MEMORY_BASE_TYPES) : MEMORY_SLOT { return { type: memType, @@ -62,6 +480,18 @@ export function getMemoryTemplate (memType: MEMORY_BASE_TYPES) : MEMORY_SLOT { } } +export const fixedBaseTemplate : MEMORY_SLOT = { + address: -1, + asmName: 'f100000000', + declaration: 'fixed', + isDeclared: false, + name: 'f100000000', + hexContent: '0000000005f5e100', + scope: '', + size: 1, + type: 'fixed' +} + export const APITableTemplate: SC_FUNCTION[] = [ { argsMemObj: [], @@ -120,256 +550,84 @@ export const APITableTemplate: SC_FUNCTION[] = [ name: 'Get_B4' }, { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Set_A1_addr', - type: 'long', - scope: 'Set_A1', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg], asmName: 'set_A1', declaration: 'void', sentences: [], name: 'Set_A1' }, { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Set_A2_addr', - type: 'long', - scope: 'Set_A2', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg], asmName: 'set_A2', declaration: 'void', sentences: [], name: 'Set_A2' }, { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Set_A3_addr', - type: 'long', - scope: 'Set_A3', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg], asmName: 'set_A3', declaration: 'void', sentences: [], name: 'Set_A3' }, { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Set_A4_addr', - type: 'long', - scope: 'Set_A4', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg], asmName: 'set_A4', declaration: 'void', sentences: [], name: 'Set_A4' }, { - argsMemObj: [ - { - address: -1, - name: 'addr1', - asmName: 'Set_A1_A2_addr1', - type: 'long', - scope: 'Set_A1_A2', - declaration: 'long', - size: 1, - isDeclared: true - }, - { - address: -1, - name: 'addr2', - asmName: 'Set_A1_A2_addr2', - type: 'long', - scope: 'Set_A1_A2', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg, longArg], asmName: 'set_A1_A2', declaration: 'void', sentences: [], name: 'Set_A1_A2' }, { - argsMemObj: [ - { - address: -1, - name: 'addr1', - asmName: 'Set_A3_A4_addr1', - type: 'long', - scope: 'Set_A3_A4', - declaration: 'long', - size: 1, - isDeclared: true - }, - { - address: -1, - name: 'addr2', - asmName: 'Set_A3_A4_addr2', - type: 'long', - scope: 'Set_A3_A4', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg, longArg], asmName: 'set_A3_A4', declaration: 'void', sentences: [], name: 'Set_A3_A4' }, - { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Set_B1_addr', - type: 'long', - scope: 'Set_B1', - declaration: 'long', - size: 1, - isDeclared: true - } - ], - asmName: 'set_B1', - declaration: 'void', - sentences: [], - name: 'Set_B1' - }, - { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Set_B2_addr', - type: 'long', - scope: 'Set_B2', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + { + argsMemObj: [longArg], + asmName: 'set_B1', + declaration: 'void', + sentences: [], + name: 'Set_B1' + }, + { + argsMemObj: [longArg], asmName: 'set_B2', declaration: 'void', sentences: [], name: 'Set_B2' }, { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Set_B3_addr', - type: 'long', - scope: 'Set_B3', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg], asmName: 'set_B3', declaration: 'void', sentences: [], name: 'Set_B3' }, { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Set_B4_addr', - type: 'long', - scope: 'Set_B4', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg], asmName: 'set_B4', declaration: 'void', sentences: [], name: 'Set_B4' }, { - argsMemObj: [ - { - address: -1, - name: 'addr1', - asmName: 'Set_B1_B2_addr1', - type: 'long', - scope: 'Set_B1_B2', - declaration: 'long', - size: 1, - isDeclared: true - }, - { - address: -1, - name: 'addr2', - asmName: 'Set_B1_B2_addr2', - type: 'long', - scope: 'Set_B1_B2', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg, longArg], asmName: 'set_B1_B2', declaration: 'void', sentences: [], name: 'Set_B1_B2' }, { - argsMemObj: [ - { - address: -1, - name: 'addr1', - asmName: 'Set_B3_B4_addr1', - type: 'long', - scope: 'Set_B3_B4', - declaration: 'long', - size: 1, - isDeclared: true - }, - { - address: -1, - name: 'addr2', - asmName: 'Set_B3_B4_addr2', - type: 'long', - scope: 'Set_B3_B4', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg, longArg], asmName: 'set_B3_B4', declaration: 'void', sentences: [], @@ -607,18 +865,7 @@ export const APITableTemplate: SC_FUNCTION[] = [ name: 'Put_Last_Block_Hash_In_A' }, { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'A_To_Tx_After_Timestamp_addr', - type: 'long', - scope: 'A_To_Tx_After_Timestamp', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg], asmName: 'A_to_Tx_after_Timestamp', declaration: 'void', sentences: [], @@ -688,18 +935,7 @@ export const APITableTemplate: SC_FUNCTION[] = [ name: 'Get_Previous_Balance' }, { - argsMemObj: [ - { - address: -1, - name: 'addr', - asmName: 'Send_To_Address_In_B_addr', - type: 'long', - scope: 'Send_To_Address_In_B', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg], asmName: 'send_to_Address_in_B', declaration: 'void', sentences: [], @@ -727,31 +963,251 @@ export const APITableTemplate: SC_FUNCTION[] = [ name: 'Send_A_To_Address_In_B' }, { - argsMemObj: [ - { - address: -1, - name: 'addr2', - asmName: 'Add_Minutes_To_Timestamp_addr2', - type: 'long', - scope: 'Add_Minutes_To_Timestamp', - declaration: 'long', - size: 1, - isDeclared: true - }, - { - address: -1, - name: 'addr3', - asmName: 'Add_Minutes_To_Timestamp_addr3', - type: 'long', - scope: 'Add_Minutes_To_Timestamp', - declaration: 'long', - size: 1, - isDeclared: true - } - ], + argsMemObj: [longArg, longArg], asmName: 'add_Minutes_to_Timestamp', declaration: 'long', sentences: [], name: 'Add_Minutes_To_Timestamp' + }, + { + argsMemObj: [], + asmName: 'Check_Sig_B_With_A', + declaration: 'long', + sentences: [], + name: 'Check_Sig_B_With_A' + }, + { + argsMemObj: [], + asmName: 'Get_Code_Hash_Id', + declaration: 'long', + sentences: [], + name: 'Get_Code_Hash_Id' + }, + { + argsMemObj: [], + asmName: 'B_To_Assets_Of_Tx_In_A', + declaration: 'void', + sentences: [], + name: 'B_To_Assets_Of_Tx_In_A' + }, + { + argsMemObj: [], + asmName: 'Get_Map_Value_Keys_In_A', + declaration: 'long', + sentences: [], + name: 'Get_Map_Value_Keys_In_A' + }, + { + argsMemObj: [], + asmName: 'Set_Map_Value_Keys_In_A', + declaration: 'void', + sentences: [], + name: 'Set_Map_Value_Keys_In_A' + }, + { + argsMemObj: [], + asmName: 'Issue_Asset', + declaration: 'long', + sentences: [], + name: 'Issue_Asset' + }, + { + argsMemObj: [], + asmName: 'Mint_Asset', + declaration: 'void', + sentences: [], + name: 'Mint_Asset' + }, + { + argsMemObj: [], + asmName: 'Distribute_To_Asset_Holders', + declaration: 'void', + sentences: [], + name: 'Distribute_To_Asset_Holders' + }, + { + argsMemObj: [], + asmName: 'Get_Asset_Holders_Count', + declaration: 'long', + sentences: [], + name: 'Get_Asset_Holders_Count' + }, + { + argsMemObj: [], + asmName: 'Get_Activation_Fee', + declaration: 'long', + sentences: [], + name: 'Get_Activation_Fee' + }, + { + argsMemObj: [], + asmName: 'Put_Last_Block_GSig_In_A', + declaration: 'void', + sentences: [], + name: 'Put_Last_Block_GSig_In_A' + }, + { + argsMemObj: [], + asmName: 'Get_Asset_Circulating', + declaration: 'long', + sentences: [], + name: 'Get_Asset_Circulating' + } +] + +export const fixedAPITableTemplate: SC_FUNCTION[] = [ + { + argsMemObj: [], + asmName: 'get_A1', + declaration: 'fixed', + sentences: [], + name: 'F_Get_A1' + }, + { + argsMemObj: [], + asmName: 'get_A2', + declaration: 'fixed', + sentences: [], + name: 'F_Get_A2' + }, + { + argsMemObj: [], + asmName: 'get_A3', + declaration: 'fixed', + sentences: [], + name: 'F_Get_A3' + }, + { + argsMemObj: [], + asmName: 'get_A4', + declaration: 'fixed', + sentences: [], + name: 'F_Get_A4' + }, + { + argsMemObj: [], + asmName: 'get_B1', + declaration: 'fixed', + sentences: [], + name: 'F_Get_B1' + }, + { + argsMemObj: [], + asmName: 'get_B2', + declaration: 'fixed', + sentences: [], + name: 'F_Get_B2' + }, + { + argsMemObj: [], + asmName: 'get_B3', + declaration: 'fixed', + sentences: [], + name: 'F_Get_B3' + }, + { + argsMemObj: [], + asmName: 'get_B4', + declaration: 'fixed', + sentences: [], + name: 'F_Get_B4' + }, + { + argsMemObj: [fixedArg], + asmName: 'set_A1', + declaration: 'void', + sentences: [], + name: 'F_Set_A1' + }, + { + argsMemObj: [fixedArg], + asmName: 'set_A2', + declaration: 'void', + sentences: [], + name: 'F_Set_A2' + }, + { + argsMemObj: [fixedArg], + asmName: 'set_A3', + declaration: 'void', + sentences: [], + name: 'F_Set_A3' + }, + { + argsMemObj: [fixedArg], + asmName: 'set_A4', + declaration: 'void', + sentences: [], + name: 'F_Set_A4' + }, + { + argsMemObj: [fixedArg], + asmName: 'set_B1', + declaration: 'void', + sentences: [], + name: 'F_Set_B1' + }, + { + argsMemObj: [fixedArg], + asmName: 'set_B2', + declaration: 'void', + sentences: [], + name: 'F_Set_B2' + }, + { + argsMemObj: [fixedArg], + asmName: 'set_B3', + declaration: 'void', + sentences: [], + name: 'F_Set_B3' + }, + { + argsMemObj: [fixedArg], + asmName: 'set_B4', + declaration: 'void', + sentences: [], + name: 'F_Set_B4' + }, + { + argsMemObj: [], + asmName: 'get_Amount_for_Tx_in_A', + declaration: 'fixed', + sentences: [], + name: 'F_Get_Amount_For_Tx_In_A' + }, + { + argsMemObj: [], + asmName: 'get_Current_Balance', + declaration: 'fixed', + sentences: [], + name: 'F_Get_Current_Balance' + }, + { + argsMemObj: [], + asmName: 'get_Previous_Balance', + declaration: 'fixed', + sentences: [], + name: 'F_Get_Previous_Balance' + }, + { + argsMemObj: [fixedArg], + asmName: 'send_to_Address_in_B', + declaration: 'void', + sentences: [], + name: 'F_Send_To_Address_In_B' + }, + { + argsMemObj: [], + asmName: 'Get_Map_Value_Keys_In_A', + declaration: 'fixed', + sentences: [], + name: 'F_Get_Map_Value_Keys_In_A' + }, + { + argsMemObj: [], + asmName: 'Get_Activation_Fee', + declaration: 'fixed', + sentences: [], + name: 'F_Get_Activation_Fee' } ] diff --git a/src/smartc.ts b/src/smartc.ts index e04f81f..b2050bd 100644 --- a/src/smartc.ts +++ b/src/smartc.ts @@ -36,10 +36,12 @@ import { CONTRACT, MACHINE_OBJECT } from './typings/contractTypes' export class SmartC { private readonly language private readonly sourceCode - private assemblyCode?: string + private preAssemblyCode?: string private MachineCode?: MACHINE_OBJECT private Program: CONTRACT = { + sourceLines: [], Global: { + BuiltInFunctions: [], APIFunctions: [], macros: [], sentences: [] @@ -49,26 +51,28 @@ export class SmartC { typesDefinitions: [], // Default configuration for compiler Config: { - compilerVersion: '1.0', - enableRandom: false, - enableLineLabels: false, + compilerVersion: '2.0.0', maxAuxVars: 3, maxConstVars: 0, optimizationLevel: 2, reuseAssignedVar: true, - sourcecodeVersion: '', - warningToError: true, APIFunctions: false, + fixedAPIFunctions: false, PName: '', PDescription: '', PActivationAmount: '', + PCreator: '', + PContract: '', PUserStackPages: 0, PCodeStackPages: 0, - outputSourceLineNumber: false - } + PCodeHashId: '', + verboseAssembly: false + }, + warnings: [] } constructor (Options: { language: 'C' | 'Assembly', sourceCode: string }) { + this.Program.sourceLines = Options.sourceCode.split('\n') this.language = Options.language this.sourceCode = Options.sourceCode } @@ -90,15 +94,16 @@ export class SmartC { parsed = parser(tokenized) shaper(this.Program, parsed) syntaxProcessor(this.Program) - this.assemblyCode = codeGenerator(this.Program) + this.preAssemblyCode = codeGenerator(this.Program) break case 'Assembly': - this.assemblyCode = this.sourceCode + this.preAssemblyCode = this.sourceCode break default: throw new Error('Invalid usage. Language must be "C" or "Assembly".') } - this.MachineCode = assembler(this.assemblyCode) + this.MachineCode = assembler(this.preAssemblyCode) + this.MachineCode.Warnings = this.Program.warnings.join('\n') return this } @@ -110,7 +115,7 @@ export class SmartC { if (!this.MachineCode) { throw new Error('Source code was not compiled.') } - return this.assemblyCode ?? '' + return this.MachineCode.AssemblyCode } /** diff --git a/src/syntaxProcessor/createTree.ts b/src/syntaxProcessor/createTree.ts index 130eb95..257891d 100644 --- a/src/syntaxProcessor/createTree.ts +++ b/src/syntaxProcessor/createTree.ts @@ -1,4 +1,4 @@ -import { assertNotUndefined } from '../repository/repository' +import { assertNotUndefined, isDeclarationType } from '../repository/repository' import { AST, LOOKUP_ASN, TOKEN } from '../typings/syntaxTypes' /** @@ -137,12 +137,37 @@ function VariableToAST (tokens: TOKEN[]) : AST { } function CodeCaveToAST (tokens: TOKEN[]) : AST { - if (tokens.length !== 1) { - throw new Error(`At line: ${tokens[0].line}. Modifiers not implemented on '${tokens[0].type}'.`) + if (tokens.length === 1) { + const newAST = createTree(tokens[0].params) + delete tokens[0].params + return newAST + } + if (tokens[0].params === undefined) { + return createTree(tokens.slice(1)) + } + if (tokens.length === 2) { + const remainingAST = createTree(tokens.slice(1)) + const askedType = tokens[0].params.reduce((previous, Tkn) => { + // Get declaration for type casting from params! + if (Tkn.type === 'Keyword') return previous + Tkn.value + if (Tkn.value === '*') return previous + '_ptr' + throw new Error(`At line: ${tokens[0].line}. Unexpected '${Tkn.type}' with value '${Tkn.value}' during type casting.`) + }, '') + if (!isDeclarationType(askedType)) { + throw new Error(`At line: ${tokens[0].line}. Unexpected declaration '${askedType}' during type casting.`) + } + if (askedType === 'struct') { + throw new Error(`At line: ${tokens[0].line}. 'struct' is not allowed for type casting.`) + } + tokens[0].declaration = askedType + delete tokens[0].params + return { + type: 'unaryASN', + Operation: tokens[0], + Center: remainingAST + } } - const newAST = createTree(tokens[0].params) - delete tokens[0].params - return newAST + throw new Error(`At line: ${tokens[0].line}. Modifiers not implemented on '${tokens[0].type}'.`) } function BinariesToAST (tokens: TOKEN[], operatorLoc: number) : AST { @@ -168,7 +193,6 @@ function KeywordToAST (tokens: TOKEN[], keywordLoc: number) : AST { ` Probable missing ';' before keyword ${tokens[keywordLoc].value}.`) } switch (tokens[0].value) { - case 'sleep': case 'goto': case 'const': case 'sizeof': @@ -191,6 +215,7 @@ function KeywordToAST (tokens: TOKEN[], keywordLoc: number) : AST { } return { type: 'endASN', Token: tokens[0] } case 'long': + case 'fixed': case 'void': case 'struct': if (tokens.length === 1) { @@ -208,6 +233,7 @@ function KeywordToAST (tokens: TOKEN[], keywordLoc: number) : AST { Center: createTree(tokens.slice(1)) } case 'return': + case 'sleep': if (tokens.length === 1) { return { type: 'endASN', Token: tokens[0] } } diff --git a/src/tokenizer/tokenizer.ts b/src/tokenizer/tokenizer.ts index 5d51bc0..e01c9e1 100644 --- a/src/tokenizer/tokenizer.ts +++ b/src/tokenizer/tokenizer.ts @@ -74,7 +74,7 @@ export default function tokenizer (inputSourceCode: string): PRE_TOKEN[] { addLength: 0 }, { // decimal numbers - start: /^(\d[\d_]*\b)/, + start: /(^\d[\d_.]*\b|^\.[\d_]+\b)/, pretokenType: 'numberDec', addLength: 0 }, @@ -84,7 +84,7 @@ export default function tokenizer (inputSourceCode: string): PRE_TOKEN[] { addLength: 2 }, { // regular keywords - start: /^(break|case|const|continue|default|do|else|exit|for|goto|halt|if|long|return|sleep|sizeof|switch|void|while)\b/, + start: /^(break|case|const|continue|default|do|else|exit|fixed|for|goto|halt|if|long|return|sleep|sizeof|switch|void|while)\b/, pretokenType: 'keyword', addLength: 0 }, diff --git a/src/typings/contractTypes.ts b/src/typings/contractTypes.ts index 2bb1286..34a300e 100644 --- a/src/typings/contractTypes.ts +++ b/src/typings/contractTypes.ts @@ -3,10 +3,6 @@ import { DECLARATION_TYPES, MEMORY_SLOT, SENTENCES, TOKEN, TYPE_DEFINITIONS } fr export type SC_CONFIG = { /** Hardcoded compiler version!!! */ compilerVersion: string, - /** Add random string to labels: #pragma enableRandom */ - enableRandom: boolean, - /** Add line number to labels: #pragma enableLineLabels */ - enableLineLabels: boolean, /** Number of auxiliary vars to be declared by compiler: #pragma maxAuxVars */ maxAuxVars: number, /** Number of auxiliary Constants to be declared by compiler: #pragma maxConstVars */ @@ -15,25 +11,28 @@ export type SC_CONFIG = { optimizationLevel: number, /** Try to reuse variable at left side of assigment: #pragma reuseAssignedVar */ reuseAssignedVar: boolean, - /** Default version for user's programs. If not on a dev version, user must - * specify #pragma version to set source code version */ - sourcecodeVersion: string, - /** Warning to error: #pragma warningToError */ - warningToError: boolean, /** Support for API Functions: #include APIFunctions */ APIFunctions: boolean, + /** Support for API Functions with fixed numbers: #include fixedAPIFunctions */ + fixedAPIFunctions: boolean, /** Program Name: #program name */ PName: string, /** Program description: #program description */ PDescription: string, /** Program activationAmount: #program activationAmount */ PActivationAmount: string, + /** Program creator: Used only in SC-Simulator. Ignored in machine code output. */ + PCreator: string, + /** Program contract ID: Used only in SC-Simulator. Ignored in machine code output. */ + PContract: string, /** User stack pages to be available: #program userStackPages */ PUserStackPages: number, /** Code stack pages to be available:: #program codeStackPages */ PCodeStackPages: number, - /** Adds a comment in generated assembly code with the respective C source code line number */ - outputSourceLineNumber: boolean, + /** Machine code hash id to be matched during compilation: #program codeHashId */ + PCodeHashId: string, + /** Adds a comment in generated assembly code with source code line number and content */ + verboseAssembly: boolean, } export type SC_MACRO = { @@ -68,6 +67,8 @@ export type SC_FUNCTION = { } export type SC_GLOBAL = { + /** Definitions for Built-In functions */ + BuiltInFunctions: SC_FUNCTION[] /** Definitions for API functions */ APIFunctions: SC_FUNCTION[] /** macros values */ @@ -79,6 +80,8 @@ export type SC_GLOBAL = { } export type CONTRACT = { + /** Source code splitted by lines */ + sourceLines: string[], /** Global statements and information */ Global: SC_GLOBAL, /** Declared functions */ @@ -89,9 +92,13 @@ export type CONTRACT = { typesDefinitions: TYPE_DEFINITIONS[], /** Compiler configurations */ Config: SC_CONFIG, + /** Compilation warnings */ + warnings: string[], } export type MACHINE_OBJECT = { + /** Warnings found */ + Warnings: string /** Number of data pages (Memory size) */ DataPages: number /** Number of code stack pages (code stack size) */ @@ -115,6 +122,8 @@ export type MACHINE_OBJECT = { label: string address: number }[] + /** Program assembly source code */ + AssemblyCode: string /** Program name */ PName: string /** Program description */ diff --git a/src/typings/syntaxTypes.ts b/src/typings/syntaxTypes.ts index 13dcb63..38f1ff5 100644 --- a/src/typings/syntaxTypes.ts +++ b/src/typings/syntaxTypes.ts @@ -6,13 +6,20 @@ export type PRE_TOKEN = { extValue?: string } +export type HEX_CONTENT = number | bigint | string + +export type CONSTANT_CONTENT = { + value: HEX_CONTENT + declaration: 'long' | 'fixed' +} + /** Allowed token types */ export type TOKEN_TYPES = 'Variable' | 'Constant' | 'Operator' | 'UnaryOperator' | 'SetUnaryOperator' | 'Assignment'| 'SetOperator'|'Comparision'|'CheckOperator'| 'Arr'|'CodeCave'|'CodeDomain'|'Delimiter'|'Terminator'|'Macro'|'Member'|'Colon'| -'Keyword'|'Function' | 'APICall' | 'Push' +'Keyword'|'Function' | 'APICall' | 'BuiltInCall' | 'Push' -export type DECLARATION_TYPES = 'void' | 'long' | 'struct' | 'void_ptr' | 'long_ptr' | 'struct_ptr' | '' +export type DECLARATION_TYPES = 'void' | 'long' | 'fixed' | 'struct' | 'void_ptr' | 'long_ptr' | 'fixed_ptr' | 'struct_ptr' | '' export type TOKEN = { line: number @@ -23,11 +30,11 @@ export type TOKEN = { value: string /** Only applicable to Arr, CodeCave, CodeDomain, Variable with modifier */ params?: TOKEN[] - /** Only applicable to types: asm, break, continue, struct or label */ + /** Only applicable to types: asm, break, continue, constant, struct or label */ extValue?: string } -export type MEMORY_BASE_TYPES = 'register' | 'long' | 'constant' | 'struct' | 'structRef' | 'array' | 'label' | 'void' +export type MEMORY_BASE_TYPES = 'register' | 'long' | 'fixed' | 'constant' | 'struct' | 'structRef' | 'array' | 'label' | 'void' /** If constant, it is the number to shift. If variable, it is the address containing the value to shift. * Stores information about variable it is pointing to. @@ -256,4 +263,9 @@ export type LONG_TYPE_DEFINITION = { name: '', MemoryTemplate: MEMORY_SLOT } -export type TYPE_DEFINITIONS = STRUCT_TYPE_DEFINITION | ARRAY_TYPE_DEFINITION | REGISTER_TYPE_DEFINITION | LONG_TYPE_DEFINITION +export type FIXED_TYPE_DEFINITION = { + type: 'fixed' + name: '', + MemoryTemplate: MEMORY_SLOT +} +export type TYPE_DEFINITIONS = STRUCT_TYPE_DEFINITION | ARRAY_TYPE_DEFINITION | REGISTER_TYPE_DEFINITION | LONG_TYPE_DEFINITION | FIXED_TYPE_DEFINITION