From 9bc1e93f3b40d30a18cfa409c4ae83af2dd5e8d2 Mon Sep 17 00:00:00 2001 From: deleterium Date: Sat, 16 Oct 2021 09:48:14 -0300 Subject: [PATCH 1/4] Commemorative contract: Hive, the tumbler --- commemorative/README.md | 3 + commemorative/v0.3_Hive_The_Tumbler.md | 432 +++++++++++++++++++++++++ 2 files changed, 435 insertions(+) create mode 100644 commemorative/v0.3_Hive_The_Tumbler.md diff --git a/commemorative/README.md b/commemorative/README.md index 5af27df..b8533d0 100644 --- a/commemorative/README.md +++ b/commemorative/README.md @@ -3,3 +3,6 @@ Be owner of SmartC keywords, support the project and also make an investment! Th ## v0.2 - Promotional Raffle 227 Advertise your brand making a fair raffle on signum blockchain! [Details](./v0.2_PromotionalRaffle227) + +## v0.3 - Hive, the tumbler +Avoid eavesdroppers to track your transactions and add a layer of obfuscation in your payments. This swarm of 257 contracts is working for you! [Details](./v0.3_Hive_The_Tumbler) diff --git a/commemorative/v0.3_Hive_The_Tumbler.md b/commemorative/v0.3_Hive_The_Tumbler.md new file mode 100644 index 0000000..c603404 --- /dev/null +++ b/commemorative/v0.3_Hive_The_Tumbler.md @@ -0,0 +1,432 @@ +# 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** +contracts to be deployed after 'Signum Speedway' fork, to take advantage of Carbon copy smarts contracts new feature. + +## Bee contract +Bees transport signa between the users and the **Hive contract**. There are 256 equal contracts. If they receive a big load from the hive, they will bounce it back, actually just delaying processing. If a bee receive a small load (less than 10 signa) it will deliver to destination. + +## Hive contract +Hive splits transactions and distribute pseudo-randomly between the bees. Its work is to ensure division in optimal payloads to the bees. Every source transaction can be splitted in maximal 16 transactions. + +## Working logic +* User **A** sends signa with a message with destination account **D** to a bee **B1**. +* B1 checks the amount. If is greater than 10 signa than deliver it to the hive **H**. If it is not, then it delivers the signa to destination D. +* H checks the amount and divide in N optimal parts, sending it to random bees **B?**. +* B? checks the amount and the loop can start again, or end with a transaction to D. + +## How to use +User A must send signa and a message to any bee. This message must be scrambled (XOR operation) with bee address and hive address. To make easier, check the website XXXXX where the calculation can be done. Set the destination, the amount to send (or the amount to be received by destination). The page will present the information need to make the transaction. Note that many contracts activations will cost a fee, around 2%. The value depends on how many hops will happen, but the webpage will show the exactly value. The process will take from 2 to 50 blocks until all balance to be delivered. + +## Error handling +If user send any amount to the bees or hive without the right verification code, the contracts will refund the amount. But on second wrong in a row, the bees contracts will hold the balance. If this happen, contact me for reimbursement. + +## Nerdy details +* Contracts with random delayed activation that will cause different transactions to mix during bounces +* Binary messages between sender-contracts and contracts-contracts are human-unfriendly +* Binary messages encoded with recipient bee address, so sending balance to same destination thru two different bees will have two different messages +* Hive chooses bees order in a pseudo random way, using simple linear feedback shift register + +## Master of confusion +* Send in amounts multiple of one bee payload. The best is to send amount that will be received in 16 bees payloads. +* Send also the same amount to self, or triangulating with a third account. + +## How to test +Contract is loaded in signum testnet and can be tested. Use the page https://deleterium.info/HiveTestnet/ for instructions. For more details check `SmartC Compiler` server on Discord. + +## Smart contract source code +For the brave: +```c +// Compilation options +// Choose only one to compile +//#define BEE_CONTRACT +#define HIVE_CONTRACT + +// Use if compiling to SC-Simulator +//#define SIMULATOR + +// Full size for 256 bees, otherwise 32 bees +#define FULL_SIZE + +// Default values +#define HIVE_ACTIVATION 8687_7000 +#define BEE_ACTIVATION 1036_3500 +#define BEE_PAYLOAD 9_1707_1500 + +// Accounts information +#ifdef SIMULATOR + #define HIVE_ACCOUNT 999 +#else + #define HIVE_ACCOUNT '??????' +#endif + +// Used for sleep when contracts are under low load +#define MAX_SLEEP_MASK 0x07 +#define MAX_SLEEP_HIVE 12 +#define MAX_SLEEP_BEE 8 + +// Common options +//#pragma version 0.3 +#pragma globalOptimization +#include APIFunctions + +// Common variables +struct TXINFO { + long txId; + long timestamp; + long sender; + long amount; + long recipient; + long verifier; +} currentTX; +long nextSleep, queenBeeWithdraw; + +#ifdef BEE_CONTRACT + + #program name Bee + #program description Worker bee + #program activationAmount BEE_ACTIVATION + + #pragma maxAuxVars 2 + #pragma maxConstVars 1 + + #ifdef SIMULATOR + // Disregard first activation + B_To_Address_Of_Creator(); + Send_All_To_Address_In_B(); + #endif + const long hive = HIVE_ACCOUNT; + const long beePayload = BEE_PAYLOAD; + // Accept overload to avoid multiple bounce when amount is near bee payload + const long triggerBounce = BEE_PAYLOAD + HIVE_ACTIVATION; + // Queen Bee is the creator and also verifier for messages + B_To_Address_Of_Creator(); + long queenBee = Get_B1(); + long ownAddress; + + // Phase 1 + // Worker bee has born. Inform hive. + while (Get_Current_Balance() < 2_9000_0000) { + // Wait to have at least 2.9 signa for startup + // Doing this way because SC can not loop + // thru messages received by multi-out payments + halt; + } + Set_B1(hive); + Set_A1_A2('newborn', queenBee); + Send_A_To_Address_In_B(); + Send_To_Address_In_B(1_8000_0000); + + // Phase 2 + // Wait to know own name. Hive will set it. + do { + A_To_Tx_After_Timestamp(currentTX.timestamp); + if (Get_A1() == 0) { + halt; + continue; + } + currentTX.amount = Get_Amount_For_Tx_In_A(); + currentTX.timestamp = Get_Timestamp_For_Tx_In_A(); + Message_From_Tx_In_A_To_B(); + ownAddress = Get_B1(); + currentTX.verifier = Get_B2(); + nextSleep = MAX_SLEEP_BEE - (Get_A1() & MAX_SLEEP_MASK); + } while (currentTX.verifier != 'ownAcc'); + + // Inform success + B_To_Address_Of_Creator(); + Set_A1_A2('Setup fi','nished!'); + Send_A_To_Address_In_B(); + Send_To_Address_In_B(Get_Current_Balance() - BEE_ACTIVATION); + + // Phase 3 + // Lifecycle of worker bee + void main (void) { + long lastRefundedAccount; + + // Lazy activation + sleep nextSleep; + + // Loop thru transactions queue + do { + A_To_Tx_After_Timestamp(currentTX.timestamp); + currentTX.txId = Get_A1(); + if (currentTX.txId == 0) { + if (queenBeeWithdraw) { + // Process withdraw only when there is no more pending transactions + queenBeeWithdraw = false; + B_To_Address_Of_Creator(); + Send_All_To_Address_In_B(); + continue; + } + exit; + } + + //fill transaction information + currentTX.amount = Get_Amount_For_Tx_In_A(); + currentTX.timestamp = Get_Timestamp_For_Tx_In_A(); + Message_From_Tx_In_A_To_B(); + currentTX.recipient = Get_B1(); + currentTX.verifier = Get_B2(); + + if (currentTX.verifier != queenBee) { + // Instruction not from hive, refund. + B_To_Address_Of_Tx_In_A(); + currentTX.sender = Get_B1(); + if (currentTX.sender == lastRefundedAccount) { + // Avoid situation where messages being bounced between bees + // If user send two consecutive wrong transactions, one must ask reimburse. + if (currentTX.sender == queenBee) { + // Creator can use this strategy to claim bee balance + queenBeeWithdraw = true; + } + continue; + } + lastRefundedAccount = currentTX.sender; + Send_To_Address_In_B(currentTX.amount); + } else if (currentTX.amount > triggerBounce) { + // Bounce transaction to hive + Set_A1_A2(currentTX.recipient ^ ownAddress, queenBee); + Set_B1(hive); + Send_A_To_Address_In_B(); + Send_To_Address_In_B(currentTX.amount); + sleep 1; + } else { + // End point + Set_B1(currentTX.recipient ^ hive ^ ownAddress); + Send_To_Address_In_B(currentTX.amount); + nextSleep = MAX_SLEEP_BEE - (currentTX.txId & MAX_SLEEP_MASK); + sleep 1; + } + } while (true); + } + +#endif + + +#ifdef HIVE_CONTRACT + + #program name Hive + #program description Manage the bee workers + #program activationAmount HIVE_ACTIVATION + + #pragma maxAuxVars 2 + #pragma maxConstVars 4 + + #define DISPATCH_LOOP_ACTIVATION (67 * 7_3500) + #ifdef FULL_SIZE + #define HIVE_SIZE 256 + #define HIVE_SIZE_MASK 255 + #else + #define HIVE_SIZE 32 + #define HIVE_SIZE_MASK 31 + #endif + + struct STATS { + long welcomedBees, dispatchedBees; + } stats; + + struct LSFR { + long seed; + long currentMagic; + long magic[4]; + } lsfr; + + struct BEECONTROL { + long lastBusyBee, currentBee, flushBees; + } beeControl; + + const long n32 = 32, n61 = 61, n64 = 64; + const lsfr.seed = 1; + #ifdef FULL_SIZE + const lsfr.magic[0] = 149; + const lsfr.magic[1] = 166; + const lsfr.magic[2] = 243; + const lsfr.magic[3] = 250; + #else + const lsfr.magic[0] = 20; + const lsfr.magic[1] = 23; + const lsfr.magic[2] = 27; + const lsfr.magic[3] = 30; + #endif + const long beePayload = BEE_PAYLOAD + BEE_ACTIVATION; + const long bounceLoad = 16 * (BEE_PAYLOAD + BEE_ACTIVATION) + HIVE_ACTIVATION + BEE_ACTIVATION; + const long doubleBounceLoad = 2 * + (16 * (BEE_PAYLOAD + BEE_ACTIVATION) + HIVE_ACTIVATION + BEE_ACTIVATION); + const long squareBounceLoad = 16 * + (16 * (BEE_PAYLOAD + BEE_ACTIVATION) + HIVE_ACTIVATION + BEE_ACTIVATION) + + HIVE_ACTIVATION + BEE_ACTIVATION; + long triggerSquareBounceLoad = squareBounceLoad + bounceLoad; + long queenBee, pseudoRandomSeed; + long workerBee[HIVE_SIZE]; + + #ifdef SIMULATOR + // disregard first activation + B_To_Address_Of_Creator(); + Send_All_To_Address_In_B(); + #endif + + // Queen Bee is the creator and also verifier for messages + B_To_Address_Of_Creator(); + queenBee = Get_B1(); + // Setup phase + setupHive(); + // Refund setup amount + B_To_Address_Of_Creator(); + Set_A1_A2('Setup fi','nished!'); + Send_A_To_Address_In_B(); + Send_To_Address_In_B(Get_Current_Balance() - HIVE_ACTIVATION); + + void main(void) { + long refundableDispatchLoops; + + // Lazy activation + sleep MAX_SLEEP_HIVE - (pseudoRandomSeed & MAX_SLEEP_MASK); + lsfr.currentMagic = lsfr.magic[pseudoRandomSeed & 3]; + beeControl.lastBusyBee = lsfr.seed; + beeControl.flushBees = 0; + + // Loop thru transactions queue // Use TxId as randomness source + for (A_To_Tx_After_Timestamp(currentTX.timestamp); + (currentTX.txId = Get_A1()) != 0; + A_To_Tx_After_Timestamp(currentTX.timestamp)) + { + //fill transaction information + getTxDetails(); + // Seed new random + pseudoRandomSeed = currentTX.txId; + + if (currentTX.verifier != queenBee) { + B_To_Address_Of_Tx_In_A(); + currentTX.sender = Get_B1(); + // If user send two consecutive wrong messages, one must ask reimburse. + if (Get_B1() == queenBee) { + // Creator can use this strategy to claim hive balance + queenBeeWithdraw = true; + continue; + } + // Not welcome bee... Refund + Send_To_Address_In_B(currentTX.amount); + continue; + } + + stats.welcomedBees++; + // Bees dispatch loop with optimal payloads + refundableDispatchLoops = 15; + if (currentTX.amount >= doubleBounceLoad) { + // minimum is bounceLoad + while (currentTX.amount >= doubleBounceLoad && refundableDispatchLoops != 0) { + refundableDispatchLoops--; + if (currentTX.amount >= triggerSquareBounceLoad) { + dispatchBee(squareBounceLoad); + currentTX.amount -= squareBounceLoad; + } else { + dispatchBee(bounceLoad); + currentTX.amount -= bounceLoad; + } + } + } else { + // maximum is bounceLoad + while (currentTX.amount > beePayload && refundableDispatchLoops != 0) { + refundableDispatchLoops--; + if (currentTX.amount >= bounceLoad) { + dispatchBee(bounceLoad); + currentTX.amount -= bounceLoad; + } else { + dispatchBee(beePayload); + currentTX.amount -= beePayload; + } + } + } + // Refund unused loops + currentTX.amount += refundableDispatchLoops * DISPATCH_LOOP_ACTIVATION; + // Last bee with remaining balance, if any. + if (currentTX.amount > BEE_ACTIVATION) { + // Only send balance to bee if it will be processed. + dispatchBee(currentTX.amount); + } + } + + if (queenBeeWithdraw) { + // Process withdraw only when there is no more pending transactions + queenBeeWithdraw = false; + B_To_Address_Of_Creator(); + Send_To_Address_In_B(Get_Current_Balance() - HIVE_ACTIVATION); + } + } + + // Controll bee dispatch, choose bee and send transaction + void dispatchBee(long amount) { + if (beeControl.flushBees == 2) { + // Flush all bees + sleep 1; + lsfr.currentMagic = lsfr.magic[pseudoRandomSeed & 3]; + // beeControl.lastBusyBee = lsfr.seed; + beeControl.flushBees = 0; + } + if (beeControl.flushBees == 1) { + // Use bee zero but next time flush all + beeControl.currentBee = 0; + beeControl.flushBees = 2; + } else { + // Pick a 'random' bee using linear-feedback shift register + if (!(lsfr.seed & 1)){ + lsfr.seed >>= 1; + } else { + lsfr.seed >>= 1; + lsfr.seed ^= lsfr.currentMagic; + } + beeControl.currentBee = lsfr.seed; + + if (beeControl.currentBee == beeControl.lastBusyBee) { + // Use last bee and signal to use bee zero next time + beeControl.flushBees = 1; + } + } + // Dispatch transaction + Set_B1(workerBee[beeControl.currentBee]); + Set_A1_A2(workerBee[beeControl.currentBee] ^ currentTX.recipient, queenBee); + Send_A_To_Address_In_B(); + Send_To_Address_In_B(amount); + // Update stats + stats.dispatchedBees++; + } + + // Flow control receiving and responding bees start up. Only these transactions + // will be processed. + void setupHive(void) { + do { + A_To_Tx_After_Timestamp(currentTX.timestamp); + if (Get_A1() == 0) { + halt; + continue; + } + // Fill transaction information + getTxDetails(); + B_To_Address_Of_Tx_In_A(); + currentTX.sender = Get_B1(); + if (currentTX.recipient == 'newborn' && currentTX.verifier == queenBee) { + // Add new worker bee to database + workerBee[stats.welcomedBees] = currentTX.sender; + stats.welcomedBees++; + // Seed random with txid + pseudoRandomSeed = Get_A1(); + // Inform worker bee his name + Set_A1_A2(currentTX.sender, 'ownAcc'); + Set_B1(currentTX.sender); + Send_A_To_Address_In_B(); + Send_To_Address_In_B(currentTX.amount); + } + } while (stats.welcomedBees != workerBee.length); + } + + 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.recipient = Get_B1(); + currentTX.verifier = Get_B2(); + } + +#endif +``` \ No newline at end of file From 3527af1e91b4066b26980fb2a8e4f46e983d5451 Mon Sep 17 00:00:00 2001 From: deleterium Date: Sat, 16 Oct 2021 09:49:07 -0300 Subject: [PATCH 2/4] Adding Changelog --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++++ README.md | 8 +++++++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6f65411 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,43 @@ +## [v0.3](https://github.com/deleterium/SmartC/tree/v0.3) (2021-10-16) + +[Commits](https://github.com/deleterium/SmartC/commits/v0.3) + +- Source code refactored from Javascript to Typescript +- Included inline comments on interfaces and types +- Massive improvement on compiler error messages +- Functions can return struct pointer +- Recursive functions +- Support for using functions with modifiers `Array` or `Member` Ex: *a = test()->next;* +- Improved rules for pointer variables verification +- Added void pointer variables +- Added property **.length** to get array size +- Special function **void catch(void)** to handle execution exceptions +- Macro 'userStackPages' and 'codeStackPages' for fine tuning memory parameters +- Struct can have recursive definition of its pointer +- More optimizations on constant variables (thru variables named nNUMBER) +- Added Machine Code Hash Information +- Improved preprocessor with **#ifdef**, **#ifndef**, **#else**, **#endif** directives +- Copy to clipboard button for easy use with SC-Simulator +- Added macro 'outputSourceLineNumber' to add verbosity in assembly generated code +- Project integration with SonarCloud and fix security vulnerabilities in regex expressions +- Increased test coverage for wrong source code + + +## [v0.2](https://github.com/deleterium/SmartC/tree/v0.2) (2021/07/23) + +[Commits](https://github.com/deleterium/SmartC/commits/v0.2) + +- Syntax highlight for source code textarea +- Fine tuning globalOptimization +- Added **#define** and **#undef** preprocessor directives. + +## [v0.1](https://github.com/deleterium/SmartC/tree/v0.1) (2021/06/30) + +[Commits](https://github.com/deleterium/SmartC/commits/v0.1) + +- Initial working release + +## v0 (2021/04/06) + + +- [Initial release](https://github.com/deleterium/SmartC/tree/bb3edafcf0d3db0153201b594157555d686a9962) in Github diff --git a/README.md b/README.md index db2d035..62738de 100644 --- a/README.md +++ b/README.md @@ -13,5 +13,11 @@ Some information and explanation about the project for users that aren't program ## Documentation [Docs](https://deleterium.github.io/SmartC/docs/) are availabe with detailed technical information about the project. Check also [SmartC playlist on Youtube](https://www.youtube.com/playlist?list=PLyu0NNtb1eg3Gcg2JCrOle8MjtuFPb-Gi), with videos for starting, simulating and deploying a smart contract. +## Changelog +Find [here](https://deleterium.github.io/SmartC/CHANGELOG) major upgrades between releases. + ## Support -Did you like the project? Consider be owner of one SmartC NFT keyword. The smart contract is online at S-2Z65-L478-VKD4-386YY. More information [here](https://deleterium.info/NFT/). Check also other [commemorative](./commemorative/) smart contracts. My address on signum: S-DKVF-VE8K-KUXB-DELET. +Did you like the project? Consider be owner of one SmartC NFT keyword. The smart contract is online at S-2Z65-L478-VKD4-386YY. More information on my [personal page](https://deleterium.info/NFT/). Check also other [commemorative](https://deleterium.github.io/SmartC/commemorative/) smart contracts. My address on signum: S-DKVF-VE8K-KUXB-DELET. + +## Social media +Join **SmartC Compiler** server in Discord to stay tuned on news or make questions. From a27a4b47a63e75d28a13400078f1ea15219ac6e7 Mon Sep 17 00:00:00 2001 From: deleterium Date: Sat, 16 Oct 2021 09:54:15 -0300 Subject: [PATCH 3/4] Snapshot of 'dev' release files to 'v0.3' folder --- v0.3/3rd-party/highlight.min.js | 340 +++++ v0.3/3rd-party/styles/tomorrow.css | 173 +++ v0.3/3rd-party/winbox.bundle.js | 23 + v0.3/favicon.svg | 147 ++ v0.3/index.html | 99 ++ v0.3/index.js | 351 +++++ v0.3/out/asmHighlight.js | 356 +++++ v0.3/out/bytecoder.js | 497 ++++++ v0.3/out/generator.js | 2240 ++++++++++++++++++++++++++++ v0.3/out/hashCode.js | 159 ++ v0.3/out/optimizer.js | 457 ++++++ v0.3/out/parser.js | 539 +++++++ v0.3/out/preprocessor.js | 141 ++ v0.3/out/shaper.js | 1635 ++++++++++++++++++++ v0.3/out/syntaxProcessor.js | 265 ++++ v0.3/out/testcases.js | 1272 ++++++++++++++++ v0.3/out/tokenizer.js | 221 +++ v0.3/out/utils.js | 294 ++++ v0.3/style.css | 241 +++ 19 files changed, 9450 insertions(+) create mode 100644 v0.3/3rd-party/highlight.min.js create mode 100644 v0.3/3rd-party/styles/tomorrow.css create mode 100644 v0.3/3rd-party/winbox.bundle.js create mode 100644 v0.3/favicon.svg create mode 100644 v0.3/index.html create mode 100644 v0.3/index.js create mode 100644 v0.3/out/asmHighlight.js create mode 100644 v0.3/out/bytecoder.js create mode 100644 v0.3/out/generator.js create mode 100644 v0.3/out/hashCode.js create mode 100644 v0.3/out/optimizer.js create mode 100644 v0.3/out/parser.js create mode 100644 v0.3/out/preprocessor.js create mode 100644 v0.3/out/shaper.js create mode 100644 v0.3/out/syntaxProcessor.js create mode 100644 v0.3/out/testcases.js create mode 100644 v0.3/out/tokenizer.js create mode 100644 v0.3/out/utils.js create mode 100644 v0.3/style.css diff --git a/v0.3/3rd-party/highlight.min.js b/v0.3/3rd-party/highlight.min.js new file mode 100644 index 0000000..278e1ef --- /dev/null +++ b/v0.3/3rd-party/highlight.min.js @@ -0,0 +1,340 @@ +/*! + Highlight.js v11.0.1 (git: 1cf31f015d) + (c) 2006-2021 Ivan Sagalaev and other contributors + License: BSD-3-Clause + Tunned for SmartC: https://github.com/deleterium/SmartC + */ +var hljs=function(){"use strict";var e={exports:{}};function t(e){ +return e instanceof Map?e.clear=e.delete=e.set=()=>{ +throw Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((n=>{var i=e[n] +;"object"!=typeof i||Object.isFrozen(i)||t(i)})),e} +e.exports=t,e.exports.default=t;var n=e.exports;class i{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function r(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function s(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t] +;return t.forEach((e=>{for(const t in e)n[t]=e[t]})),n}const o=e=>!!e.kind +;class a{constructor(e,t){ +this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){ +this.buffer+=r(e)}openNode(e){if(!o(e))return;let t=e.kind +;t=e.sublanguage?"language-"+t:((e,{prefix:t})=>{if(e.includes(".")){ +const n=e.split(".") +;return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ") +}return`${t}${e}`})(t,{prefix:this.classPrefix}),this.span(t)}closeNode(e){ +o(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}class l{constructor(){this.rootNode={ +children:[]},this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const t={kind:e,children:[]} +;this.add(t),this.stack.push(t)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){ +return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t), +t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +l._collapse(e)})))}}class c extends l{constructor(e){super(),this.options=e} +addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())} +addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root +;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){ +return new a(this,this.options).value()}finalize(){return!0}}function g(e){ +return e?"string"==typeof e?e:e.source:null}function d(...e){ +return e.map((e=>g(e))).join("")}function u(...e){return"("+((e=>{ +const t=e[e.length-1] +;return"object"==typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{} +})(e).capture?"":"?:")+e.map((e=>g(e))).join("|")+")"}function h(e){ +return RegExp(e.toString()+"|").exec("").length-1} +const f=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function p(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n +;let i=g(e),r="";for(;i.length>0;){const e=f.exec(i);if(!e){r+=i;break} +r+=i.substring(0,e.index), +i=i.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?r+="\\"+(Number(e[1])+t):(r+=e[0], +"("===e[0]&&n++)}return r})).map((e=>`(${e})`)).join(t)} +const b="[a-zA-Z]\\w*",m="[a-zA-Z_]\\w*",E="\\b\\d+(\\.\\d+)?",x="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",y="\\b(0b[01]+)",w={ +begin:"\\\\[\\s\\S]",relevance:0},_={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[w]},v={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[w]},O=(e,t,n={})=>{const i=s({scope:"comment",begin:e,end:t, +contains:[]},n);i.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=u("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return i.contains.push({begin:d(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i +},k=O("//","$"),N=O("/\\*","\\*/"),S=O("#","$");var M=Object.freeze({ +__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:b,UNDERSCORE_IDENT_RE:m, +NUMBER_RE:E,C_NUMBER_RE:x,BINARY_NUMBER_RE:y, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const t=/^#![ ]*\// +;return e.binary&&(e.begin=d(t,/.*\b/,e.binary,/\b.*/)),s({scope:"meta",begin:t, +end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)}, +BACKSLASH_ESCAPE:w,APOS_STRING_MODE:_,QUOTE_STRING_MODE:v,PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},COMMENT:O,C_LINE_COMMENT_MODE:k,C_BLOCK_COMMENT_MODE:N,HASH_COMMENT_MODE:S, +NUMBER_MODE:{scope:"number",begin:E,relevance:0},C_NUMBER_MODE:{scope:"number", +begin:x,relevance:0},BINARY_NUMBER_MODE:{scope:"number",begin:y,relevance:0}, +REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//, +end:/\/[gimuy]*/,illegal:/\n/,contains:[w,{begin:/\[/,end:/\]/,relevance:0, +contains:[w]}]}]},TITLE_MODE:{scope:"title",begin:b,relevance:0}, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:m,relevance:0},METHOD_GUARD:{ +begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{ +t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function R(e,t){ +"."===e.input[e.index-1]&&t.ignoreMatch()}function j(e,t){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function A(e,t){ +t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=R,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function I(e,t){ +Array.isArray(e.illegal)&&(e.illegal=u(...e.illegal))}function B(e,t){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function T(e,t){ +void 0===e.relevance&&(e.relevance=1)}const L=(e,t)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t] +})),e.keywords=n.keywords, +e.begin=d(n.beforeMatch,d("(?=",n.begin,")")),e.starts={relevance:0, +contains:[Object.assign(n,{endsParent:!0})]},e.relevance=0,delete n.beforeMatch +},D=["of","and","for","in","not","or","if","then","parent","list","value"] +;function P(e,t,n="keyword"){const i=Object.create(null) +;return"string"==typeof e?r(n,e.split(" ")):Array.isArray(e)?r(n,e):Object.keys(e).forEach((n=>{ +Object.assign(i,P(e[n],t,n))})),i;function r(e,n){ +t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|") +;i[n[0]]=[e,C(n[0],n[1])]}))}}function C(e,t){ +return t?Number(t):(e=>D.includes(e.toLowerCase()))(e)?0:1}const H={},$=e=>{ +console.error(e)},U=(e,...t)=>{console.log("WARN: "+e,...t)},z=(e,t)=>{ +H[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),H[`${e}/${t}`]=!0) +},K=Error();function W(e,t,{key:n}){let i=0;const r=e[n],s={},o={} +;for(let e=1;e<=t.length;e++)o[e+i]=r[e],s[e+i]=!0,i+=h(t[e-1]) +;e[n]=o,e[n]._emit=s,e[n]._multi=!0}function X(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw $("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +K +;if("object"!=typeof e.beginScope||null===e.beginScope)throw $("beginScope must be object"), +K;W(e,e.begin,{key:"beginScope"}),e.begin=p(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw $("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +K +;if("object"!=typeof e.endScope||null===e.endScope)throw $("endScope must be object"), +K;W(e,e.end,{key:"endScope"}),e.end=p(e.end,{joinWith:""})}})(e)}function G(e){ +function t(t,n){return RegExp(g(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))} +class n{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,t){ +t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]), +this.matchAt+=h(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=t(p(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const t=this.matcherRe.exec(e);if(!t)return null +;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),i=this.matchIndexes[n] +;return t.splice(0,n),Object.assign(t,i)}}class i{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n +;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))), +t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){ +this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){ +const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex +;let n=t.exec(e) +;if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{ +const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)} +return n&&(this.regexIndex+=n.position+1, +this.regexIndex===this.count&&this.considerAll()),n}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=s(e.classNameAliases||{}),function n(r,o){const a=r +;if(r.isCompiled)return a +;[j,B,X,L].forEach((e=>e(r,o))),e.compilerExtensions.forEach((e=>e(r,o))), +r.__beforeBegin=null,[A,I,T].forEach((e=>e(r,o))),r.isCompiled=!0;let l=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +l=r.keywords.$pattern, +delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=P(r.keywords,e.case_insensitive)), +a.keywordPatternRe=t(l,!0), +o&&(r.begin||(r.begin=/\B|\b/),a.beginRe=t(r.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(a.endRe=t(r.end)), +a.terminatorEnd=g(r.end)||"",r.endsWithParent&&o.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+o.terminatorEnd)), +r.illegal&&(a.illegalRe=t(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>s(e,{ +variants:null},t)))),e.cachedVariants?e.cachedVariants:Z(e)?s(e,{ +starts:e.starts?s(e.starts):null +}):Object.isFrozen(e)?s(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{n(e,a) +})),r.starts&&n(r.starts,o),a.matcher=(e=>{const t=new i +;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(a),a}(e)}function Z(e){ +return!!e&&(e.endsWithParent||Z(e.starts))}const F=r,V=s,q=Symbol("nomatch") +;var J=(e=>{const t=Object.create(null),r=Object.create(null),s=[];let o=!0 +;const a="Could not find the language '{}', did you forget to load/include a language module?",l={ +disableAutodetect:!0,name:"Plain text",contains:[]};let g={ +ignoreUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:c};function d(e){ +return g.noHighlightRe.test(e)}function u(e,t,n,i){let r="",s="" +;"object"==typeof t?(r=e, +n=t.ignoreIllegals,s=t.language,i=void 0):(z("10.7.0","highlight(lang, code, ...args) has been deprecated."), +z("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +s=e,r=t),void 0===n&&(n=!0);const o={code:r,language:s};w("before:highlight",o) +;const a=o.result?o.result:h(o.language,o.code,n,i) +;return a.code=o.code,w("after:highlight",a),a}function h(e,n,r,s){ +const l=Object.create(null);function c(){if(!k.keywords)return void S.addText(M) +;let e=0;k.keywordPatternRe.lastIndex=0;let t=k.keywordPatternRe.exec(M),n="" +;for(;t;){n+=M.substring(e,t.index) +;const r=_.case_insensitive?t[0].toLowerCase():t[0],s=(i=r,k.keywords[i]);if(s){ +const[e,i]=s +;if(S.addText(n),n="",l[r]=(l[r]||0)+1,l[r]<=7&&(R+=i),e.startsWith("_"))n+=t[0];else{ +const n=_.classNameAliases[e]||e;S.addKeyword(t[0],n)}}else n+=t[0] +;e=k.keywordPatternRe.lastIndex,t=k.keywordPatternRe.exec(M)}var i +;n+=M.substr(e),S.addText(n)}function d(){null!=k.subLanguage?(()=>{ +if(""===M)return;let e=null;if("string"==typeof k.subLanguage){ +if(!t[k.subLanguage])return void S.addText(M) +;e=h(k.subLanguage,M,!0,N[k.subLanguage]),N[k.subLanguage]=e._top +}else e=f(M,k.subLanguage.length?k.subLanguage:null) +;k.relevance>0&&(R+=e.relevance),S.addSublanguage(e._emitter,e.language) +})():c(),M=""}function u(e,t){let n=1;for(;void 0!==t[n];){if(!e._emit[n]){n++ +;continue}const i=_.classNameAliases[e[n]]||e[n],r=t[n] +;i?S.addKeyword(r,i):(M=r,c(),M=""),n++}}function p(e,t){ +return e.scope&&"string"==typeof e.scope&&S.openNode(_.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(S.addKeyword(M,_.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +M=""):e.beginScope._multi&&(u(e.beginScope,t),M="")),k=Object.create(e,{parent:{ +value:k}}),k}function b(e,t,n){let r=((e,t)=>{const n=e&&e.exec(t) +;return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){const n=new i(e) +;e["on:end"](t,n),n.isMatchIgnored&&(r=!1)}if(r){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return b(e.parent,t,n)}function m(e){ +return 0===k.matcher.regexIndex?(M+=e[0],1):(I=!0,0)}function x(e){ +const t=e[0],i=n.substr(e.index),r=b(k,e,i);if(!r)return q;const s=k +;k.endScope&&k.endScope._wrap?(d(), +S.addKeyword(t,k.endScope._wrap)):k.endScope&&k.endScope._multi?(d(), +u(k.endScope,e)):s.skip?M+=t:(s.returnEnd||s.excludeEnd||(M+=t), +d(),s.excludeEnd&&(M=t));do{ +k.scope&&!k.isMultiClass&&S.closeNode(),k.skip||k.subLanguage||(R+=k.relevance), +k=k.parent}while(k!==r.parent) +;return r.starts&&p(r.starts,e),s.returnEnd?0:t.length}let y={};function w(t,s){ +const a=s&&s[0];if(M+=t,null==a)return d(),0 +;if("begin"===y.type&&"end"===s.type&&y.index===s.index&&""===a){ +if(M+=n.slice(s.index,s.index+1),!o){const t=Error(`0 width match regex (${e})`) +;throw t.languageName=e,t.badRule=y.rule,t}return 1} +if(y=s,"begin"===s.type)return(e=>{ +const t=e[0],n=e.rule,r=new i(n),s=[n.__beforeBegin,n["on:begin"]] +;for(const n of s)if(n&&(n(e,r),r.isMatchIgnored))return m(t) +;return n.skip?M+=t:(n.excludeBegin&&(M+=t), +d(),n.returnBegin||n.excludeBegin||(M=t)),p(n,e),n.returnBegin?0:t.length})(s) +;if("illegal"===s.type&&!r){ +const e=Error('Illegal lexeme "'+a+'" for mode "'+(k.scope||"")+'"') +;throw e.mode=k,e}if("end"===s.type){const e=x(s);if(e!==q)return e} +if("illegal"===s.type&&""===a)return 1 +;if(A>1e5&&A>3*s.index)throw Error("potential infinite loop, way more iterations than matches") +;return M+=a,a.length}const _=E(e) +;if(!_)throw $(a.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const v=G(_);let O="",k=s||v;const N={},S=new g.__emitter(g);(()=>{const e=[] +;for(let t=k;t!==_;t=t.parent)t.scope&&e.unshift(t.scope) +;e.forEach((e=>S.openNode(e)))})();let M="",R=0,j=0,A=0,I=!1;try{ +for(k.matcher.considerAll();;){ +A++,I?I=!1:k.matcher.considerAll(),k.matcher.lastIndex=j +;const e=k.matcher.exec(n);if(!e)break;const t=w(n.substring(j,e.index),e) +;j=e.index+t}return w(n.substr(j)),S.closeAllNodes(),S.finalize(),O=S.toHTML(),{ +language:e,value:O,relevance:R,illegal:!1,_emitter:S,_top:k}}catch(t){ +if(t.message&&t.message.includes("Illegal"))return{language:e,value:F(n), +illegal:!0,relevance:0,_illegalBy:{message:t.message,index:j, +context:n.slice(j-100,j+100),mode:t.mode,resultSoFar:O},_emitter:S};if(o)return{ +language:e,value:F(n),illegal:!1,relevance:0,errorRaised:t,_emitter:S,_top:k} +;throw t}}function f(e,n){n=n||g.languages||Object.keys(t);const i=(e=>{ +const t={value:F(e),illegal:!1,relevance:0,_top:l,_emitter:new g.__emitter(g)} +;return t._emitter.addText(e),t})(e),r=n.filter(E).filter(y).map((t=>h(t,e,!1))) +;r.unshift(i);const s=r.sort(((e,t)=>{ +if(e.relevance!==t.relevance)return t.relevance-e.relevance +;if(e.language&&t.language){if(E(e.language).supersetOf===t.language)return 1 +;if(E(t.language).supersetOf===e.language)return-1}return 0})),[o,a]=s,c=o +;return c.secondBest=a,c}function p(e){let t=null;const n=(e=>{ +let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"" +;const n=g.languageDetectRe.exec(t);if(n){const t=E(n[1]) +;return t||(U(a.replace("{}",n[1])), +U("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"} +return t.split(/\s+/).find((e=>d(e)||E(e)))})(e);if(d(n))return +;w("before:highlightElement",{el:e,language:n +}),!g.ignoreUnescapedHTML&&e.children.length>0&&(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/issues/2886"), +console.warn(e)),t=e;const i=t.textContent,s=n?u(i,{language:n,ignoreIllegals:!0 +}):f(i);e.innerHTML=s.value,((e,t,n)=>{const i=t&&r[t]||n +;e.classList.add("hljs"),e.classList.add("language-"+i) +})(e,n,s.language),e.result={language:s.language,re:s.relevance, +relevance:s.relevance},s.secondBest&&(e.secondBest={ +language:s.secondBest.language,relevance:s.secondBest.relevance +}),w("after:highlightElement",{el:e,result:s,text:i})}let b=!1;function m(){ +"loading"!==document.readyState?document.querySelectorAll(g.cssSelector).forEach(p):b=!0 +}function E(e){return e=(e||"").toLowerCase(),t[e]||t[r[e]]} +function x(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +r[e.toLowerCase()]=t}))}function y(e){const t=E(e) +;return t&&!t.disableAutodetect}function w(e,t){const n=e;s.forEach((e=>{ +e[n]&&e[n](t)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +b&&m()}),!1),Object.assign(e,{highlight:u,highlightAuto:f,highlightAll:m, +highlightElement:p, +highlightBlock:e=>(z("10.7.0","highlightBlock will be removed entirely in v12.0"), +z("10.7.0","Please use highlightElement now."),p(e)),configure:e=>{g=V(g,e)}, +initHighlighting:()=>{ +m(),z("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +m(),z("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(n,i)=>{let r=null;try{r=i(e)}catch(e){ +if($("Language definition for '{}' could not be registered.".replace("{}",n)), +!o)throw e;$(e),r=l} +r.name||(r.name=n),t[n]=r,r.rawDefinition=i.bind(null,e),r.aliases&&x(r.aliases,{ +languageName:n})},unregisterLanguage:e=>{delete t[e] +;for(const t of Object.keys(r))r[t]===e&&delete r[t]}, +listLanguages:()=>Object.keys(t),getLanguage:E,registerAliases:x, +autoDetection:y,inherit:V,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{ +e["before:highlightBlock"](Object.assign({block:t.el},t)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{ +e["after:highlightBlock"](Object.assign({block:t.el},t))})})(e),s.push(e)} +}),e.debugMode=()=>{o=!1},e.safeMode=()=>{o=!0},e.versionString="11.0.1" +;for(const e in M)"object"==typeof M[e]&&n(M[e]);return Object.assign(e,M),e +})({}),Y=Object.freeze({__proto__:null});const Q=J +;for(const e of Object.keys(Y)){const t=e.replace("grmr_","") +;Q.registerLanguage(t,Y[e])}return Q}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("c",(()=>{"use strict";function e(e){ +return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(?:",e,")?") +}return n=>{const t=n.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),s="[a-zA-Z_]\\w*::",r="(decltype\\(auto\\)|"+e(s)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",a={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},i={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[n.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},n.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d\']+(\\.[\\d\']*)?|\\.[\\d\']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9_\']+|(\\b\\d[\\d_\']*(\\.[\\d\']*)?|\\.[\\d\']+)([eE][-+]?[\\d\']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma program ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},n.inherit(i,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,n.C_BLOCK_COMMENT_MODE]},o={ +className:"title",begin:e(s)+n.IDENT_RE,relevance:0 +},d=e(s)+n.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","const","continue","default","do","else","enum", "exit", "extern","for","fortran","goto","halt", "if","inline","register","restrict","return","sizeof", "sleep", "static","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"Get_A1 Get_A2 Get_A3 Get_A4 Get_B1 Get_B2 Get_B3 Get_B4 Set_A1 Set_A2 Set_A3 Set_A4 Set_A1_A2 Set_A3_A4 Set_B1 Set_B2 Set_B3 Set_B4 Set_B1_B2 Set_B3_B4 Clear_A Clear_B Clear_A_And_B Copy_A_From_B Copy_B_From_A Check_A_Is_Zero Check_B_Is_Zero Check_A_Equals_B Swap_A_and_B OR_A_with_B OR_B_with_A AND_A_with_B AND_B_with_A XOR_A_with_B XOR_B_with_A Add_A_To_B Add_B_To_A Sub_A_From_B Sub_B_From_A Mul_A_By_B Mul_B_By_A Div_A_By_B Div_B_By_A MD5_A_To_B Check_MD5_A_With_B HASH160_A_To_B Check_HASH160_A_With_B SHA256_A_To_B Check_SHA256_A_With_B Get_Block_Timestamp Get_Creation_Timestamp Get_Last_Block_Timestamp Put_Last_Block_Hash_In_A A_To_Tx_After_Timestamp Get_Type_For_Tx_In_A Get_Amount_For_Tx_In_A Get_Timestamp_For_Tx_In_A Get_Random_Id_For_Tx_In_A Message_From_Tx_In_A_To_B B_To_Address_Of_Tx_In_A B_To_Address_Of_Creator Get_Current_Balance Get_Previous_Balance Send_To_Address_In_B Send_All_To_Address_In_B Send_Old_To_Address_In_B Send_A_To_Address_In_B Add_Minutes_To_Timestamp" +},g=[c,a,t,n.C_BLOCK_COMMENT_MODE,l,i],m={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:g.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:g.concat(["self"]),relevance:0}]),relevance:0},_={ +begin:"("+r+"[\\*&\\s]+)+"+d,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:"decltype\\(auto\\)", +keywords:u,relevance:0},{begin:d,returnBegin:!0,contains:[n.inherit(o,{ +className:"title.function"})],relevance:0},{relevance:0,match:/,/},{ +className:"params",begin:/\(/,end:/\)/,keywords:u,relevance:0, +contains:[t,n.C_BLOCK_COMMENT_MODE,i,l,a,{begin:/\(/,end:/\)/,keywords:u, +relevance:0,contains:["self",t,n.C_BLOCK_COMMENT_MODE,i,l,a]}] +},a,t,n.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},n.TITLE_MODE]}]),exports:{preprocessor:c, +strings:i,keywords:u}}}})()); \ No newline at end of file diff --git a/v0.3/3rd-party/styles/tomorrow.css b/v0.3/3rd-party/styles/tomorrow.css new file mode 100644 index 0000000..6270bce --- /dev/null +++ b/v0.3/3rd-party/styles/tomorrow.css @@ -0,0 +1,173 @@ + +/*! + Theme: Tomorrow + Author: Chris Kempson (http://chriskempson.com) + License: ~ MIT (or more permissive) [via base16-schemes-source] + Maintainer: @highlightjs/core-team + Version: 2021.05.0 +*/ + +/* + WARNING: DO NOT EDIT THIS FILE DIRECTLY. + This theme file was auto-generated from the Base16 scheme tomorrow + by the Highlight.js Base16 template builder. + - https://github.com/highlightjs/base16-highlightjs +*/ + +/* +base00 #ffffff Default Background +base01 #e0e0e0 Lighter Background (Used for status bars, line number and folding marks) +base02 #d6d6d6 Selection Background +base03 #8e908c Comments, Invisibles, Line Highlighting +base04 #969896 Dark Foreground (Used for status bars) +base05 #4d4d4c Default Foreground, Caret, Delimiters, Operators +base06 #282a2e Light Foreground (Not often used) +base07 #1d1f21 Light Background (Not often used) +base08 #c82829 Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted +base09 #f5871f Integers, Boolean, Constants, XML Attributes, Markup Link Url +base0A #eab700 Classes, Markup Bold, Search Text Background +base0B #718c00 Strings, Inherited Class, Markup Code, Diff Inserted +base0C #3e999f Support, Regular Expressions, Escape Characters, Markup Quotes +base0D #4271ae Functions, Methods, Attribute IDs, Headings +base0E #8959a8 Keywords, Storage, Selector, Markup Italic, Diff Changed +base0F #a3685a Deprecated, Opening/Closing Embedded Language Tags, e.g. +*/ + +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em; +} + +code.hljs { + padding: 3px 5px; +} + +.hljs { + color: #4d4d4c; + background: #ffffff; +} + +.hljs ::selection { + color: #d6d6d6; +} + +/* purposely do not highlight these things */ +/* comment just to remeber they exist and can be used: +.hljs-formula, .hljs-params, .hljs-property */ + +/* base03 - #8e908c - Comments, Invisibles, Line Highlighting */ +.hljs-comment { + color: #8e908c; +} + +/* base04 - #969896 - Dark Foreground (Used for status bars) */ +.hljs-tag { + color: #969896; +} + +/* base05 - #4d4d4c - Default Foreground, Caret, Delimiters, Operators */ +.hljs-subst, +.hljs-punctuation, +.hljs-operator { + color: #4d4d4c; +} + +.hljs-operator { + opacity: 0.7; +} + +/* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */ +.hljs-bullet, +.hljs-variable, +.hljs-template-variable, +.hljs-selector-tag, +.hljs-name, +.hljs-deletion { + color: #c82829; +} + +/* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */ +.hljs-symbol, +.hljs-number, +.hljs-link, +.hljs-attr, +.hljs-variable.constant_, +.hljs-literal { + color: #ff0000; +} + +/* base0A - Classes, Markup Bold, Search Text Background */ +.hljs-title, +.hljs-class .hljs-title, +.hljs-title.class_ +{ + color: palevioletred; +} + +.hljs-strong { + font-weight:bold; + color: #eab700; +} + +/* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */ +.hljs-code, +.hljs-addition, +.hljs-title.class_.inherited__, +.hljs-string { + font-weight:bold; + color: green; +} + +/* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */ +.hljs-built_in, +.hljs-doctag, /* guessing */ +.hljs-quote, +.hljs-keyword.hljs-atrule, +.hljs-regexp { + color: #2d57a5; +} + +/* base0D - Functions, Methods, Attribute IDs, Headings */ +.hljs-function .hljs-title, +.hljs-attribute, +.ruby .hljs-property, +.hljs-title.function_, +.hljs-section { + font-weight:bold; + color: mediumblue; +} + +/* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */ +.hljs-type, +/* .hljs-selector-id, */ +/* .hljs-selector-class, */ +/* .hljs-selector-attr, */ +/* .hljs-selector-pseudo, */ +.hljs-template-tag, +.diff .hljs-meta, +.hljs-keyword { + color: purple; +} +.hljs-emphasis { + color: #8959a8; + font-style: italic; +} + +/* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. */ +.hljs-meta, +/* + prevent top level .keyword and .string scopes + from leaking into meta by accident +*/ +.hljs-meta .hljs-keyword, +.hljs-meta .hljs-string +{ + color: #6e3f33; +} + +.hljs-meta .hljs-keyword, +/* for v10 compatible themes */ +.hljs-meta-keyword { + font-weight: bold; +} \ No newline at end of file diff --git a/v0.3/3rd-party/winbox.bundle.js b/v0.3/3rd-party/winbox.bundle.js new file mode 100644 index 0000000..4a8648c --- /dev/null +++ b/v0.3/3rd-party/winbox.bundle.js @@ -0,0 +1,23 @@ +/** + * WinBox.js v0.1.9 (Bundle) + * Copyright 2021 Nextapps GmbH + * Author: Thomas Wilkerling + * Licence: Apache-2.0 + * https://github.com/nextapps-de/winbox + */ +(function(){'use strict';var e,h=document.createElement("style");h.innerHTML="@keyframes fade-in{0%{opacity:0}to{opacity:.85}}.winbox.modal:after,.winbox.modal:before{content:''}.winbox{position:fixed;left:0;top:0;background:#0050ff;box-shadow:0 14px 28px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.22);transition:width .3s,height .3s,transform .3s;transition-timing-function:cubic-bezier(.3,1,.3,1);will-change:transform,width,height;contain:layout size;text-align:left;touch-action:none}.max,.no-shadow{box-shadow:none}.wb-header,.winbox iframe{position:absolute;width:100%}.wb-header{left:0;top:0;height:35px;color:#fff;overflow:hidden}.wb-body,.wb-n,.wb-s{position:absolute;left:0}.wb-n,.wb-s{height:10px}.wb-body{right:0;top:35px;bottom:0;overflow:auto;-webkit-overflow-scrolling:touch;overflow-scrolling:touch;will-change:contents;background:#fff;margin-top:0!important;contain:strict}.wb-title{font-family:Arial,sans-serif;font-size:14px;padding-left:10px;cursor:move;line-height:35px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wb-n{top:-5px;right:0;cursor:n-resize}.wb-e{position:absolute;top:0;right:-5px;bottom:0;width:10px;cursor:w-resize}.wb-s,.wb-se,.wb-sw{bottom:-5px}.wb-s{right:0;cursor:n-resize}.wb-w,.winbox.modal:before{position:absolute;top:0;bottom:0}.wb-w{left:-5px;width:10px;cursor:w-resize}.wb-ne,.wb-nw,.wb-sw{width:15px;height:15px;position:absolute}.wb-nw{top:-5px;left:-5px;cursor:nw-resize}.wb-ne,.wb-sw{cursor:ne-resize}.wb-ne{top:-5px;right:-5px}.wb-sw{left:-5px}.wb-se{position:absolute;right:-5px;width:15px;height:15px;cursor:nw-resize}.wb-icon{float:right;height:35px;max-width:100%;text-align:center}.wb-icon *{display:inline-block;width:30px;height:100%;background-position:center;background-repeat:no-repeat;cursor:pointer;max-width:100%}.no-close .wb-close,.no-full .wb-full,.no-header .wb-header,.no-max .wb-max,.no-min .wb-min,.no-resize .wb-body~div,.winbox.min .wb-body>*,.winbox.min .wb-full,.winbox.min .wb-min,.winbox.modal .wb-full,.winbox.modal .wb-max,.winbox.modal .wb-min{display:none}.winbox.max .wb-title,.winbox.min .wb-title{cursor:default}.wb-min{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAyIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNOCAwaDdhMSAxIDAgMCAxIDAgMkgxYTEgMSAwIDAgMSAwLTJoN3oiLz48L3N2Zz4=);background-size:14px auto;background-position:center bottom 11px}.wb-max{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIHZpZXdCb3g9IjAgMCA5NiA5NiI+PHBhdGggZD0iTTIwIDcxLjMxMUMxNS4zNCA2OS42NyAxMiA2NS4yMyAxMiA2MFYyMGMwLTYuNjMgNS4zNy0xMiAxMi0xMmg0MGM1LjIzIDAgOS42NyAzLjM0IDExLjMxMSA4SDI0Yy0yLjIxIDAtNCAxLjc5LTQgNHY1MS4zMTF6Ii8+PHBhdGggZD0iTTkyIDc2VjM2YzAtNi42My01LjM3LTEyLTEyLTEySDQwYy02LjYzIDAtMTIgNS4zNy0xMiAxMnY0MGMwIDYuNjMgNS4zNyAxMiAxMiAxMmg0MGM2LjYzIDAgMTItNS4zNyAxMi0xMnptLTUyIDRjLTIuMjEgMC00LTEuNzktNC00VjM2YzAtMi4yMSAxLjc5LTQgNC00aDQwYzIuMjEgMCA0IDEuNzkgNCA0djQwYzAgMi4yMS0xLjc5IDQtNCA0SDQweiIvPjwvc3ZnPg==);background-size:17px auto}.wb-close{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xIC0xIDE4IDE4Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJtMS42MTMuMjEuMDk0LjA4M0w4IDYuNTg1IDE0LjI5My4yOTNsLjA5NC0uMDgzYTEgMSAwIDAgMSAxLjQwMyAxLjQwM2wtLjA4My4wOTRMOS40MTUgOGw2LjI5MiA2LjI5M2ExIDEgMCAwIDEtMS4zMiAxLjQ5N2wtLjA5NC0uMDgzTDggOS40MTVsLTYuMjkzIDYuMjkyLS4wOTQuMDgzQTEgMSAwIDAgMSAuMjEgMTQuMzg3bC4wODMtLjA5NEw2LjU4NSA4IC4yOTMgMS43MDdBMSAxIDAgMCAxIDEuNjEzLjIxeiIvPjwvc3ZnPg==);background-size:15px auto}.wb-full{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjIuNSIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNOCAzSDVhMiAyIDAgMCAwLTIgMnYzbTE4IDBWNWEyIDIgMCAwIDAtMi0yaC0zbTAgMThoM2EyIDIgMCAwIDAgMi0ydi0zTTMgMTZ2M2EyIDIgMCAwIDAgMiAyaDMiLz48L3N2Zz4=);background-size:16px auto}.winbox.max .wb-body~div,.winbox.min .wb-body~div,.winbox.modal .wb-body~div,.winbox.modal .wb-title{pointer-events:none}.max .wb-body{margin:0!important}.winbox iframe{height:100%;border:0}.winbox.modal:before{left:0;right:0;background:inherit;border-radius:inherit}.winbox.modal:after{position:absolute;top:-100vh;left:-100vw;right:-100vw;bottom:-100vh;background:#0d1117;animation:fade-in .2s ease-out forwards;z-index:-1}.no-animation{transition:none}.no-header .wb-body{top:0}.no-move:not(.min) .wb-title{pointer-events:none}"; +var l=document.getElementsByTagName("head")[0];l.firstChild?l.insertBefore(h,l.firstChild):l.appendChild(h);var q=document.createElement("div");q.innerHTML="
";function r(a,b,c,g){a.addEventListener(b,c,g||!1===g?g:!0)}function t(a){a.stopPropagation();a.cancelable&&a.preventDefault()}function w(a,b,c){c=""+c;a["_s_"+b]!==c&&(a.style.setProperty(b,c),a["_s_"+b]=c)};var x=document.documentElement,y=[],B=0,C=0,D,G,H,K,L,N,O; +function Q(a,b){if(!(this instanceof Q))return new Q(a);D||R();this.g=q.cloneNode(!0);this.body=this.g.getElementsByClassName("wb-body")[0];var c,g;if(a){if(b){var f=a;a=b}if("string"===typeof a)f=a;else{if(g=a.modal)var u=c="center";var z=a.id;var I=a.root;f=f||a.title;var E=a.mount;var d=a.html;var A=a.url;var k=a.width;var m=a.height;u=a.x||u;c=a.y||c;var F=a.max;var n=a.top;var p=a.left;var v=a.bottom;var J=a.right;D=a.index||D;var X=a.onclose;var Y=a.onfocus;var Z=a.onblur;var aa=a.onmove;var ba= +a.onresize;b=a.background;var P=a.border;var M=a["class"];b&&this.setBackground(b);P&&w(this.body,"margin",P+(isNaN(P)?"":"px"))}}this.setTitle(f||"");a=N;f=O;n=n?S(n,f):0;v=v?S(v,f):0;p=p?S(p,a):0;J=J?S(J,a):0;a-=p+J;f-=n+v;k=k?S(k,a):a/2|0;m=m?S(m,f):f/2|0;u=u?S(u,a,k):p;c=c?S(c,f,m):n;D=D||10;this.g.id=this.id=z||"winbox-"+ ++B;this.g.className="winbox"+(M?" "+("string"===typeof M?M:M.join(" ")):"")+(g?" modal":"");this.x=u;this.y=c;this.width=k;this.height=m;this.top=n;this.right=J;this.bottom= +v;this.left=p;this.max=this.min=!1;this.j=X;this.l=Y;this.i=Z;this.o=aa;this.m=ba;F?this.maximize():this.move().resize();this.focus();E?this.mount(E):d?this.body.innerHTML=d:A&&this.setUrl(A);ca(this);(I||document.body).appendChild(this.g)}Q["new"]=function(a){return new Q(a)};function S(a,b,c){"string"===typeof a&&("center"===a?a=(b-c)/2|0:"right"===a||"bottom"===a?a=b-c:(c=parseFloat(a),a="%"===(""+c!==a&&a.substring((""+c).length))?b/100*c|0:c));return a} +function R(){var a=document.body;a[K="requestFullscreen"]||a[K="msRequestFullscreen"]||a[K="webkitRequestFullscreen"]||a[K="mozRequestFullscreen"]||(K="");L=K&&K.replace("request","exit").replace("mozRequest","mozCancel").replace("Request","Exit");r(window,"resize",function(){N=x.clientWidth;O=x.clientHeight;T()});N=x.clientWidth;O=x.clientHeight} +function ca(a){U(a,"title");U(a,"n");U(a,"s");U(a,"w");U(a,"e");U(a,"nw");U(a,"ne");U(a,"se");U(a,"sw");r(a.g.getElementsByClassName("wb-min")[0],"click",function(b){t(b);a.minimize()});r(a.g.getElementsByClassName("wb-max")[0],"click",function(b){t(b);a.focus().maximize()});K?r(a.g.getElementsByClassName("wb-full")[0],"click",function(b){t(b);a.focus().fullscreen()}):a.addClass("no-full");r(a.g.getElementsByClassName("wb-close")[0],"click",function(b){t(b);a.close()||(a=null)});r(a.g,"click",function(){a.focus()}, +!1)}function V(a){y.splice(y.indexOf(a),1);T();a.removeClass("min");a.min=!1;a.g.title=""}function T(){for(var a=y.length,b=0,c,g;bk){a.maximize();return}}a.max||(w(a.g,"transition","none"),(z=d.touches)&&(z=z[0])?(d=z,r(window,"touchmove",g),r(window,"touchend",f)):(r(window,"mousemove",g),r(window,"mouseup",f)),I=d.pageX,E=d.pageY,a.focus())}}function g(d){t(d);z&&(d=d.touches[0]);var A=d.pageX;d=d.pageY;var k=A-I,m=d-E,F;if("title"===b){a.x+=k;a.y+=m;var n=F=1}else{if("e"===b||"se"===b||"ne"===b){a.width+=k;var p= +1}else if("w"===b||"sw"===b||"nw"===b)a.x+=k,a.width-=k,n=p=1;if("s"===b||"se"===b||"sw"===b){a.height+=m;var v=1}else if("n"===b||"ne"===b||"nw"===b)a.y+=m,a.height-=m,F=v=1}if(p||v)p&&(a.width=Math.max(Math.min(a.width,N-a.x-a.right),150)),v&&(a.height=Math.max(Math.min(a.height,O-a.y-a.bottom),35)),a.resize();if(n||F)n&&(a.x=Math.max(Math.min(a.x,N-a.width-a.right),a.left)),F&&(a.y=Math.max(Math.min(a.y,O-a.height-a.bottom),a.top)),a.move();I=A;E=d}function f(d){t(d);w(a.g,"transition","");z?(window.removeEventListener("touchmove", +g,!0),window.removeEventListener("touchend",f,!0)):(window.removeEventListener("mousemove",g,!0),window.removeEventListener("mouseup",f,!0))}var u=a.g.getElementsByClassName("wb-"+b)[0],z,I,E;r(u,"mousedown",c);r(u,"touchstart",c,{passive:!1})}e=Q.prototype;e.mount=function(a){this.unmount();a.h||(a.h=a.parentNode);this.body.textContent="";this.body.appendChild(a);return this};e.unmount=function(a){var b=this.body.firstChild;if(b){var c=a||b.h;c&&c.appendChild(b);b.h=a}return this}; +e.setTitle=function(a){a=this.title=a;this.g.getElementsByClassName("wb-title")[0].firstChild.nodeValue=a;return this};e.setBackground=function(a){w(this.g,"background",a);return this};e.setUrl=function(a){this.body.innerHTML='';return this};e.focus=function(){H!==this&&(w(this.g,"z-index",D++),this.addClass("focus"),H&&(H.removeClass("focus"),H.i&&H.i()),H=this,this.l&&this.l());return this};e.hide=function(){return this.addClass("hide")};e.show=function(){return this.removeClass("hide")}; +e.minimize=function(a){G&&W();!a&&this.min?(V(this),this.resize().move().focus()):!1===a||this.min||(y.push(this),T(),this.g.title=this.title,this.addClass("min"),this.min=!0);this.max&&(this.removeClass("max"),this.max=!1);return this};e.maximize=function(a){if("undefined"===typeof a||a!==this.max)this.min&&V(this),(this.max=!this.max)?this.addClass("max").resize(N-this.left-this.right,O-this.top-this.bottom,!0).move(this.left,this.top,!0):this.resize().move().removeClass("max");return this}; +e.fullscreen=function(a){if("undefined"===typeof a||a!==G)this.min&&(this.resize().move(),V(this)),G&&W()||(this.body[K](),G=!0);return this};function W(){G=!1;if(document.fullscreen||document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement)return document[L](),!0}e.close=function(a){if(this.j&&this.j(a))return!0;this.min&&V(this);this.unmount();this.g.parentNode.removeChild(this.g);H===this&&(H=null)}; +e.move=function(a,b,c){a||0===a?c||(this.x=a?a=S(a,N-this.left-this.right,this.width):0,this.y=b?b=S(b,O-this.top-this.bottom,this.height):0):(a=this.x,b=this.y);w(this.g,"transform","translate("+a+"px,"+b+"px)");this.o&&this.o(a,b);return this};e.resize=function(a,b,c){a||0===a?c||(this.width=a?a=S(a,N-this.left-this.right):0,this.height=b?b=S(b,O-this.top-this.bottom):0):(a=this.width,b=this.height);w(this.g,"width",a+"px");w(this.g,"height",b+"px");this.m&&this.m(a,b);return this}; +e.addClass=function(a){this.g.classList.add(a);return this};e.removeClass=function(a){this.g.classList.remove(a);return this};window.WinBox=Q;}).call(this); diff --git a/v0.3/favicon.svg b/v0.3/favicon.svg new file mode 100644 index 0000000..0ee326f --- /dev/null +++ b/v0.3/favicon.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/v0.3/index.html b/v0.3/index.html new file mode 100644 index 0000000..0234cae --- /dev/null +++ b/v0.3/index.html @@ -0,0 +1,99 @@ + + + + + + SmartC - C Compiler for Signum - dev + + + + + + + + + + + + + + + + + + + + + + + + +

SmartC - C Compiler for Signum - dev

+
❐ Source code: +
+ +

+            Cursor: 0:0
+        
+
+ +
❐ Actions: +
+ + +
+ + + + + + + +
+
+ +
❐ Status: +
+

+        
+
+ +
❐ Assembly output: +
+

+

+        
+
+ +
❐ Localhost node deployment form for smart contract: +
+
+
Deployment:
+    +
+
+
+
+
+ +
Contract Name:
+
Description:
+
Bytecode:
+
Bytedata:
+
Data Pages:
+
Code Stack Pages:
+
User Stack Pages:
+
Min. Activation Amount NQT:
+
Secret Phrase:
+
Fee (NQT):
+
Tx Deadline (minutes):
+
Broadcast Tx?
+
+
+
+
Response:
+ +
+
+ + diff --git a/v0.3/index.js b/v0.3/index.js new file mode 100644 index 0000000..b1fca6a --- /dev/null +++ b/v0.3/index.js @@ -0,0 +1,351 @@ +/* Following global functions are define on the files: +preprocess -> out/preprocessor.js +tokenize -> out/tokenizer.js +parse -> out/parser.js +shape -> out/shaper.js +syntaxProcess -> out/syntaxProcessor.js +generate -> out/generator.js +bytecode -> out/bytecoder.js +runTestCases -> out/testcases.js +asmHighlight -> out/asmHighlight.js +WinBox -> 3rd-party/winbox.bundle.js +hljs -> 3rd-party/highlight.min.js +*/ + +/* global preprocess tokenize parse shape syntaxProcess generate asmHighlight bytecode runTestCases hljs WinBox */ + +window.onload = () => { + const scode = document.getElementById('source-code') + scode.addEventListener('keyup', textKeyUp) + scode.addEventListener('keydown', textKeyUp) + scode.addEventListener('click', textKeyUp) + scode.addEventListener('mousedown', SetSourceCode) + scode.addEventListener('mouseup', textKeyUp) + scode.addEventListener('paste', textKeyUp) + + document.getElementById('compile').addEventListener('click', compileCode) + document.getElementById('test').addEventListener('click', testCode) + document.getElementById('btn_help').addEventListener('click', detachHelp) + document.getElementById('source_is_c').addEventListener('click', toggleLang) + document.getElementById('save').addEventListener('click', SaveSource) + document.getElementById('load').addEventListener('click', LoadSource) + + document.getElementById('copy_assembly').addEventListener('click', () => navigator.clipboard.writeText(document.getElementById('assembly_output').innerText)) + + document.getElementById('source_legend').addEventListener('click', detachSource) + document.getElementById('actions_legend').addEventListener('click', detachActions) + document.getElementById('status_legend').addEventListener('click', detachStatus) + document.getElementById('assembly_legend').addEventListener('click', detachAssembly) + document.getElementById('deployment_legend').addEventListener('click', detachDeployment) + + const radios = document.querySelectorAll('input[name="deploy"]') + radios.forEach(Dom => { + Dom.addEventListener('click', deployClick) + }) + + textKeyUp() + toggleLang(document.getElementById('source_is_c')) + + detachDeployment().minimize(true) +} + +const PageGlobal = { + colorMode: 'source', + colorToggleTimeout: 0 +} + +function compileCode () { + const codeString = document.getElementById('source-code').value + const t0 = new Date() + + try { + let asmCode + if (document.getElementById('source_is_c').checked) { + const preprocessOut = preprocess(codeString) + const tokenizeOut = tokenize(preprocessOut) + const parseOut = parse(tokenizeOut) + const shapeOut = shape(parseOut) + const syntaxOut = syntaxProcess(shapeOut) + asmCode = generate(syntaxOut) + } else { + asmCode = codeString + } + + document.getElementById('assembly_output').innerHTML = asmHighlight(asmCode) + const bcode = bytecode(asmCode) + + const t1 = new Date() + let compileMessage = `Compile sucessfull!!! Done at ${t1.getHours()}:${t1.getMinutes()}:${t1.getSeconds()} in ${t1 - t0} ms.` + compileMessage += `
Machine code hash ID: ${bcode.MachineCodeHashId}` + if (document.getElementById('debug').checked) { + compileMessage += '\n\n' + JSON.stringify(bcode, null, ' ') + } + document.getElementById('status_output').innerHTML = compileMessage + '
' + + fillForm(bcode) + } catch (e) { + document.getElementById('assembly_output').innerHTML = '' + clearForm() + let compileMessage = `Compile failed\n\n${e.message}` + if (document.getElementById('debug').checked) { + compileMessage += '\n\n' + e.stack + } + document.getElementById('status_output').innerHTML = compileMessage + } +} + +function textKeyUp (force) { + const elem = document.getElementById('source-code') + const text = elem.value + let i + + SetSourceCode(force) + + // grows text area + const oldrow = elem.rows + const newrow = (text.match(/\n/g) || '').length + 5 + + // eye-candy (resize in 8 steps using polinomial interpolation) + i = 1 + const end = 9 + let id + function frame () { + if (i > 9) { + clearInterval(id) + } else { + i++ + elem.rows = Math.round(i * i * (oldrow - newrow) / (end * end) + i * (newrow - oldrow) * (2 / end) + oldrow) + document.getElementById('color_code').style.height = elem.offsetHeight + 'px' + } + } + if (newrow - oldrow > 3 || newrow - oldrow < -3) id = setInterval(frame, 100) + else elem.rows = newrow + // eye-candy end + + document.getElementById('color_code').style.height = elem.offsetHeight + 'px' + + // update tooltip info (line:column) + const cpos = elem.value.substr(0, elem.selectionStart).split('\n') + document.getElementById('tooltip_span').innerHTML = 'Cursor: ' + cpos.length + ':' + cpos[cpos.length - 1].length +} + +function SetColorCode () { + const source = document.getElementById('source-code') + + if (source.selectionStart !== source.selectionEnd) { + // Do not highlight if some text is selected. This prevents + // text selection to fade away. + return + } + + if (PageGlobal.colorMode !== 'color') { + PageGlobal.colorMode = 'color' + clearTimeout(PageGlobal.colorToggleTimeout) + const dest = document.getElementById('color_code') + + if (document.getElementById('source_is_c').checked) { + dest.innerHTML = hljs.highlight(source.value, { language: 'c' }).value + '\n\n\n\n\n' + } else { + dest.innerHTML = asmHighlight(source.value) + '\n\n\n\n\n' + } + source.className = 'transp' + } +} + +function SetSourceCode (force) { + clearTimeout(PageGlobal.colorToggleTimeout) + PageGlobal.colorToggleTimeout = setTimeout(SetColorCode, 500) + if (PageGlobal.colorMode !== 'source' || force === true) { + PageGlobal.colorMode = 'source' + document.getElementById('source-code').className = 'opaque' + } +} + +function SaveSource () { + const text = document.getElementById('save').innerHTML + localStorage.setItem('program', document.getElementById('source-code').value) + setTimeout(() => { + document.getElementById('save').innerHTML = text + }, 5000) + document.getElementById('save').innerHTML = '☑' +} + +function LoadSource () { + let temp = document.getElementById('source-code').value + if (temp === '' || confirm('Sure over-write current program?') === true) { + document.getElementById('source-code').value = localStorage.getItem('program') + textKeyUp(true) + temp = document.getElementById('load').innerHTML + setTimeout(() => { + document.getElementById('load').innerHTML = temp + }, 5000) + document.getElementById('load').innerHTML = '☑' + } + document.getElementById('source-code').focus() +} + +/* debug use only +function stringifyReplacer(key, value) { + if (typeof value === 'bigint') { + return value.toString(16) + 'n'; + } else if (typeof value === 'number'){ + return value.toString(16); + } else { + return value; + } +} +*/ + +function detachSource () { + const ret = WinBox.new({ + title: 'Source code', + height: '100%', + top: 50, + mount: document.getElementById('source_window'), + onclose: function () { + document.getElementById('source_fieldset').style.display = 'block' + } + }) + document.getElementById('source_fieldset').style.display = 'none' + return ret +} + +function detachStatus () { + WinBox.new({ + title: 'Status', + mount: document.getElementById('status_window'), + height: '25%', + width: '50%', + x: '50%', + y: '75%', + top: 50, + onclose: function () { + document.getElementById('status_fieldset').style.display = 'block' + } + }) + document.getElementById('status_fieldset').style.display = 'none' +} + +function detachActions () { + WinBox.new({ + title: 'Actions', + mount: document.getElementById('actions_window'), + height: '20%', + width: '50%', + x: '50%', + y: '55%', + top: 50, + onclose: function () { + document.getElementById('actions_fieldset').style.display = 'block' + } + }) + document.getElementById('actions_fieldset').style.display = 'none' +} + +function detachDeployment () { + const ret = WinBox.new({ + title: 'Smart Contract Deployment', + mount: document.getElementById('deployment_window'), + top: 50, + height: '95%', + onclose: function () { + document.getElementById('deployment_fieldset').style.display = 'block' + } + }) + document.getElementById('deployment_fieldset').style.display = 'none' + return ret +} + +function detachAssembly () { + WinBox.new({ + title: 'Assembly output', + mount: document.getElementById('assembly_window'), + height: '50%', + width: '50%', + x: '50%', + y: '50', + top: 50, + onclose: function () { + document.getElementById('assembly_fieldset').style.display = 'block' + } + }) + document.getElementById('assembly_fieldset').style.display = 'none' +} + +function detachHelp () { + const helpage = WinBox.new({ + title: 'Help page', + url: 'https://deleterium.github.io/SmartC/docs/', + height: '70%', + width: '70%', + x: 'center', + y: 'center', + top: 50 + }) + helpage.focus() +} + +function fillForm (AtInfo) { + let myDom + myDom = document.getElementsByName('name') + myDom[0].value = AtInfo.PName + myDom = document.getElementsByName('description') + myDom[0].value = AtInfo.PDescription + myDom = document.getElementsByName('code') + myDom[0].value = AtInfo.ByteCode + myDom = document.getElementsByName('data') + myDom[0].value = AtInfo.ByteData + myDom = document.getElementsByName('dpages') + myDom[0].value = AtInfo.DataPages + myDom = document.getElementsByName('cspages') + myDom[0].value = AtInfo.CodeStackPages + myDom = document.getElementsByName('uspages') + myDom[0].value = AtInfo.UserStackPages + myDom = document.getElementsByName('minActivationAmountNQT') + myDom[0].value = AtInfo.PActivationAmount + myDom = document.getElementsByName('feeNQT') + myDom[0].value = AtInfo.MinimumFeeNQT +} + +function clearForm () { + let myDom + myDom = document.getElementsByName('name') + myDom[0].value = '' + myDom = document.getElementsByName('description') + myDom[0].value = '' + myDom = document.getElementsByName('code') + myDom[0].value = '' + myDom = document.getElementsByName('data') + myDom[0].value = '' + myDom = document.getElementsByName('dpages') + myDom[0].value = '0' + myDom = document.getElementsByName('cspages') + myDom[0].value = '0' + myDom = document.getElementsByName('uspages') + myDom[0].value = '0' + myDom = document.getElementsByName('minActivationAmountNQT') + myDom[0].value = '' + myDom = document.getElementsByName('feeNQT') + myDom[0].value = '' +} + +function testCode () { + document.getElementById('assembly_output').innerHTML = '' + clearForm() + document.getElementById('status_output').innerHTML = runTestCases() +} + +function toggleLang (ev) { + if ((ev.checked !== undefined && ev.checked) || (ev.target !== undefined && ev.target.checked)) { + document.getElementById('bt1').innerText = 'C' + } else { + document.getElementById('bt1').innerText = 'Assembly' + } + document.getElementById('assembly_output').innerHTML = '' + textKeyUp(true) +} + +function deployClick (evt) { + const formDom = document.getElementById('deploy_form') + formDom.action = `http://localhost:${evt.currentTarget.value}/burst` +} diff --git a/v0.3/out/asmHighlight.js b/v0.3/out/asmHighlight.js new file mode 100644 index 0000000..7f0ac2d --- /dev/null +++ b/v0.3/out/asmHighlight.js @@ -0,0 +1,356 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +// Some parts based on w3schools code for highlighting howto +/** + * Converts assembly code in HTML code with syntax highlight + * @param asmSourceCode plain text assembly program + * @returns same text with syntax highlight in html + */ +// eslint-disable-next-line no-unused-vars +function asmHighlight(asmSourceCode) { + const Config = { + divId: 'asmCodeline', + divClass: 'asmLine', + spanErrorClass: 'asmError', + spanLabelClass: 'asmLabel', + spanNumberClass: 'asmNumber', + spanCommentClass: 'asmComment', + spanVariableClass: 'asmVariable', + spanDirectiveClass: 'asmDirective', + spanInstructionClass: 'asmInstruction' + }; + const allowedCodes = [ + { opCode: 0xf0, size: 0, regex: /^\s*$/ }, + { opCode: 0xf1, size: 0, regex: /^\s*(\w+):\s*$/ }, + { opCode: 0xf2, size: 0, regex: /^(\s*\^comment)(\s+.*)/ }, + { opCode: 0xf3, size: 0, regex: /^(\s*\^declare)(\s+\w+\s*)$/ }, + { opCode: 0xf4, size: 0, regex: /^(\s*\^const)(\s+.*)/ }, + { opCode: 0xf5, size: 0, regex: /^(\s*\^program\s+\w+\b)(.*)$/ }, + { opCode: 0x01, size: 13, regex: /^(\s*SET\s+)(@\w+\s+)(#[\da-f]{16}\b\s*)$/ }, + { opCode: 0x02, size: 9, regex: /^(\s*SET\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x03, size: 5, regex: /^(\s*CLR\s+)(@\w+\s*)$/ }, + { opCode: 0x04, size: 5, regex: /^(\s*INC\s+)(@\w+\s*)$/ }, + { opCode: 0x05, size: 5, regex: /^(\s*DEC\s+)(@\w+\s*)$/ }, + { opCode: 0x06, size: 9, regex: /^(\s*ADD\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x07, size: 9, regex: /^(\s*SUB\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x08, size: 9, regex: /^(\s*MUL\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x09, size: 9, regex: /^(\s*DIV\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x0a, size: 9, regex: /^(\s*BOR\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x0b, size: 9, regex: /^(\s*AND\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x0c, size: 9, regex: /^(\s*XOR\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x0d, size: 5, regex: /^(\s*NOT\s+)(@\w+\s*)$/ }, + { opCode: 0x0e, size: 9, regex: /^(\s*SET\s+)(@\w+)(\s+\$\()(\$\w+)(\)\s*)$/ }, + { opCode: 0x0f, size: 13, regex: /^(\s*SET\s+)(@\w+\s+)(\$\()(\$\w+)(\s*\+\s*)(\$\w+)(\)\s*)$/ }, + { opCode: 0x10, size: 5, regex: /^(\s*PSH\s+)(\$\w+\s*)$/ }, + { opCode: 0x11, size: 5, regex: /^(\s*POP\s+)(@\w+\s*)$/ }, + { opCode: 0x12, size: 5, regex: /^(\s*JSR\s+)(:\w+\s*)$/ }, + { opCode: 0x13, size: 1, regex: /^\s*RET\s*$/ }, + { opCode: 0x14, size: 9, regex: /^(\s*SET\s+)(@\()(\$\w+)(\)\s+)(\$\w+\s*)$/ }, + { opCode: 0x15, size: 13, regex: /^(\s*SET\s+)(@\()(\$\w+)(\s*\+\s*)(\$\w+)(\)\s+)(\$\w+\s*)$/ }, + { opCode: 0x16, size: 9, regex: /^(\s*MOD\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x17, size: 9, regex: /^(\s*SHL\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x18, size: 9, regex: /^(\s*SHR\s+)(@\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x1a, size: 5, regex: /^(\s*JMP\s+)(:\w+\s*)$/ }, + { opCode: 0x1b, size: 6, regex: /^(\s*BZR\s+)(\$\w+\s+)(:\w+\s*)$/ }, + { opCode: 0x1e, size: 6, regex: /^(\s*BNZ\s+)(\$\w+\s+)(:\w+\s*)$/ }, + { opCode: 0x1f, size: 10, regex: /^(\s*BGT\s+)(\$\w+\s+)(\$\w+\s+)(:\w+\s*)$/ }, + { opCode: 0x20, size: 10, regex: /^(\s*BLT\s+)(\$\w+\s+)(\$\w+\s+)(:\w+\s*)$/ }, + { opCode: 0x21, size: 10, regex: /^(\s*BGE\s+)(\$\w+\s+)(\$\w+\s+)(:\w+\s*)$/ }, + { opCode: 0x22, size: 10, regex: /^(\s*BLE\s+)(\$\w+\s+)(\$\w+\s+)(:\w+\s*)$/ }, + { opCode: 0x23, size: 10, regex: /^(\s*BEQ\s+)(\$\w+\s+)(\$\w+\s+)(:\w+\s*)$/ }, + { opCode: 0x24, size: 10, regex: /^(\s*BNE\s+)(\$\w+\s+)(\$\w+\s+)(:\w+\s*)$/ }, + { opCode: 0x25, size: 5, regex: /^(\s*SLP\s+)(\$\w+\s*)$/ }, + { opCode: 0x26, size: 5, regex: /^(\s*FIZ\s+)(\$\w+\s*)$/ }, + { opCode: 0x27, size: 5, regex: /^(\s*STZ\s+)(\$\w+\s*)$/ }, + { opCode: 0x28, size: 1, regex: /^\s*FIN\s*$/ }, + { opCode: 0x29, size: 1, regex: /^\s*STP\s*$/ }, + { opCode: 0x2b, size: 5, regex: /^(\s*ERR\s+)(:\w+\s*)$/ }, + { opCode: 0x30, size: 1, regex: /^\s*PCS\s*$/ }, + { opCode: 0x32, size: 3, regex: /^(\s*FUN\s+)(\w+\s*)$/ }, + { opCode: 0x33, size: 7, regex: /^(\s*FUN\s+)(\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x34, size: 11, regex: /^(\s*FUN\s+)(\w+\s+)(\$\w+\s+)(\$(\w+)\s*)$/ }, + { opCode: 0x35, size: 7, regex: /^(\s*FUN\s+)(@\w+\s+)(\w+\s*)$/ }, + { opCode: 0x36, size: 11, regex: /^\s*(FUN)\s+@(\w+)\s+(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x37, size: 15, regex: /^(\s*FUN\s+)(@\w+\s+)(\w+\s+)(\$\w+\s+)(\$\w+\s*)$/ }, + { opCode: 0x7f, size: 1, regex: /^\s*NOP\s*$/ } + ]; + const allowedFunctions = [ + { fnCode: 0x0100, fnName: 'get_A1' }, + { fnCode: 0x0101, fnName: 'get_A2' }, + { fnCode: 0x0102, fnName: 'get_A3' }, + { fnCode: 0x0103, fnName: 'get_A4' }, + { fnCode: 0x0104, fnName: 'get_B1' }, + { fnCode: 0x0105, fnName: 'get_B2' }, + { fnCode: 0x0106, fnName: 'get_B3' }, + { fnCode: 0x0107, fnName: 'get_B4' }, + { fnCode: 0x0110, fnName: 'set_A1' }, + { fnCode: 0x0111, fnName: 'set_A2' }, + { fnCode: 0x0112, fnName: 'set_A3' }, + { fnCode: 0x0113, fnName: 'set_A4' }, + { fnCode: 0x0114, fnName: 'set_A1_A2' }, + { fnCode: 0x0115, fnName: 'set_A3_A4' }, + { fnCode: 0x0116, fnName: 'set_B1' }, + { fnCode: 0x0117, fnName: 'set_B2' }, + { fnCode: 0x0118, fnName: 'set_B3' }, + { fnCode: 0x0119, fnName: 'set_B4' }, + { fnCode: 0x011a, fnName: 'set_B1_B2' }, + { fnCode: 0x011b, fnName: 'set_B3_B4' }, + { fnCode: 0x0120, fnName: 'clear_A' }, + { fnCode: 0x0121, fnName: 'clear_B' }, + { fnCode: 0x0122, fnName: 'clear_A_B' }, + { fnCode: 0x0123, fnName: 'copy_A_From_B' }, + { fnCode: 0x0124, fnName: 'copy_B_From_A' }, + { fnCode: 0x0125, fnName: 'check_A_Is_Zero' }, + { fnCode: 0x0126, fnName: 'check_B_Is_Zero' }, + { fnCode: 0x0127, fnName: 'check_A_equals_B' }, + { fnCode: 0x0128, fnName: 'swap_A_and_B' }, + { fnCode: 0x0129, fnName: 'OR_A_with_B' }, + { fnCode: 0x012a, fnName: 'OR_B_with_A' }, + { fnCode: 0x012b, fnName: 'AND_A_with_B' }, + { fnCode: 0x012c, fnName: 'AND_B_with_A' }, + { fnCode: 0x012d, fnName: 'XOR_A_with_B' }, + { fnCode: 0x012e, fnName: 'XOR_B_with_A' }, + { fnCode: 0x0140, fnName: 'add_A_to_B' }, + { fnCode: 0x0141, fnName: 'add_B_to_A' }, + { fnCode: 0x0142, fnName: 'sub_A_from_B' }, + { fnCode: 0x0143, fnName: 'sub_B_from_A' }, + { fnCode: 0x0144, fnName: 'mul_A_by_B' }, + { fnCode: 0x0145, fnName: 'mul_B_by_A' }, + { fnCode: 0x0146, fnName: 'div_A_by_B' }, + { fnCode: 0x0147, fnName: 'div_B_by_A' }, + { fnCode: 0x0200, fnName: 'MD5_A_to_B' }, + { fnCode: 0x0201, fnName: 'check_MD5_A_with_B' }, + { fnCode: 0x0202, fnName: 'HASH160_A_to_B' }, + { fnCode: 0x0203, fnName: 'check_HASH160_A_with_B' }, + { fnCode: 0x0204, fnName: 'SHA256_A_to_B' }, + { fnCode: 0x0205, fnName: 'check_SHA256_A_with_B' }, + { fnCode: 0x0300, fnName: 'get_Block_Timestamp' }, + { fnCode: 0x0301, fnName: 'get_Creation_Timestamp' }, + { fnCode: 0x0302, fnName: 'get_Last_Block_Timestamp' }, + { fnCode: 0x0303, fnName: 'put_Last_Block_Hash_In_A' }, + { fnCode: 0x0304, fnName: 'A_to_Tx_after_Timestamp' }, + { fnCode: 0x0305, fnName: 'get_Type_for_Tx_in_A' }, + { fnCode: 0x0306, fnName: 'get_Amount_for_Tx_in_A' }, + { fnCode: 0x0307, fnName: 'get_Timestamp_for_Tx_in_A' }, + { fnCode: 0x0308, fnName: 'get_Ticket_Id_for_Tx_in_A' }, + { fnCode: 0x0309, fnName: 'message_from_Tx_in_A_to_B' }, + { fnCode: 0x030a, fnName: 'B_to_Address_of_Tx_in_A' }, + { fnCode: 0x030b, fnName: 'B_to_Address_of_Creator' }, + { fnCode: 0x0400, fnName: 'get_Current_Balance' }, + { fnCode: 0x0401, fnName: 'get_Previous_Balance' }, + { fnCode: 0x0402, fnName: 'send_to_Address_in_B' }, + { fnCode: 0x0403, fnName: 'send_All_to_Address_in_B' }, + { fnCode: 0x0404, fnName: 'send_Old_to_Address_in_B' }, + { fnCode: 0x0405, fnName: 'send_A_to_Address_in_B' }, + { fnCode: 0x0406, fnName: 'add_Minutes_to_Timestamp' } + ]; + /** + * Main function to loop thru lines + * @param assemblySource + * @param addDivLine true: add a
for every line; false: add
+ * @returns HTML string + */ + function toHTML(assemblySource, addDivLine = true) { + // process line by line + const lines = assemblySource.split('\n'); + let ret = ''; + // loop thru all lines + lines.forEach((line, idx) => { + if (addDivLine === true) { + ret += `
`; + } + // Find a rule for instruction + const FoundRule = allowedCodes.find(Rule => Rule.regex.exec(line) !== null); + ret += colorThisLine(line, FoundRule); + if (addDivLine === true) + ret += '
'; + else + ret += '
'; + }); + return ret; + } + function toSpan(text, classname) { + return `${text}`; + } + function colorThisLine(asmLine, Rule) { + let apiName; + if (Rule === undefined) { + return toSpan(asmLine, Config.spanErrorClass); + } + const parts = Rule.regex.exec(asmLine); + if (parts === null) { + return toSpan(asmLine, Config.spanErrorClass); + } + switch (Rule.opCode) { + case 0xf0: // is empty line + return asmLine; + case 0xf1: // is label line + return toSpan(parts[0], Config.spanLabelClass); + case 0xf2: // comment + return toSpan(parts[1], Config.spanDirectiveClass) + + toSpan(parts[2], Config.spanCommentClass); + case 0xf3: // declare + return toSpan(parts[1], Config.spanDirectiveClass) + + toSpan(parts[2], Config.spanVariableClass); + case 0xf4: { // const + const SetRule = allowedCodes.find(Obj => Obj.opCode === 0x01); + return toSpan(parts[1], Config.spanDirectiveClass) + + colorThisLine(parts[2], SetRule); + } + case 0xf5: // program + return toSpan(parts[1], Config.spanDirectiveClass) + + parts[2]; + case 0x01: + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + toSpan(parts[3], Config.spanNumberClass); + case 0x02: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x16: + case 0x17: + case 0x18: + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + toSpan(parts[3], Config.spanVariableClass); + case 0x03: + case 0x04: + case 0x05: + case 0x0d: + case 0x10: + case 0x11: + case 0x25: + case 0x26: + case 0x27: + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass); + case 0x13: + case 0x28: + case 0x29: + case 0x30: + case 0x7f: + return toSpan(parts[0], Config.spanInstructionClass); + case 0x0e: + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + parts[3] + + toSpan(parts[4], Config.spanVariableClass) + + parts[5]; + case 0x0f: + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + parts[3] + + toSpan(parts[4], Config.spanVariableClass) + + parts[5] + + toSpan(parts[6], Config.spanVariableClass) + + parts[7]; + case 0x14: + return toSpan(parts[1], Config.spanInstructionClass) + + parts[2] + + toSpan(parts[3], Config.spanVariableClass) + + parts[4] + + toSpan(parts[5], Config.spanVariableClass); + case 0x15: + return toSpan(parts[1], Config.spanInstructionClass) + + parts[2] + + toSpan(parts[3], Config.spanVariableClass) + + parts[4] + + toSpan(parts[5], Config.spanVariableClass) + + parts[6] + + toSpan(parts[7], Config.spanVariableClass); + case 0x12: + case 0x1a: + case 0x2b: + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanLabelClass); + case 0x1b: + case 0x1e: + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + toSpan(parts[3], Config.spanLabelClass); + case 0x1f: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + toSpan(parts[3], Config.spanVariableClass) + + toSpan(parts[4], Config.spanLabelClass); + case 0x32: + apiName = parts[2].trim(); + if (allowedFunctions.findIndex(Obj => Obj.fnName === apiName) === -1) { + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanErrorClass); + } + else { + return toSpan(parts[0], Config.spanInstructionClass); + } + case 0x33: + apiName = parts[2].trim(); + if (allowedFunctions.findIndex(Obj => Obj.fnName === apiName) === -1) { + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanErrorClass) + + toSpan(parts[3], Config.spanVariableClass); + } + else { + return toSpan(parts[1] + parts[2], Config.spanInstructionClass) + + toSpan(parts[3], Config.spanVariableClass); + } + case 0x34: + apiName = parts[2].trim(); + if (allowedFunctions.findIndex(Obj => Obj.fnName === apiName) === -1) { + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanErrorClass) + + toSpan(parts[3], Config.spanVariableClass) + + toSpan(parts[4], Config.spanVariableClass); + } + else { + return toSpan(parts[1] + parts[2], Config.spanInstructionClass) + + toSpan(parts[3], Config.spanVariableClass) + + toSpan(parts[4], Config.spanVariableClass); + } + case 0x35: + apiName = parts[3].trim(); + if (allowedFunctions.findIndex(Obj => Obj.fnName === apiName) === -1) { + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + toSpan(parts[3], Config.spanErrorClass); + } + else { + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + toSpan(parts[3], Config.spanInstructionClass); + } + case 0x37: + apiName = parts[3].trim(); + if (allowedFunctions.findIndex(Obj => Obj.fnName === apiName) === -1) { + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + toSpan(parts[3], Config.spanInstructionClass) + + toSpan(parts[4], Config.spanVariableClass) + + toSpan(parts[5], Config.spanVariableClass); + } + else { + return toSpan(parts[1], Config.spanInstructionClass) + + toSpan(parts[2], Config.spanVariableClass) + + toSpan(parts[3], Config.spanErrorClass) + + toSpan(parts[4], Config.spanVariableClass) + + toSpan(parts[5], Config.spanVariableClass); + } + case 0x36: + default: + } + // this should never be reached + return toSpan(asmLine, Config.spanErrorClass); + } + return toHTML(asmSourceCode, false); +} diff --git a/v0.3/out/bytecoder.js b/v0.3/out/bytecoder.js new file mode 100644 index 0000000..71de192 --- /dev/null +++ b/v0.3/out/bytecoder.js @@ -0,0 +1,497 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/** + * Transforms assembly code into machine code + * @param assemblySourceCode string + * @returns {BYTECODE_RETURN_OBJECT} with all details needed for + * smart contract deployment. + * @throws {Error} on any source code mistake. + */ +// eslint-disable-next-line no-unused-vars +function bytecode(assemblySourceCode) { + const opCodeTable = [ + { opCode: 0xf0, name: 'blank', size: 0, argsType: [], regex: /^\s*$/ }, + { opCode: 0xf1, name: 'label', size: 0, argsType: [], regex: /^\s*(\w+):\s*$/ }, + { opCode: 0xf2, name: 'comment', size: 0, argsType: [], regex: /^\s*\^comment\s+(.*)/ }, + { opCode: 0xf3, name: 'declare', size: 0, argsType: [], regex: /^\s*\^declare\s+(\w+)\s*$/ }, + { opCode: 0xf4, name: 'const', size: 0, argsType: [], regex: /^\s*\^const\s+SET\s+@(\w+)\s+#([\da-f]{16})\b\s*$/ }, + { opCode: 0xf5, name: 'program', size: 0, argsType: [], regex: /^\s*\^program\s+(\w+\b)(.*)$/ }, + { opCode: 0x01, name: 'SET_VAL', size: 13, argsType: ['I', 'L'], regex: /^\s*SET\s+@(\w+)\s+#([\da-f]{16})\b\s*$/ }, + { opCode: 0x02, name: 'SET_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SET\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x03, name: 'CLR_DAT', size: 5, argsType: ['I'], regex: /^\s*CLR\s+@(\w+)\s*$/ }, + { opCode: 0x04, name: 'INC_DAT', size: 5, argsType: ['I'], regex: /^\s*INC\s+@(\w+)\s*$/ }, + { opCode: 0x05, name: 'DEC_DAT', size: 5, argsType: ['I'], regex: /^\s*DEC\s+@(\w+)\s*$/ }, + { opCode: 0x06, name: 'ADD_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*ADD\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x07, name: 'SUB_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SUB\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x08, name: 'MUL_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*MUL\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x09, name: 'DIV_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*DIV\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x0a, name: 'BOR_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*BOR\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x0b, name: 'AND_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*AND\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x0c, name: 'XOR_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*XOR\s+@(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x0d, name: 'NOT_DAT', size: 5, argsType: ['I'], regex: /^\s*NOT\s+@(\w+)\s*$/ }, + { opCode: 0x0e, name: 'SET_IND', size: 9, argsType: ['I', 'I'], regex: /^\s*SET\s+@(\w+)\s+\$\(\$(\w+)\)\s*$/ }, + { opCode: 0x0f, name: 'SET_IDX', size: 13, argsType: ['I', 'I', 'I'], regex: /^\s*SET\s+@(\w+)\s+\$\(\$(\w+)\s*\+\s*\$(\w+)\)\s*$/ }, + { opCode: 0x10, name: 'PSH_DAT', size: 5, argsType: ['I'], regex: /^\s*PSH\s+\$(\w+)\s*$/ }, + { opCode: 0x11, name: 'POP_DAT', size: 5, argsType: ['I'], regex: /^\s*POP\s+@(\w+)\s*$/ }, + { opCode: 0x12, name: 'JMP_SUB', size: 5, argsType: ['J'], regex: /^\s*JSR\s+:(\w+)\s*$/ }, + { opCode: 0x13, name: 'RET_SUB', size: 1, argsType: [], regex: /^\s*RET\s*$/ }, + { opCode: 0x14, name: 'IND_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SET\s+@\(\$(\w+)\)\s+\$(\w+)\s*$/ }, + { opCode: 0x15, name: 'IDX_DAT', size: 13, argsType: ['I', 'I', 'I'], regex: /^\s*SET\s+@\(\$(\w+)\s*\+\s*\$(\w+)\)\s+\$(\w+)\s*$/ }, + { 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: 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*$/ }, + { opCode: 0x1f, name: 'BGT_DAT', size: 10, argsType: ['I', 'I', 'B'], regex: /^\s*BGT\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/ }, + { opCode: 0x20, name: 'BLT_DAT', size: 10, argsType: ['I', 'I', 'B'], regex: /^\s*BLT\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/ }, + { opCode: 0x21, name: 'BGE_DAT', size: 10, argsType: ['I', 'I', 'B'], regex: /^\s*BGE\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/ }, + { opCode: 0x22, name: 'BLE_DAT', size: 10, argsType: ['I', 'I', 'B'], regex: /^\s*BLE\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/ }, + { opCode: 0x23, name: 'BEQ_DAT', size: 10, argsType: ['I', 'I', 'B'], regex: /^\s*BEQ\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/ }, + { opCode: 0x24, name: 'BNE_DAT', size: 10, argsType: ['I', 'I', 'B'], regex: /^\s*BNE\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/ }, + { opCode: 0x25, name: 'SLP_DAT', size: 5, argsType: ['I'], regex: /^\s*SLP\s+\$(\w+)\s*$/ }, + { opCode: 0x26, name: 'FIZ_DAT', size: 5, argsType: ['I'], regex: /^\s*FIZ\s+\$(\w+)\s*$/ }, + { 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: 0x2b, name: 'ERR_ADR', size: 5, argsType: ['J'], regex: /^\s*ERR\s+:(\w+)\s*$/ }, + { 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*$/ }, + { opCode: 0x34, name: 'EXT_FUN_DAT_2', size: 11, argsType: ['F', 'I', 'I'], regex: /^\s*FUN\s+(\w+)\s+\$(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x35, name: 'EXT_FUN_RET', size: 7, argsType: ['F', 'I'], regex: /^\s*FUN\s+@(\w+)\s+(\w+)\s*$/ }, + { opCode: 0x36, name: 'EXT_FUN_RET_DAT', size: 11, argsType: ['F', 'I', 'I'], regex: /^\s*FUN\s+@(\w+)\s+(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x37, name: 'EXT_FUN_RET_DAT_2', size: 15, argsType: ['F', 'I', 'I', 'I'], regex: /^\s*FUN\s+@(\w+)\s+(\w+)\s+\$(\w+)\s+\$(\w+)\s*$/ }, + { opCode: 0x7f, name: 'NOP', size: 1, argsType: [], regex: /^\s*NOP\s*$/ } + ]; + const apiCodeTable = [ + { name: 'get_A1', apiCode: 0x0100, opCode: 0x35 }, + { name: 'get_A2', apiCode: 0x0101, opCode: 0x35 }, + { name: 'get_A3', apiCode: 0x0102, opCode: 0x35 }, + { name: 'get_A4', apiCode: 0x0103, opCode: 0x35 }, + { name: 'get_B1', apiCode: 0x0104, opCode: 0x35 }, + { name: 'get_B2', apiCode: 0x0105, opCode: 0x35 }, + { name: 'get_B3', apiCode: 0x0106, opCode: 0x35 }, + { name: 'get_B4', apiCode: 0x0107, opCode: 0x35 }, + { name: 'set_A1', apiCode: 0x0110, opCode: 0x33 }, + { name: 'set_A2', apiCode: 0x0111, opCode: 0x33 }, + { name: 'set_A3', apiCode: 0x0112, opCode: 0x33 }, + { name: 'set_A4', apiCode: 0x0113, opCode: 0x33 }, + { name: 'set_A1_A2', apiCode: 0x0114, opCode: 0x34 }, + { name: 'set_A3_A4', apiCode: 0x0115, opCode: 0x34 }, + { name: 'set_B1', apiCode: 0x0116, opCode: 0x33 }, + { name: 'set_B2', apiCode: 0x0117, opCode: 0x33 }, + { name: 'set_B3', apiCode: 0x0118, opCode: 0x33 }, + { name: 'set_B4', apiCode: 0x0119, opCode: 0x33 }, + { name: 'set_B1_B2', apiCode: 0x011a, opCode: 0x34 }, + { name: 'set_B3_B4', apiCode: 0x011b, opCode: 0x34 }, + { name: 'clear_A', apiCode: 0x0120, opCode: 0x32 }, + { name: 'clear_B', apiCode: 0x0121, opCode: 0x32 }, + { name: 'clear_A_B', apiCode: 0x0122, opCode: 0x32 }, + { name: 'copy_A_From_B', apiCode: 0x0123, opCode: 0x32 }, + { name: 'copy_B_From_A', apiCode: 0x0124, opCode: 0x32 }, + { name: 'check_A_Is_Zero', apiCode: 0x0125, opCode: 0x35 }, + { name: 'check_B_Is_Zero', apiCode: 0x0126, opCode: 0x35 }, + { name: 'check_A_equals_B', apiCode: 0x0127, opCode: 0x35 }, + { name: 'swap_A_and_B', apiCode: 0x0128, opCode: 0x32 }, + { name: 'OR_A_with_B', apiCode: 0x0129, opCode: 0x32 }, + { name: 'OR_B_with_A', apiCode: 0x012a, opCode: 0x32 }, + { name: 'AND_A_with_B', apiCode: 0x012b, opCode: 0x32 }, + { name: 'AND_B_with_A', apiCode: 0x012c, opCode: 0x32 }, + { name: 'XOR_A_with_B', apiCode: 0x012d, opCode: 0x32 }, + { name: 'XOR_B_with_A', apiCode: 0x012e, opCode: 0x32 }, + { name: 'add_A_to_B', apiCode: 0x0140, opCode: 0x32 }, + { name: 'add_B_to_A', apiCode: 0x0141, opCode: 0x32 }, + { name: 'sub_A_from_B', apiCode: 0x0142, opCode: 0x32 }, + { name: 'sub_B_from_A', apiCode: 0x0143, opCode: 0x32 }, + { name: 'mul_A_by_B', apiCode: 0x0144, opCode: 0x32 }, + { name: 'mul_B_by_A', apiCode: 0x0145, opCode: 0x32 }, + { name: 'div_A_by_B', apiCode: 0x0146, opCode: 0x32 }, + { name: 'div_B_by_A', apiCode: 0x0147, opCode: 0x32 }, + { name: 'MD5_A_to_B', apiCode: 0x0200, opCode: 0x32 }, + { name: 'check_MD5_A_with_B', apiCode: 0x0201, opCode: 0x35 }, + { name: 'HASH160_A_to_B', apiCode: 0x0202, opCode: 0x32 }, + { 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: 'get_Block_Timestamp', apiCode: 0x0300, opCode: 0x35 }, + { name: 'get_Creation_Timestamp', apiCode: 0x0301, opCode: 0x35 }, + { name: 'get_Last_Block_Timestamp', apiCode: 0x0302, opCode: 0x35 }, + { name: 'put_Last_Block_Hash_In_A', apiCode: 0x0303, opCode: 0x32 }, + { name: 'A_to_Tx_after_Timestamp', apiCode: 0x0304, opCode: 0x33 }, + { name: 'get_Type_for_Tx_in_A', apiCode: 0x0305, opCode: 0x35 }, + { name: 'get_Amount_for_Tx_in_A', apiCode: 0x0306, opCode: 0x35 }, + { name: 'get_Timestamp_for_Tx_in_A', apiCode: 0x0307, opCode: 0x35 }, + { name: 'get_Ticket_Id_for_Tx_in_A', apiCode: 0x0308, opCode: 0x35 }, + { 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_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 } + ]; + const AsmObj = { + memory: [], + code: [], + labels: [], + PName: '', + PDescription: '', + PActivationAmount: '', + PUserStackPages: 0, + PCodeStackPages: 0, + bytecode: '', + bytedata: '' + }; + function genCodeInstr() { + return { + source: '', + address: -1, + station: '', + size: 0, + instructionValues: [], + compiledInstruction: '' + }; + } + /* * * Main function! * * */ + function bytecodeMain() { + // process line by line + const line = assemblySourceCode.split('\n'); + // first pass, fill address, opcodes, apicodes, constants + line.forEach((codeLine, idx) => { + for (const currOpCodeTable of opCodeTable) { + const parts = currOpCodeTable.regex.exec(codeLine); + if (parts !== null) { + process(parts, currOpCodeTable); + return; + } + } + throw new Error(`bytecode() error #1. No rule found to process line ${idx}: "${codeLine}".`); + }); + // second pass, solve branches offsets + do { + AsmObj.labels = []; + let currAddr = 0; + // Rebuild AsmObj.labels + AsmObj.code.forEach(element => { + element.address = currAddr; + if (element.station.length !== 0) { + AsmObj.labels.push({ label: element.station, address: currAddr }); + } + currAddr += element.size; + }); + } while (!AsmObj.code.every(checkBranches)); + // third pass, push jump an branches. + AsmObj.code.forEach(fillJumpsAndBranches); + // last pass, join all contents in little endian notation (code and data) + AsmObj.code.forEach(finishHim); + AsmObj.bytedata = fatality(AsmObj.memory); + return buildRetObj(); + } + /** Process one matched instruction */ + function process(parts, instruction) { + function getMemoryAddress(varName) { + const idx = AsmObj.memory.findIndex(Obj => Obj.name === varName); + if (idx === -1) { + return AsmObj.memory.push({ name: varName, value: 0n }) - 1; + } + return idx; + } + // blank line + if (instruction.opCode === 0xF0) { + return; + } + // '^comment user_comment' + if (instruction.opCode === 0xF2) { + return; + } + // '^declare varName' + if (instruction.opCode === 0xF3) { + getMemoryAddress(parts[1]); + return; + } + // '^const SET @(varName) #(hex_content)' + if (instruction.opCode === 0xF4) { + // This can cause a bug if const instruction become before declare instruction. + // But this is forbidden by generator, so only bug if compiling from wrong man + // made assembly code. + const addr = getMemoryAddress(parts[1]); + AsmObj.memory[addr].value = BigInt('0x' + parts[2]); + return; + } + // '^program type information' + if (instruction.opCode === 0xF5) { + if (parts[1] === 'name') { + AsmObj.PName = parts[2].trim(); + } + if (parts[1] === 'description') { + AsmObj.PDescription = parts[2].trim(); + } + if (parts[1] === 'activationAmount') { + AsmObj.PActivationAmount = parts[2].trim(); + } + if (parts[1] === 'userStackPages') { + AsmObj.PUserStackPages = Number(parts[2].trim()); + } + if (parts[1] === 'codeStackPages') { + AsmObj.PCodeStackPages = Number(parts[2].trim()); + } + return; + } + // Create a new object + const CodeObj = genCodeInstr(); + CodeObj.source = parts[0]; + // label: + if (instruction.opCode === 0xF1) { + CodeObj.station = parts[1]; + AsmObj.code.push(CodeObj); + return; + } + CodeObj.size = instruction.size; + CodeObj.instructionValues.push({ type: 'O', value: BigInt(instruction.opCode) }); + let i = 0; + if (instruction.opCode >= 0x35 && instruction.opCode <= 0x37) { + // 0x35, 0x36 and 0x37 are exceptions for args order, treat now + const search = apiCodeTable.find(obj => obj.name === parts[2] && obj.opCode === instruction.opCode); + if (search === undefined) { + throw new Error(`bytecode() error #2. API function not found. Instruction: "${CodeObj.source}"`); + } + CodeObj.instructionValues.push({ type: 'F', value: BigInt(search.apiCode) }); + CodeObj.instructionValues.push({ type: 'I', value: BigInt(BigInt(getMemoryAddress(parts[1]))) }); + i = 2; + } + for (; i < instruction.argsType.length; i++) { + // process generic instructions + if (instruction.argsType[i] === 'I') { + CodeObj.instructionValues.push({ type: 'I', value: BigInt(getMemoryAddress(parts[i + 1])) }); + continue; + } + if (instruction.argsType[i] === 'L') { + CodeObj.instructionValues.push({ type: 'L', value: BigInt('0x' + parts[i + 1]) }); + continue; + } + if (instruction.argsType[i] === 'B') { + // branch offset will be processed later + CodeObj.branchLabel = parts[i + 1]; + continue; + } + if (instruction.argsType[i] === 'J') { + // jump will be processed later + CodeObj.jumpLabel = parts[i + 1]; + continue; + } + if (instruction.argsType[i] === 'F') { + // function name for opCodes 0x32, 0x33, 0x34 + const search = apiCodeTable.find(obj => obj.name === parts[1] && obj.opCode === instruction.opCode); + if (search === undefined) { + throw new Error(`bytecode() error #3. API function not found. Instruction: "${CodeObj.source}"`); + } + CodeObj.instructionValues.push({ type: 'F', value: BigInt(search.apiCode) }); + } + } + AsmObj.code.push(CodeObj); + } + /** + * Check if branches offsets are in range. If not, update AsmObj + * inverting branches and adding jump instruction. + * @param currItem Current instruction to update address + * @param idx Current index of asmObj + * @returns true if instruction is not branch or if branch offset + * is in range. False if AsmObj was modified and process need to + * be restarted. + */ + function checkBranches(currItem, idx) { + if (currItem.branchLabel !== undefined) { + const search = AsmObj.labels.find(obj => obj.label === currItem.branchLabel); + if (search === undefined) { + throw new Error(`bytecode() error #4. Unknow label ${currItem.branchLabel}. Instruction: '${currItem.source}'`); + } + const offset = search.address - currItem.address; + if (offset < -128 || offset > 127) { + // We have branch offset overflow: solve it! + // create jump operation + const JumpCode = genCodeInstr(); + JumpCode.source = `JUMP: ${currItem.source}`; + JumpCode.size = 5; + JumpCode.jumpLabel = currItem.branchLabel; + JumpCode.instructionValues.push({ type: 'O', value: 0x1an }); + // change opCode + switch (currItem.instructionValues[0].value) { + case 0x1bn: + currItem.instructionValues[0].value = 0x1en; + break; // BZR_DAT -> BNZ_DAT + case 0x1en: + currItem.instructionValues[0].value = 0x1bn; + break; // BNZ_DAT -> BZR_DAT + case 0x1fn: + currItem.instructionValues[0].value = 0x22n; + break; // BGT_DAT -> BLE_DAT + case 0x22n: + currItem.instructionValues[0].value = 0x1fn; + break; // BLE_DAT -> BGT_DAT + case 0x21n: + currItem.instructionValues[0].value = 0x20n; + break; // BGE_DAT -> BLT_DAT + case 0x20n: + currItem.instructionValues[0].value = 0x21n; + break; // BLT_DAT -> BGE_DAT + case 0x23n: + currItem.instructionValues[0].value = 0x24n; + break; // BEQ_DAT -> BNE_DAT + case 0x24n: + currItem.instructionValues[0].value = 0x23n; + break; // BNE_DAT -> BEQ_DAT + } + // change branch destination + currItem.branchLabel = `__${currItem.address}`; + if (AsmObj.code[idx + 1].station.length !== 0) { + // station already filled, add a new code for label + const LabelCode = genCodeInstr(); + LabelCode.source = `JUMP: ${currItem.source}`; + LabelCode.size = 0; + LabelCode.station = `__${currItem.address}`; + // insert jump+label operation + AsmObj.code.splice(idx + 1, 0, JumpCode, LabelCode); + } + else { + // station is free, no need to add a new code for label + AsmObj.code[idx + 1].station = `__${currItem.address}`; + // insert jump operation + AsmObj.code.splice(idx + 1, 0, JumpCode); + } + // AsmObj modified... Do it again. + return false; + } + } + // Item has no branch property or branch is in range + return true; + } + /** + * Get values from 'branchLabel' or 'jumpLabel' and pushes into + * 'content' and 'content_type' + * @param currItem Current instruction + */ + function fillJumpsAndBranches(currItem) { + if (currItem.branchLabel !== undefined) { + const search = AsmObj.labels.find(obj => obj.label === currItem.branchLabel); + if (search === undefined) { + throw new Error(`bytecode() error #6: Unknow branch label ${currItem.branchLabel}. Source: "${currItem.source}"`); + } + const offset = search.address - currItem.address; + currItem.instructionValues.push({ type: 'B', value: BigInt(offset) }); + delete currItem.branchLabel; + } + else if (currItem.jumpLabel !== undefined) { + const search = AsmObj.labels.find(obj => obj.label === currItem.jumpLabel); + if (search === undefined) { + throw new Error(`bytecode() error #5: Unknow jump label ${currItem.jumpLabel}. Source: "${currItem.source}"`); + } + currItem.instructionValues.push({ type: 'J', value: BigInt(search.address) }); + delete currItem.jumpLabel; + } + } + /** Builds returnObject with values from AsmObj */ + function buildRetObj() { + let cspages = 0; + let uspages = 0; + if (assemblySourceCode.indexOf('JSR ') !== -1 || assemblySourceCode.indexOf('RET') !== -1) { + if (AsmObj.PCodeStackPages > 0) { + cspages = AsmObj.PCodeStackPages; + } + else { + cspages = 1; + } + } + if (assemblySourceCode.indexOf('POP ') !== -1 || assemblySourceCode.indexOf('PSH ') !== -1) { + if (AsmObj.PUserStackPages > 0) { + uspages = AsmObj.PUserStackPages; + } + else { + uspages = 1; + } + } + const datapages = Math.ceil(AsmObj.memory.length / 32); + const codepages = Math.ceil(AsmObj.bytecode.length / (32 * 16)); + const minimumfee = (cspages + uspages + datapages + codepages) * 7350000; + return { + DataPages: datapages, + CodeStackPages: cspages, + UserStackPages: uspages, + CodePages: codepages, + MinimumFeeNQT: minimumfee.toString(10), + ByteCode: AsmObj.bytecode, + MachineCodeHashId: hashMachineCode(AsmObj.bytecode), + ByteData: AsmObj.bytedata, + Memory: AsmObj.memory.map(Obj => Obj.name), + Labels: AsmObj.labels, + PName: AsmObj.PName, + PDescription: AsmObj.PDescription, + PActivationAmount: AsmObj.PActivationAmount + }; + } + /** + * Translate obj.content to obj.hexstring + * @param currItem Current instruction object + */ + function finishHim(currItem) { + currItem.instructionValues.forEach(instrObj => { + currItem.compiledInstruction += number2hexstring(instrObj.value, instrObj.type); + }); + AsmObj.bytecode += currItem.compiledInstruction; + } + /** Finds last non zero memory value and returns hexstring with memory values from zero until last non zero. */ + function fatality(mem) { + let lastIndex = -1; + let retString = ''; + for (let i = mem.length - 1; i >= 0; i--) { + if (mem[i].value !== 0n) { + lastIndex = i; + break; + } + } + for (let i = 0; i <= lastIndex; i++) { + retString += number2hexstring(mem[i].value, 'L'); + } + return retString; + } + /** + * + * @param value Value to be converted + * @param valType To indicate number of bytes + * @returns Value in hexstring in little endian format + */ + function number2hexstring(value, valType) { + if (valType === 'O') { + return value.toString(16).padStart(2, '0'); + } + if (valType === 'B') { + if (value >= 0n) { + return value.toString(16).padStart(2, '0'); + } + else { + return (256n + value).toString(16).padStart(2, '0'); + } + } + let bytes = 0; + let retString = ''; + if (valType === 'F') + bytes = 2; + if (valType === 'J') + bytes = 4; + if (valType === 'I') + bytes = 4; + if (valType === 'L') + bytes = 8; + for (let i = 0, base = 256n; i < bytes; i++) { + retString += (value % base).toString(16).padStart(2, '0'); + value = value / base; + } + return retString; + } + return bytecodeMain(); +} diff --git a/v0.3/out/generator.js b/v0.3/out/generator.js new file mode 100644 index 0000000..b3af58b --- /dev/null +++ b/v0.3/out/generator.js @@ -0,0 +1,2240 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/* global TOKEN TOKEN_MODIFIER CONTRACT SENTENCES optimize MEMORY_SLOT DECLARATION_TYPES AST utils ARRAY_TYPE_DEFINITION +STRUCT_TYPE_DEFINITION */ +/** + * Code generator. Translates a Program into assembly source code + * @param Program object holding information + * @returns assembly source code + */ +// eslint-disable-next-line no-unused-vars +function generate(Program) { + // holds variables needed during compilation + const generateUtils = { + latestLoopId: [], + jumpId: 0, + assemblyCode: '', + currFunctionIndex: -1, + currSourceLine: 0, + getNewJumpID: function (line) { + let id = ''; + if (Program.Config.enableLineLabels) { + id += line + '_'; + } + if (Program.Config.enableRandom === true) { + return id + Math.random().toString(36).substr(2, 5); + } + this.jumpId++; + return id + this.jumpId.toString(36); + }, + getLatestLoopId: function () { + // error check must be in code! + return this.latestLoopId[this.latestLoopId.length - 1]; + } + }; + // main function for bigastCompile method, only run once. + function generateMain() { + // add Config Info + configDeclarationGenerator(); + // add variables declaration + Program.memory.forEach(assemblerDeclarationGenerator); + writeAsmLine(''); // blank line to be nice to debugger! + // First instruction is add error handling function + if (Program.functions.findIndex(obj => obj.name === 'catch') !== -1) { + writeAsmLine('ERR :__fn_catch'); + } + // Add code for global sentences + generateUtils.currFunctionIndex = -1; + Program.Global.sentences.forEach(compileSentence); + // jump to main function, or program ends. + if (Program.functions.find(obj => obj.name === 'main') === undefined) { + writeAsmLine('FIN'); + } + else { + writeAsmLine('JMP :__fn_main'); + } + // For every function: + Program.functions.forEach((currentFunction, index) => { + generateUtils.currFunctionIndex = index; + writeAsmLine(''); // blank line to be nice to debugger! + functionHeaderGenerator(); + // add code for functions sentences. + if (currentFunction.sentences !== undefined) { + currentFunction.sentences.forEach(compileSentence); + } + functionTailGenerator(); + }); + // Optimize code; + if (Program.Config.globalOptimization) { + return optimize(generateUtils.assemblyCode, Program.Config.maxConstVars); + } + return generateUtils.assemblyCode; + } + /** + * Traverse the AST created by syntaxer and creates assembly source code. + * @param cgAST AST to be compiled + * @param jumpTarget must be set if the evaluation is part of conditionals or + * loops. It shall be the location where to jump if the evaluated + * expression is false. + * @param jumpNotTarget It is the jump location for complementary logic. + * @param isReversedLogic to use reverse logic for expression evaluation. + * @returns Assembly source code + */ + function codeGenerator(cgAST, jumpTarget, jumpNotTarget, isReversedLogic) { + const auxVars = { + registerInfo: [], + postOperations: '', + funcArgs: [], + declaring: '', + isLeftSideOfAssignment: false, + isConstSentence: false, + hasVoidArray: false, + isTemp(loc) { + if (loc === -1) + return false; + const id = this.registerInfo.find(OBJ => OBJ.Template.address === loc); + if (id?.inUse === true) { + return true; + } + return false; + }, + getNewRegister() { + const id = this.registerInfo.find(OBJ => OBJ.inUse === false); + if (id === undefined) { + throw new RangeError("No more registers available. Try to reduce nested operations or increase 'max_auxVars'"); + } + id.inUse = true; + return JSON.parse(JSON.stringify(id.Template)); + }, + freeRegister(loc) { + if (loc === undefined || loc === -1) { + return; + } + const id = this.registerInfo.find(OBJ => OBJ.Template.address === loc); + if (id === undefined) + return; + id.inUse = false; + }, + createTmpVarsTable() { + const regs = Program.memory.filter(OBJ => /^r\d$/.exec(OBJ.asmName) !== null); + regs.forEach(MEM => { + this.registerInfo.push({ + inUse: false, + Template: MEM + }); + }); + }, + getPostOperations() { + const ret = this.postOperations; + this.postOperations = ''; + return ret; + } + }; + if (cgAST === undefined) { + throw new TypeError('At unknow line. Undefined AST arrived at codeGenerator. BugReport please.'); + } + auxVars.createTmpVarsTable(); + if (isReversedLogic === undefined) { + isReversedLogic = false; + } + const code = genCode(cgAST, jumpTarget !== undefined, isReversedLogic, jumpTarget, jumpNotTarget); + if (Program.Config.warningToError && jumpTarget === undefined && code.MemObj.type === 'register') { + if ((cgAST.type === 'unaryASN' && cgAST.Operation.value !== '*') || + (cgAST.type === 'binaryASN' && (cgAST.Operation.type === 'Comparision' || cgAST.Operation.type === 'Operator'))) { + throw new TypeError(`At line: ${cgAST.Operation.line}. Warning: Operation returning a value that is not being used.`); + } + } + code.instructionset += auxVars.postOperations; + // optimizations for jumps and labels + if (code.instructionset.indexOf(':') >= 0) { + if (cgAST.type === 'endASN') { + if (cgAST.Token.type === 'Keyword' && cgAST.Token.value === 'label') { + return code.instructionset; // do not optimize!!! + } + } + code.instructionset = utils.miniOptimizeJumps(code.instructionset); + } + return code.instructionset; + /** + * Hardwork to compile expressions. Function is recursive to traverse all ASN. + * @param objTree AST to traverse + * @param logicalOp true if wanted return object to be suitable for logical operations + * @param revLogic true if wanted to reverse logic for logical operations + * @param jumpFalse Label to jump if logical operation is false + * @param jumpTrue Label to jump if logical operatio is true + * @returns Object with currently variable returned and the string with assembly code for the remaining AST evaluated + */ + function genCode(objTree, logicalOp, revLogic, jumpFalse, jumpTrue) { + let retMemObj; + let instructionstrain = ''; + let arrayIndex = -1; + switch (objTree.type) { + case 'nullASN': + return { MemObj: utils.createVoidMemObj(), instructionset: '' }; + case 'endASN': + if (logicalOp === true) { + if (objTree.Token.type === 'Constant') { + if (revLogic === false) { + if (objTree.Token.value === '0000000000000000') { + return { MemObj: utils.createVoidMemObj(), instructionset: createSimpleInstruction('Jump', jumpFalse) }; + } + return { MemObj: utils.createVoidMemObj(), instructionset: '' }; + } + if (objTree.Token.value !== '0000000000000000') { + return { MemObj: utils.createVoidMemObj(), instructionset: createSimpleInstruction('Jump', jumpTrue) }; + } + return { MemObj: utils.createVoidMemObj(), instructionset: '' }; + } + if (objTree.Token.type === 'Variable') { + const LGenObj = genCode(objTree, false, revLogic); + instructionstrain += LGenObj.instructionset; + instructionstrain += createInstruction(utils.genNotEqualToken(), LGenObj.MemObj, utils.createConstantMemObj(0), revLogic, jumpFalse, jumpTrue); + auxVars.freeRegister(LGenObj.MemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + throw new TypeError('At line: ' + objTree.Token.line + ". Object type '" + objTree.Token.type + ' at logical operation... Do not know what to do.'); + } + else { + if (objTree.Token.type === 'Variable') { + retMemObj = getMemoryObjectByName(objTree.Token.value, objTree.Token.line, auxVars.declaring); + return { MemObj: retMemObj, instructionset: instructionstrain }; + } + if (objTree.Token.type === 'Keyword') { + if (objTree.Token.value === 'break' || objTree.Token.value === 'continue' || + objTree.Token.value === 'label' || objTree.Token.value === 'asm' || + objTree.Token.value === 'exit' || objTree.Token.value === 'halt') { + return { MemObj: utils.createVoidMemObj(), instructionset: createInstruction(objTree.Token) }; + } + if (objTree.Token.value === 'return') { // this is 'return;' + if (generateUtils.currFunctionIndex === -1) { + throw new TypeError('At line: ' + objTree.Token.line + ". Can not use 'return' in global statements."); + } + if (Program.functions[generateUtils.currFunctionIndex].declaration !== 'void') { + throw new TypeError('At line: ' + objTree.Token.line + ". Function '" + + Program.functions[generateUtils.currFunctionIndex].name + "' must return a '" + + Program.functions[generateUtils.currFunctionIndex].declaration + "' value."); + } + if (Program.functions[generateUtils.currFunctionIndex].name === 'main' || + Program.functions[generateUtils.currFunctionIndex].name === 'catch') { + return { MemObj: utils.createVoidMemObj(), instructionset: createSimpleInstruction('exit') }; + } + return { MemObj: utils.createVoidMemObj(), instructionset: createInstruction(objTree.Token) }; + } + throw new TypeError('At line: ' + objTree.Token.line + ". Keywords '" + objTree.Token.value + "' not implemented."); + } + if (objTree.Token.type === 'Constant') { // ok + retMemObj = utils.createConstantMemObj(); + retMemObj.size = objTree.Token.value.length / 16; + retMemObj.hexContent = objTree.Token.value; + return { MemObj: retMemObj, instructionset: '' }; + } + throw new TypeError('At line:' + objTree.Token.line + '. End object not implemented: ' + objTree.Token.type + ' ' + objTree.Token); + } + case 'lookupASN': + if (objTree.Token.type === 'Variable') { + retMemObj = getMemoryObjectByName(objTree.Token.value, objTree.Token.line, auxVars.declaring); + } + else if (objTree.Token.type === 'Function') { + let isAPI = false; + const APIargs = []; + let subSentences; + if (objTree.FunctionArgs === undefined) { + throw new TypeError(`At line: ${objTree.Token.line}. Missing function arguments. BugReport please.`); + } + if (objTree.Token.extValue === undefined) { + throw new TypeError(`At line: ${objTree.Token.line}. Invalid function structure. BugReport please.`); + } + const fnName = objTree.Token.extValue; + let search = Program.functions.find(val => val.name === fnName); + if (search === undefined) { + if (Program.Config.APIFunctions) { + search = Program.Global.APIFunctions.find(val => val.name === fnName); + if (search === undefined) { + throw new TypeError(`At line: ${objTree.Token.line}. Function '${fnName}' not declared.`); + } + isAPI = true; + } + else { + throw new TypeError(`At line: ${objTree.Token.line}. Function '${fnName}' not declared.`); + } + } + if (isAPI) { + if (search.declaration === 'void') { + retMemObj = utils.createVoidMemObj(); + } + else { + retMemObj = auxVars.getNewRegister(); // reserve tempvar for return type + } + subSentences = utils.splitASTOnDelimiters(objTree.FunctionArgs); + if (subSentences[0].type === 'nullASN') { + subSentences.pop(); + } + if (subSentences.length !== search.argsMemObj.length) { + throw new TypeError(`At line: ${objTree.Token.line}. Wrong number of arguments for function '${fnName}'. It must have '${search.argsMemObj.length}' args.`); + } + subSentences.forEach(stnc => { + const RGenObj = genCode(stnc, false, false); + instructionstrain += RGenObj.instructionset; + if (utils.getDeclarationFromMemory(RGenObj.MemObj) !== 'long') { + if (Program.Config.warningToError) { + throw new TypeError(`WARNING: At line: ${objTree.Token.line}. API Function parameter type is different from variable: 'long' and '${RGenObj.MemObj.declaration}'.`); + } + // Override declaration protection rules + utils.setMemoryDeclaration(RGenObj.MemObj, 'long'); + } + APIargs.push(RGenObj.MemObj); + }); + instructionstrain += createAPICallInstruction(utils.genAPICallToken(objTree.Token.line, search.asmName), retMemObj, APIargs); + APIargs.forEach(varnm => auxVars.freeRegister(varnm.address)); + } + else { // if is regular function call + let isRecursive = false; + if (generateUtils.currFunctionIndex >= 0 && search.name === Program.functions[generateUtils.currFunctionIndex].name) { + isRecursive = true; + // stack current scope variables + Program.memory.filter(OBJ => OBJ.scope === fnName && OBJ.address > 0).reverse().forEach(MEM => { + instructionstrain += createSimpleInstruction('Push', MEM.asmName); + }); + } + // Save registers currently in use in stack. Function execution will overwrite them + const registerStack = auxVars.registerInfo.filter(OBJ => OBJ.inUse === true).reverse(); + registerStack.forEach(OBJ => { + instructionstrain += createSimpleInstruction('Push', OBJ.Template.asmName); + }); + // Check function arguments + subSentences = utils.splitASTOnDelimiters(objTree.FunctionArgs); + if (subSentences[0].type === 'nullASN') { + subSentences.pop(); + } + if (subSentences.length !== search.argsMemObj.length) { + throw new TypeError(`At line: ${objTree.Token.line}. Wrong number of arguments for function '${fnName}'. It must have '${search.argsMemObj.length}' args.`); + } + // Push arguments into stack + for (let i = subSentences.length - 1; i >= 0; i--) { + const RGenObj = genCode(subSentences[i], false, false); + const fnArg = search.argsMemObj[i]; + if (utils.isNotValidDeclarationOp(fnArg.declaration, RGenObj.MemObj)) { + if (Program.Config.warningToError) { + throw new TypeError(`WARNING: At line: ${objTree.Token.line}. Function parameter type is different from variable: '${fnArg.declaration}' and '${RGenObj.MemObj.declaration}'.`); + } + // Override declaration protection rules + utils.setMemoryDeclaration(RGenObj.MemObj, fnArg.declaration); + } + instructionstrain += RGenObj.instructionset; + instructionstrain += createInstruction(utils.genPushToken(objTree.Token.line), RGenObj.MemObj); + auxVars.freeRegister(RGenObj.MemObj.address); + } + instructionstrain += createSimpleInstruction('Function', fnName); + // Pop return value from stack + if (search.declaration === 'void') { + retMemObj = utils.createVoidMemObj(); + } + else { + retMemObj = auxVars.getNewRegister(); + retMemObj.declaration = search.declaration; + retMemObj.typeDefinition = search.typeDefinition; + instructionstrain += createSimpleInstruction('Pop', retMemObj.asmName); + } + // Load registers again + registerStack.reverse(); + registerStack.forEach(OBJ => { + instructionstrain += createSimpleInstruction('Pop', OBJ.Template.asmName); + }); + if (isRecursive) { + // unstack current scope variables + Program.memory.filter(OBJ => OBJ.scope === fnName && OBJ.address > 0).forEach(MEM => { + instructionstrain += createSimpleInstruction('Pop', MEM.asmName); + }); + } + } + } + else { + throw new TypeError(`At line: ${objTree.Token.line}. Modifiers on '${objTree.Token.type}' not implemmented. BugReport please.`); + } + if (objTree.modifiers.length !== 0 && retMemObj.type === 'void') { + throw new TypeError(`At line: ${objTree.Token.line}. Function returning void value can not have modifiers.`); + } + objTree.modifiers.forEach(CurrentModifier => { + if (CurrentModifier.type.includes('Member')) { + // Commom part for MemberByVal and MemberByRef + if (CurrentModifier.Center.type !== 'Variable') { + throw new TypeError(`At line: ${objTree.Token.line}. Can not use variables as struct members.`); + } + const memberName = CurrentModifier.Center.value; + if (memberName === 'length' && CurrentModifier.type === 'MemberByVal') { + // Special array property + let typeDef; + // precedence 1: type definition in offset property + if (retMemObj.Offset) + typeDef = retMemObj.Offset?.typeDefinition; + // precedence 2: base memory type definition + else + typeDef = retMemObj.typeDefinition; + const TypeD = Program.typesDefinitions.find(obj => obj.type === 'array' && obj.name === typeDef); + if (TypeD === undefined) { + throw new TypeError(`At line: ${objTree.Token.line}. Array type definition not found for variable '${retMemObj.name}'.`); + } + const len = TypeD.MemoryTemplate.arrItem?.totalSize; + if (len === undefined) { + throw new TypeError(`At line: ${objTree.Token.line}. Array total size not found for '${retMemObj.name}'.`); + } + if (retMemObj.Offset?.type === 'variable') + auxVars.freeRegister(retMemObj.Offset.addr); + auxVars.freeRegister(retMemObj.address); + retMemObj = utils.createConstantMemObj((len - 1) / TypeD.MemoryTemplate.size); + instructionstrain = ''; + return; + } + let typeName; + if (retMemObj.Offset?.declaration === 'struct') { + // Precedence 1: Info on Offset + typeName = retMemObj.Offset.typeDefinition; + } + else { + // Precedence 2: regular case + typeName = retMemObj.typeDefinition; + } + if (typeName === undefined) { + throw new TypeError(`At line: ${objTree.Token.line}. Variable '${retMemObj.name}' has no struct type definition`); + } + const TypeD = Program.typesDefinitions.find(obj => obj.type === 'struct' && obj.name === typeName); + if (TypeD === undefined) { + throw new TypeError(`At line: ${objTree.Token.line}. Type definition '${typeName}' not found. BugReport please.`); + } + let memberIdx = -1; + for (let i = 0; i < TypeD.structAccumulatedSize.length; i++) { + if (TypeD.structAccumulatedSize[i][0] === memberName) { + memberIdx = i; + break; + } + } + if (memberIdx === -1) { + throw new TypeError(`At line: ${objTree.Token.line}. Member '${memberName}' not found on struct type definition.`); + } + let adder = 0; + const MembersDefinitions = TypeD.structMembers[memberIdx]; + if (MembersDefinitions.arrItem) { + // Update arrItem information + retMemObj.arrItem = { + declaration: MembersDefinitions.arrItem.declaration, + totalSize: MembersDefinitions.arrItem.totalSize, + type: MembersDefinitions.arrItem.type, + typeDefinition: MembersDefinitions.arrItem.typeDefinition + }; + adder++; + } + arrayIndex = -1; + if (CurrentModifier.type === 'MemberByRef') { + if (utils.getDeclarationFromMemory(retMemObj) !== 'struct_ptr') { + throw new TypeError(`At line: ${objTree.Token.line}. Variable '${retMemObj.name}' not defined as struct pointer. Try to use '.' instead.`); + } + if (retMemObj.Offset === undefined) { + retMemObj.Offset = { + type: 'constant', + value: adder + TypeD.structAccumulatedSize[memberIdx][1], + declaration: MembersDefinitions.declaration, + typeDefinition: MembersDefinitions.typeDefinition + }; + } + else if (retMemObj.Offset.type === 'constant') { + if (utils.getDeclarationFromMemory(retMemObj) === 'struct_ptr') { + // Deference location and continue + const TmpMemObj = auxVars.getNewRegister(); + TmpMemObj.declaration = retMemObj.Offset.declaration; + TmpMemObj.typeDefinition = retMemObj.Offset.typeDefinition; + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, retMemObj); + TmpMemObj.Offset = { + type: 'constant', + value: TypeD.structAccumulatedSize[memberIdx][1], + declaration: MembersDefinitions.declaration, + typeDefinition: MembersDefinitions.typeDefinition + }; + retMemObj = TmpMemObj; + } + else { + retMemObj.Offset.value += adder + TypeD.structAccumulatedSize[memberIdx][1]; + retMemObj.Offset.declaration = MembersDefinitions.declaration; + retMemObj.Offset.typeDefinition = MembersDefinitions.typeDefinition; + } + } + else /* if (retMemObj.Modifier.type === "variable") */ { + throw new TypeError(`At line: ${objTree.Token.line}. Inspection needed.`); + } + } + else if (CurrentModifier.type === 'MemberByVal') { + if (utils.getDeclarationFromMemory(retMemObj) === 'struct_ptr') { + throw new TypeError(`At line: ${objTree.Token.line}. Using wrong member notation. Try to use '->' instead.`); + } + if (retMemObj.Offset === undefined) { + retMemObj = getMemoryObjectByLocation(Number('0x' + retMemObj.hexContent) + TypeD.structAccumulatedSize[memberIdx][1]); + // retMemObj = getMemoryObjectByName(retMemObj.asmName + '_' + MembersDefinitions.asmName) + } + else if (retMemObj.Offset.type === 'constant') { + const newLoc = retMemObj.Offset.value + Number('0x' + retMemObj.hexContent); + retMemObj = getMemoryObjectByLocation(newLoc + TypeD.structAccumulatedSize[memberIdx][1]); + } + else /* if (retMemObj.offset_type === "variable") */ { + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), getMemoryObjectByLocation(retMemObj.Offset.addr, objTree.Token.line), utils.createConstantMemObj(adder + TypeD.structAccumulatedSize[memberIdx][1])); + retMemObj.Offset.declaration = MembersDefinitions.declaration; + retMemObj.Offset.typeDefinition = MembersDefinitions.typeDefinition; + } + } + } + else if (CurrentModifier.type === 'Array') { + arrayIndex++; + let TmpMemObj; + let multiplier; + let typeDef; + // precedence 1: type definition in offset property + if (retMemObj.Offset) + typeDef = retMemObj.Offset?.typeDefinition; + // precedence 2: base memory type definition + else + typeDef = retMemObj.typeDefinition; + const TypeD = Program.typesDefinitions.find(obj => obj.type === 'array' && obj.name === typeDef); + if (TypeD === undefined) { + if (utils.getDeclarationFromMemory(retMemObj).includes('_ptr') === false) { + throw new TypeError(`At line: ${objTree.Token.line}. Array type definition not found. Is '${retMemObj.name}' declared as array or pointer?`); + } + multiplier = 1; // allow use of array notation on pointer variables. + } + else { + multiplier = TypeD.arrayMultiplierDim[arrayIndex]; + } + if (retMemObj.arrItem === undefined) { + // pointer operation. + if (retMemObj.Offset === undefined) { + // Create generic array definition + retMemObj.arrItem = { + type: retMemObj.type, + declaration: retMemObj.declaration === 'void_ptr' ? 'long' : retMemObj.declaration.slice(0, -4), + typeDefinition: '', + totalSize: 0 + }; + } + else { + // Copy information from Offset + retMemObj.arrItem = { + type: 'long', + declaration: retMemObj.Offset.declaration === 'void_ptr' ? 'long' : retMemObj.Offset.declaration.slice(0, -4), + typeDefinition: retMemObj.Offset.typeDefinition, + totalSize: 0 + }; + } + } + const ParamMemObj = genCode(CurrentModifier.Center, false, false); + instructionstrain += ParamMemObj.instructionset; + if (ParamMemObj.MemObj.type === 'void') { // special case for text assignment + auxVars.hasVoidArray = true; + return { MemObj: retMemObj, instructionset: instructionstrain }; + } + // big decision tree depending on retMemObj.Offset.value and ParamMemObj.address + const paramAddress = ParamMemObj.MemObj.address; + if (retMemObj.Offset === undefined) { + if (paramAddress === -1) { + retMemObj.Offset = { + type: 'constant', + value: Number(`0x${ParamMemObj.MemObj.hexContent}`) * multiplier, + declaration: retMemObj.arrItem.declaration, + typeDefinition: retMemObj.arrItem.typeDefinition + }; + } + else if (auxVars.isTemp(paramAddress)) { + retMemObj.Offset = { + type: 'variable', + addr: ParamMemObj.MemObj.address, + declaration: retMemObj.arrItem.declaration, + typeDefinition: retMemObj.arrItem.typeDefinition + }; + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), ParamMemObj.MemObj, utils.createConstantMemObj(multiplier)); + } + else /* if ( paramAddress is variable ) */ { + if (multiplier === 1) { + retMemObj.Offset = { + type: 'variable', + addr: ParamMemObj.MemObj.address, + declaration: retMemObj.arrItem.declaration, + typeDefinition: retMemObj.arrItem.typeDefinition + }; + } + else { + TmpMemObj = auxVars.getNewRegister(); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(multiplier)); + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), TmpMemObj, ParamMemObj.MemObj); + retMemObj.Offset = { + type: 'variable', + addr: TmpMemObj.address, + declaration: retMemObj.arrItem.declaration, + typeDefinition: retMemObj.arrItem.typeDefinition + }; + } + } + } + else if (retMemObj.Offset.type === 'constant') { + if (paramAddress === -1) { + retMemObj.Offset.value += Number(`0x${ParamMemObj.MemObj.hexContent}`) * multiplier; + retMemObj.Offset.declaration = retMemObj.arrItem.declaration; + retMemObj.Offset.typeDefinition = retMemObj.arrItem.typeDefinition; + } + else if (auxVars.isTemp(paramAddress)) { + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), ParamMemObj.MemObj, utils.createConstantMemObj(multiplier)); + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), ParamMemObj.MemObj, utils.createConstantMemObj(retMemObj.Offset.value)); + retMemObj.Offset = { + type: 'variable', + addr: ParamMemObj.MemObj.address, + declaration: retMemObj.arrItem.declaration, + typeDefinition: retMemObj.arrItem.typeDefinition + }; + } + else /* if ( paramAddress is variable ) */ { + if (multiplier === 1 && retMemObj.Offset.value === 0) { + retMemObj.Offset = { + type: 'variable', + addr: ParamMemObj.MemObj.address, + declaration: retMemObj.arrItem.declaration, + typeDefinition: retMemObj.arrItem.typeDefinition + }; + } + else { + TmpMemObj = auxVars.getNewRegister(); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, ParamMemObj.MemObj); + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), TmpMemObj, utils.createConstantMemObj(multiplier)); + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), TmpMemObj, utils.createConstantMemObj(retMemObj.Offset.value)); + retMemObj.Offset = { + type: 'variable', + addr: TmpMemObj.address, + declaration: retMemObj.arrItem.declaration, + typeDefinition: retMemObj.arrItem.typeDefinition + }; + } + } + } + else if (auxVars.isTemp(retMemObj.Offset.addr)) { + retMemObj.Offset.declaration = retMemObj.arrItem.declaration; + retMemObj.Offset.typeDefinition = retMemObj.arrItem.typeDefinition; + if (paramAddress === -1) { + const adder = Number('0x' + ParamMemObj.MemObj.hexContent) * multiplier; + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), getMemoryObjectByLocation(retMemObj.Offset.addr, objTree.Token.line), utils.createConstantMemObj(adder)); + } + else if (auxVars.isTemp(paramAddress)) { + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), ParamMemObj.MemObj, utils.createConstantMemObj(multiplier)); + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), getMemoryObjectByLocation(retMemObj.Offset.addr, objTree.Token.line), ParamMemObj.MemObj); + auxVars.freeRegister(ParamMemObj.MemObj.address); + } + else /* if (paramAddress is variable ) */ { + if (multiplier === 1) { + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), getMemoryObjectByLocation(retMemObj.Offset.addr, objTree.Token.line), ParamMemObj.MemObj); + } + else { + TmpMemObj = auxVars.getNewRegister(); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, ParamMemObj.MemObj); + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), TmpMemObj, utils.createConstantMemObj(multiplier)); + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), getMemoryObjectByLocation(retMemObj.Offset.addr, objTree.Token.line), TmpMemObj); + auxVars.freeRegister(TmpMemObj.address); + } + } + } + else /* if ( retMemObj.Offset.addr is variable not temp ) */ { + retMemObj.Offset.declaration = retMemObj.arrItem.declaration; + retMemObj.Offset.typeDefinition = retMemObj.arrItem.typeDefinition; + if (paramAddress === -1) { + if (ParamMemObj.MemObj.hexContent !== '0000000000000000') { + TmpMemObj = auxVars.getNewRegister(); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(ParamMemObj.MemObj.hexContent)); + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), TmpMemObj, utils.createConstantMemObj(multiplier)); + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), TmpMemObj, getMemoryObjectByLocation(retMemObj.Offset.addr, objTree.Token.line)); + retMemObj.Offset.addr = TmpMemObj.address; + } + } + else if (auxVars.isTemp(paramAddress)) { + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), ParamMemObj.MemObj, utils.createConstantMemObj(multiplier)); + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), ParamMemObj.MemObj, getMemoryObjectByLocation(retMemObj.Offset.addr, objTree.Token.line)); + retMemObj.Offset.addr = ParamMemObj.MemObj.address; + } + else /* if (paramAddress is variable )) */ { + TmpMemObj = auxVars.getNewRegister(); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, ParamMemObj.MemObj); + instructionstrain += createInstruction(utils.genMulToken(objTree.Token.line), TmpMemObj, utils.createConstantMemObj(multiplier)); + instructionstrain += createInstruction(utils.genAddToken(objTree.Token.line), TmpMemObj, getMemoryObjectByLocation(retMemObj.Offset.addr, objTree.Token.line)); + retMemObj.Offset.addr = TmpMemObj.address; + } + } + } + else { + throw new TypeError(`At line: ${objTree.Token.line}. Modifier '${CurrentModifier.type}' not implemented.`); + } + }); + if (logicalOp === true) { + if (retMemObj.type === 'void') { + throw new TypeError(`At line: ${objTree.Token.line}. Function returning void value can not be used in conditionals decision.`); + } + instructionstrain += createInstruction(utils.genNotEqualToken(), retMemObj, utils.createConstantMemObj(0), revLogic, jumpFalse, jumpTrue); + auxVars.freeRegister(retMemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + return { MemObj: retMemObj, instructionset: instructionstrain }; + case 'unaryASN': + if (objTree.Operation.type === 'UnaryOperator') { + if (objTree.Operation.value === '!') { // logical NOT + if (logicalOp === true) { + // Swapped arguments "jumpTrue, jumpFalse" because we are swapping the logic! + return genCode(objTree.Center, true, !revLogic, jumpTrue, jumpFalse); + } + else { + const rnd = generateUtils.getNewJumpID(objTree.Operation.line); + const IDNotSF = '__NOT_' + rnd + '_sF'; // set false + const IDNotST = '__NOT_' + rnd + '_sT'; // set true + const IDEnd = '__NOT_' + rnd + '_end'; + const CGenObj = genCode(objTree.Center, true, !revLogic, IDNotST, IDNotSF); + instructionstrain += CGenObj.instructionset; + const TmpMemObj = auxVars.getNewRegister(); + // Logical return is long value! + instructionstrain += createSimpleInstruction('Label', IDNotST); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(1)); + instructionstrain += createSimpleInstruction('Jump', IDEnd); + instructionstrain += createSimpleInstruction('Label', IDNotSF); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(0)); + instructionstrain += createSimpleInstruction('Label', IDEnd); + auxVars.freeRegister(CGenObj.MemObj.address); + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + } + if (objTree.Operation.value === '+') { // unary plus -> do nothing + return genCode(objTree.Center, logicalOp, revLogic, jumpFalse, jumpTrue); + } + if (objTree.Operation.value === '*') { // unary star -> pointer operation + if (auxVars.declaring.length !== 0) { + // do not do any other operation when declaring a pointer. + return genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + } + const CGenObj = genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += CGenObj.instructionset; + const declar = utils.getDeclarationFromMemory(CGenObj.MemObj); + if (declar.includes('_ptr') === false) { + if (Program.Config.warningToError) { + if (objTree.Center.type === 'endASN' || objTree.Center.type === 'lookupASN') { + throw new TypeError(`At line: ${objTree.Operation.line}. Trying to read/set content of variable ${objTree.Center.Token.value} that is not declared as pointer.`); + } + throw new TypeError(`At line: ${objTree.Operation.line}. Trying to read/set content of a value that is not declared as pointer.`); + } + utils.setMemoryDeclaration(CGenObj.MemObj, (declar + '_ptr')); + } + if (CGenObj.MemObj.Offset) { + // Double deference: deference and continue + const TmpMemObj = auxVars.getNewRegister(); + TmpMemObj.declaration = utils.getDeclarationFromMemory(CGenObj.MemObj); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, CGenObj.MemObj); + if (CGenObj.MemObj.Offset.type === 'variable') + auxVars.freeRegister(CGenObj.MemObj.Offset.addr); + auxVars.freeRegister(CGenObj.MemObj.address); + CGenObj.MemObj = TmpMemObj; + } + CGenObj.MemObj.Offset = { + type: 'constant', + value: 0, + declaration: 'long' + }; + if (logicalOp === true) { + instructionstrain += createInstruction(utils.genNotEqualToken(), CGenObj.MemObj, utils.createConstantMemObj(0), revLogic, jumpFalse, jumpTrue); + auxVars.freeRegister(CGenObj.MemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + return { MemObj: CGenObj.MemObj, instructionset: instructionstrain }; + } + if (objTree.Operation.value === '-') { + const CGenObj = genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += CGenObj.instructionset; + const TmpMemObj = auxVars.getNewRegister(); + TmpMemObj.declaration = utils.getDeclarationFromMemory(CGenObj.MemObj); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(0)); + instructionstrain += createInstruction(utils.genSubToken(objTree.Operation.line), TmpMemObj, CGenObj.MemObj); + auxVars.freeRegister(CGenObj.MemObj.address); + if (logicalOp === true) { + instructionstrain += createInstruction(utils.genNotEqualToken(), TmpMemObj, utils.createConstantMemObj(0), revLogic, jumpFalse, jumpTrue); + auxVars.freeRegister(TmpMemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + if (objTree.Operation.value === '~') { + let clearVar = false; + const CGenObj = genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += CGenObj.instructionset; + let TmpMemObj; + if (!auxVars.isTemp(CGenObj.MemObj.address)) { + TmpMemObj = auxVars.getNewRegister(); + TmpMemObj.declaration = utils.getDeclarationFromMemory(CGenObj.MemObj); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, CGenObj.MemObj); + clearVar = true; + } + else { + TmpMemObj = CGenObj.MemObj; + } + instructionstrain += createInstruction(objTree.Operation, TmpMemObj); + if (logicalOp === true) { + instructionstrain += createInstruction(utils.genNotEqualToken(), TmpMemObj, utils.createConstantMemObj(0), revLogic, jumpFalse, jumpTrue); + auxVars.freeRegister(CGenObj.MemObj.address); + auxVars.freeRegister(TmpMemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + if (clearVar) { + auxVars.freeRegister(CGenObj.MemObj.address); + } + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + if (objTree.Operation.value === '&') { + if (jumpTarget !== undefined || jumpFalse !== undefined) { + throw new SyntaxError(`At line: ${objTree.Operation.line}. Can not use UnaryOperator '&' during logical operations with branches.`); + } + const CGenObj = genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += CGenObj.instructionset; + let TmpMemObj; + switch (CGenObj.MemObj.type) { + case 'void': + throw new TypeError(`At line: ${objTree.Operation.line}. Trying to get address of void value.`); + case 'register': + if (Program.Config.warningToError) { + throw new TypeError(`WARNING: At line: ${objTree.Operation.line}. Returning address of a register.`); + } + TmpMemObj = utils.createConstantMemObj(CGenObj.MemObj.address); + break; + case 'constant': + throw new TypeError(`At line: ${objTree.Operation.line}. Trying to get address of a constant value.`); + case 'array': + if (CGenObj.MemObj.Offset !== undefined) { + if (CGenObj.MemObj.Offset.type === 'constant') { + TmpMemObj = utils.createConstantMemObj(utils.addHexContents(CGenObj.MemObj.hexContent, CGenObj.MemObj.Offset.value)); + TmpMemObj.declaration = CGenObj.MemObj.declaration; + } + else { + const Copyvar = JSON.parse(JSON.stringify(CGenObj.MemObj)); + delete Copyvar.Offset; + TmpMemObj = auxVars.getNewRegister(); + TmpMemObj.declaration = CGenObj.MemObj.declaration; + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, Copyvar); + instructionstrain += createInstruction(utils.genAddToken(), TmpMemObj, getMemoryObjectByLocation(CGenObj.MemObj.Offset.addr)); + } + } + else { + TmpMemObj = utils.createConstantMemObj(CGenObj.MemObj.address); + } + break; + case 'struct': + TmpMemObj = utils.createConstantMemObj(CGenObj.MemObj.hexContent); + TmpMemObj.declaration = 'struct_ptr'; + break; + case 'structRef': + if (CGenObj.MemObj.Offset !== undefined) { + throw new TypeError(`At line: ${objTree.Operation.line}. Get address of 'structRef' with offset not implemented. `); + } + TmpMemObj = utils.createConstantMemObj(CGenObj.MemObj.address); + TmpMemObj.declaration = 'struct_ptr'; + break; + case 'long': + TmpMemObj = utils.createConstantMemObj(CGenObj.MemObj.address); + TmpMemObj.declaration = 'long'; + break; + case 'label': + throw new TypeError(`At line: ${objTree.Operation.line}. Trying to get address of a Label`); + default: + throw new TypeError(`At line: ${objTree.Operation.line}. Get address of '${CGenObj.MemObj.type}' not implemented.`); + } + if (TmpMemObj.declaration.includes('_ptr') === false) { + TmpMemObj.declaration += '_ptr'; + } + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + throw new TypeError(`At line: ${objTree.Operation.line}. Unknow unary operator: ${objTree.Operation.value}.`); + } + if (objTree.Operation.type === 'CodeCave') { + return genCode(objTree.Center, logicalOp, revLogic, jumpFalse, jumpTrue); + } + if (objTree.Operation.type === 'Keyword') { + if (objTree.Operation.value === 'long' || objTree.Operation.value === 'void') { + auxVars.declaring = objTree.Operation.value; + return genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + } + if (objTree.Operation.value === 'const') { + auxVars.isConstSentence = true; + return genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + } + if (objTree.Operation.value === 'return') { + if (generateUtils.currFunctionIndex === -1) { + throw new TypeError('At line: ' + objTree.Operation.line + ". Can not use 'return' in global statements."); + } + const currentFunction = Program.functions[generateUtils.currFunctionIndex]; + if (currentFunction.declaration === 'void') { + throw new TypeError('At line: ' + objTree.Operation.line + ". Function '" + + currentFunction.name + "' must return a '" + + currentFunction.declaration + "' value."); + } + if (currentFunction.name === 'main' || + currentFunction.name === 'catch') { + throw new TypeError(`At line: ${objTree.Operation.line}. Special function ${currentFunction.name} must return void value.`); + } + const RGenObj = genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += RGenObj.instructionset; + instructionstrain += auxVars.getPostOperations(); + if (utils.isNotValidDeclarationOp(currentFunction.declaration, RGenObj.MemObj)) { + if (Program.Config.warningToError) { + throw new TypeError(`WARNING: At line: ${objTree.Operation.line}. Function ${currentFunction.name} must return '` + + `${currentFunction.declaration}' value, but it is returning '${RGenObj.MemObj.declaration}'.`); + } + // Override declaration protection rules + utils.setMemoryDeclaration(RGenObj.MemObj, currentFunction.declaration); + } + instructionstrain += createInstruction(objTree.Operation, RGenObj.MemObj); + auxVars.freeRegister(RGenObj.MemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + if (objTree.Operation.value === 'goto') { + const RGenObj = genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += RGenObj.instructionset; + instructionstrain += auxVars.getPostOperations(); + if (RGenObj.MemObj.type !== 'label') { + throw new TypeError(`At line: ${objTree.Operation.line}. Argument for keyword 'goto' is not a label.`); + } + instructionstrain += createInstruction(objTree.Operation, RGenObj.MemObj); + auxVars.freeRegister(RGenObj.MemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + if (objTree.Operation.value === 'sleep') { + const RGenObj = genCode(objTree.Center, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += RGenObj.instructionset; + instructionstrain += auxVars.getPostOperations(); + instructionstrain += createInstruction(objTree.Operation, RGenObj.MemObj); + auxVars.freeRegister(RGenObj.MemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + if (objTree.Operation.value === 'struct') { // nothing to do here + return { MemObj: utils.createVoidMemObj(), instructionset: '' }; + } + throw new TypeError(`At line: ${objTree.Operation.line}. Invalid use of keyword '${objTree.Operation.value}'`); + } + break; + case 'exceptionASN': + if (objTree.Operation.type === 'SetUnaryOperator') { + if (jumpTarget !== undefined) { + throw new SyntaxError('At line: ' + objTree.Operation.line + '. Can not use SetUnaryOperator (++ or --) during logical operations with branches'); + } + if (jumpFalse !== undefined) { + throw new SyntaxError('At line: ' + objTree.Operation.line + '. Can not use SetUnaryOperator (++ or --) during logical operations with branches'); + } + if (objTree.Left !== undefined) { + const LGenObj = genCode(objTree.Left, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += createInstruction(objTree.Operation, LGenObj.MemObj); + return { MemObj: LGenObj.MemObj, instructionset: instructionstrain }; + } + if (objTree.Right !== undefined) { + const RGenObj = genCode(objTree.Right, false, revLogic, jumpFalse, jumpTrue); + auxVars.postOperations += createInstruction(objTree.Operation, RGenObj.MemObj); + return { MemObj: RGenObj.MemObj, instructionset: '' }; + } + throw new TypeError(`At line: ${objTree.Operation.line}. Unknow SetUnaryOperator operation requested.`); + } + break; + case 'binaryASN': + if (objTree.Operation.type === 'Comparision') { + if (logicalOp === false && jumpFalse === undefined) { // need to transform arithmetic to logical + const rnd = generateUtils.getNewJumpID(objTree.Operation.line); + const IDCompSF = '__CMP_' + rnd + '_sF'; // set false + const IDCompST = '__CMP_' + rnd + '_sT'; // set true + const IDEnd = '__CMP_' + rnd + '_end'; + let ret; + let TmpMemObj; + if (objTree.Operation.value === '||') { // Code optimization + ret = genCode(objTree, true, true, IDCompSF, IDCompST); // do it again, now with jump defined + instructionstrain += ret.instructionset; + TmpMemObj = auxVars.getNewRegister(); + // Logical return is long value! + instructionstrain += createSimpleInstruction('Label', IDCompSF); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(0)); + instructionstrain += createSimpleInstruction('Jump', IDEnd); + instructionstrain += createSimpleInstruction('Label', IDCompST); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(1)); + instructionstrain += createSimpleInstruction('Label', IDEnd); + } + else { + ret = genCode(objTree, true, false, IDCompSF, IDCompST); // do it again, now with jump defined + instructionstrain += ret.instructionset; + TmpMemObj = auxVars.getNewRegister(); + // Logical return is long value! + instructionstrain += createSimpleInstruction('Label', IDCompST); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(1)); + instructionstrain += createSimpleInstruction('Jump', IDEnd); + instructionstrain += createSimpleInstruction('Label', IDCompSF); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, utils.createConstantMemObj(0)); + instructionstrain += createSimpleInstruction('Label', IDEnd); + } + auxVars.freeRegister(ret.MemObj.address); + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + if (objTree.Operation.value === '||') { + const rnd = generateUtils.getNewJumpID(objTree.Operation.line); + const IDNextStmt = '__OR_' + rnd + '_next'; + const LGenObj = genCode(objTree.Left, true, true, IDNextStmt, jumpTrue); + instructionstrain += LGenObj.instructionset; + if (auxVars.isTemp(LGenObj.MemObj.address)) { // maybe it was an arithmetic operation + instructionstrain += createInstruction(utils.genNotEqualToken(), LGenObj.MemObj, utils.createConstantMemObj(0), true, jumpFalse, jumpTrue); + } + instructionstrain += createSimpleInstruction('Label', IDNextStmt); + const RGenObj = genCode(objTree.Right, true, true, jumpFalse, jumpTrue); + instructionstrain += RGenObj.instructionset; + if (auxVars.isTemp(RGenObj.MemObj.address)) { // maybe it was an arithmetic operation + instructionstrain += createInstruction(utils.genNotEqualToken(), RGenObj.MemObj, utils.createConstantMemObj(0), true, jumpFalse, jumpTrue); + } + instructionstrain += createSimpleInstruction('Jump', jumpFalse); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + if (objTree.Operation.value === '&&') { + const rnd = generateUtils.getNewJumpID(objTree.Operation.line); + const IDNextStmt = '__AND_' + rnd + '_next'; + const LGenObj = genCode(objTree.Left, true, false, jumpFalse, IDNextStmt); + instructionstrain += LGenObj.instructionset; + if (auxVars.isTemp(LGenObj.MemObj.address)) { // maybe it was an arithmetic operation + instructionstrain += createInstruction(utils.genNotEqualToken(), LGenObj.MemObj, utils.createConstantMemObj(0), false, jumpFalse, jumpTrue); + } + instructionstrain += createSimpleInstruction('Label', IDNextStmt); + const RGenObj = genCode(objTree.Right, true, false, jumpFalse, jumpTrue); + instructionstrain += RGenObj.instructionset; + if (auxVars.isTemp(RGenObj.MemObj.address)) { // maybe it was an arithmetic operation + instructionstrain += createInstruction(utils.genNotEqualToken(), RGenObj.MemObj, utils.createConstantMemObj(0), false, jumpFalse, jumpTrue); + } + instructionstrain += createSimpleInstruction('Jump', jumpTrue); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + // other comparisions operators: ==, !=, <, >, <=, >= + const LGenObj = genCode(objTree.Left, false, revLogic); //, jumpFalse, jumpTrue); must be undefined to evaluate expressions + instructionstrain += LGenObj.instructionset; + const RGenObj = genCode(objTree.Right, false, revLogic); //, jumpFalse, jumpTrue); must be undefined to evaluate expressions + instructionstrain += RGenObj.instructionset; + instructionstrain += createInstruction(objTree.Operation, LGenObj.MemObj, RGenObj.MemObj, revLogic, jumpFalse, jumpTrue); + auxVars.freeRegister(LGenObj.MemObj.address); + auxVars.freeRegister(RGenObj.MemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + if (objTree.Operation.type === 'Delimiter') { + if (jumpTarget !== undefined) { + throw new TypeError(`At line: ${objTree.Operation.line}. It is not possible to evaluate multiple sentences in logical operations.`); + } + const LGenObj = genCode(objTree.Left, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += LGenObj.instructionset; + instructionstrain += auxVars.getPostOperations(); + const RGenObj = genCode(objTree.Right, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += RGenObj.instructionset; + // Note: RGenObj always have MemObj, because jumpTarget is undefined. + auxVars.freeRegister(RGenObj.MemObj.address); + instructionstrain += auxVars.getPostOperations(); + return { MemObj: LGenObj.MemObj, instructionset: instructionstrain }; + } + if (objTree.Operation.type === 'Operator') { + let LGenObj = genCode(objTree.Left, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += LGenObj.instructionset; + let RGenObj = genCode(objTree.Right, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += RGenObj.instructionset; + // Error handling + if (LGenObj.MemObj.type === 'void' || RGenObj.MemObj.type === 'void') { + throw new TypeError(`At line: ${objTree.Operation.line}. Can not make operations with void values.`); + } + // optimization on constant codes: + if (LGenObj.MemObj.type === 'constant' && RGenObj.MemObj.type === 'constant') { + let TmpMemObj; + if (objTree.Operation.value === '+') { + TmpMemObj = utils.createConstantMemObj(utils.addHexContents(LGenObj.MemObj.hexContent, RGenObj.MemObj.hexContent)); + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + else if (objTree.Operation.value === '*') { + TmpMemObj = utils.createConstantMemObj(utils.mulHexContents(LGenObj.MemObj.hexContent, RGenObj.MemObj.hexContent)); + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + else if (objTree.Operation.value === '/') { + TmpMemObj = utils.createConstantMemObj(utils.divHexContents(LGenObj.MemObj.hexContent, RGenObj.MemObj.hexContent)); + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + else if (objTree.Operation.value === '-') { + TmpMemObj = utils.createConstantMemObj(utils.subHexContents(LGenObj.MemObj.hexContent, RGenObj.MemObj.hexContent)); + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + } + // Try optimization if left side is constant (only commutativa operations!) + if (LGenObj.MemObj.type === 'constant') { + if (checkOperatorOptimization(objTree.Operation.value, LGenObj.MemObj)) { + const temp = RGenObj; + RGenObj = LGenObj; + LGenObj = temp; + } + // Try optimization if operation is commutative, right side is register and left side is not + } + else if (auxVars.isTemp(RGenObj.MemObj.address) && !auxVars.isTemp(LGenObj.MemObj.address) && + (objTree.Operation.value === '+' || objTree.Operation.value === '*' || objTree.Operation.value === '&' || + objTree.Operation.value === '^' || objTree.Operation.value === '|')) { + const temp = RGenObj; + RGenObj = LGenObj; + LGenObj = temp; + // Try optimization if operation is commutative, right side is constant () + } + else if (RGenObj.MemObj.type === 'constant') { + if (!checkOperatorOptimization(objTree.Operation.value, RGenObj.MemObj)) { + // if there is a better otimization, dont try this one + if (objTree.Operation.value === '+' || objTree.Operation.value === '*' || objTree.Operation.value === '&' || + objTree.Operation.value === '^' || objTree.Operation.value === '|') { + const temp = RGenObj; + RGenObj = LGenObj; + LGenObj = temp; + } + } + } + let TmpMemObj; + if (LGenObj.MemObj.type !== 'register') { + TmpMemObj = auxVars.getNewRegister(); + TmpMemObj.declaration = utils.getDeclarationFromMemory(LGenObj.MemObj); + instructionstrain += createInstruction(utils.genAssignmentToken(), TmpMemObj, LGenObj.MemObj); + auxVars.freeRegister(LGenObj.MemObj.address); + } + else { + TmpMemObj = LGenObj.MemObj; + } + // Pointer verifications + if (utils.getDeclarationFromMemory(RGenObj.MemObj).includes('_ptr') && !TmpMemObj.declaration.includes('_ptr')) { + // Operation with pointers + TmpMemObj.declaration += '_ptr'; + } + if (TmpMemObj.declaration.includes('_ptr')) { + if (objTree.Operation.value !== '+' && objTree.Operation.value !== '-') { + throw new TypeError(`At line: ${objTree.Operation.line}. Operation not allowed on pointers. Only '+', '-', '++' and '--' are.`); + } + } + instructionstrain += createInstruction(objTree.Operation, TmpMemObj, RGenObj.MemObj); + if (logicalOp === true) { + instructionstrain += createInstruction(utils.genNotEqualToken(), TmpMemObj, utils.createConstantMemObj(0), revLogic, jumpFalse, jumpTrue); + auxVars.freeRegister(RGenObj.MemObj.address); + auxVars.freeRegister(TmpMemObj.address); + return { MemObj: utils.createVoidMemObj(), instructionset: instructionstrain }; + } + auxVars.freeRegister(RGenObj.MemObj.address); + return { MemObj: TmpMemObj, instructionset: instructionstrain }; + } + if (objTree.Operation.type === 'Assignment' || objTree.Operation.type === 'SetOperator') { + if (jumpFalse !== undefined) { + throw new SyntaxError('At line: ' + objTree.Operation.line + '. Can not use assignment during logical operations with branches'); + } + if (objTree.Left.type === 'binaryASN' || (objTree.Left.type === 'unaryASN' && objTree.Left.Operation.value !== '*')) { + throw new SyntaxError(`At line: ${objTree.Operation.line}. Invalid left value for assignment.`); + } + auxVars.isLeftSideOfAssignment = true; + auxVars.hasVoidArray = false; + const LGenObj = genCode(objTree.Left, false, revLogic, jumpFalse, jumpTrue); + instructionstrain += LGenObj.instructionset; + auxVars.isLeftSideOfAssignment = false; + // Error condition checks + if (LGenObj.MemObj.type === 'void') { + throw new SyntaxError(`At line: ${objTree.Operation.line}. Trying to assign a void variable.`); + } + if (LGenObj.MemObj.address === -1) { + throw new TypeError(`At line: ${objTree.Operation.line}. Invalid left value for ${objTree.Operation.type}.`); + } + if (LGenObj.MemObj.type === 'array' && auxVars.hasVoidArray === false) { + if (LGenObj.MemObj.Offset === undefined) { + // Array assignment base type + throw new TypeError(`At line: ${objTree.Operation.line}. Invalid left value for '${objTree.Operation.type}'. Can not reassign an array.`); + } + } + else if (LGenObj.MemObj.Offset && LGenObj.MemObj.Offset.declaration.includes('_ptr') && LGenObj.MemObj.Offset.typeDefinition !== undefined && auxVars.hasVoidArray === false) { + // Array assignment inside struct + throw new TypeError(`At line: ${objTree.Operation.line}. Invalid left value for '${objTree.Operation.type}'. Can not reassign an array.`); + } + if (auxVars.hasVoidArray && (objTree.Right.type !== 'endASN' || (objTree.Right.type === 'endASN' && objTree.Right.Token.type !== 'Constant'))) { + throw new TypeError(`At line: ${objTree.Operation.line}. Invalid right value for multi-array assignment. It must be a constant.`); + } + let savedDeclaration = ''; + if (auxVars.declaring.length !== 0) { + savedDeclaration = auxVars.declaring; + auxVars.declaring = ''; + } + if (LGenObj.MemObj.type === 'array' && LGenObj.MemObj.Offset !== undefined && LGenObj.MemObj.Offset.type === 'constant') { // if it is an array item we know, change to the item (and do optimizations) + LGenObj.MemObj = getMemoryObjectByLocation(utils.addHexContents(LGenObj.MemObj.hexContent, LGenObj.MemObj.Offset.value)); + } + // check if we can reuse variables used on assignment + // then add it to auxVars.tmpvars + let RGenObj; + if (objTree.Operation.type === 'Assignment' && + Program.Config.reuseAssignedVar === true && + LGenObj.MemObj.type === 'long' && + LGenObj.MemObj.Offset === undefined && + CanReuseAssignedVar(LGenObj.MemObj.address, objTree.Right)) { + const registerInitialState = JSON.parse(JSON.stringify(auxVars.registerInfo)); + const newRegister = JSON.parse(JSON.stringify(LGenObj.MemObj)); + newRegister.type = 'register'; + newRegister.declaration = 'long'; + auxVars.registerInfo.unshift({ + inUse: false, + Template: newRegister + }); + RGenObj = genCode(objTree.Right, false, revLogic, jumpFalse, jumpTrue); + auxVars.registerInfo.shift(); + const registerFinalState = JSON.parse(JSON.stringify(auxVars.registerInfo)); + if (RGenObj.MemObj.address !== LGenObj.MemObj.address && RGenObj.MemObj.address < Program.Config.maxAuxVars) { + // if returning var is not the reused one, put it in that returning location. + const index = RGenObj.MemObj.address + 1; + auxVars.registerInfo = registerInitialState; + auxVars.registerInfo.splice(index, 0, { inUse: false, Template: newRegister }); + const TestRGenObj = genCode(objTree.Right, false, revLogic, jumpFalse, jumpTrue); + if (TestRGenObj.MemObj.address === LGenObj.MemObj.address) { + // alteration suceed! + RGenObj = TestRGenObj; + auxVars.registerInfo.splice(index, 1); + } + else { + // not suceed, undo changes. + auxVars.registerInfo = registerFinalState; + } + } + } + else { + RGenObj = genCode(objTree.Right, false, revLogic, jumpFalse, jumpTrue); + } + instructionstrain += RGenObj.instructionset; + if (savedDeclaration.length !== 0) { + auxVars.declaring = savedDeclaration; + } + if (RGenObj.MemObj.type === 'void') { + throw new TypeError('At line: ' + objTree.Operation.line + '. Invalid right value for ' + objTree.Operation.type + '. Possible void value.'); + } + if (utils.isNotValidDeclarationOp(utils.getDeclarationFromMemory(LGenObj.MemObj), RGenObj.MemObj)) { + const lDecl = utils.getDeclarationFromMemory(LGenObj.MemObj); + const rDecl = utils.getDeclarationFromMemory(RGenObj.MemObj); + // Allow SetOperator and pointer operation + if (!(lDecl === rDecl + '_ptr' && (objTree.Operation.value === '+=' || objTree.Operation.value === '-='))) { + if (Program.Config.warningToError) { + throw new TypeError('WARNING: At line: ' + objTree.Operation.line + ". Left and right values does not match. Values are: '" + LGenObj.MemObj.declaration + "' and '" + RGenObj.MemObj.declaration + "'."); + } + // Override declaration protection rules + LGenObj.MemObj.declaration = RGenObj.MemObj.declaration; + } + } + instructionstrain += createInstruction(objTree.Operation, LGenObj.MemObj, RGenObj.MemObj); + if (auxVars.isConstSentence === true) { + if (RGenObj.MemObj.address !== -1 || RGenObj.MemObj.type !== 'constant' || RGenObj.MemObj.hexContent === undefined) { + throw new TypeError('At line: ' + objTree.Operation.line + ". Right side of an assigment with 'const' keyword must be a constant."); + } + // Inspect ASM code and change accordingly + instructionstrain = setConstAsmCode(instructionstrain, objTree.Operation.line); + return { MemObj: LGenObj.MemObj, instructionset: instructionstrain }; + } + auxVars.freeRegister(RGenObj.MemObj.address); + return { MemObj: LGenObj.MemObj, instructionset: instructionstrain }; + } + break; + } + throw new TypeError(`At line: ${objTree.Operation.line}. Code generation error: Unknown operation '${objTree.Operation.type}'.`); + } + /** Transforms a instruction into const instruction */ + function setConstAsmCode(code, line) { + const codelines = code.split('\n'); + const retlines = []; + codelines.forEach(instruction => { + if (instruction.length === 0) { + retlines.push(''); + return; + } + const parts = /^\s*SET\s+@(\w+)\s+#([\da-f]{16})\b\s*$/.exec(instruction); + if (parts === null) { + const clrpart = /^\s*CLR\s+@(\w+)\s*$/.exec(instruction); + if (clrpart !== null) { + // allow CLR instruction and change to SET zero + retlines.push('^const SET @' + clrpart[1] + ' #0000000000000000'); + return; + } + const setpart = /^\s*SET\s+@(\w+)\s+\$n(\d+)\s*$/.exec(instruction); + if (setpart !== null) { + // allow set const on optimized const vars + retlines.push(`^const SET @${setpart[1]} #${BigInt(setpart[2]).toString(16).padStart(16, '0')}`); + return; + } + throw new TypeError(`At line: ${line}. No operations can be done during 'const' assignment.`); + } + const search = Program.memory.find(obj => obj.asmName === parts[1]); + if (search === undefined) { + throw new TypeError(`At line: ${line}. Variable ${parts[1]} not found in memory.`); + } + if (search.hexContent !== undefined) { + throw new TypeError(`At line: ${line}. Left side of an assigment with 'const' keyword already has been set.`); + } + search.hexContent = parts[2]; + retlines.push('^const ' + instruction); + }); + return retlines.join('\n'); + } + // all cases here must be implemented in createInstruction code oKSx4ab + // place here only commutative operations!!! + function checkOperatorOptimization(operator, ConstantObj) { + if (operator === '+' || operator === '+=') { + if (ConstantObj.hexContent === '0000000000000000' || + ConstantObj.hexContent === '0000000000000001' || + ConstantObj.hexContent === '0000000000000002') { + return true; + } + } + else if (operator === '*' || operator === '*=') { + if (ConstantObj.hexContent === '0000000000000000' || + ConstantObj.hexContent === '0000000000000001') { + return true; + } + } + return false; + } + /** Traverse an AST searching a variable name. In this case is the + * right side of an assignment. If variable 'name' is found, it + * can not be reused as temporary var (register) + */ + function CanReuseAssignedVar(loc, ObjAST) { + const SeekObj = getMemoryObjectByLocation(loc); + const vname = SeekObj.name; + let canreuse; + let left, right; + switch (ObjAST.type) { + case 'nullASN': + return true; + case 'endASN': + if (ObjAST.Token.type === 'Variable' && ObjAST.Token.value === vname) { + return false; + } + return true; + case 'lookupASN': + canreuse = ObjAST.modifiers.find(CurrentModifier => { + if (CurrentModifier.type === 'Array') { + if (CanReuseAssignedVar(loc, CurrentModifier.Center) === false) { + return true; + } + } + return false; + }); + if (canreuse === undefined) { + if (ObjAST.Token.type === 'Function' && ObjAST.FunctionArgs !== undefined) { + return CanReuseAssignedVar(loc, ObjAST.FunctionArgs); + } + return true; + } + return false; + case 'unaryASN': + return CanReuseAssignedVar(loc, ObjAST.Center); + case 'binaryASN': + left = CanReuseAssignedVar(loc, ObjAST.Left); + right = CanReuseAssignedVar(loc, ObjAST.Right); + if (left && right) + return true; + return false; + case 'exceptionASN': + if (ObjAST.Left !== undefined) { + return CanReuseAssignedVar(loc, ObjAST.Left); + } + if (ObjAST.Right !== undefined) { + return CanReuseAssignedVar(loc, ObjAST.Right); + } + } + throw new TypeError('Unkown object arrived at CanReuseAssignedVar().'); + } + function chooseBranch(value, useBZR, cbRevLogic) { + if (useBZR) { + if (cbRevLogic) { + if (value === '==') + return 'BZR'; + if (value === '!=') + return 'BNZ'; + } + else { + if (value === '==') + return 'BNZ'; + if (value === '!=') + return 'BZR'; + } + throw new TypeError(`Invalid use of Branch Zero: ${value}`); + } + else { + if (cbRevLogic) { + if (value === '>') + return 'BGT'; + if (value === '>=') + return 'BGE'; + if (value === '<') + return 'BLT'; + if (value === '<=') + return 'BLE'; + if (value === '==') + return 'BEQ'; + if (value === '!=') + return 'BNE'; + } + else { + if (value === '>') + return 'BLE'; + if (value === '>=') + return 'BLT'; + if (value === '<') + return 'BGE'; + if (value === '<=') + return 'BGT'; + if (value === '==') + return 'BNE'; + if (value === '!=') + return 'BEQ'; + } + } + throw new TypeError(`Unknow branch operation: ${value}`); + } + // Creates one simple assembly instruction + function createSimpleInstruction(instruction, param1 = '') { + switch (instruction) { + case 'Jump': + return `JMP :${param1}\n`; + case 'Push': + return `PSH $${param1}\n`; + case 'Pop': + return `POP @${param1}\n`; + case 'exit': + return 'FIN\n'; + case 'Label': + return `${param1}:\n`; + case 'Function': + return `JSR :__fn_${param1}\n`; + default: + throw new TypeError(`Unknow simple instruction: ${instruction}`); + } + } + function createAPICallInstruction(objoperator, param1, param2) { + let retinstr = ''; + const tempvar = []; + param2.forEach((varObj) => { + const Temp = flattenMemory(varObj, -1); + retinstr += Temp.instructionset; + tempvar.push(Temp); + }); + retinstr += 'FUN'; + if (param1.type !== 'void') { + retinstr += ' @' + param1.asmName; + } + retinstr += ' ' + objoperator.value; + tempvar.forEach(arg => { + retinstr += ' $' + arg.MoldedObj.asmName; + }); + retinstr += '\n'; + tempvar.forEach(arg => auxVars.freeRegister(arg.MoldedObj.address)); + return retinstr; + } + /** + * 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 conversion and a boolean to indicate if it is + * a new object (that must be free later on). + */ + function flattenMemory(ParamMemObj, line) { + let RetObj; + let retInstructions = ''; + let retIsNew = false; + const paramDec = utils.getDeclarationFromMemory(ParamMemObj); + if (ParamMemObj.type === 'constant') { + if (ParamMemObj.hexContent === undefined) { + throw new TypeError(`At line: ${line}. Missing hexContent parameter. BugReport please.`); + } + if (ParamMemObj.hexContent.length > 17) { + throw new RangeError(`At line: ${line}. Overflow on long value assignment. Value bigger than 64 bits).`); + } + const OptMem = Program.memory.find(MEM => MEM.asmName === 'n' + Number('0x' + ParamMemObj.hexContent) && MEM.hexContent === ParamMemObj.hexContent); + if (OptMem) { + return { MoldedObj: OptMem, instructionset: '', isNew: false }; + } + RetObj = auxVars.getNewRegister(); + RetObj.declaration = paramDec; + if (ParamMemObj.hexContent === '0000000000000000') { + retInstructions += `CLR @${RetObj.asmName}\n`; + } + else { + retInstructions += `SET @${RetObj.asmName} #${ParamMemObj.hexContent}\n`; + } + return { MoldedObj: RetObj, instructionset: retInstructions, isNew: true }; + } + if (ParamMemObj.Offset === undefined) { + return { MoldedObj: ParamMemObj, instructionset: '', isNew: false }; + } + if (ParamMemObj.type === 'register' || ParamMemObj.type === 'long') { + if (ParamMemObj.Offset.type === 'constant') { + RetObj = auxVars.getNewRegister(); + RetObj.declaration = paramDec; + if (ParamMemObj.Offset.value === 0) { + retInstructions += `SET @${RetObj.asmName} $($${ParamMemObj.asmName})\n`; + } + else { + const FlatConstant = flattenMemory(utils.createConstantMemObj(ParamMemObj.Offset.value), line); + retInstructions += FlatConstant.instructionset; + retInstructions += `SET @${RetObj.asmName} $($${ParamMemObj.asmName} + $${FlatConstant.MoldedObj.asmName})\n`; + if (FlatConstant.isNew) + auxVars.freeRegister(FlatConstant.MoldedObj.address); + } + retIsNew = true; + } + else { // ParamMemObj.Offset.type === 'variable' + RetObj = auxVars.getNewRegister(); + RetObj.declaration = paramDec; + retInstructions += `SET @${RetObj.asmName} $($${ParamMemObj.asmName} + $${getMemoryObjectByLocation(ParamMemObj.Offset.addr, line).asmName})\n`; + retIsNew = true; + } + } + else if (ParamMemObj.type === 'array') { + if (ParamMemObj.Offset.type === 'constant') { // Looks like an array but can be converted to regular variable + RetObj = getMemoryObjectByLocation(utils.addHexContents(ParamMemObj.hexContent, ParamMemObj.Offset.value), line); + auxVars.freeRegister(ParamMemObj.address); + retIsNew = true; + } + else { // ParamMemObj.Offset.type === 'variable' + RetObj = auxVars.getNewRegister(); + RetObj.declaration = paramDec; + retInstructions += `SET @${RetObj.asmName} $($${ParamMemObj.asmName} + $${getMemoryObjectByLocation(ParamMemObj.Offset.addr, line).asmName})\n`; + retIsNew = true; + } + } + else if (ParamMemObj.type === 'struct') { + // Impossible condition because struct variables have their type changed during LOOKUP_ASN processing + throw new Error(`At line: ${line}. Strange error. BugReport please.`); + } + else if (ParamMemObj.type === 'structRef') { + if (ParamMemObj.Offset.type === 'constant') { + RetObj = auxVars.getNewRegister(); + RetObj.declaration = paramDec; + const FlatConstant = flattenMemory(utils.createConstantMemObj(ParamMemObj.Offset.value), line); + retInstructions += FlatConstant.instructionset; + retInstructions += `SET @${RetObj.asmName} $($${ParamMemObj.asmName} + $${FlatConstant.MoldedObj.asmName})\n`; + if (FlatConstant.isNew) + auxVars.freeRegister(FlatConstant.MoldedObj.address); + retIsNew = true; + } + else { // ParamMemObj.Offset.type === 'variable') { + RetObj = auxVars.getNewRegister(); + RetObj.declaration = paramDec; + retInstructions += `SET @${RetObj.asmName} $($${ParamMemObj.asmName} + $${getMemoryObjectByLocation(ParamMemObj.Offset.addr, line).asmName})\n`; + retIsNew = true; + } + } + else { + throw new TypeError(`At line: ${line}. Not implemented type in flattenMemory(): ParamMemObj.type = '${ParamMemObj.type}'.`); + } + return { MoldedObj: RetObj, instructionset: retInstructions, isNew: retIsNew }; + } + // Translate one single instruction from ast to assembly code + function createInstruction(objoperator, param1, param2, rLogic, jpFalse, jpTrue) { + let retinstr = ''; + if (objoperator.type === 'Assignment') { + if (param1 === undefined || param2 === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing parameters. BugReport please.`); + } + switch (param1.type) { + case 'constant': + throw new TypeError(`At line: ${objoperator.line}. Invalid left side for assigment.`); + case 'register': + case 'long': + if (param1.Offset === undefined) { + switch (param2.type) { + case 'constant': + if (param2.hexContent === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing hexContent parameter. BugReport please.`); + } + if (param2.hexContent === '0000000000000000') { + return 'CLR @' + param1.asmName + '\n'; + } + if (Program.memory.find(MEM => MEM.asmName === 'n' + Number('0x' + param2.hexContent) && MEM.hexContent === param2.hexContent)) { + return `SET @${param1.asmName} $n${(Number('0x' + param2.hexContent))}\n`; + } + if (param2.hexContent.length > 17) { + throw new RangeError('At line: ' + objoperator.line + '.Overflow on long value assignment (value bigger than 64 bits)'); + } + return 'SET @' + param1.asmName + ' #' + param2.hexContent + '\n'; + case 'register': + case 'long': + if (param2.Offset === undefined) { + if ((param1.declaration === param2.declaration) || + (param1.declaration === 'long_ptr' && param2.declaration === 'void_ptr') || + (param1.declaration === 'void_ptr' && param2.declaration === 'long_ptr')) { + if (param1.address === param2.address) + return ''; + else + return 'SET @' + param1.asmName + ' $' + param2.asmName + '\n'; + } + else { + if ((param1.declaration === 'long' && param2.declaration === 'long_ptr') || + (param1.declaration === 'long' && param2.declaration === 'void_ptr')) { + return `SET @${param1.asmName} $($${param2.asmName})\n`; + } + if ((param1.declaration === 'long_ptr' && param2.declaration === 'long') || + (param1.declaration === 'void_ptr' && param2.declaration === 'long')) { + return `SET @($${param1.asmName}) $${param2.asmName}\n`; + } + throw new RangeError(`At line: ${objoperator.line}. Strange param declaration. BugReport Please.`); + } + } + else if (param2.Offset.type === 'constant') { + if (!param2.declaration.includes('_ptr')) { + throw new Error('Strange error'); + } + if (param2.Offset.value === 0) { + retinstr += `SET @${param1.asmName} $($${param2.asmName})\n`; + } + else { + const FlatOffset = flattenMemory(utils.createConstantMemObj(param2.Offset.value), objoperator.line); + retinstr += FlatOffset.instructionset; + retinstr += `SET @${param1.asmName} $($${param2.asmName} + $${FlatOffset.MoldedObj.asmName})\n`; + if (FlatOffset.isNew) + auxVars.freeRegister(FlatOffset.MoldedObj.address); + } + return retinstr; + } + else { // param2.Offset.type === 'variable' + if (!param2.declaration.includes('_ptr')) { + throw new Error('Strange error'); + } + return `SET @${param1.asmName} $($${param2.asmName} + $${getMemoryObjectByLocation(param2.Offset.addr, objoperator.line).asmName})\n`; + } + case 'array': + if (param2.Offset === undefined) { + return 'SET @' + param1.asmName + ' $' + param2.asmName + '\n'; + } + else if (param2.Offset.type === 'constant') { + return 'SET @' + param1.asmName + ' $' + getMemoryObjectByLocation(utils.addHexContents(param2.hexContent, param2.Offset.value), objoperator.line).asmName + '\n'; + } + else { // param2.Offset.type === 'variable' + return 'SET @' + param1.asmName + ' $($' + param2.asmName + ' + $' + getMemoryObjectByLocation(param2.Offset.addr, objoperator.line).asmName + ')\n'; + } + case 'struct': + // Impossible condition because struct variables have their type changed during LOOKUP_ASN processing + throw new Error(`At line: ${objoperator.line}. Strange error. BugReport please.`); + case 'structRef': + if (param2.Offset === undefined) { + if (param1.declaration === 'long_ptr' || param1.declaration === 'void_ptr') { + if (param1.address === param2.address) + return ''; + else + return `SET @${param1.asmName} $${param2.asmName}\n`; + } + throw new TypeError(`At line: ${objoperator.line}. Forbidden assignment: '${param1.declaration}' and '${param2.declaration}'.`); + } + else if (param2.Offset.type === 'constant') { + const FlatConstant = flattenMemory(utils.createConstantMemObj(param2.Offset.value), objoperator.line); + retinstr += FlatConstant.instructionset; + retinstr += `SET @${param1.asmName} $($${param2.asmName} + $${FlatConstant.MoldedObj.asmName})\n`; + if (FlatConstant.isNew) + auxVars.freeRegister(FlatConstant.MoldedObj.address); + return retinstr; + } + else { // param2.Offset.type === 'variable' + retinstr += `SET @${param1.asmName} $($${param2.asmName} + $${getMemoryObjectByLocation(param2.Offset.addr, objoperator.line).asmName})\n`; + return retinstr; + } + } + throw new TypeError('At line: ' + objoperator.line + ". Unknow combination at createInstruction: param1 type '" + param1.type + "' and param2 type: '" + param2.type + "'."); + } + else if (param1.Offset.type === 'constant') { + const FlatMem = flattenMemory(param2, objoperator.line); + retinstr += FlatMem.instructionset; + if (param1.Offset.value === 0) { + retinstr += `SET @($${param1.asmName}) $${FlatMem.MoldedObj.asmName}\n`; + } + else { + const FlatConstant = flattenMemory(utils.createConstantMemObj(param1.Offset.value), objoperator.line); + retinstr += FlatConstant.instructionset; + retinstr += `SET @($${param1.asmName} + $${FlatConstant.MoldedObj.asmName}) $${FlatMem.MoldedObj.asmName}\n`; + if (FlatConstant.isNew) + auxVars.freeRegister(FlatConstant.MoldedObj.address); + } + if (FlatMem.isNew) + auxVars.freeRegister(FlatMem.MoldedObj.address); + return retinstr; + } + else { // param1.Offset.type === 'variable' + const FlatMem = flattenMemory(param2, objoperator.line); + retinstr += FlatMem.instructionset; + retinstr += `SET @($${param1.asmName} + $${getMemoryObjectByLocation(param1.Offset.addr).asmName}) $${FlatMem.MoldedObj.asmName}\n`; + if (FlatMem.isNew) + auxVars.freeRegister(FlatMem.MoldedObj.address); + return retinstr; + } + case 'array': + if (param1.Offset === undefined) { + if (param2.type === 'constant') { + // special case for multi-long text assignment + if (param1.arrItem === undefined || param2.hexContent === undefined) { + throw new RangeError(`At line: ${objoperator.line}. Anomaly detected. BugReport please.`); + } + const arraySize = param1.arrItem.totalSize - 1; + if (param2.size > arraySize) { + throw new RangeError('At line: ' + objoperator.line + '. Overflow on array value assignment (value bigger than array size).'); + } + const paddedLong = param2.hexContent.padStart(arraySize * 16, '0'); + for (let i = 0; i < arraySize; i++) { + retinstr += createInstruction(utils.genAssignmentToken(), getMemoryObjectByLocation(utils.addHexContents(param1.hexContent, i), objoperator.line), utils.createConstantMemObj(paddedLong.slice(16 * (arraySize - i - 1), 16 * (arraySize - i)))); + } + return retinstr; + } + } + else if (param1.Offset.type === 'constant') { + return createInstruction(objoperator, getMemoryObjectByLocation(utils.addHexContents(param1.hexContent, param1.Offset.value), objoperator.line), param2); + } + else { // param1.Offset.type === 'variable' + const FlatMem = flattenMemory(param2, objoperator.line); + retinstr += FlatMem.instructionset; + // retinstr += `SET @($${param1.asmName}) $${FlatMem.MoldedObj.asmName}\n` + retinstr += `SET @($${param1.asmName} + $${getMemoryObjectByLocation(param1.Offset.addr, objoperator.line).asmName}) $${FlatMem.MoldedObj.asmName}\n`; + if (FlatMem.isNew) + auxVars.freeRegister(FlatMem.MoldedObj.address); + return retinstr; + } + throw new TypeError('At line: ' + objoperator.line + ". Unknow combination at createInstruction: param1 type '" + param1.type + "' and param2 type: '" + param2.type + "'."); + case 'struct': + // Impossible condition because struct variables have their type changed during LOOKUP_ASN processing + throw new Error(`At line: ${objoperator.line}. Strange error. BugReport please.`); + case 'structRef': + if (param1.Offset === undefined) { + // no modifier + switch (param2.type) { + case 'constant': + if (param2.hexContent === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing hexContent parameter. BugReport please.`); + } + if (param2.hexContent.length > 17) { + throw new RangeError('At line: ' + objoperator.line + '. Overflow on long value assignment (value bigger than 64 bits)'); + } + if (param2.hexContent === '0000000000000000') { + return `CLR @${param1.asmName}\n`; + } + if (Program.memory.find(MEM => MEM.asmName === 'n' + Number('0x' + param2.hexContent) && MEM.hexContent === param2.hexContent)) { + return `SET @${param1.asmName} $n${(Number('0x' + param2.hexContent))}\n`; + } + return `SET @${param1.asmName} #${param2.hexContent}\n`; + case 'register': + case 'long': + if (param2.Offset === undefined) { + if (param2.declaration === 'long_ptr' || param2.declaration === 'struct_ptr' || param2.declaration === 'void_ptr') { + return `SET @${param1.asmName} $${param2.asmName}\n`; + } + throw new TypeError(`At line: ${objoperator.line}. Forbidden assignment: '${param1.declaration}' and '${param2.declaration}'.`); + } + else if (param2.Offset.type === 'constant') { + const FlatOffset = flattenMemory(utils.createConstantMemObj(param2.Offset.value), objoperator.line); + retinstr += FlatOffset.instructionset; + retinstr += `SET @${param1.asmName} $($${param2.asmName} + $${FlatOffset.MoldedObj.asmName})\n`; + if (FlatOffset.isNew) + auxVars.freeRegister(FlatOffset.MoldedObj.address); + return retinstr; + } + else { + return `SET @${param1.asmName} $($${param2.asmName} + $${getMemoryObjectByLocation(param2.Offset.addr, objoperator.line).asmName})\n`; + } + case 'array': + throw new TypeError('Not implemented: structRef -> array'); + case 'structRef': + if (param2.Offset === undefined) { + return `SET @${param1.asmName} $${param2.asmName}\n`; + } + else if (param2.Offset.type === 'constant') { + if (param2.Offset.declaration !== 'long_ptr' && param2.Offset.declaration !== 'struct_ptr' && param2.Offset.declaration !== 'void_ptr') { + throw new TypeError(`At line: ${objoperator.line}. Forbidden assignment: '${param1.declaration}' and '${param2.Offset.declaration}'.`); + } + const FlatOffset = flattenMemory(utils.createConstantMemObj(param2.Offset.value), objoperator.line); + retinstr += FlatOffset.instructionset; + retinstr += `SET @${param1.asmName} $($${param2.asmName} + $${FlatOffset.MoldedObj.asmName})\n`; + if (FlatOffset.isNew) + auxVars.freeRegister(FlatOffset.MoldedObj.address); + return retinstr; + } + else { // param2.Offset.type === 'variable' + if (param2.Offset.declaration !== 'long_ptr' && param2.Offset.declaration !== 'struct_ptr' && param2.Offset.declaration !== 'void_ptr') { + throw new TypeError(`At line: ${objoperator.line}. Forbidden assignment: '${param1.declaration}' and '${param2.Offset.declaration}'.`); + } + return `SET @${param1.asmName} $($${param2.asmName} + $${getMemoryObjectByLocation(param2.Offset.addr, objoperator.line).asmName})\n`; + } + } + } + else if (param1.Offset.type === 'constant') { + const FlatP2 = flattenMemory(param2, objoperator.line); + retinstr += FlatP2.instructionset; + if (FlatP2.isNew) { + auxVars.freeRegister(param2.address); + if (param2.Offset?.type === 'variable') { + auxVars.freeRegister(param2.Offset.addr); + } + } + const FlatOffset = flattenMemory(utils.createConstantMemObj(param1.Offset.value), objoperator.line); + retinstr += FlatOffset.instructionset; + retinstr += `SET @($${param1.asmName} + $${FlatOffset.MoldedObj.asmName}) $${FlatP2.MoldedObj.asmName}\n`; + if (FlatOffset.isNew) + auxVars.freeRegister(FlatOffset.MoldedObj.address); + if (FlatP2.isNew) + auxVars.freeRegister(FlatP2.MoldedObj.address); + return retinstr; + } + else { // param1.Offset.type === 'variable' + const FlatP2 = flattenMemory(param2, objoperator.line); + retinstr += FlatP2.instructionset; + retinstr += `SET @($${param1.asmName} + $${getMemoryObjectByLocation(param1.Offset.addr, objoperator.line).asmName}) $${FlatP2.MoldedObj.asmName}\n`; + if (FlatP2.isNew) + auxVars.freeRegister(FlatP2.MoldedObj.address); + return retinstr; + } + } + throw new TypeError(`At line: ${objoperator.line}. Unknow combination at createInstruction: param1 '${param1.type}' and param2 '${param2.type}'.`); + } + if (objoperator.type === 'Operator' || objoperator.type === 'SetOperator') { + let allowOptimization = false; + let optimized = false; + if (param1 === undefined || param2 === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing parameters. BugReport please.`); + } + if (param1.type === 'constant') { + throw new TypeError(`At line: ${objoperator.line}. Can not createInstruction. BugReport please.`); + } + const TmpMemObj1 = flattenMemory(param1, objoperator.line); + retinstr += TmpMemObj1.instructionset; + if (param2.type === 'constant') { + allowOptimization = true; + } + const TmpMemObj2 = flattenMemory(param2, objoperator.line); + retinstr += TmpMemObj2.instructionset; + if (allowOptimization === true) { + function removeLastButOne() { + if (retinstr.length > 0) { + const codes = retinstr.split('\n'); + codes.pop(); + codes.pop(); + codes.push(''); + retinstr = codes.join('\n'); + } + } + // if add new condition here, add also in checkOperatorOptimization code oKSx4ab + // here we can have optimizations for all operations. + if (objoperator.value === '+' || objoperator.value === '+=') { + if (param2.hexContent === '0000000000000000') { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + return ''; + } + if (param2.hexContent === '0000000000000001') { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + removeLastButOne(); + retinstr += createInstruction(utils.genIncToken(), TmpMemObj1.MoldedObj); + optimized = true; + } + if (param2.hexContent === '0000000000000002') { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + removeLastButOne(); + const OptMem = Program.memory.find(MEM => MEM.asmName === 'n2' && MEM.hexContent === '0000000000000002'); + if (OptMem === undefined) { + retinstr += createInstruction(utils.genIncToken(), TmpMemObj1.MoldedObj); + retinstr += createInstruction(utils.genIncToken(), TmpMemObj1.MoldedObj); + optimized = true; + } + } + } + else if (objoperator.value === '-' || objoperator.value === '-=') { + if (param2.hexContent === '0000000000000000') { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + return ''; + } + if (param2.hexContent === '0000000000000001') { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + removeLastButOne(); + retinstr += createInstruction(utils.genDecToken(), TmpMemObj1.MoldedObj); + optimized = true; + } + } + else if (objoperator.value === '*' || objoperator.value === '*=') { + if (param2.hexContent === '0000000000000000') { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + removeLastButOne(); + retinstr += createInstruction(utils.genAssignmentToken(), TmpMemObj1.MoldedObj, param2); + optimized = true; + } + if (param2.hexContent === '0000000000000001') { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + return ''; + } + } + else if (objoperator.value === '/' || objoperator.value === '/=') { + if (param2.hexContent === '0000000000000001') { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + return ''; + } + } + } + if (optimized === false) { + if (objoperator.value === '+' || objoperator.value === '+=') { + retinstr += 'ADD'; + } + else if (objoperator.value === '-' || objoperator.value === '-=') { + retinstr += 'SUB'; + } + else if (objoperator.value === '*' || objoperator.value === '*=') { + retinstr += 'MUL'; + } + else if (objoperator.value === '/' || objoperator.value === '/=') { + retinstr += 'DIV'; + } + else if (objoperator.value === '|' || objoperator.value === '|=') { + retinstr += 'BOR'; + } + else if (objoperator.value === '&' || objoperator.value === '&=') { + retinstr += 'AND'; + } + else if (objoperator.value === '^' || objoperator.value === '^=') { + retinstr += 'XOR'; + } + else if (objoperator.value === '%' || objoperator.value === '%=') { + retinstr += 'MOD'; + } + else if (objoperator.value === '<<' || objoperator.value === '<<=') { + retinstr += 'SHL'; + } + else if (objoperator.value === '>>' || objoperator.value === '>>=') { + retinstr += 'SHR'; + } + else { + throw new TypeError('At line: ' + objoperator.line + '.Operator not supported ' + objoperator.value); + } + retinstr += ' @' + TmpMemObj1.MoldedObj.asmName + ' $' + TmpMemObj2.MoldedObj.asmName + '\n'; + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + } + if (TmpMemObj1.isNew === true) { + retinstr += createInstruction(utils.genAssignmentToken(), param1, TmpMemObj1.MoldedObj); + auxVars.freeRegister(TmpMemObj1.MoldedObj.address); + } + return retinstr; + } + if (objoperator.type === 'UnaryOperator' || objoperator.type === 'SetUnaryOperator') { + if (param1 === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing parameters. BugReport please.`); + } + if (objoperator.value === '++') { + return 'INC @' + param1.asmName + '\n'; + } + if (objoperator.value === '--') { + return 'DEC @' + param1.asmName + '\n'; + } + if (objoperator.value === '~') { + return 'NOT @' + param1.asmName + '\n'; + } + if (objoperator.value === '+') { + return ''; + } + throw new TypeError('At line: ' + objoperator.line + '. Unary operator not supported: ' + objoperator.value); + } + if (objoperator.type === 'Comparision') { + if (param1 === undefined || param2 === undefined || rLogic === undefined || jpFalse === undefined || jpTrue === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing parameters. BugReport please.`); + } + let jump = jpFalse; + if (rLogic) { + jump = jpTrue; + } + const TmpMemObj1 = flattenMemory(param1, objoperator.line); + retinstr += TmpMemObj1.instructionset; + if (TmpMemObj1.isNew) { + if (param1.Offset?.type === 'variable') { + auxVars.freeRegister(param1.Offset.addr); + } + auxVars.freeRegister(param1.address); + } + if (param2.type === 'constant' && param2.hexContent === '0000000000000000' && (objoperator.value === '!=' || objoperator.value === '==')) { + retinstr += chooseBranch(objoperator.value, true, rLogic); + retinstr += ' $' + TmpMemObj1.MoldedObj.asmName + ' :' + jump + '\n'; + if (TmpMemObj1.isNew === true) { + auxVars.freeRegister(TmpMemObj1.MoldedObj.address); + } + return retinstr; + } + const TmpMemObj2 = flattenMemory(param2, objoperator.line); + retinstr += TmpMemObj2.instructionset; + retinstr += chooseBranch(objoperator.value, false, rLogic); + retinstr += ' $' + TmpMemObj1.MoldedObj.asmName + ' $' + TmpMemObj2.MoldedObj.asmName + ' :' + jump + '\n'; + if (TmpMemObj1.isNew === true) { + auxVars.freeRegister(TmpMemObj1.MoldedObj.address); + } + if (TmpMemObj2 !== undefined && TmpMemObj2.isNew === true) { + auxVars.freeRegister(TmpMemObj2.MoldedObj.address); + } + return retinstr; + } + if (objoperator.type === 'Push') { + if (param1 === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing parameter for PSH. BugReport please.`); + } + const TmpMemObj = flattenMemory(param1, objoperator.line); + retinstr += TmpMemObj.instructionset; + retinstr += 'PSH $' + TmpMemObj.MoldedObj.asmName + '\n'; + if (TmpMemObj.isNew === true) { + auxVars.freeRegister(TmpMemObj.MoldedObj.address); + } + return retinstr; + } + if (objoperator.type === 'Keyword') { + if (objoperator.value === 'break' || objoperator.value === 'continue') { + return 'JMP :' + generateUtils.getLatestLoopId() + '_' + objoperator.value + '\n'; + } + if (objoperator.value === 'label') { + return objoperator.extValue + ':\n'; + } + if (objoperator.value === 'goto') { + if (param1 === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing parameter for goto. BugReport please.`); + } + return 'JMP :' + param1.name + '\n'; + } + if (objoperator.value === 'halt') { + return 'STP\n'; + } + if (objoperator.value === 'exit') { + return 'FIN\n'; + } + if (objoperator.value === 'return' || objoperator.value === 'sleep') { + if (param1 === undefined) { + if (objoperator.value === 'return') { + return 'RET\n'; + } + throw new TypeError(`At line: ${objoperator.line}. Missing parameter for sleep. BugReport please.`); + } + let retinstr = ''; + const TmpMemObj = flattenMemory(param1, objoperator.line); + retinstr += TmpMemObj.instructionset; + if (objoperator.value === 'return') { + retinstr += 'PSH $' + TmpMemObj.MoldedObj.asmName + '\n'; + retinstr += 'RET\n'; + } + else if (objoperator.value === 'sleep') { + retinstr += 'SLP $' + TmpMemObj.MoldedObj.asmName + '\n'; + } + auxVars.freeRegister(param1.address); + if (TmpMemObj.isNew === true) { + auxVars.freeRegister(TmpMemObj.MoldedObj.address); + } + return retinstr; + } + if (objoperator.value === 'asm') { + if (objoperator.extValue === undefined) { + throw new TypeError(`At line: ${objoperator.line}. Missing extValue for asm. BugReport please.`); + } + let lines = objoperator.extValue.split('\n'); + lines = lines.map(value => value.trim()); + return lines.join('\n').trim() + '\n'; + } + } + throw new TypeError('At line: ' + objoperator.line + '. ' + objoperator.type + ' not supported'); + } + } + function writeAsmLine(lineContent, sourceCodeLine = 0) { + if (Program.Config.outputSourceLineNumber === true && + sourceCodeLine !== 0 && + sourceCodeLine !== generateUtils.currSourceLine) { + generateUtils.assemblyCode += `^comment line ${sourceCodeLine}\n`; + generateUtils.currSourceLine = sourceCodeLine; + } + generateUtils.assemblyCode += lineContent + '\n'; + } + function writeAsmCode(lines, sourceCodeLine = 0) { + if (lines.length === 0) { + return; + } + if (Program.Config.outputSourceLineNumber === true && + sourceCodeLine !== 0 && + sourceCodeLine !== generateUtils.currSourceLine) { + generateUtils.assemblyCode += `^comment line ${sourceCodeLine}\n`; + generateUtils.currSourceLine = sourceCodeLine; + } + generateUtils.assemblyCode += lines; + } + /** Add content of macro 'program' information to assembly code */ + function configDeclarationGenerator() { + if (Program.Config.PName !== '') { + writeAsmLine(`^program name ${Program.Config.PName}`); + } + if (Program.Config.PDescription !== '') { + writeAsmLine(`^program description ${Program.Config.PDescription}`); + } + if (Program.Config.PActivationAmount !== '') { + writeAsmLine('^program activationAmount ' + Program.Config.PActivationAmount); + } + if (Program.Config.PUserStackPages !== 0) { + writeAsmLine(`^program userStackPages ${Program.Config.PUserStackPages}`); + } + if (Program.Config.PCodeStackPages !== 0) { + writeAsmLine(`^program codeStackPages ${Program.Config.PCodeStackPages}`); + } + } + /** Handles variables declarations to assembly code. */ + function assemblerDeclarationGenerator(MemObj) { + if (MemObj.address !== -1) { + writeAsmLine(`^declare ${MemObj.asmName}`); + if (MemObj.hexContent !== undefined) { + writeAsmLine(`^const SET @${MemObj.asmName} #${MemObj.hexContent}`); + } + } + } + /** + * Search and return a copy of memory object with name varname. + * Object can be global or local function scope. + * if not found, throws exception with line number. + */ + function getMemoryObjectByName(varName, line = -1, varDeclaration = '') { + let search; + if (generateUtils.currFunctionIndex !== -1) { // find function scope variable + search = Program.memory.find(obj => obj.name === varName && obj.scope === Program.functions[generateUtils.currFunctionIndex].name); + } + if (search === undefined) { + // do a global scope search + search = Program.memory.find(obj => obj.name === varName && obj.scope === ''); + } + // Checks to allow use: + if (varDeclaration !== '') { // we are in declarations sentence + if (search === undefined) { + throw new SyntaxError(`At line: ${line}. Variable '${varName}' not declared. BugReport Please.`); + } + search.isDeclared = true; + return JSON.parse(JSON.stringify(search)); + } + // else, not in declaration: + if (search === undefined) { + // maybe this is a label. Check! Labels always global + const labelSearch = Program.labels.find(obj => obj === varName); + if (labelSearch === undefined) { + throw new SyntaxError(`At line: ${line}. Using variable '${varName}' before declaration.`); + } + // return label fakevar + return { + type: 'label', + isDeclared: true, + declaration: '', + address: -1, + asmName: '', + name: varName, + scope: '', + size: 0 + }; + } + return JSON.parse(JSON.stringify(search)); + } + /** + * Search and return a copy of memory object in addres 'loc'. + * Object can be global or local function scope. + * if not found, throws exception with line number. + */ + function getMemoryObjectByLocation(loc, line = -1) { + let addr; + if (typeof (loc) === 'number') { + addr = loc; + } + else if (typeof (loc) === 'string') { + addr = parseInt(loc, 16); + } + else + throw new TypeError(`At line: ${line}. Wrong type in getMemoryObjectByLocation.`); + const search = Program.memory.find(obj => obj.address === addr); + if (search === undefined) { + throw new SyntaxError(`At line: ${line}. No variable found at address '0x${addr}'.`); + } + return JSON.parse(JSON.stringify(search)); + } + /** + * Handle function initialization + */ + function functionHeaderGenerator() { + const fname = Program.functions[generateUtils.currFunctionIndex].name; + if (fname === 'main' || fname === 'catch') { + writeAsmLine(`__fn_${fname}:`, Program.functions[generateUtils.currFunctionIndex].line); + writeAsmLine('PCS'); + return; + } + writeAsmLine(`__fn_${fname}:`, Program.functions[generateUtils.currFunctionIndex].line); + Program.functions[generateUtils.currFunctionIndex].argsMemObj.forEach(Obj => { + writeAsmLine(`POP @${Obj.asmName}`); + }); + } + /** + * Handle function end + */ + function functionTailGenerator() { + const fname = Program.functions[generateUtils.currFunctionIndex].name; + if (fname === 'main' || fname === 'catch') { + if (generateUtils.assemblyCode.lastIndexOf('FIN') + 4 !== generateUtils.assemblyCode.length) { + writeAsmLine('FIN'); + } + return; + } + if (generateUtils.assemblyCode.lastIndexOf('RET') + 4 !== generateUtils.assemblyCode.length) { + if (Program.functions[generateUtils.currFunctionIndex].declaration === 'void') { + writeAsmLine('RET'); + } + else { // return zero to prevent stack overflow + writeAsmLine('CLR @r0'); + writeAsmLine('PSH $r0'); + writeAsmLine('RET'); + } + } + } + /** Hot stuff!!! Assemble sentences!! */ + function compileSentence(Sentence) { + let sentenceID; + switch (Sentence.type) { + case 'phrase': + writeAsmCode(codeGenerator(Sentence.CodeAST), Sentence.line); + break; + case 'ifEndif': + sentenceID = '__if' + generateUtils.getNewJumpID(Sentence.line); + writeAsmCode(codeGenerator(Sentence.ConditionAST, sentenceID + '_endif', sentenceID + '_start'), Sentence.line); + writeAsmLine(sentenceID + '_start:'); + Sentence.trueBlock.forEach(compileSentence); + writeAsmLine(sentenceID + '_endif:'); + break; + case 'ifElse': + sentenceID = '__if' + generateUtils.getNewJumpID(Sentence.line); + writeAsmCode(codeGenerator(Sentence.ConditionAST, sentenceID + '_else', sentenceID + '_start'), Sentence.line); + writeAsmLine(sentenceID + '_start:'); + Sentence.trueBlock.forEach(compileSentence); + writeAsmLine('JMP :' + sentenceID + '_endif'); + writeAsmLine(sentenceID + '_else:'); + Sentence.falseBlock.forEach(compileSentence); + writeAsmLine(sentenceID + '_endif:'); + break; + case 'while': + sentenceID = '__loop' + generateUtils.getNewJumpID(Sentence.line); + writeAsmLine(sentenceID + '_continue:', Sentence.line); + writeAsmCode(codeGenerator(Sentence.ConditionAST, sentenceID + '_break', sentenceID + '_start')); + writeAsmLine(sentenceID + '_start:'); + generateUtils.latestLoopId.push(sentenceID); + Sentence.trueBlock.forEach(compileSentence); + generateUtils.latestLoopId.pop(); + writeAsmLine('JMP :' + sentenceID + '_continue'); + writeAsmLine(sentenceID + '_break:'); + break; + case 'do': + sentenceID = '__loop' + generateUtils.getNewJumpID(Sentence.line); + writeAsmLine(sentenceID + '_continue:', Sentence.line); + generateUtils.latestLoopId.push(sentenceID); + Sentence.trueBlock.forEach(compileSentence); + generateUtils.latestLoopId.pop(); + writeAsmCode(codeGenerator(Sentence.ConditionAST, sentenceID + '_break', sentenceID + '_continue', true)); + writeAsmLine(sentenceID + '_break:'); + break; + case 'for': + sentenceID = '__loop' + generateUtils.getNewJumpID(Sentence.line); + writeAsmCode(codeGenerator(Sentence.threeSentences[0].CodeAST), Sentence.line); + writeAsmLine(sentenceID + '_condition:'); + writeAsmCode(codeGenerator(Sentence.threeSentences[1].CodeAST, sentenceID + '_break', sentenceID + '_start')); + writeAsmLine(sentenceID + '_start:'); + generateUtils.latestLoopId.push(sentenceID); + Sentence.trueBlock.forEach(compileSentence); + generateUtils.latestLoopId.pop(); + writeAsmLine(sentenceID + '_continue:'); + writeAsmCode(codeGenerator(Sentence.threeSentences[2].CodeAST)); + writeAsmLine('JMP :' + sentenceID + '_condition'); + writeAsmLine(sentenceID + '_break:'); + break; + case 'struct': + // Nothing to do here + } + } + return generateMain(); +} diff --git a/v0.3/out/hashCode.js b/v0.3/out/hashCode.js new file mode 100644 index 0000000..b0690bc --- /dev/null +++ b/v0.3/out/hashCode.js @@ -0,0 +1,159 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/* LICENSE notes for function binb_sha256: + * + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ +/** + * Calculates a digest ID of a given machine code. + * @param hexCode Machine code to be calculated, as hex string. + * @returns A string representing a hash ID of given contract + */ +// eslint-disable-next-line no-unused-vars +function hashMachineCode(hexCode) { + // Pad input to match codepage length + const hexLen = Number(((BigInt(hexCode.length) / 512n) + 1n) * 512n); + hexCode = hexCode.padEnd(hexLen, '0'); + // Split program into words (32-bit) + const parts = hexCode.match(/[\s\S]{8}/g); + if (parts === null) { + return 'ERROR'; + } + // Translate hex text to words. + const codeWordArr = parts.map(str => unsigned2signed(Number('0x' + str))); + // Calculate sha-256 and toggle result to little endian + const wordsHash = toggleEndianWordsArr(binb_sha256(codeWordArr, 4 * 8 * codeWordArr.length)); + // Get parts for digest ID + const lsp = BigInt(signed2unsigned(wordsHash[0])); + const msp = BigInt(signed2unsigned(wordsHash[1])); + // Calculate ID value and return it as string. + return ((msp << 32n) + lsp).toString(10); + function toggleEndianWordsArr(wordArr) { + const bi = wordArr.map(x => signed2unsigned(x)); + const retArr = []; + let val; + for (const currBi of bi) { + val = (currBi >> 24) & 0xff; + val |= ((currBi >> 16) & 0xff) << 8; + val |= ((currBi >> 8) & 0xff) << 16; + val |= (currBi & 0xff) << 24; + retArr.push(unsigned2signed(val)); + } + return retArr; + } + // For 32-bit Number + function unsigned2signed(unsigned) { + if (unsigned >= 0x80000000) { + return unsigned - 0x100000000; + } + return unsigned; + } + // For 32-bit Number + function signed2unsigned(signed) { + if (signed < 0) { + return (signed + 0x100000000); + } + return signed; + } + /* eslint-disable camelcase */ + /* Calculate the SHA-256 of an array of big-endian words, and a bit length. */ + function binb_sha256(m, l) { + function safe_add(x, y) { + const lsw = (x & 0xFFFF) + (y & 0xFFFF); + const msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + /* sha256 support functions */ + function sha256_S(X, n) { + return (X >>> n) | (X << (32 - n)); + } + function sha256_R(X, n) { + return (X >>> n); + } + function sha256_Ch(x, y, z) { + return ((x & y) ^ ((~x) & z)); + } + function sha256_Maj(x, y, z) { + return ((x & y) ^ (x & z) ^ (y & z)); + } + function sha256_Sigma0256(x) { + return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22)); + } + function sha256_Sigma1256(x) { + return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25)); + } + function sha256_Gamma0256(x) { + return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3)); + } + function sha256_Gamma1256(x) { + return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10)); + } + /* Main sha256 function */ + const sha256_K = [ + 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, + -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987, + 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522, + 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, + -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585, + 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, + 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, + -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344, + 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, + 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, + -1866530822, -1538233109, -1090935817, -965641998 + ]; + const HASH = [1779033703, -1150833019, 1013904242, -1521486534, + 1359893119, -1694144372, 528734635, 1541459225]; + const W = new Array(64); + let a, b, c, d, e, f, g, h; + let i, j, T1, T2; + /* append padding */ + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + for (i = 0; i < m.length; i += 16) { + a = HASH[0]; + b = HASH[1]; + c = HASH[2]; + d = HASH[3]; + e = HASH[4]; + f = HASH[5]; + g = HASH[6]; + h = HASH[7]; + for (j = 0; j < 64; j++) { + if (j < 16) { + W[j] = m[j + i]; + } + else { + W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), sha256_Gamma0256(W[j - 15])), W[j - 16]); + } + T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), sha256_K[j]), W[j]); + T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c)); + h = g; + g = f; + f = e; + e = safe_add(d, T1); + d = c; + c = b; + b = a; + a = safe_add(T1, T2); + } + HASH[0] = safe_add(a, HASH[0]); + HASH[1] = safe_add(b, HASH[1]); + HASH[2] = safe_add(c, HASH[2]); + HASH[3] = safe_add(d, HASH[3]); + HASH[4] = safe_add(e, HASH[4]); + HASH[5] = safe_add(f, HASH[5]); + HASH[6] = safe_add(g, HASH[6]); + HASH[7] = safe_add(h, HASH[7]); + } + return HASH; + } + /* eslint-enable camelcase */ +} diff --git a/v0.3/out/optimizer.js b/v0.3/out/optimizer.js new file mode 100644 index 0000000..6453489 --- /dev/null +++ b/v0.3/out/optimizer.js @@ -0,0 +1,457 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/** + * Optimize assembly code globally with peephole strategy. + * @param assemblySourceCode + * @returns Optimized source code + */ +// eslint-disable-next-line no-unused-vars +function optimize(assemblySourceCode, maxConstVars) { + let codeLines = assemblySourceCode.split('\n'); + let jumpToLabels; + let dest; + let jmpto, lbl; + let setdat, opdat, clrdat, popdat; + let branchdat; + let pshslpdat; + let notdat, setdat2; + let optimizedLines; + do { + jumpToLabels = []; + optimizedLines = 0; + // Collect jumps information + codeLines.forEach(function (value) { + jmpto = /^.+\s:(\w+)$/.exec(value); // match JMP JSR ERR and all branches + if (jmpto !== null) { + jumpToLabels.push(jmpto[1]); + } + }); + // remove labels without reference + // remove lines marked as DELETE + codeLines = codeLines.filter(function (value) { + lbl = /^(\w+):$/.exec(value); + if (lbl !== null) { + if (jumpToLabels.indexOf(lbl[1]) !== -1) { + return true; + } + else { + optimizedLines++; + return false; + } + } + if (value === 'DELETE') { + optimizedLines++; + return false; + } + return true; + }); + // Optimization rules here! + codeLines.forEach((value, index, array) => { + let i; + // do not analyze these values or compiler directives + if (value === 'DELETE' || value === '' || /^\s*\^\w+(.*)/.exec(array[index]) !== null) { + return; + } + // change SET_VAL to SET_DAT for values defined in ConstVars + // This also enables more optimizations on pointers and PSH! + if (maxConstVars > 0) { + setdat = /^\s*SET\s+@(\w+)\s+#([\da-f]{16})\b\s*$/.exec(value); + if (setdat !== null) { + const val = parseInt(setdat[2], 16); + if (val <= maxConstVars && setdat[1] !== 'n' + val) { + array[index] = 'SET @' + setdat[1] + ' $n' + val; + optimizedLines++; + } + } + } + // BNE $r0 $var37 :lab_f75 + // JMP :lab_fa2 + // lab_f75: + // turns BEQ $r0 $var37 :lab_fa2 + branchdat = /^\s*(BGT|BLT|BGE|BLE|BEQ|BNE)\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/.exec(value); + if (branchdat !== null) { + lbl = /^\s*(\w+):\s*$/.exec(array[index + 2]); + if (lbl !== null && branchdat[4] === lbl[1]) { + jmpto = /^\s*JMP\s+:(\w+)\s*$/.exec(array[index + 1]); + if (jmpto !== null) { + // if jump location is RET or FIN, optimize to RET or FIN. + dest = getLabeldestination(jmpto[1]); + if (/^\s*RET\s*$/.exec(dest) !== null) { + array[index + 1] = 'RET'; // if jump to return, just return from here + optimizedLines++; + return; + } + if (/^\s*FIN\s*$/.exec(dest) !== null) { + array[index + 1] = 'FIN'; // if jump to exit, just exit from here + optimizedLines++; + return; + } + let instr = ''; + if (branchdat[1] === 'BGT') + instr = 'BLE'; + else if (branchdat[1] === 'BLT') + instr = 'BGE'; + else if (branchdat[1] === 'BGE') + instr = 'BLT'; + else if (branchdat[1] === 'BLE') + instr = 'BGT'; + else if (branchdat[1] === 'BEQ') + instr = 'BNE'; + else + instr = 'BEQ'; + array[index] = instr + ' $' + branchdat[2] + ' $' + branchdat[3] + ' :' + jmpto[1]; + array[index + 1] = 'DELETE'; + optimizedLines++; + return; + } + } + } + // BNZ $r0 :lab_f75 + // JMP :lab_fa2 + // lab_f75: + // turns BZR $r0 :lab_fa2 + branchdat = /^\s*(BZR|BNZ)\s+\$(\w+)\s+:(\w+)\s*$/.exec(value); + if (branchdat !== null) { + lbl = /^\s*(\w+):\s*$/.exec(array[index + 2]); // matches labels + if (lbl !== null && branchdat[3] === lbl[1]) { + jmpto = /^\s*JMP\s+:(\w+)\s*$/.exec(array[index + 1]); + if (jmpto !== null) { + // if jump location is RET or FIN, optimize to RET or FIN. + dest = getLabeldestination(jmpto[1]); + if (/^\s*RET\s*$/.exec(dest) !== null) { + array[index + 1] = 'RET'; // if jump to return, just return from here + optimizedLines++; + return; + } + if (/^\s*FIN\s*$/.exec(dest) !== null) { + array[index + 1] = 'FIN'; // if jump to exit, just exit from here + optimizedLines++; + return; + } + let instr = ''; + if (branchdat[1] === 'BZR') + instr = 'BNZ'; + else + instr = 'BZR'; + array[index] = instr + ' $' + branchdat[2] + ' :' + jmpto[1]; + array[index + 1] = 'DELETE'; + optimizedLines++; + return; + } + } + } + jmpto = /^\s*JMP\s+:(\w+)\s*$/.exec(value); + // optimize jumps + if (jmpto !== null) { + // if instruction is jump, unreachable code until a label found + i = index; + while (++i < array.length - 1) { + lbl = /^\s*(\w+):\s*$/.exec(array[i]); + if (lbl === null) { + if (array[i] === '' || array[i] === 'DELETE' || /^\s*\^\w+(.*)/.exec(array[i]) !== null) { // matches assembly compiler directives + continue; + } + array[i] = 'DELETE'; + optimizedLines++; + continue; + } + break; + } + // if referenced label is next instruction, meaningless jump + i = index; + while (++i < array.length - 1) { + lbl = /^\s*(\w+):\s*$/.exec(array[i]); + if (lbl === null) { + if (array[i] === '' || array[i] === 'DELETE') { + continue; + } + break; + } + if (jmpto[1] === lbl[1]) { + array[index] = 'DELETE'; + optimizedLines++; + return; + } + } + // inspect jump location + dest = getLabeldestination(jmpto[1]); + if (/^\s*RET\s*$/.exec(dest) !== null) { + array[index] = 'RET'; // if jump to return, just return from here + optimizedLines++; + return; + } + if (/^\s*FIN\s*$/.exec(dest) !== null) { + array[index] = 'FIN'; // if jump to exit, just exit from here + optimizedLines++; + return; + } + lbl = /^\s*(\w+):\s*$/.exec(dest); + if (lbl !== null) { + array[index] = 'JMP :' + lbl[1]; // if jump to other jump, just jump over there + optimizedLines++; + return; + } + } + jmpto = /^\s*(RET|FIN)\s*$/.exec(value); + // Inspect RET and FIN + if (jmpto !== null) { + // if instruction RET or FIN, unreachable code until a label found + i = index; + while (++i < array.length - 1) { + lbl = /^\s*(\w+):\s*$/.exec(array[i]); + if (lbl === null) { + if (array[i] === '' || array[i] === 'DELETE' || /^\s*\^\w+(.*)/.exec(array[i]) !== null) { + continue; + } + array[i] = 'DELETE'; + optimizedLines++; + continue; + } + break; + } + } + // inspect branches and optimize branch to jumps + jmpto = /^\s*B.+:(\w+)$/.exec(value); // matches all branches instructions + if (jmpto !== null) { + // if referenced label is next instruction, meaningless jump + i = index; + while (++i < array.length - 1) { + lbl = /^\s*(\w+):\s*$/.exec(array[i]); + if (lbl === null) { + if (array[i] === '' || array[i] === 'DELETE') { + continue; + } + break; + } + if (jmpto[1] === lbl[1]) { + array[index] = 'DELETE'; + optimizedLines++; + return; + } + } + // inspect jump location + dest = getLabeldestination(jmpto[1]); + lbl = /^\s*(\w+):\s*$/.exec(dest); + if (lbl !== null) { + array[index] = jmpto[0].replace(jmpto[1], lbl[1]); // if branch to other jump, just branch over there + optimizedLines++; + return; + } + } + // ADD @r0 $b + // SET @b $r0 + // turns ADD @b $r0 + opdat = /^\s*(\w+)\s+@(\w+)\s+\$(\w+)\s*$/.exec(value); + if (opdat !== null) { + setdat = /^\s*SET\s+@(\w+)\s+\$(\w+)\s*$/.exec(array[index + 1]); + if (setdat !== null && opdat[2] === setdat[2] && opdat[3] === setdat[1]) { + if (opdat[1] === 'ADD' || opdat[1] === 'MUL' || opdat[1] === 'AND' || opdat[1] === 'XOR' || opdat[1] === 'BOR') { + array[index] = opdat[1] + ' @' + opdat[3] + ' $' + opdat[2]; + array[index + 1] = 'DELETE'; + optimizedLines++; + return; + } + } + } + // SET @r0 $a + // ADD @b $r0 + // turns ADD @b $a + setdat = /^\s*SET\s+@(\w+)\s+\$(\w+)\s*$/.exec(value); + if (setdat !== null) { + opdat = /^\s*(\w+)\s+@(\w+)\s+\$(\w+)\s*$/.exec(array[index + 1]); + if (opdat !== null && setdat[1] === opdat[3]) { + if (opdat[1] === 'ADD' || opdat[1] === 'SUB' || opdat[1] === 'MUL' || opdat[1] === 'DIV' || + opdat[1] === 'AND' || opdat[1] === 'XOR' || opdat[1] === 'BOR' || + opdat[1] === 'MOD' || opdat[1] === 'SHL' || opdat[1] === 'SHR') { + array[index] = opdat[1] + ' @' + opdat[2] + ' $' + setdat[2]; + array[index + 1] = 'DELETE'; + optimizedLines++; + return; + } + } + if (setdat[1] === setdat[2]) { // SET @a_1 $a_1 turns delete + array[index] = 'DELETE'; + optimizedLines++; + return; + } + // SET @r0 $a + // PSH $r0 / SLP $r0 + // turns PSH $a / SLP $a + pshslpdat = /^\s*(PSH|SLP)\s+\$(\w+)\s*$/.exec(array[index + 1]); + if (pshslpdat !== null && isRegister(setdat[1])) { + if (pshslpdat[2] === setdat[1]) { + array[index] = 'DELETE'; + array[index + 1] = pshslpdat[1] + ' $' + setdat[2]; + optimizedLines++; + return; + } + } + i = index; + while (++i < array.length - 1) { + lbl = /^\s*(\w+):\s*$/.exec(array[i]); + if (lbl !== null) { + break; + } + jmpto = /^.+\s:(\w+)$/.exec(array[i]); // match JMP JSR ERR and all branches + if (jmpto !== null) { + break; + } + jmpto = /^\s*(RET|FIN)\s*$/.exec(array[i]); + if (jmpto !== null) { + break; + } + if (array[i].indexOf(setdat[1]) >= 0) { + // SET @r0 $a + // SET @z $($val + $r0) + // turns SET @z $($val + $a) + setdat2 = /^\s*SET\s+@(\w+)\s+\$\(\$(\w+)\s*\+\s*\$(\w+)\)\s*$/.exec(array[i]); + if (setdat2 !== null && setdat[1] === setdat2[3]) { + array[index] = 'DELETE'; + array[i] = 'SET @' + setdat2[1] + ' $($' + setdat2[2] + ' + $' + setdat[2] + ')'; + optimizedLines++; + continue; + } + // SET @r0 $a + // SET @($val + $r0) $z + // turns SET $($val + $a) $z + setdat2 = /^\s*SET\s+@\(\$(\w+)\s*\+\s*\$(\w+)\)\s+\$(\w+)\s*$/.exec(array[i]); + if (setdat2 !== null && setdat[1] === setdat2[2]) { + array[index] = 'DELETE'; + array[i] = 'SET @($' + setdat2[1] + ' + $' + setdat[2] + ') $' + setdat2[3]; + optimizedLines++; + continue; + } + // SET @r0 $a + // SET @($val + $z) $r0 + // turns SET $($val + $z) $a + if (setdat2 !== null && setdat[1] === setdat2[3]) { + array[index] = 'DELETE'; + array[i] = 'SET @($' + setdat2[1] + ' + $' + setdat2[2] + ') $' + setdat[2]; + optimizedLines++; + continue; + } + break; + } + } + // SET @r0 $n2 + // BLT $i $r0 :__if151_c_endif + // turns BLT $i $n2 :__if151_c_endif (very specific optimization) + branchdat = /^\s*(BGT|BLT|BGE|BLE|BEQ|BNE)\s+\$(\w+)\s+\$(\w+)\s+:(\w+)\s*$/.exec(array[index + 1]); + if (branchdat !== null && branchdat[3] === setdat[1] && isRegister(setdat[1]) && /^n\d$/.exec(setdat[2]) !== null) { + array[index] = branchdat[1] + ' $' + branchdat[2] + ' $' + setdat[2] + ' :' + branchdat[4]; + array[index + 1] = 'DELETE'; + optimizedLines++; + return; + } + // SET @r0 $a + // NOT @r0 (only registers) + // SET @a $r0 + // turns NOT @a (safe!) + notdat = /^\s*NOT\s+@(r\d+)\s*$/.exec(array[index + 1]); + if (notdat !== null && notdat[1] === setdat[1]) { + setdat2 = /^\s*SET\s+@(\w+)\s+\$(\w+)\s*$/.exec(array[index + 2]); + if (setdat2 !== null && setdat[1] === setdat2[2] && setdat[2] === setdat2[1]) { + array[index] = 'NOT @' + setdat[2]; + array[index + 1] = 'DELETE'; + array[index + 2] = 'DELETE'; + optimizedLines++; + return; + } + } + } + // POP @r0 + // SET @z $r0 + // turns POP @z + popdat = /^\s*POP\s+@(\w+)\s*$/.exec(value); + if (popdat !== null && isRegister(popdat[1])) { + setdat = /^\s*SET\s+@(\w+)\s+\$(\w+)\s*$/.exec(array[index + 1]); + if (setdat !== null && setdat[2] === popdat[1]) { + array[index] = 'POP @' + setdat[1]; + array[index + 1] = 'DELETE'; + optimizedLines++; + return; + } + // POP @r0 + // PSH $r0 + // turns nothing (safe for registers) + pshslpdat = /^\s*(PSH|SLP)\s+\$(r\d+)\s*$/.exec(array[index + 1]); + if (pshslpdat !== null) { + if (pshslpdat[2] === popdat[1]) { + array[index] = 'DELETE'; + array[index + 1] = 'DELETE'; + optimizedLines++; + return; + } + } + } + // Optimize pointer operations with zero index + clrdat = /^\s*CLR\s+@(\w+)\s*$/.exec(value); + if (clrdat !== null) { + i = index; + while (++i < array.length - 1) { + lbl = /^\s*(\w+):\s*$/.exec(array[i]); + if (lbl !== null) { + break; + } + jmpto = /^.+\s:(\w+)$/.exec(array[i]); // match JMP JSR ERR and all branches + if (jmpto !== null) { + break; + } + jmpto = /^\s*(RET|FIN)\s*$/.exec(array[i]); + if (jmpto !== null) { + break; + } + if (array[i].indexOf(clrdat[1]) >= 0) { + setdat = /^\s*SET\s+@(\w+)\s+\$\(\$(\w+)\s*\+\s*\$(\w+)\)\s*$/.exec(array[i]); + if (setdat !== null && clrdat[1] === setdat[3]) { + array[index] = 'DELETE'; + array[i] = 'SET @' + setdat[1] + ' $($' + setdat[2] + ')'; + optimizedLines++; + continue; + } + setdat = /^\s*SET\s+@\(\$(\w+)\s*\+\s*\$(\w+)\)\s+\$(\w+)\s*$/.exec(array[i]); + if (setdat !== null && clrdat[1] === setdat[2]) { + array[index] = 'DELETE'; + array[i] = 'SET @($' + setdat[1] + ') $' + setdat[3]; + optimizedLines++; + continue; + } + break; + } + } + } + }); + } while (optimizedLines !== 0); + function getLabeldestination(label) { + let lbl, jmpdest; + let idx = codeLines.findIndex(obj => obj.indexOf(label + ':') !== -1); + if (idx === -1) { + return ''; + } + while (++idx < codeLines.length - 1) { + lbl = /^\s*(\w+):\s*$/.exec(codeLines[idx]); + if (lbl !== null) { + continue; + } + if (codeLines[idx] === '' || codeLines[idx] === 'DELETE') { + continue; + } + jmpdest = /^\s*JMP\s+:(\w+)\s*$/.exec(codeLines[idx]); + if (jmpdest !== null) { + return jmpdest[1] + ':'; + } + return codeLines[idx]; + } + return ''; + } + function isRegister(name) { + if (/^r\d$/.exec(name) !== null) { + // matches r0 .. r9 Note that some regex in code have this hardcoded. + return true; + } + return false; + } + return codeLines.join('\n'); +} diff --git a/v0.3/out/parser.js b/v0.3/out/parser.js new file mode 100644 index 0000000..f5fc4ea --- /dev/null +++ b/v0.3/out/parser.js @@ -0,0 +1,539 @@ +"use strict"; +/** Translate an array of pre tokens to an array of tokens. First phase of parsing. + * @param tokens Array of pre-tokens + * @returns Array of TOKENS. Recursive on Arr, CodeCave and CodeDomain types + * @throws {TypeError | SyntaxError} at any mistakes + */ +// eslint-disable-next-line no-unused-vars +function parse(preTokens) { + // This object stores a recipe to transform one or more pre_tokens into one + // token. All non-recursive items are here. The order they are + // arrange are important to decide in cases where same element token can be + // more than one functional token depending the other tokens after it. + // Recursive tokens will be treated on getNextToken() function. + const notRecursiveTokensSpecs = [ + // single-tokens easy + { + sequence: ['tilde'], + action(tokenID) { + return { type: 'UnaryOperator', precedence: 2, value: '~', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['semi'], + action(tokenID) { + return { type: 'Terminator', precedence: 12, value: ';', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['comma'], + action(tokenID) { + return { type: 'Delimiter', precedence: 11, value: ',', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['colon'], + action(tokenID) { + return { type: 'Colon', precedence: 0, value: ':', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['dot'], + action(tokenID) { + return { type: 'Member', precedence: 0, value: '.', line: preTokens[tokenID].line }; + } + }, + // single-tokens medium + { + sequence: ['macro'], + action(tokenID) { + return { type: 'Macro', precedence: 0, value: preTokens[tokenID].value, line: preTokens[tokenID].line }; + } + }, + // single-tokens hard + { + sequence: ['keyword'], + action(tokenID) { + const node = { type: 'Keyword', precedence: 12, value: preTokens[tokenID].value, line: preTokens[tokenID].line }; + if (preTokens[tokenID].value === 'asm' || preTokens[tokenID].value === 'struct') { + node.extValue = preTokens[tokenID].extValue; + } + return node; + } + }, + { + sequence: ['numberDec'], + action(tokenID) { + const ptkn = preTokens[tokenID]; + let val = BigInt(ptkn.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: ptkn.line }; + } + }, + { + sequence: ['numberHex'], + action(tokenID) { + const ptkn = preTokens[tokenID]; + let val = ptkn.value.replace(/_/g, '').toLowerCase(); + val = val.padStart((Math.floor((val.length - 1) / 16) + 1) * 16, '0'); + return { type: 'Constant', precedence: 0, value: val, line: ptkn.line }; + } + }, + { + sequence: ['string'], + action(tokenID) { + let val; + const ptkn = preTokens[tokenID]; + const parts = /^(BURST-|S-|TS-)([0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{5})/.exec(ptkn.value); + if (parts !== null) { + val = rsDecode(parts[2]); + } + else { + val = str2long(ptkn.value); + } + return { type: 'Constant', precedence: 0, value: val, line: ptkn.line }; + } + }, + // multi-tokens easy + { + sequence: ['equal', 'equal'], + action(tokenID) { + return { type: 'Comparision', precedence: 6, value: '==', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['equal'], + action(tokenID) { + return { type: 'Assignment', precedence: 10, value: '=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['not', 'equal'], + action(tokenID) { + return { type: 'Comparision', precedence: 6, value: '!=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['not'], + action(tokenID) { + return { type: 'UnaryOperator', precedence: 2, value: '!', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['forwardslash', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '/=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['forwardslash'], + action(tokenID) { + return { type: 'Operator', precedence: 3, value: '/', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['percent', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '%=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['percent'], + action(tokenID) { + return { type: 'Operator', precedence: 3, value: '%', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['less', 'less', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '<<=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['less', 'less'], + action(tokenID) { + return { type: 'Operator', precedence: 5, value: '<<', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['less', 'equal'], + action(tokenID) { + return { type: 'Comparision', precedence: 6, value: '<=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['less'], + action(tokenID) { + return { type: 'Comparision', precedence: 6, value: '<', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['pipe', 'pipe'], + action(tokenID) { + return { type: 'Comparision', precedence: 9, value: '||', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['pipe', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '|=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['pipe'], + action(tokenID) { + return { type: 'Operator', precedence: 7, value: '|', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['greater', 'equal'], + action(tokenID) { + return { type: 'Comparision', precedence: 6, value: '>=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['greater', 'greater', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '>>=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['greater', 'greater'], + action(tokenID) { + return { type: 'Operator', precedence: 5, value: '>>', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['greater'], + action(tokenID) { + return { type: 'Comparision', precedence: 6, value: '>', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['caret', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '^=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['caret'], + action(tokenID) { + return { type: 'Operator', precedence: 7, value: '^', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['variable', 'colon'], + action(tokenID) { + return { type: 'Keyword', precedence: 12, value: 'label', extValue: preTokens[tokenID].value, line: preTokens[tokenID].line }; + } + }, + { + sequence: ['variable'], + action(tokenID) { + return { type: 'Variable', precedence: 0, value: preTokens[tokenID].value, line: preTokens[tokenID].line }; + } + }, + // multi-tokens medium + { + sequence: ['star', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '*=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['star'], + action(tokenID) { + if (isBinaryOperator(tokenID)) { + return { type: 'Operator', precedence: 3, value: '*', line: preTokens[tokenID].line }; + } + else { + return { type: 'UnaryOperator', precedence: 2, value: '*', line: preTokens[tokenID].line }; + } + } + }, + { + sequence: ['plus', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '+=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['plus', 'plus'], + action(tokenID) { + return { type: 'SetUnaryOperator', precedence: 2, value: '++', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['plus'], + action(tokenID) { + if (isBinaryOperator(tokenID)) { + return { type: 'Operator', precedence: 4, value: '+', line: preTokens[tokenID].line }; + } + else { + return { type: 'UnaryOperator', precedence: 2, value: '+', line: preTokens[tokenID].line }; + } + } + }, + { + sequence: ['minus', 'minus'], + action(tokenID) { + return { type: 'SetUnaryOperator', precedence: 2, value: '--', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['minus', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '-=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['minus', 'greater'], + action(tokenID) { + return { type: 'Member', precedence: 0, value: '->', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['minus'], + action(tokenID) { + if (isBinaryOperator(tokenID)) { + return { type: 'Operator', precedence: 4, value: '-', line: preTokens[tokenID].line }; + } + else { + return { type: 'UnaryOperator', precedence: 2, value: '-', line: preTokens[tokenID].line }; + } + } + }, + { + sequence: ['and', 'and'], + action(tokenID) { + return { type: 'Comparision', precedence: 8, value: '&&', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['and', 'equal'], + action(tokenID) { + return { type: 'SetOperator', precedence: 10, value: '&=', line: preTokens[tokenID].line }; + } + }, + { + sequence: ['and'], + action(tokenID) { + if (isBinaryOperator(tokenID)) { + return { type: 'Operator', precedence: 7, value: '&', line: preTokens[tokenID].line }; + } + else { + return { type: 'UnaryOperator', precedence: 2, value: '&', line: preTokens[tokenID].line }; + } + } + } + ]; + // Process element preTokens started at position mainLoopIndex (outer scope) and returns a functional token + function getNextToken() { + const currentPreToken = preTokens[mainLoopIndex]; + let retToken; + // take care of not recursive tokens + const foundRule = notRecursiveTokensSpecs.find(matchRule); + if (foundRule !== undefined) { + retToken = foundRule.action(mainLoopIndex); + mainLoopIndex += foundRule.sequence.length; + return retToken; + } + // take care of recursive tokens + switch (currentPreToken.value) { + case ']': + case ')': + case '}': + throw new SyntaxError(`At line: ${currentPreToken.line}. Unmatched closing '${currentPreToken.value}'.`); + case '[': + retToken = { type: 'Arr', value: '', precedence: 0, line: currentPreToken.line }; + mainLoopIndex++; + retToken.params = []; + while (preTokens[mainLoopIndex].value !== ']') { + retToken.params.push(getNextToken()); + // getNextToken will increase mainLoopIndex for loop + if (preTokens[mainLoopIndex] === undefined) { + throw new SyntaxError(`At end of file. Missing closing ']' for Arr started at line: ${retToken.line}.`); + } + } + // discard closing bracket + mainLoopIndex++; + return retToken; + case '(': + if (mainLoopIndex > 0 && preTokens[mainLoopIndex - 1].type === 'variable') { + retToken = { type: 'Function', value: '', precedence: 0, line: currentPreToken.line }; + } + else { + retToken = { type: 'CodeCave', value: '', precedence: 1, line: currentPreToken.line }; + } + mainLoopIndex++; + retToken.params = []; + while (preTokens[mainLoopIndex].value !== ')') { + retToken.params.push(getNextToken()); + // getNextToken will increase mainLoopIndex for loop + if (preTokens[mainLoopIndex] === undefined) { + throw new SyntaxError(`At end of file. Missing closing ')' for ${retToken.type} started at line: ${retToken.line}.`); + } + } + mainLoopIndex++; + return retToken; + case '{': + retToken = { type: 'CodeDomain', value: '', precedence: 1, line: currentPreToken.line }; + mainLoopIndex++; + retToken.params = []; + while (preTokens[mainLoopIndex].value !== '}') { + retToken.params.push(getNextToken()); + // getNextToken will increase mainLoopIndex for loop + if (preTokens[mainLoopIndex] === undefined) { + throw new SyntaxError(`At end of file. Missing closing '}' for CodeDomain started at line: ${retToken.line}.`); + } + } + mainLoopIndex++; + return retToken; + } + throw new TypeError(`At line: ${currentPreToken.line}. Unknow token found: type: '${currentPreToken.type}' value: '${currentPreToken.value}'.`); + } + function matchRule(ruleN) { + for (let i = 0; i < ruleN.sequence.length; i++) { + if (preTokens[mainLoopIndex + i]?.type === ruleN.sequence[i]) + continue; + return false; // proceed to next rule + } + return true; // all sequence matched! + } + // Use to detect if a token at some position is Binary or Unary + function isBinaryOperator(position) { + if (position >= 2) { + if ((preTokens[position - 1].type === 'plus' && preTokens[position - 2].type === 'plus') || + (preTokens[position - 1].type === 'minus' && preTokens[position - 2].type === 'minus')) { + return true; + } + } + if (position >= 1) { + if (preTokens[position - 1].type === 'variable' || + preTokens[position - 1].type === 'numberDec' || + preTokens[position - 1].type === 'numberHex' || + preTokens[position - 1].type === 'string' || + preTokens[position - 1].value === ']' || + preTokens[position - 1].value === ')') { + return true; + } + } + return false; + } + // Input: javascript string (utf-16) + // Output: string representing same string in hexadecimal utf-8 + function str2long(inStr) { + const byarr = []; + let ret = ''; + let c, c1, i, j; + if (inStr.length === 0) + byarr.push(0); + for (i = 0; i < inStr.length; i++) { + c = inStr.charCodeAt(i); + if (c < 128) { + byarr.push(c); + } + else { + if (c < 2048) { + byarr.push(c >> 6 | 0xc0); // ok + byarr.push((c & 63) | 128); // ok + } + else { + if (c < 55296 || c > 57343) { + byarr.push(((c >> 12) & 63) | 0xe0); // ok + byarr.push(((c >> 6) & 63) | 128); // ok + byarr.push((c & 63) | 128); // ok + } + else { + i++; + c1 = inStr.charCodeAt(i); + if ((c & 0xFC00) === 0xd800 && (c1 & 0xFC00) === 0xDC00) { + c = ((c & 0x3FF) << 10) + (c1 & 0x3FF) + 0x10000; + byarr.push(((c >> 18) & 63) | 0xf0); // ok + byarr.push(((c >> 12) & 63) | 128); // ok + byarr.push(((c >> 6) & 63) | 128); // ok + byarr.push((c & 63) | 128); // ok + } + } + } + } + } + for (j = 0; j < (Math.floor((byarr.length - 1) / 8) + 1) * 8; j++) { + if (j >= byarr.length) { + ret = '00' + ret; + } + else { + ret = byarr[j].toString(16).padStart(2, '0') + ret; + } + } + return (ret); + } + /* eslint-disable camelcase */ + // Decode REED-SALOMON signum address from string to long value + // Adapted from https://github.com/signum-network/signumj + function rsDecode(cypher_string) { + const gexp = [1, 2, 4, 8, 16, 5, 10, 20, 13, 26, 17, 7, 14, 28, 29, 31, 27, 19, 3, 6, 12, 24, 21, 15, 30, 25, 23, 11, 22, 9, 18, 1]; + const glog = [0, 0, 1, 18, 2, 5, 19, 11, 3, 29, 6, 27, 20, 8, 12, 23, 4, 10, 30, 17, 7, 22, 28, 26, 21, 25, 9, 16, 13, 14, 24, 15]; + const alphabet = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; + const codeword_map = [3, 2, 1, 0, 7, 6, 5, 4, 13, 14, 15, 16, 12, 8, 9, 10, 11]; + function gmult(a, b) { + if (a === 0 || b === 0) { + return 0; + } + const idx = (glog[a] + glog[b]) % 31; + return gexp[idx]; + } + function is_codeword_valid(codeword_to_test) { + let sum = 0; + let i, j, t, pos; + for (i = 1; i < 5; i++) { + t = 0; + for (j = 0; j < 31; j++) { + if (j > 12 && j < 27) { + continue; + } + pos = j; + if (j > 26) { + pos -= 14; + } + t ^= gmult(codeword_to_test[pos], gexp[(i * j) % 31]); + } + sum |= t; + } + return sum === 0; + } + let codeword_length = 0; + const codeword = []; + let codework_index; + for (let i = 0; i < cypher_string.length; i++) { + const position_in_alphabet = alphabet.indexOf(cypher_string.charAt(i)); + if (position_in_alphabet <= -1) { + continue; + } + codework_index = codeword_map[codeword_length]; + codeword[codework_index] = position_in_alphabet; + codeword_length++; + } + if (codeword_length !== 17 || !is_codeword_valid(codeword)) { + throw new TypeError(`Error decoding address: S-${cypher_string}`); + } + // base32 to base10 conversion + const length = 13; + let val = 0n; + let mul = 1n; + for (let i = 0; i < length; i++) { + val += mul * BigInt(codeword[i]); + mul *= 32n; + } + return val.toString(16).padStart(16, '0'); + } + /* eslint-enable camelcase */ + /* * * Main function! * * */ + let mainLoopIndex = 0; + const tokenTrain = []; + // this is the mainLoop! + while (mainLoopIndex < preTokens.length) { + tokenTrain.push(getNextToken()); + } + return tokenTrain; +} diff --git a/v0.3/out/preprocessor.js b/v0.3/out/preprocessor.js new file mode 100644 index 0000000..5a68064 --- /dev/null +++ b/v0.3/out/preprocessor.js @@ -0,0 +1,141 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/** + * Process macro substitutions on source code + * @param sourcecode Source code input + * @returns preprocessed string + */ +// eslint-disable-next-line no-unused-vars +function preprocess(sourcecode) { + const preprocessorCodes = [ + // Regex order is important! + { regex: /^\s*#\s*define\s+(\w+)\s*$/, type: 'DEFINE_NULL' }, + { 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' } + ]; + let preprocessorReplacements = [ + { cname: 'true', value: '1' }, + { cname: 'false', value: '0' }, + { cname: 'NULL', value: '0' }, + { cname: 'SMARTC', value: '' } + ]; + const ifActive = [{ active: true, flipped: false }]; + let currentIfLevel = 0; + function getPrepRule(codeline) { + for (const currCode of preprocessorCodes) { + const parts = currCode.regex.exec(codeline); + if (parts !== null) { + return { + Code: currCode, + parts: parts + }; + } + } + return null; + } + function replaceDefines(codeline) { + preprocessorReplacements.forEach(function (replacement) { + const rep = new RegExp('\\b' + replacement.cname + '\\b', 'g'); + codeline = codeline.replace(rep, replacement.value); + }); + return codeline; + } + const lines = sourcecode.split('\n'); + const ret = []; + lines.forEach((currentLine, lineNo) => { + const PrepRule = getPrepRule(currentLine); + const lineActive = ifActive[currentIfLevel].active; + let idx; + if (PrepRule === null) { + if (lineActive) { + ret.push(replaceDefines(currentLine)); + } + else { + // push empty line so line numbers will not be messed + ret.push(''); + } + return; + } + switch (PrepRule.Code.type) { + case 'DEFINE_NULL': + if (lineActive === false) + break; + idx = preprocessorReplacements.findIndex(obj => obj.cname === PrepRule.parts[1]); + if (idx === -1) { + preprocessorReplacements.push({ cname: PrepRule.parts[1], value: '' }); + } + else { + preprocessorReplacements[idx].value = ''; + } + break; + case 'DEFINE_VAL': + if (lineActive === false) + break; + idx = preprocessorReplacements.findIndex(obj => obj.cname === PrepRule.parts[1]); + if (idx === -1) { + preprocessorReplacements.push({ cname: PrepRule.parts[1], value: replaceDefines(PrepRule.parts[2]) }); + } + else { + preprocessorReplacements[idx].value = PrepRule.parts[2]; + } + break; + case 'UNDEF': + if (lineActive === false) + break; + preprocessorReplacements = preprocessorReplacements.filter(obj => obj.cname !== PrepRule.parts[1]); + break; + case 'IFDEF': + if (lineActive) + currentIfLevel++; + idx = preprocessorReplacements.findIndex(obj => obj.cname === PrepRule.parts[1]); + if (idx !== -1) + ifActive.push({ active: true, flipped: false }); + else + ifActive.push({ active: false, flipped: false }); + break; + case 'IFNDEF': + if (lineActive) + currentIfLevel++; + idx = preprocessorReplacements.findIndex(obj => obj.cname === PrepRule.parts[1]); + if (idx === -1) + ifActive.push({ active: true, flipped: false }); + else + ifActive.push({ active: false, flipped: false }); + break; + case 'ELSE': { + const lastIfInfo = ifActive.pop(); + if (lastIfInfo === undefined) + throw new SyntaxError(`At line: ${lineNo + 1}. Unmatched '#else' directive.`); + if (lastIfInfo.flipped === true) + throw new SyntaxError(`At line: ${lineNo + 1}. Unmatched '#else' directive.`); + ifActive.push({ active: !lastIfInfo.active, flipped: true }); + if (ifActive.length === 1) { + throw new SyntaxError(`At line: ${lineNo + 1}. '#else' directive not associated with '#ifdef', '#ifndef' nor '#if'.`); + } + break; + } + case 'ENDIF': + if (ifActive.length - 1 === currentIfLevel) + currentIfLevel--; + ifActive.pop(); + if (ifActive.length === 0) { + throw new SyntaxError(`At line: ${lineNo + 1}. '#endif' directive not associated with '#ifdef', '#ifndef' nor '#if'.`); + } + break; + default: + // not implementd + } + // push empty line so line numbers will not be messed + ret.push(''); + }); + if (ifActive.length !== 1) { + throw new SyntaxError("At line: EOF. Unmatched directives '#ifdef', '#ifndef' nor '#if'."); + } + return ret.join('\n'); +} diff --git a/v0.3/out/shaper.js b/v0.3/out/shaper.js new file mode 100644 index 0000000..c034a23 --- /dev/null +++ b/v0.3/out/shaper.js @@ -0,0 +1,1635 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/** Translate an array of tokens to an object representing the program. + * This is the second phase of parser + * @param tokenAST Array of tokens + * @returns CONTRACT object in final type, but still incomplete. + * @throws {TypeError | SyntaxError} at any mistakes + */ +// eslint-disable-next-line no-unused-vars +function shape(tokenAST) { + const Program = { + Global: { + APIFunctions: [], + macros: [], + sentences: [] + }, + functions: [], + memory: [], + labels: [], + typesDefinitions: [], + // Default configuration for compiler + Config: { + compilerVersion: 'dev', + enableRandom: false, + enableLineLabels: false, + globalOptimization: false, + maxAuxVars: 3, + maxConstVars: 0, + reuseAssignedVar: true, + version: 'dev', + warningToError: true, + APIFunctions: false, + PName: '', + PDescription: '', + PActivationAmount: '', + PUserStackPages: 0, + PCodeStackPages: 0, + outputSourceLineNumber: false + } + }; + const AuxVars = { + currentToken: 0, + latestLoopId: [], + setIsDeclared: false, + currentScopeName: '', + currentPrefix: '' + }; + /* * * Main function! * * */ + function shapeMain() { + splitGlobalAndFunctions(); + if (Program.Global.macros !== undefined) { + Program.Global.macros.forEach(processMacro); + } + if (Program.Config.version === '') { + throw new TypeError(`Compiler version not set. Pin current compiler version in your program adding '#pragma version ${Program.Config.compilerVersion}' to code.`); + } + if (Program.Config.version !== Program.Config.compilerVersion) { + throw new TypeError(`This compiler is version '${Program.Config.compilerVersion}'. File needs a compiler version '${Program.Config.version}'. Update '#pragma version' macro or run another SmartC version.`); + } + Program.typesDefinitions = createDefaultTypesTable(); + addRegistersInMemory(); + addConstantsInMemory(); + AuxVars.currentScopeName = ''; + AuxVars.currentPrefix = ''; + AuxVars.currentToken = 0; + Program.Global.sentences = code2sentenceS(Program.Global.code); + createMemoryTable(Program.Global.sentences); + delete Program.Global.code; + for (let i = 0; i < Program.functions.length; i++) { + processFunction(i); + delete Program.functions[i].arguments; + delete Program.functions[i].code; + const fnSentences = Program.functions[i].sentences; + if (fnSentences !== undefined && fnSentences.length > 0) { + createMemoryTable(fnSentences); + } + } + if (Program.Config.APIFunctions) { + Program.Global.APIFunctions = createAPItable(); + } + checkDoublesDefinitions(); + consolidateMemory(); + return Program; + } + /** + * Organize incoming tokens (tokenAST) into only tree posibilities: + * 1) global statements, + * 2) macros or + * 3) functions + * */ + function splitGlobalAndFunctions() { + Program.Global.code = []; + for (AuxVars.currentToken = 0; AuxVars.currentToken < tokenAST.length; AuxVars.currentToken++) { + if (AuxVars.currentToken + 3 < tokenAST.length && + tokenAST[AuxVars.currentToken].type === 'Keyword' && + tokenAST[AuxVars.currentToken + 1].type === 'Variable' && + tokenAST[AuxVars.currentToken + 2].type === 'Function' && + tokenAST[AuxVars.currentToken + 3].type === 'CodeDomain') { + // Function found. Does not return pointer + if (tokenAST[AuxVars.currentToken].value === 'struct') { + throw new SyntaxError(`At line: ${tokenAST[AuxVars.currentToken].line}. Function returning a struct currently not implemented.`); + } + Program.functions.push({ + argsMemObj: [], + sentences: [], + declaration: tokenAST[AuxVars.currentToken].value, + line: tokenAST[AuxVars.currentToken + 1].line, + name: tokenAST[AuxVars.currentToken + 1].value, + arguments: tokenAST[AuxVars.currentToken + 2].params, + code: tokenAST[AuxVars.currentToken + 3].params + }); + AuxVars.currentToken += 3; + continue; + } + if (AuxVars.currentToken + 4 < tokenAST.length && + tokenAST[AuxVars.currentToken].type === 'Keyword' && + tokenAST[AuxVars.currentToken + 1].type === 'UnaryOperator' && + tokenAST[AuxVars.currentToken + 1].value === '*' && + tokenAST[AuxVars.currentToken + 2].type === 'Variable' && + tokenAST[AuxVars.currentToken + 3].type === 'Function' && + tokenAST[AuxVars.currentToken + 4].type === 'CodeDomain') { + Program.functions.push({ + argsMemObj: [], + sentences: [], + declaration: (tokenAST[AuxVars.currentToken].value + '_ptr'), + typeDefinition: tokenAST[AuxVars.currentToken].extValue, + line: tokenAST[AuxVars.currentToken + 2].line, + name: tokenAST[AuxVars.currentToken + 2].value, + arguments: tokenAST[AuxVars.currentToken + 3].params, + code: tokenAST[AuxVars.currentToken + 4].params + }); + AuxVars.currentToken += 4; + continue; + } + if (tokenAST[AuxVars.currentToken].type === 'Macro') { + const fields = tokenAST[AuxVars.currentToken].value.replace(/\s\s+/g, ' ').split(' '); + Program.Global.macros.push({ type: fields[0], property: fields[1], value: fields.slice(2).join(' '), line: tokenAST[AuxVars.currentToken].line }); + continue; + } + // Not function neither macro, so it is global statement + Program.Global.code.push(tokenAST[AuxVars.currentToken]); + } + } + // Expects one or more sentences inside codetrain + function code2sentenceS(codetrain = [], addTerminator = false) { + if (addTerminator) { + codetrain.push({ type: 'Terminator', value: ';', precedence: 11, line: codetrain[AuxVars.currentToken].line }); + } + let sentences = []; + for (; AuxVars.currentToken < codetrain.length; AuxVars.currentToken++) { + sentences = sentences.concat(code2sentence(codetrain)); + } + return sentences; + } + // Expects only one sentence in codetrain + function code2sentence(codetrain) { + const phrase = []; + let lineOfFirstInstruction = 0; + if (codetrain[AuxVars.currentToken].type === 'CodeDomain') { + const savedPosition = AuxVars.currentToken; + AuxVars.currentToken = 0; + const temp = code2sentenceS(codetrain[savedPosition].params); + AuxVars.currentToken = savedPosition; + return temp; + } + // One sentence ending with terminator (or maybe another loop/conditional) + while (AuxVars.currentToken < codetrain.length) { + if (codetrain[AuxVars.currentToken].type === 'Terminator') { + // end of sentence! + return [{ type: 'phrase', code: phrase, line: lineOfFirstInstruction }]; + } + if (codetrain[AuxVars.currentToken].type === 'CodeCave') { + if (codetrain[AuxVars.currentToken - 1].value === 'if') { + if (phrase.length > 1) { + throw new SyntaxError(`At line: ${phrase[0].line}. Statement including 'if' in wrong way. Possible missing ';'.`); + } + phrase.pop(); + const id = `__if${codetrain[AuxVars.currentToken].line}`; + const line = codetrain[AuxVars.currentToken].line; + const condition = codetrain[AuxVars.currentToken].params; + AuxVars.currentToken++; + const trueBlock = code2sentence(codetrain); + if (AuxVars.currentToken + 1 < codetrain.length) { + if (codetrain[AuxVars.currentToken + 1].type === 'Keyword' && codetrain[AuxVars.currentToken + 1].value === 'else') { + AuxVars.currentToken += 2; + return [{ + type: 'ifElse', + id: id, + line: line, + condition: condition, + trueBlock: trueBlock, + falseBlock: code2sentence(codetrain) + }]; + } + } + return [{ + type: 'ifEndif', + id: id, + line: line, + condition: condition, + trueBlock: trueBlock + }]; + } + if (codetrain[AuxVars.currentToken - 1].value === 'while') { + if (phrase.length > 1) { + throw new SyntaxError(`At line: ${phrase[0].line}'. Statement including 'while' in wrong way. Possible missing ';'.`); + } + phrase.pop(); + const id = `__loop${codetrain[AuxVars.currentToken].line}`; + const line = codetrain[AuxVars.currentToken].line; + const condition = codetrain[AuxVars.currentToken].params; + AuxVars.currentToken++; + AuxVars.latestLoopId.push(id); + const trueBlock = code2sentence(codetrain); + AuxVars.latestLoopId.pop(); + return [{ + type: 'while', + id: id, + line: line, + condition: condition, + trueBlock: trueBlock + }]; + } + if (codetrain[AuxVars.currentToken - 1].value === 'for') { + if (phrase.length > 1) { + throw new SyntaxError(`At line: ${phrase[0].line}. Statement including 'for' in wrong way. Possible missing ';'.`); + } + phrase.pop(); + const id = `__loop${codetrain[AuxVars.currentToken].line}`; + const line = codetrain[AuxVars.currentToken].line; + const savePosition = AuxVars.currentToken; + AuxVars.currentToken = 0; + const threeSentences = code2sentenceS(codetrain[savePosition].params, true); + AuxVars.currentToken = savePosition; + if (threeSentences.length !== 3) { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Expected 3 sentences for 'for(;;){}' loop. Got ${threeSentences.length}`); + } + if (threeSentences[0].type !== 'phrase' || threeSentences[1].type !== 'phrase' || threeSentences[2].type !== 'phrase') { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Sentences inside 'for(;;)' can not be other loops or conditionals`); + } + AuxVars.currentToken++; + AuxVars.latestLoopId.push(id); + const trueBlock = code2sentence(codetrain); + AuxVars.latestLoopId.pop(); + return [{ + type: 'for', + id: id, + line: line, + threeSentences: threeSentences, + trueBlock: trueBlock + }]; + } + } + if (codetrain[AuxVars.currentToken].type === 'Keyword') { + if (codetrain[AuxVars.currentToken].value === 'else') { + throw new SyntaxError('At line: ' + codetrain[AuxVars.currentToken].line + ". 'else' not associated with an 'if(){}else{}' sentence"); + } + if (codetrain[AuxVars.currentToken].value === 'asm') { + if (phrase.length !== 0) { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Keyword 'asm' is not at start of sentence. Possible missing ';' before it.`); + } + return [{ type: 'phrase', code: [codetrain[AuxVars.currentToken]], line: codetrain[AuxVars.currentToken].line }]; + } + if (codetrain[AuxVars.currentToken].value === 'label') { + if (phrase.length !== 0) { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Label '${codetrain[AuxVars.currentToken].extValue}' is not at start of sentence. Possible missing ';' before it.`); + } + return [{ type: 'phrase', code: [codetrain[AuxVars.currentToken]], line: codetrain[AuxVars.currentToken].line }]; + } + if (codetrain[AuxVars.currentToken].value === 'do') { + const id = `__loop${codetrain[AuxVars.currentToken].line}`; + const line = codetrain[AuxVars.currentToken].line; + AuxVars.currentToken++; + AuxVars.latestLoopId.push(id); + const trueBlock = code2sentence(codetrain); + AuxVars.latestLoopId.pop(); + AuxVars.currentToken++; + if (AuxVars.currentToken + 2 >= codetrain.length) { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Incomplete do{}while(); sentence.`); + } + if (codetrain[AuxVars.currentToken].type !== 'Keyword' || codetrain[AuxVars.currentToken].value !== 'while') { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Wrong do{}while(); sentence.`); + } + AuxVars.currentToken++; + if (codetrain[AuxVars.currentToken].type !== 'CodeCave') { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Wrong do{}while(); sentence.`); + } + const condition = codetrain[AuxVars.currentToken].params; + AuxVars.currentToken++; + if (codetrain[AuxVars.currentToken].type !== 'Terminator') { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Missing ';', found '${codetrain[AuxVars.currentToken].type}'.`); + } + return [{ + type: 'do', + id: id, + line: line, + trueBlock: trueBlock, + condition: condition + }]; + } + if (codetrain[AuxVars.currentToken].value === 'struct') { + if (AuxVars.currentToken + 1 >= codetrain.length) { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Missing arguments for 'struct' sentence.`); + } + if (codetrain[AuxVars.currentToken + 1].type === 'CodeDomain') { + const structName = codetrain[AuxVars.currentToken].extValue; + if (structName === undefined || structName === '') { + throw new SyntaxError(`At line: ${codetrain[AuxVars.currentToken].line}. Missing struct type name.`); + } + AuxVars.currentToken++; + const Node = { + type: 'struct', + line: codetrain[AuxVars.currentToken - 1].line, + name: structName, + members: code2sentence(codetrain), + Phrase: { type: 'phrase', line: codetrain[AuxVars.currentToken - 1].line } + }; + Node.Phrase.code = [codetrain[AuxVars.currentToken - 1]]; + AuxVars.currentToken++; + while (AuxVars.currentToken < codetrain.length) { + if (codetrain[AuxVars.currentToken].type === 'Terminator') { + return [Node]; + } + Node.Phrase.code.push(codetrain[AuxVars.currentToken]); + AuxVars.currentToken++; + } + throw new SyntaxError("At end of file. Wrong 'struct' declaration. Missing ';'"); + } + } + if (codetrain[AuxVars.currentToken].value === 'break' || codetrain[AuxVars.currentToken].value === 'continue') { + if (AuxVars.latestLoopId.length === 0) { + throw new SyntaxError('At line: ' + codetrain[AuxVars.currentToken].line + ". '" + codetrain[AuxVars.currentToken].value + "' outside a loop."); + } + // Just update information and continue on loop + codetrain[AuxVars.currentToken].extValue = AuxVars.latestLoopId[AuxVars.latestLoopId.length - 1]; + } + } + phrase.push(codetrain[AuxVars.currentToken]); + if (lineOfFirstInstruction === 0) { + lineOfFirstInstruction = codetrain[AuxVars.currentToken].line; + } + AuxVars.currentToken++; + } + if (phrase.length !== 0) { + throw new SyntaxError('At line: ' + codetrain[AuxVars.currentToken - 1].line + ". Missing ';'. "); + } + // Never reach this point + throw new SyntaxError('At line: ' + codetrain[AuxVars.currentToken - 1].line + '. Strange error. '); + } + // Not recursive. Only top level declarations allowed. + // This creates only global variables or function scope variables. + function createMemoryTable(sntcs) { + sntcs.forEach(function (phrs) { + let memTemplate; + if (phrs.type === 'struct') { + struct2typedefinition(phrs); + memTemplate = phrase2memoryObject(phrs.Phrase); + } + else if (phrs.type === 'phrase') { + memTemplate = phrase2memoryObject(phrs); + } + if (memTemplate !== undefined && memTemplate.length > 0) { + Program.memory = Program.memory.concat(memTemplate); + } + }); + } + function getArraySize(tkn = [], line = -1) { + if (tkn.length !== 1 || tkn[0].type !== 'Constant') { + throw new TypeError('At line: ' + line + '. Wrong array declaration. Only constant size declarations allowed.'); + } + return parseInt(tkn[0].value, 16); + } + function struct2typedefinition(structPhrase) { + // create struct type definition + const StructTypeD = { + name: AuxVars.currentPrefix + structPhrase.name, + type: 'struct', + structMembers: [], + structAccumulatedSize: [], + MemoryTemplate: { + address: -1, + asmName: '', + name: '', + type: 'struct', + typeDefinition: AuxVars.currentPrefix + structPhrase.name, + scope: AuxVars.currentScopeName, + size: -1, + isDeclared: AuxVars.setIsDeclared, + declaration: 'struct' + } + }; + const savedPrefix = AuxVars.currentPrefix; + AuxVars.currentPrefix = ''; + structPhrase.members.forEach(struphrs => { + const memobj = phrase2memoryObject(struphrs, structPhrase.name); + if (memobj !== undefined) { + StructTypeD.structMembers = StructTypeD.structMembers.concat(memobj); + } + }); + StructTypeD.MemoryTemplate.size = StructTypeD.structMembers.length; + let accumulatedSize = 0; + StructTypeD.structMembers.forEach(function (memb) { + StructTypeD.structAccumulatedSize.push([memb.name, accumulatedSize]); + if (memb.type !== 'struct') { // Remeber to change here code yolj1A + accumulatedSize++; + } + }); + AuxVars.currentPrefix = savedPrefix; + Program.typesDefinitions.push(StructTypeD); + } + /** Takes a phrase and process variables to MEMORY_SLOT + * Fills types definitions of necessary + * Inserts labels at Program.labels */ + function phrase2memoryObject(phrs, structName = '') { + let ret = []; + let ispointer = false; + if (structName !== '') { + structName += '_'; + } + if (phrs.type === undefined || phrs.type !== 'phrase') { + throw new TypeError('Unknow object type arrived at phrase2memoryObject.'); + } + const phraseCode = phrs.code; + if (phraseCode === undefined || phraseCode.length === 0) { // empty statement + return; + } + if (phraseCode[0].type === 'Keyword' && phraseCode[0].value === 'label') { + const labelID = phraseCode[0].extValue; + if (labelID === undefined || labelID === '') { + throw new TypeError(`At line: ${phraseCode[0].line}. Found a label without id.`); + } + if (Program.labels.find(val => val === labelID) !== undefined) { + throw new TypeError(`At line: ${phraseCode[0].line}. Label name already in use.`); + } + Program.labels.push(labelID); + return; + } + if (phraseCode.length < 2) { + return; + } + if (phraseCode[0].type === 'Keyword') { + let TokenConst; + if (phraseCode[0].value === 'return' || + phraseCode[0].value === 'goto') { + return; + } + if (phraseCode[0].value === 'const') { + // remove token so no influence in declarations + TokenConst = phraseCode.shift(); + } + let keywordIndex = 0; + let end = false; + while (end === false) { + end = true; + if (phraseCode[keywordIndex].value === 'struct') { + if (keywordIndex + 2 > phraseCode.length) { + return; + } + const structNameDef = phraseCode[keywordIndex].extValue; + let idx = keywordIndex + 1; + while (idx < phraseCode.length) { + const dimensions = []; + if (phraseCode[idx].type === 'Delimiter') { + if (keywordIndex + 1 === idx) { + throw new TypeError(`At line: ${phraseCode[idx].line}. Delimiter ',' not expected.`); + } + idx++; + continue; + } + if (phraseCode[idx].type === 'Keyword') { + keywordIndex = idx; + end = false; + break; + } + if (phraseCode[idx].value === '*' && phraseCode[idx + 1]?.type === 'Variable') { + ispointer = true; + idx++; + } + else { + ispointer = false; + } + if (phraseCode[idx].type === 'Variable') { + let MemTemplate; + let search = Program.typesDefinitions.find(obj => obj.name === structNameDef && obj.type === 'struct'); + if (search === undefined && AuxVars.currentPrefix.length > 0) { + search = Program.typesDefinitions.find(obj => obj.name === AuxVars.currentPrefix + structNameDef && obj.type === 'struct'); + } + if (ispointer) { + if (search === undefined) { + // Maybe recursive definition. + MemTemplate = { + address: -1, + name: phraseCode[idx].value, + asmName: AuxVars.currentPrefix + phraseCode[idx].value, + type: 'structRef', + // Recursive struct works only with global definitions + typeDefinition: structNameDef, + scope: AuxVars.currentScopeName, + size: 1, + isDeclared: AuxVars.setIsDeclared, + declaration: 'struct_ptr' + }; + } + else { + // not recursive definition + MemTemplate = JSON.parse(JSON.stringify(search.MemoryTemplate)); + MemTemplate.name = phraseCode[idx].value; + MemTemplate.asmName = AuxVars.currentPrefix + phraseCode[idx].value; + MemTemplate.scope = AuxVars.currentScopeName; + MemTemplate.isDeclared = AuxVars.setIsDeclared; + MemTemplate.declaration = 'struct_ptr'; + MemTemplate.type = 'structRef'; + } + } + else { // is not pointer + if (search === undefined) { + throw new TypeError(`At line: ${phraseCode[keywordIndex].line}. Could not find type definition for 'struct' '${phraseCode[keywordIndex].extValue}'.`); + } + MemTemplate = JSON.parse(JSON.stringify(search.MemoryTemplate)); + MemTemplate.name = phraseCode[idx].value; + MemTemplate.asmName = AuxVars.currentPrefix + phraseCode[idx].value; + MemTemplate.scope = AuxVars.currentScopeName; + MemTemplate.isDeclared = AuxVars.setIsDeclared; + } + while (idx + 1 < phraseCode.length) { + if (phraseCode[idx + 1].type === 'Arr') { // Array declaration + idx++; + dimensions.push(getArraySize(phraseCode[idx].params, phraseCode[idx].line)); + } + else { + break; + } + } + if (dimensions.length > 0) { // is array of structs + if (phraseCode[0].extValue === undefined) { + throw new TypeError("Could not find type definition for an undefined 'struct'."); + } + MemTemplate.typeDefinition = MemTemplate.asmName; + MemTemplate.asmName = AuxVars.currentPrefix + MemTemplate.name; + MemTemplate.arrItem = { + type: MemTemplate.type, + declaration: MemTemplate.declaration, + typeDefinition: AuxVars.currentPrefix + structNameDef, + totalSize: 0 + }; + MemTemplate.type = 'array'; + if (MemTemplate.declaration.includes('_ptr') === false) { + MemTemplate.declaration += '_ptr'; + } + MemTemplate.arrItem.totalSize = 1 + dimensions.reduce(function (total, num) { + return total * num; + }, MemTemplate.size); + ret.push(MemTemplate); + for (let x = 0, i = 0; x < dimensions.length; x++) { + for (let y = 0; y < dimensions[x]; y++) { + ret = ret.concat(assignStructVariable(phraseCode[0].extValue, phraseCode[idx - dimensions.length].value + '_' + i, ispointer)); + i++; + } + } + // create array type definition + if (dimensions.length > 0) { + const TypeD = { + name: AuxVars.currentPrefix + phraseCode[idx - dimensions.length].value, + type: 'array', + arrayDimensions: dimensions, + arrayMultiplierDim: [], + // CHECK unneed? + MemoryTemplate: MemTemplate + }; + let j = dimensions.length - 1; + let acc = MemTemplate.size; + do { + TypeD.arrayMultiplierDim.unshift(acc); + acc *= dimensions[j]; + j--; + } while (j >= 0); + Program.typesDefinitions.push(TypeD); + } + } + else { // is not array of structs + if (ispointer) { + ret = ret.concat(MemTemplate); + } + else { + if (phraseCode[0].extValue === undefined) { + throw new TypeError("Could not find type definition for an undefined 'struct'."); + } + ret = ret.concat(assignStructVariable(phraseCode[0].extValue, phraseCode[idx].value, ispointer)); + } + } + idx++; + continue; + } + if (phraseCode[idx].type === 'Terminator') { + break; + } + throw new TypeError('At line: ' + phraseCode[idx].line + ". Invalid element (type: '" + phraseCode[idx].type + "' value: '" + phraseCode[idx].value + "') found in struct definition!"); + } + if (phraseCode[idx] !== undefined && phraseCode[idx].type === 'Keyword') { + continue; + } + return ret; + } + else if (phraseCode[keywordIndex].value === 'long' || + phraseCode[keywordIndex].value === 'void') { + let idx = keywordIndex + 1; + let valid = true; + while (idx < phraseCode.length) { + if (phraseCode[idx].type === 'Delimiter') { + if (keywordIndex + 1 === idx) { + throw new TypeError(`At line: ${phraseCode[idx].line}. Delimiter ',' not expected.`); + } + idx++; + valid = true; + continue; + } + if (phraseCode[idx].type === 'Keyword') { + keywordIndex = idx; + end = false; + break; + } + if (valid === true && phraseCode[idx].value === '*' && phraseCode[idx + 1]?.type === 'Variable') { + ispointer = true; + idx++; + } + else { + ispointer = false; + } + if (valid === true) { + const dimensions = []; + const search = Program.typesDefinitions.find(obj => obj.type === 'long'); + if (search === undefined) { + throw new TypeError(`At line: ${phraseCode[idx].line}. Type definition for 'long' not found`); + } + const MemTemplate = JSON.parse(JSON.stringify(search.MemoryTemplate)); + MemTemplate.name = phraseCode[idx].value; + MemTemplate.asmName = AuxVars.currentPrefix + phraseCode[idx].value; + MemTemplate.scope = AuxVars.currentScopeName; + if (phraseCode[keywordIndex].value === 'void') { + if (ispointer) { + MemTemplate.declaration = 'void_ptr'; + } + else { + throw new TypeError(`At line: ${phraseCode[idx].line}. Can not declare variables as void.`); + } + } + else { // phraseCode[keywordIndex].value === 'long' + if (ispointer) { + MemTemplate.declaration += '_ptr'; + } + } + MemTemplate.isDeclared = AuxVars.setIsDeclared; + while (idx + 1 < phraseCode.length) { + if (phraseCode[idx + 1].type === 'Arr') { // Array declaration + idx++; + dimensions.push(getArraySize(phraseCode[idx].params, phraseCode[idx].line)); + } + else { + break; + } + } + if (dimensions.length === 0) { + // NOT array + ret.push(MemTemplate); + } + else { + // IS array + // fill more information in memory template + MemTemplate.type = 'array'; + MemTemplate.typeDefinition = structName + MemTemplate.asmName; + MemTemplate.arrItem = { + type: 'long', + declaration: MemTemplate.declaration, + typeDefinition: structName + MemTemplate.asmName, + totalSize: 0 + }; + // CHECK22 + if (MemTemplate.declaration.includes('_ptr') === false) { + MemTemplate.declaration += '_ptr'; + } + MemTemplate.arrItem.totalSize = 1 + dimensions.reduce(function (total, num) { + return total * num; + }, 1); + // Create item in memory_template + ret.push(MemTemplate); + // Create array items in memory_template + for (let i = 1; i < MemTemplate.arrItem.totalSize; i++) { + const Mem2 = JSON.parse(JSON.stringify(search.MemoryTemplate)); + Mem2.name = `${MemTemplate.name}_${i - 1}`; + Mem2.asmName = `${MemTemplate.asmName}_${i - 1}`; + Mem2.scope = AuxVars.currentScopeName; + Mem2.declaration = MemTemplate.arrItem.declaration; + ret.push(Mem2); + } + // create array type definition + if (MemTemplate.arrItem.totalSize > 1) { + const TypeD = { + name: structName + MemTemplate.asmName, + type: 'array', + arrayDimensions: dimensions, + arrayMultiplierDim: [], + // CHECK unneed? + MemoryTemplate: MemTemplate + }; + let j = dimensions.length - 1; + let acc = 1; + do { + TypeD.arrayMultiplierDim.unshift(acc); + acc *= dimensions[j]; + j--; + } while (j >= 0); + Program.typesDefinitions.push(TypeD); + } + } + valid = false; + } + idx++; + } + } + } + if (TokenConst !== undefined) { + // give token back! + phraseCode.unshift(TokenConst); + } + } + return ret; + } + function assignStructVariable(structName, varName, ispointer) { + let search = Program.typesDefinitions.find(obj => obj.type === 'struct' && obj.name === structName); + if (search === undefined && AuxVars.currentPrefix.length > 0) { + search = Program.typesDefinitions.find(obj => obj.type === 'struct' && obj.name === AuxVars.currentPrefix + structName); + } + if (search === undefined) { + throw new TypeError(`Could not find type definition for 'struct' '${structName}'`); + } + let newmemory = [JSON.parse(JSON.stringify(search.MemoryTemplate))]; + if (!ispointer) { + newmemory = newmemory.concat(JSON.parse(JSON.stringify(search.structMembers))); + } + newmemory.forEach(Mem => { + if (Mem.name === '') { + Mem.name = varName; + } + else { + Mem.name = varName + '_' + Mem.name; + } + Mem.asmName = AuxVars.currentPrefix + Mem.name; + }); + return newmemory; + } + /** Reads/verifies one macro token and add it into Program.Config object + * */ + function processMacro(Token) { + let boolVal; + if (Token.value === undefined || Token.value === '') { + boolVal = true; + } + else if (Token.value === 'true' || Token.value === '1') { + boolVal = true; + } + else if (Token.value === 'false' || Token.value === '0') { + boolVal = false; + } + if (Token.type === 'pragma') { + if (Token.property === 'maxAuxVars') { + if (Token.value !== undefined) { + const num = parseInt(Token.value); + if (num < 1 || num > 10) { + throw new RangeError(`At line: ${Token.line}. Value out of permitted range 1..10.`); + } + Program.Config.maxAuxVars = num; + return; + } + } + if (Token.property === 'maxConstVars') { + if (Token.value !== undefined) { + const num = parseInt(Token.value); + if (num < 0 || num > 10) { + throw new RangeError(`At line: ${Token.line}. Value out of permitted range 0..10.`); + } + Program.Config.maxConstVars = num; + return; + } + } + if (Token.property === 'reuseAssignedVar' && boolVal !== undefined) { + Program.Config.reuseAssignedVar = boolVal; + return; + } + if (Token.property === 'enableRandom' && boolVal !== undefined) { + Program.Config.enableRandom = boolVal; + return; + } + if (Token.property === 'enableLineLabels' && boolVal !== undefined) { + Program.Config.enableLineLabels = boolVal; + return; + } + if (Token.property === 'globalOptimization' && boolVal !== undefined) { + Program.Config.globalOptimization = boolVal; + return; + } + if (Token.property === 'version') { + Program.Config.version = Token.value; + if (Program.Config.version !== undefined) { + return; + } + } + if (Token.property === 'warningToError' && boolVal !== undefined) { + Program.Config.warningToError = boolVal; + return; + } + if (Token.property === 'outputSourceLineNumber' && boolVal !== undefined) { + Program.Config.outputSourceLineNumber = boolVal; + return; + } + } + if (Token.type === 'include') { + if (Token.property === 'APIFunctions' && boolVal !== undefined) { + Program.Config.APIFunctions = boolVal; + return; + } + } + if (Token.type === 'program') { + if (Token.property === 'name') { + const parts = /^[0-9a-zA-Z]{1,30}$/.exec(Token.value); + if (parts === null) { + throw new TypeError(`At line: ${Token.line}. Program name must contains only letters [a-z][A-Z][0-9], from 1 to 30 chars.`); + } + Program.Config.PName = Token.value; + return; + } + if (Token.property === 'description') { + if (Token.value.length >= 1000) { + throw new TypeError(`At line: ${Token.line}. Program description max lenght is 1000 chars. It is ${Token.value.length} chars.`); + } + Program.Config.PDescription = Token.value; + return; + } + if (Token.property === 'activationAmount') { + const parts = /^[0-9_]{1,20}$/.exec(Token.value); + if (parts === null) { + throw new TypeError(`At line: ${Token.line}. Program activation must be only numbers or '_'.`); + } + Program.Config.PActivationAmount = Token.value.replace(/_/g, ''); + return; + } + if (Token.property === 'userStackPages') { + const parts = /^\d\s*$|^10\s*$/.exec(Token.value); + if (parts === null) { + throw new TypeError(`At line: ${Token.line}. Program user stack pages must be a number between 0 and 10, included.`); + } + Program.Config.PUserStackPages = Number(Token.value); + return; + } + if (Token.property === 'codeStackPages') { + const parts = /^\d\s*$|^10\s*$/.exec(Token.value); + if (parts === null) { + throw new TypeError(`At line: ${Token.line}. Program code stack pages must be a number between 0 and 10, included.`); + } + Program.Config.PCodeStackPages = Number(Token.value); + return; + } + } + throw new TypeError(`At line: ${Token.line}. Unknow macro property and/or value: '#${Token.type} ${Token.property} ${Token.value}'. Please check valid values on Help page`); + } + function addRegistersInMemory() { + const search = Program.typesDefinitions.find(obj => obj.type === 'register'); + if (search === undefined) { + throw new TypeError("Not found type 'register' at types definitions."); + } + for (let i = 0; i < Program.Config.maxAuxVars; i++) { + const MemTemplate = JSON.parse(JSON.stringify(search.MemoryTemplate)); + MemTemplate.name = `r${i}`; + MemTemplate.asmName = `r${i}`; + Program.memory.push(MemTemplate); + } + } + function addConstantsInMemory() { + const search = Program.typesDefinitions.find(obj => obj.type === 'register'); + if (search === undefined) { + throw new TypeError("Not found type 'register' at types definitions."); + } + for (let i = 1; i <= Program.Config.maxConstVars; i++) { + const MemTemplate = JSON.parse(JSON.stringify(search.MemoryTemplate)); + MemTemplate.name = `n${i}`; + MemTemplate.asmName = `n${i}`; + MemTemplate.hexContent = i.toString(16).padStart(16, '0'); + Program.memory.push(MemTemplate); + } + } + /** Process/checks function arguments and code, transforming them into argsMemObj and sentences properties */ + function processFunction(fnNum) { + const currentFunction = Program.functions[fnNum]; + if (currentFunction.code === undefined) { + currentFunction.sentences = []; + } + else { + AuxVars.currentToken = 0; + currentFunction.sentences = code2sentenceS(currentFunction.code); + } + if (currentFunction.arguments === undefined || currentFunction.arguments.length === 0) { + throw new TypeError(`At line: ${currentFunction.line}. Missing arguments for function '${currentFunction.name}'. Do you mean 'void'?`); + } + if (currentFunction.arguments.length === 1 && currentFunction.arguments[0].type === 'Keyword' && currentFunction.arguments[0].value === 'void') { + currentFunction.arguments.pop(); + } + AuxVars.currentScopeName = currentFunction.name; + AuxVars.currentPrefix = AuxVars.currentScopeName + '_'; + if (currentFunction.arguments.length === 0) { + currentFunction.argsMemObj = []; + } + else { + AuxVars.setIsDeclared = true; + AuxVars.currentToken = 0; + const sentence = code2sentenceS(currentFunction.arguments, true); + if (sentence.length > 1) { + throw new TypeError(`At line: ${currentFunction.line}. Wrong arguments for function '${currentFunction.name}'.`); + } + const memObj = phrase2memoryObject(sentence[0]); + if (memObj === undefined || memObj.length === 0) { + throw new TypeError(`At line: ${currentFunction.line}. Variable declaration not found on arguments for function '${currentFunction.name}'.`); + } + currentFunction.argsMemObj = memObj; + Program.memory = Program.memory.concat(memObj); + AuxVars.setIsDeclared = false; + } + } + function checkDoublesDefinitions() { + let i, j; + for (i = 0; i < Program.memory.length - 1; i++) { + for (j = i + 1; j < Program.memory.length; j++) { + if (Program.memory[i].asmName === Program.memory[j].asmName) { + throw new TypeError(`Error: Variable '${Program.memory[i].name}' was declared more than one time.`); + } + } + for (j = 0; j < Program.labels.length; j++) { + if (Program.memory[i].asmName === Program.labels[j]) { + throw new TypeError(`Error: It was found a variable and label with same name. Change variable named '${Program.memory[i].name}'`); + } + } + } + for (i = 0; i < Program.functions.length; i++) { + for (j = i + 1; j < Program.functions.length; j++) { + if (Program.functions[i].name === Program.functions[j].name) { + throw new TypeError("Error: Function '" + Program.functions[i].name + "' was declared more than one time."); + } + } + if (Program.Config.APIFunctions === true) { + for (j = 0; j < Program.Global.APIFunctions.length; j++) { + if (Program.functions[i].name === Program.Global.APIFunctions[j].name || + Program.functions[i].name === Program.Global.APIFunctions[j].asmName) { + throw new TypeError("Error: Function '" + Program.functions[i].name + "' has same name of one API Functions."); + } + } + } + } + } + // Fills the correct address of memory objects. + function consolidateMemory() { + let counter = 0; + Program.memory.forEach(function (thisvar) { + if (thisvar.type === 'struct') { // Remeber to change here code yolj1A + thisvar.hexContent = counter.toString(16).padStart(16, '0'); + } + else if (thisvar.type === 'array') { + thisvar.address = counter; + counter++; + thisvar.hexContent = counter.toString(16).padStart(16, '0'); + } + else { + thisvar.address = counter; + counter++; + } + }); + } + function createDefaultTypesTable() { + return [{ + type: 'register', + name: '', + MemoryTemplate: { + address: -1, + name: '', + asmName: '', + type: 'register', + scope: '', + declaration: 'long', + size: 1, + isDeclared: true + } + }, { + type: 'long', + name: '', + MemoryTemplate: { + address: -1, + name: '', + asmName: '', + type: 'long', + scope: '', + declaration: 'long', + size: 1, + isDeclared: false + } + }]; + } + // Returns the API functions object + function createAPItable() { + return [ + { + argsMemObj: [], + asmName: 'get_A1', + declaration: 'long', + name: 'Get_A1' + }, + { + argsMemObj: [], + asmName: 'get_A2', + declaration: 'long', + name: 'Get_A2' + }, + { + argsMemObj: [], + asmName: 'get_A3', + declaration: 'long', + name: 'Get_A3' + }, + { + argsMemObj: [], + asmName: 'get_A4', + declaration: 'long', + name: 'Get_A4' + }, + { + argsMemObj: [], + asmName: 'get_B1', + declaration: 'long', + name: 'Get_B1' + }, + { + argsMemObj: [], + asmName: 'get_B2', + declaration: 'long', + name: 'Get_B2' + }, + { + argsMemObj: [], + asmName: 'get_B3', + declaration: 'long', + name: 'Get_B3' + }, + { + argsMemObj: [], + asmName: 'get_B4', + declaration: 'long', + name: 'Get_B4' + }, + { + argsMemObj: [ + { + address: -1, + name: 'addr', + asmName: 'Set_A1_addr', + type: 'long', + scope: 'Set_A1', + declaration: 'long', + size: 1, + isDeclared: true + } + ], + asmName: 'set_A1', + declaration: 'void', + name: 'Set_A1' + }, + { + argsMemObj: [ + { + address: -1, + name: 'addr', + asmName: 'Set_A2_addr', + type: 'long', + scope: 'Set_A2', + declaration: 'long', + size: 1, + isDeclared: true + } + ], + asmName: 'set_A2', + declaration: 'void', + name: 'Set_A2' + }, + { + argsMemObj: [ + { + address: -1, + name: 'addr', + asmName: 'Set_A3_addr', + type: 'long', + scope: 'Set_A3', + declaration: 'long', + size: 1, + isDeclared: true + } + ], + asmName: 'set_A3', + declaration: 'void', + name: 'Set_A3' + }, + { + argsMemObj: [ + { + address: -1, + name: 'addr', + asmName: 'Set_A4_addr', + type: 'long', + scope: 'Set_A4', + declaration: 'long', + size: 1, + isDeclared: true + } + ], + asmName: 'set_A4', + declaration: 'void', + 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 + } + ], + asmName: 'set_A1_A2', + declaration: 'void', + 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 + } + ], + asmName: 'set_A3_A4', + declaration: 'void', + 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', + name: 'Set_B1' + }, + { + argsMemObj: [ + { + address: -1, + name: 'addr', + asmName: 'Set_B2_addr', + type: 'long', + scope: 'Set_B2', + declaration: 'long', + size: 1, + isDeclared: true + } + ], + asmName: 'set_B2', + declaration: 'void', + name: 'Set_B2' + }, + { + argsMemObj: [ + { + address: -1, + name: 'addr', + asmName: 'Set_B3_addr', + type: 'long', + scope: 'Set_B3', + declaration: 'long', + size: 1, + isDeclared: true + } + ], + asmName: 'set_B3', + declaration: 'void', + name: 'Set_B3' + }, + { + argsMemObj: [ + { + address: -1, + name: 'addr', + asmName: 'Set_B4_addr', + type: 'long', + scope: 'Set_B4', + declaration: 'long', + size: 1, + isDeclared: true + } + ], + asmName: 'set_B4', + declaration: 'void', + 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 + } + ], + asmName: 'set_B1_B2', + declaration: 'void', + 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 + } + ], + asmName: 'set_B3_B4', + declaration: 'void', + name: 'Set_B3_B4' + }, + { + argsMemObj: [], + asmName: 'clear_A', + declaration: 'void', + name: 'Clear_A' + }, + { + argsMemObj: [], + asmName: 'clear_B', + declaration: 'void', + name: 'Clear_B' + }, + { + argsMemObj: [], + asmName: 'clear_A_B', + declaration: 'void', + name: 'Clear_A_And_B' + }, + { + argsMemObj: [], + asmName: 'copy_A_From_B', + declaration: 'void', + name: 'Copy_A_From_B' + }, + { + argsMemObj: [], + asmName: 'copy_B_From_A', + declaration: 'void', + name: 'Copy_B_From_A' + }, + { + argsMemObj: [], + asmName: 'check_A_Is_Zero', + declaration: 'long', + name: 'Check_A_Is_Zero' + }, + { + argsMemObj: [], + asmName: 'check_B_Is_Zero', + declaration: 'long', + name: 'Check_B_Is_Zero' + }, + { + argsMemObj: [], + asmName: 'check_A_equals_B', + declaration: 'long', + name: 'Check_A_Equals_B' + }, + { + argsMemObj: [], + asmName: 'swap_A_and_B', + declaration: 'void', + name: 'Swap_A_and_B' + }, + { + argsMemObj: [], + asmName: 'OR_A_with_B', + declaration: 'void', + name: 'OR_A_with_B' + }, + { + argsMemObj: [], + asmName: 'OR_B_with_A', + declaration: 'void', + name: 'OR_B_with_A' + }, + { + argsMemObj: [], + asmName: 'AND_A_with_B', + declaration: 'void', + name: 'AND_A_with_B' + }, + { + argsMemObj: [], + asmName: 'AND_B_with_A', + declaration: 'void', + name: 'AND_B_with_A' + }, + { + argsMemObj: [], + asmName: 'XOR_A_with_B', + declaration: 'void', + name: 'XOR_A_with_B' + }, + { + argsMemObj: [], + asmName: 'XOR_B_with_A', + declaration: 'void', + name: 'XOR_B_with_A' + }, + { + argsMemObj: [], + asmName: 'add_A_to_B', + declaration: 'void', + name: 'Add_A_To_B' + }, + { + argsMemObj: [], + asmName: 'add_B_to_A', + declaration: 'void', + name: 'Add_B_To_A' + }, + { + argsMemObj: [], + asmName: 'sub_A_from_B', + declaration: 'void', + name: 'Sub_A_From_B' + }, + { + argsMemObj: [], + asmName: 'sub_B_from_A', + declaration: 'void', + name: 'Sub_B_From_A' + }, + { + argsMemObj: [], + asmName: 'mul_A_by_B', + declaration: 'void', + name: 'Mul_A_By_B' + }, + { + argsMemObj: [], + asmName: 'mul_B_by_A', + declaration: 'void', + name: 'Mul_B_By_A' + }, + { + argsMemObj: [], + asmName: 'div_A_by_B', + declaration: 'void', + name: 'Div_A_By_B' + }, + { + argsMemObj: [], + asmName: 'div_B_by_A', + declaration: 'void', + name: 'Div_B_By_A' + }, + { + argsMemObj: [], + asmName: 'MD5_A_to_B', + declaration: 'void', + name: 'MD5_A_To_B' + }, + { + argsMemObj: [], + asmName: 'check_MD5_A_with_B', + declaration: 'long', + name: 'Check_MD5_A_With_B' + }, + { + argsMemObj: [], + asmName: 'HASH160_A_to_B', + declaration: 'void', + name: 'HASH160_A_To_B' + }, + { + argsMemObj: [], + asmName: 'check_HASH160_A_with_B', + declaration: 'long', + name: 'Check_HASH160_A_With_B' + }, + { + argsMemObj: [], + asmName: 'SHA256_A_to_B', + declaration: 'void', + name: 'SHA256_A_To_B' + }, + { + argsMemObj: [], + asmName: 'check_SHA256_A_with_B', + declaration: 'long', + name: 'Check_SHA256_A_With_B' + }, + { + argsMemObj: [], + asmName: 'get_Block_Timestamp', + declaration: 'long', + name: 'Get_Block_Timestamp' + }, + { + argsMemObj: [], + asmName: 'get_Creation_Timestamp', + declaration: 'long', + name: 'Get_Creation_Timestamp' + }, + { + argsMemObj: [], + asmName: 'get_Last_Block_Timestamp', + declaration: 'long', + name: 'Get_Last_Block_Timestamp' + }, + { + argsMemObj: [], + asmName: 'put_Last_Block_Hash_In_A', + declaration: 'void', + 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 + } + ], + asmName: 'A_to_Tx_after_Timestamp', + declaration: 'void', + name: 'A_To_Tx_After_Timestamp' + }, + { + argsMemObj: [], + asmName: 'get_Type_for_Tx_in_A', + declaration: 'long', + name: 'Get_Type_For_Tx_In_A' + }, + { + argsMemObj: [], + asmName: 'get_Amount_for_Tx_in_A', + declaration: 'long', + name: 'Get_Amount_For_Tx_In_A' + }, + { + argsMemObj: [], + asmName: 'get_Timestamp_for_Tx_in_A', + declaration: 'long', + name: 'Get_Timestamp_For_Tx_In_A' + }, + { + argsMemObj: [], + asmName: 'get_Ticket_Id_for_Tx_in_A', + declaration: 'long', + name: 'Get_Random_Id_For_Tx_In_A' + }, + { + argsMemObj: [], + asmName: 'message_from_Tx_in_A_to_B', + declaration: 'void', + name: 'Message_From_Tx_In_A_To_B' + }, + { + argsMemObj: [], + asmName: 'B_to_Address_of_Tx_in_A', + declaration: 'void', + name: 'B_To_Address_Of_Tx_In_A' + }, + { + argsMemObj: [], + asmName: 'B_to_Address_of_Creator', + declaration: 'void', + name: 'B_To_Address_Of_Creator' + }, + { + argsMemObj: [], + asmName: 'get_Current_Balance', + declaration: 'long', + name: 'Get_Current_Balance' + }, + { + argsMemObj: [], + asmName: 'get_Previous_Balance', + declaration: 'long', + 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 + } + ], + asmName: 'send_to_Address_in_B', + declaration: 'void', + name: 'Send_To_Address_In_B' + }, + { + argsMemObj: [], + asmName: 'send_All_to_Address_in_B', + declaration: 'void', + name: 'Send_All_To_Address_In_B' + }, + { + argsMemObj: [], + asmName: 'send_Old_to_Address_in_B', + declaration: 'void', + name: 'Send_Old_To_Address_In_B' + }, + { + argsMemObj: [], + asmName: 'send_A_to_Address_in_B', + declaration: 'void', + 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 + } + ], + asmName: 'add_Minutes_to_Timestamp', + declaration: 'long', + name: 'Add_Minutes_To_Timestamp' + } + ]; + } + return shapeMain(); +} diff --git a/v0.3/out/syntaxProcessor.js b/v0.3/out/syntaxProcessor.js new file mode 100644 index 0000000..94b1ce6 --- /dev/null +++ b/v0.3/out/syntaxProcessor.js @@ -0,0 +1,265 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/** + * Traverse Program transforming some sentences properties from arrays of + * tokens into an actually abstract syntax tree. Check operators + * precedence and let operations in correct order for assembler. + * This is parser third and final pass. + * @param Program to be processed + * @returns Program processed + * @throws {TypeError|SyntaxError} on any mistake. + */ +// eslint-disable-next-line no-unused-vars +function syntaxProcess(Program) { + /** + * Traverse an array of tokens to create a real AST based on + * simple operations. Only unary or binary operations permitted here + * and the tree will always end in a END NODE. + * Uses precedence values to decide the operations order. + */ + function createSyntacticTree(tokenArray) { + if (tokenArray === undefined) { + throw new SyntaxError('Undefined AST to create syntactic tree'); + } + if (tokenArray.length === 0) { + return { type: 'nullASN' }; + } + // precedente evaluation loop + let currentIdx = 0; + let end = false; + for (let precedenceHeight = 12; precedenceHeight > 0 && end === false; precedenceHeight--) { + if (precedenceHeight === 12 || precedenceHeight === 10 || precedenceHeight === 2) { + // Right to left associativity for + // 12) Terminator, semi, keywords + // 10) Assignment operators + // 2) Unary operators + for (currentIdx = 0; currentIdx < tokenArray.length; currentIdx++) { + if (tokenArray[currentIdx].precedence === precedenceHeight) { + end = true; + break; + } + } + } + else { + // Left to right associativity for others + for (currentIdx = tokenArray.length - 1; currentIdx >= 0; currentIdx--) { + if (tokenArray[currentIdx].precedence === precedenceHeight) { + end = true; + break; + } + } + } + } + if (end === false) { + // he have only precedente == 0: variable, constant, array, member, function) + if (tokenArray[0].type === 'Variable' && tokenArray.length === 1) { + return { type: 'endASN', Token: tokenArray[0] }; + } + if (tokenArray[0].type === 'Variable') { + // We have a combination for structs and/or arrays. + currentIdx = 1; + if (tokenArray[1].type === 'Function') { + // Stores function name at extValue + tokenArray[1].extValue = tokenArray[0].value; + currentIdx++; + } + const retNode = { + type: 'lookupASN', + Token: tokenArray[currentIdx - 1], + modifiers: [] + }; + if (retNode.Token.type === 'Function') { + retNode.FunctionArgs = createSyntacticTree(retNode.Token.params); + delete retNode.Token.params; + } + for (; currentIdx < tokenArray.length; currentIdx++) { + if (tokenArray[currentIdx].type === 'Arr') { + retNode.modifiers.push({ + type: 'Array', + Center: createSyntacticTree(tokenArray[currentIdx].params) + }); + } + else if (tokenArray[currentIdx].type === 'Member') { + if (tokenArray[currentIdx].value === '.') { + retNode.modifiers.push({ + type: 'MemberByVal', + Center: tokenArray[currentIdx + 1] + }); + } + else { + retNode.modifiers.push({ + type: 'MemberByRef', + Center: tokenArray[currentIdx + 1] + }); + } + } + else if (tokenArray[currentIdx].type !== 'Variable') { + // Variable type is the modifier allowed. Any other thing is error. + throw new TypeError(`At line: ${tokenArray[currentIdx].line}. Invalid type of variable modifier: '${tokenArray[currentIdx].type}'.`); + } + } + return retNode; + } + if (tokenArray[0].type === 'Constant') { + if (tokenArray.length === 1) { + return { type: 'endASN', Token: tokenArray[0] }; + } + } + throw new SyntaxError(`At line: ${tokenArray[0].line}. Unknown token sequence: '${tokenArray[0].type}' with value: '${tokenArray[0].value}'.`); + // Here we start to process operations tokens (precedente >= 1) + } + else if (tokenArray[currentIdx].type === 'Operator' || + tokenArray[currentIdx].type === 'Assignment' || + tokenArray[currentIdx].type === 'SetOperator' || + tokenArray[currentIdx].type === 'Comparision' || + tokenArray[currentIdx].type === 'Delimiter') { + if (currentIdx === 0) { + throw new SyntaxError(`At line: ${tokenArray[0].line}. Missing left value for binary operator '${tokenArray[currentIdx].value}'.`); + } + if (currentIdx === tokenArray.length - 1) { + throw new SyntaxError(`At line: ${tokenArray[0].line}. Missing right value for binary operator '${tokenArray[currentIdx].value}'.`); + } + return { + type: 'binaryASN', + Left: createSyntacticTree(tokenArray.slice(0, currentIdx)), + Operation: tokenArray[currentIdx], + Right: createSyntacticTree(tokenArray.slice(currentIdx + 1)) + }; + } + else if (tokenArray[currentIdx].type === 'CodeDomain' || tokenArray[currentIdx].type === 'CodeCave') { + const newAST = createSyntacticTree(tokenArray[currentIdx].params); + delete tokenArray[currentIdx].params; + if (tokenArray.length !== 1) { + throw new SyntaxError(`At line: ${tokenArray[currentIdx].line}. Modifiers not implemented on '${tokenArray[currentIdx].type}'.`); + } + return { + type: 'unaryASN', + Operation: tokenArray[currentIdx], + Center: newAST + }; + } + else if (tokenArray[currentIdx].type === 'Keyword') { + if (tokenArray.length === 1) { + if (tokenArray[0].value === 'sleep' || tokenArray[0].value === 'goto' || tokenArray[0].value === 'const') { + throw new TypeError(`At line: ${tokenArray[0].line}. Missing arguments for keyword '${tokenArray[0].value}'.`); + } + return { type: 'endASN', Token: tokenArray[0] }; + } + else { + if (currentIdx !== 0) { + throw new SyntaxError(`At line: ${tokenArray[currentIdx].line}. Probable missing ';' before keyword ${tokenArray[currentIdx].value}.`); + } + if (tokenArray[0].value === 'exit' || tokenArray[0].value === 'halt' || tokenArray[0].value === 'break' || tokenArray[0].value === 'continue') { + throw new TypeError(`At line: ${tokenArray[0].line}. Keyword '${tokenArray[0].value}' does not accept arguments.`); + } + return { + type: 'unaryASN', + Operation: tokenArray[0], + Center: createSyntacticTree(tokenArray.slice(1)) + }; + } + } + else if (tokenArray[currentIdx].type === 'UnaryOperator' && currentIdx === 0) { + if (tokenArray.length === 1) { + throw new SyntaxError(`At line: ${tokenArray[currentIdx].line}. Missing value to apply unary operator '${tokenArray[currentIdx].value}'.`); + } + if (tokenArray[currentIdx].value === '*' && tokenArray.length > currentIdx) { + if (tokenArray[currentIdx + 1].type !== 'Variable' && tokenArray[currentIdx + 1].type !== 'CodeCave' && tokenArray[currentIdx + 1].type !== 'SetUnaryOperator') { + throw new SyntaxError(`At line: ${tokenArray[currentIdx + 1].line}. Invalid lvalue for pointer operation. Can not have type '${tokenArray[currentIdx + 1].type}'.`); + } + } + return { + type: 'unaryASN', + Center: createSyntacticTree(tokenArray.slice(currentIdx + 1)), + Operation: tokenArray[currentIdx] + }; + } + else if (tokenArray[0].type === 'SetUnaryOperator') { + if (tokenArray.length === 1) { + throw new SyntaxError(`At line: ${tokenArray[0].line}. Missing value to apply 'SetUnaryOperator' '${tokenArray[0].value}'.`); + } + if (tokenArray[1].type !== 'Variable') { + throw new SyntaxError(`At line: ${tokenArray[0].line}. 'SetUnaryOperator' '${tokenArray[0].value}' expecting a variable, got a '${tokenArray[1].type}'.`); + } + for (let j = 1; j < tokenArray.length; j++) { + if (tokenArray[j].type === 'Variable' || tokenArray[j].type === 'Member') { + continue; + } + throw new SyntaxError('At line: ' + tokenArray[0].line + ". Can not use 'SetUnaryOperator' with types '" + tokenArray[j].type + "'."); + } + return { + type: 'exceptionASN', + Left: createSyntacticTree(tokenArray.slice(1)), + Operation: tokenArray[0] + }; + } + else if (tokenArray.length > 1 && tokenArray[tokenArray.length - 1].type === 'SetUnaryOperator') { + // Process exceptions for post increment and post decrement (left-to-right associativity) + if (tokenArray[0].type !== 'Variable') { + throw new SyntaxError(`At line: ${tokenArray[0].line}. 'SetUnaryOperator' '${tokenArray[tokenArray.length - 1].value}' expecting a variable, got a '${tokenArray[0].type}'.`); + } + for (let j = 1; j < tokenArray.length - 1; j++) { + if (tokenArray[j].type === 'Variable' || tokenArray[j].type === 'Member') { + continue; + } + throw new SyntaxError('At line: ' + tokenArray[0].line + ". Can not use 'SetUnaryOperator' with types '" + tokenArray[j].type + "'."); + } + return { + type: 'exceptionASN', + Right: createSyntacticTree(tokenArray.slice(0, tokenArray.length - 1)), + Operation: tokenArray[tokenArray.length - 1] + }; + } + throw new SyntaxError('At line: ' + tokenArray[0].line + ". Token '" + tokenArray[0].type + "' with value '" + tokenArray[0].value + "' does not match any syntax rules."); + } + // Process recursively one Sentence object, creating an CodeAST, that was + // processed sintacticly. + function processSentence(SentenceObj) { + switch (SentenceObj.type) { + case 'phrase': + if (SentenceObj.code === undefined) { + throw new TypeError('Unknow object arrived at processSentence'); + } + SentenceObj.CodeAST = createSyntacticTree(SentenceObj.code); + delete SentenceObj.code; + break; + case 'ifElse': + SentenceObj.falseBlock.forEach(processSentence); + // eslint-disable-next-line no-fallthrough + case 'ifEndif': + case 'while': + case 'do': + if (SentenceObj.condition === undefined) { + throw new TypeError(`At line ${SentenceObj.line}.Unknow object arrived at processSentence`); + } + if (SentenceObj.condition.length === 0) { + throw new SyntaxError(`At line ${SentenceObj.line}. Sentence condition can not be empty`); + } + SentenceObj.ConditionAST = createSyntacticTree(SentenceObj.condition); + delete SentenceObj.condition; + SentenceObj.trueBlock.forEach(processSentence); + break; + case 'for': + SentenceObj.threeSentences.forEach(processSentence); + SentenceObj.trueBlock.forEach(processSentence); + break; + case 'struct': + processSentence(SentenceObj.Phrase); + break; + } + } + /* * * Main function! * * */ + if (Program === undefined || Program.Global.sentences === undefined) { + throw new TypeError('Undefined AST arrived at syntax()'); + } + Program.Global.sentences.forEach(processSentence); + Program.functions.forEach(CurrentFunction => { + if (CurrentFunction.sentences === undefined) { + throw new TypeError('Undefined AST arrived at syntax()'); + } + CurrentFunction.sentences.forEach(processSentence); + }); + return Program; +} diff --git a/v0.3/out/testcases.js b/v0.3/out/testcases.js new file mode 100644 index 0000000..7b3238a --- /dev/null +++ b/v0.3/out/testcases.js @@ -0,0 +1,1272 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/* global generate syntaxProcess shape parse tokenize preprocess bytecode */ +// eslint-disable-next-line no-unused-vars +function runTestCases() { + function encodedStr(rawStr) { + return rawStr.replace(/[\u00A0-\u9999<>&]/g, function (i) { + return '&#' + i.charCodeAt(0) + ';'; + }); + } + const tests = [ + ['Preprocessor error tests', null, 'div'], + ['#ifdef debug\nlong a; a++;', true, ''], + ['#else\nlong a; a++;', true, ''], + ['#endif\nlong a; a++;', true, ''], + ['#ifdef debug\n#pragma maxAuxVars 1\n#else\n#pragma maxAuxVars 5\n#else\n#pragma maxAuxVars 7\n#endif\nlong a; a++;', true, ''], + ['Tokenizer error tests', null, 'div'], + ['long a;/*asdf', true, ''], + ['long a="asdf;', true, ''], + ['long a=\'asdf;', true, ''], + ['long a; asm PSH $A;', true, ''], + ['long a; asm { PSH $A;', true, ''], + ['long a; struct { long b; }', true, ''], + ['long a; ²;', true, ''], + ['Something very wrong', true, ''], + ['Parser error tests', null, 'div'], + ['long a; };', true, ''], + ['long a[ ;', true, ''], + ['void main ((void) { long a++; }', true, ''], + ['void main (void) { long a++;', true, ''], + ['long a = "TS-MPMZ-8CD9-HZMD-A7R1X";', true, ''], + ['long a = "TS-MPMZ-8CD9-HZMD-A9R2X";', true, ''], + ['Shaper error tests', null, 'div'], + ['long a, b; test2() while (a) a++; long test2(void) { b++; return b; }', true, ''], + ['long a, b; test2() for (;;) a++; long test2(void) { b++; return b; }', true, ''], + ['long a, b; goto a; a++; a: b++;', true, ''], + ['long a, b; else a++; a: b++;', true, ''], + ['long a, b; do { a++; };', true, ''], + ['long a, b; do { a++; } long long;', true, ''], + ['long a, b; do { a++; } long (a);', true, ''], + ['long a, b; do { a++; } while (a) b++;', true, ''], + ['long a, b; break; b++;', true, ''], + ['long a, b; continue; b++;', true, ''], + ['long a, b; test(); void test() { c++; }', true, ''], + ['long a, b; test(b, a); void test(d++) { long c; c++; }', true, ''], + ['long a, b; test(b, a); void test(d) { long c; c++; }', true, ''], + ['long a, test_b; test(a); void test(long b) { long c; c++; }', true, ''], + ['syntaxProcessor error tests', null, 'div'], + ['long a; sleep; a++;', true, ''], + ['long a; const ;', true, ''], + ['long a; goto; a++;', true, ''], + ['long a, b; halt 1; a++;', true, ''], + ['long a, b; break 1; a++;', true, ''], + ['long a, b; continue a; a++;', true, ''], + ['long a, b; 4++; a++;', true, ''], + ['long a, b; ++4; a++;', true, ''], + ['long a, b; test()++; a++;', true, ''], + ['Generator error tests', null, 'div'], + ['long a, b; for a++;', true, ''], + ['long a; goto a; a++;', true, ''], + ['long a; return;', true, ''], + ['long b, a = 0; test();', true, ''], + ['#include APIFunctions\nlong b, a = 0; test();', true, ''], + ['long a = test()[4]; void test(void) {}', true, ''], + ['struct KOMBI { long driver, collector, passenger; } car; long carro; carro.driver=0;', true, ''], + ['struct KOMBI { long driver, collector, passenger; } car; car.nobody=0;', true, ''], + ['struct KOMBI { long driver, collector, passenger; } car; car->driver=0;', true, ''], + ['struct KOMBI { long driver, collector, passenger; } *car; car.driver=0;', true, ''], + ['long a, b; if (a, b) { a++;}', true, ''], + ['long a, b; a = b + test(); void test(void) { a++; }', true, ''], + ['struct KOMBI { long driver, collector[4]; } car; long *b; car.collector=b;', true, ''], + // void test + ['Arithmetic tests', null, 'div'], + ['Void test;', null, 'div'], + ['', false, '^declare r0\n^declare r1\n^declare r2\n\nFIN\n'], + [';', false, '^declare r0\n^declare r1\n^declare r2\n\nFIN\n'], + [';;;', false, '^declare r0\n^declare r1\n^declare r2\n\nFIN\n'], + // Assignment + ['Assignment;', null, 'div'], + ['long a, b; a=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $b\nFIN\n'], + ['long a; a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFIN\n'], + ['long a; a=;', true, ''], + ['long a; =a;', true, ''], + // SetOperator + ['SetOperator;', null, 'div'], + ['long a, b; a+=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nADD @a $b\nFIN\n'], + ['long b; +=b;', true, ''], + ['long a; a+=;', true, ''], + // Constant + ['Constant;', null, 'div'], + ['long a; a=2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #0000000000000002\nFIN\n'], + ['2;', false, '^declare r0\n^declare r1\n^declare r2\n\nFIN\n'], + ['long a; a=0xA;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #000000000000000a\nFIN\n'], + ['long a; a=0;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nCLR @a\nFIN\n'], + ['long a; a+=2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nINC @a\nFIN\n'], + ['long a; a+=0xfffffff;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #000000000fffffff\nADD @a $r0\nFIN\n'], + ["long a; a='BURST-MKCL-2226-W6AH-7ARVS';", false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #5c6ee8000049c552\nFIN\n'], + ["long a; a='TS-MKCL-2226-W6AH-7ARVS';", false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #5c6ee8000049c552\nFIN\n'], + ["long a; a='S-MKCL-2226-W6AH-7ARVS';", false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #5c6ee8000049c552\nFIN\n'], + ["long a; a='';", false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nCLR @a\nFIN\n'], + ['long a; a=6660515985630020946;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #5c6ee8000049c552\nFIN\n'], + ['long a; a=18446744073709551615;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #ffffffffffffffff\nFIN\n'], + ['long a; a=18446744073709551616;', true, ''], + ['long a; a=18446744073709551617;', true, ''], + // allow '_' in decimal and hexadecimal numbers + ['long a, b, c, d; a=5_0000_0000; b=5_0000_0000; c=0x00ff_00fe_7fff; d=0x00ff00fe7fff;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a #000000001dcd6500\nSET @b #000000001dcd6500\nSET @c #000000ff00fe7fff\nSET @d #000000ff00fe7fff\nFIN\n'], + ['long a; a="Hi there";', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #6572656874206948\nFIN\n'], + ['long a; a="Hi there big";', true, ''], + ['long a; 2=a;', true, ''], + // Operator + ['Operator;', null, 'div'], + ['long a, b, c; a=b/c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nDIV @a $c\nFIN\n'], + ['long a, b, c; a=b%c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nMOD @a $c\nFIN\n'], + ['long a, b, c; a=b<>c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nSHR @a $c\nFIN\n'], + ['long a, b, c; a=b|c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nBOR @a $c\nFIN\n'], + ['long a, b, c; a=b^c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nXOR @a $c\nFIN\n'], + // UnaryOperator + ['UnaryOperator;', null, 'div'], + ['long a, b; a=!b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBNZ $b :__NOT_1_sF\nSET @a #0000000000000001\nJMP :__NOT_1_end\n__NOT_1_sF:\nCLR @a\n__NOT_1_end:\nFIN\n'], + ['long a, b; a=~b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $b\nNOT @a\nFIN\n'], + ['long a, b; a^=~b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @r0 $b\nNOT @r0\nXOR @a $r0\nFIN\n'], + ['long a, b; a=~0xff;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a #00000000000000ff\nNOT @a\nFIN\n'], + ['long a, b, c; a>>=b^~c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $c\nNOT @r0\nXOR @r0 $b\nSHR @a $r0\nFIN\n'], + ['long a, b; a=~~b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $b\nNOT @a\nNOT @a\nFIN\n'], + ['long a, b, c; a=~b/c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nNOT @a\nDIV @a $c\nFIN\n'], + ['long a, b, c; a=~b/~c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nNOT @a\nSET @r0 $c\nNOT @r0\nDIV @a $r0\nFIN\n'], + ['long a, b, c; a=b/~c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $c\nNOT @r0\nSET @a $b\nDIV @a $r0\nFIN\n'], + ['long a, b; ~a=b;', true, ''], + // SetUnaryOperator + ['SetUnaryOperator;', null, 'div'], + ['long a; a++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n'], + ['long a; a--;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nDEC @a\nFIN\n'], + ['long a; ++a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n'], + ['long a; --a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nDEC @a\nFIN\n'], + ['long a, b, c; a=b++/c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nDIV @a $c\nINC @b\nFIN\n'], + ['long a, b, c; a=--b/c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nDEC @b\nSET @a $b\nDIV @a $c\nFIN\n'], + ['long a, b, c; a=~--b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nDEC @b\nSET @a $b\nNOT @a\nFIN\n'], + ['long a, b, c; a+=~b++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $b\nNOT @r0\nADD @a $r0\nINC @b\nFIN\n'], + ['long a, b, c; a=~b++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nNOT @a\nINC @b\nFIN\n'], + ['long a; a++=2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #0000000000000002\nINC @a\nFIN\n'], + ['long a; a=2++;', true, ''], + ['--;', true, ''], + ['2++;', true, ''], + ['long a, b, c; a=b- -c;', true, ''], + // CheckOperator Unary + ['CheckOperator Unary;', null, 'div'], + ['long a, b; a=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $b\nFIN\n'], + ['long a, b; a+=-b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @r0\nSUB @r0 $b\nADD @a $r0\nFIN\n'], + ['long a, b; a=-b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @a\nSUB @a $b\nFIN\n'], + ['long a, b, c; a=b/-c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @r0\nSUB @r0 $c\nSET @a $b\nDIV @a $r0\nFIN\n'], + ['long a, b, c; a=-b/c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @a\nSUB @a $b\nDIV @a $c\nFIN\n'], + ['long a, b; a=-2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @a\nSET @r0 #0000000000000002\nSUB @a $r0\nFIN\n'], + ['long a, b; a=-~b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @r0 $b\nNOT @r0\nCLR @a\nSUB @a $r0\nFIN\n'], + ['long a, b; a=~-b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @a\nSUB @a $b\nNOT @a\nFIN\n'], + ['long a, b; a=-b-- ;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @a\nSUB @a $b\nDEC @b\nFIN\n'], + ['long a, b; a=---b;', true, ''], + ['long a, b; a=+b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $b\nFIN\n'], + ['long a, b, c; a=b/+c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nDIV @a $c\nFIN\n'], + ['long a, b, c; a=+b/-c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @r0\nSUB @r0 $c\nSET @a $b\nDIV @a $r0\nFIN\n'], + ['long a, b, c; a=+b/+c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nDIV @a $c\nFIN\n'], + ['long a; a=+2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #0000000000000002\nFIN\n'], + ['long a; a-=+~2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #0000000000000002\nNOT @r0\nSUB @a $r0\nFIN\n'], + ['long a; a=~+2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #0000000000000002\nNOT @a\nFIN\n'], + ['long a; a=+;', true, ''], + ['long *a, b; a=&b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a #0000000000000004\nFIN\n'], + ['long a, *b; a=*b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $($b)\nFIN\n'], + ['long *a, b; *a=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @($a) $b\nFIN\n'], + ['long a, *b; a=*b/5;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $($b)\nSET @r0 #0000000000000005\nDIV @a $r0\nFIN\n'], + ['long a, *b; a=5/ *b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a #0000000000000005\nSET @r0 $($b)\nDIV @a $r0\nFIN\n'], + ['long a, *b; a*=*b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @r0 $($b)\nMUL @a $r0\nFIN\n'], + ['long a, *b, *c; a=*b<<*c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $($b)\nSET @r0 $($c)\nSHL @a $r0\nFIN\n'], + ['long a, *b; a=~*b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $($b)\nNOT @a\nFIN\n'], + ['long a, *b; a=-*b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @a\nSET @r0 $($b)\nSUB @a $r0\nFIN\n'], + ['long a, *b; a=*--b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nDEC @b\nSET @a $($b)\nFIN\n'], + ['long a, *b; a=*b--;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $($b)\nDEC @b\nFIN\n'], + ['long a, b; a=*~b;', true, ''], + ['long a, b; a=++*b;', true, ''], + ['long a, b; a=**b;', true, ''], + // CheckOperator Binary + ['CheckOperator Binary;', null, 'div'], + ['long a, b, c; a=b+c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nADD @a $c\nFIN\n'], + ['long a, b, c; a=b-c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nSUB @a $c\nFIN\n'], + ['long a, b, c; a=b*c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nMUL @a $c\nFIN\n'], + ['long a, b, c; a=b&c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nAND @a $c\nFIN\n'], + ['long a, b, c; a-=b+c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $b\nADD @r0 $c\nSUB @a $r0\nFIN\n'], + ['long a, b, c; a=b-2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nSET @r0 #0000000000000002\nSUB @a $r0\nFIN\n'], + ['long a, b; a="0"+b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a #0000000000000030\nADD @a $b\nFIN\n'], + ['long a, b; a=2*b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a #0000000000000002\nMUL @a $b\nFIN\n'], + ['long a, b, c; a<<=b*-c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @r0\nSUB @r0 $c\nMUL @r0 $b\nSHL @a $r0\nFIN\n'], + ['long a, b, c; a^=~b&c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $b\nNOT @r0\nAND @r0 $c\nXOR @a $r0\nFIN\n'], + ['long a, b, c; a^=b&~c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $c\nNOT @r0\nAND @r0 $b\nXOR @a $r0\nFIN\n'], + ['long a, b, c; a^=-b&-c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @r0\nSUB @r0 $b\nCLR @r1\nSUB @r1 $c\nAND @r0 $r1\nXOR @a $r0\nFIN\n'], + ['long a, b, c; a=b&~0xff;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a #00000000000000ff\nNOT @a\nAND @a $b\nFIN\n'], + ['long a, b, c; a=~0x7fb&~0xff;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a #00000000000007fb\nNOT @a\nSET @r0 #00000000000000ff\nNOT @r0\nAND @a $r0\nFIN\n'], + ['long a, b, c; a>>=b-~c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $c\nNOT @r0\nSET @r1 $b\nSUB @r1 $r0\nSHR @a $r1\nFIN\n'], + ['long a, b, c; a=b++-c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nSUB @a $c\nINC @b\nFIN\n'], + ['long a, b, c; a=--b&c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nDEC @b\nSET @a $b\nAND @a $c\nFIN\n'], + ['long a, b, c; a+=-b+c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @r0\nSUB @r0 $b\nADD @r0 $c\nADD @a $r0\nFIN\n'], + ['long a, b, *c; a=-b**c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @a\nSUB @a $b\nSET @r0 $($c)\nMUL @a $r0\nFIN\n'], + ['long a, b, c; a=b*-2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @a\nSET @r0 #0000000000000002\nSUB @a $r0\nMUL @a $b\nFIN\n'], + ['long a, b, c; a=-2&~b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @a\nSET @r0 #0000000000000002\nSUB @a $r0\nSET @r0 $b\nNOT @r0\nAND @a $r0\nFIN\n'], + ['long a, b, c; a=b&~-c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @a\nSUB @a $c\nNOT @a\nAND @a $b\nFIN\n'], + ['long a, b, c; a=~-b&c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @a\nSUB @a $b\nNOT @a\nAND @a $c\nFIN\n'], + ['long a, b, c; a=b*-c--;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @a\nSUB @a $c\nMUL @a $b\nDEC @c\nFIN\n'], + ['long a, b, c; a=-b--*c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @a\nSUB @a $b\nMUL @a $c\nDEC @b\nFIN\n'], + ['long a, b, c; a/=b*-c--;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nCLR @r0\nSUB @r0 $c\nMUL @r0 $b\nDIV @a $r0\nDEC @c\nFIN\n'], + ['long a, b, c; a+b=c;', true, ''], + ['long a, b, c; a-b=c;', true, ''], + ['long a, b, c; a*b=c;', true, ''], + ['long a, b, c; a&b=c;', true, ''], + ['long a, b, c; a=b*/c;', true, ''], + // Delimiter + ['Delimiter;', null, 'div'], + ['long a, b, c, d; a=b,c=d;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nSET @c $d\nFIN\n'], + ['long a, b; a,b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nFIN\n'], + ['long a, b, c, d, e, f; a=b,c=d,e=f;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n^declare f\n\nSET @a $b\nSET @c $d\nSET @e $f\nFIN\n'], + ['long a, b, c, d, e, f; a=b++,c=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n^declare f\n\nSET @a $b\nINC @b\nSET @c $b\nFIN\n'], + ['long a, b, c, d, e, f; a=b++,c=b++,d=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n^declare f\n\nSET @a $b\nINC @b\nSET @c $b\nINC @b\nSET @d $b\nFIN\n'], + ['long a; a+=1/2,a=2/2,a+=3/2,a=4/2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #0000000000000001\nINC @a\nSET @a #0000000000000002\nFIN\n'], + [',;', true, ''], + [',,,,;', true, ''], + ['long a, b, c, d; a=b,,c=d;', true, ''], + ['long a, b; a=,b;', true, ''], + // CodeCave + ['CodeCave;', null, 'div'], + ['long a, b; a=(b);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a $b\nFIN\n'], + ['long a, b; a*=(b);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nMUL @a $b\nFIN\n'], + ['long a; a=(2);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #0000000000000002\nFIN\n'], + ['long a, *b, c, d; a=*(b);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $($b)\nFIN\n'], + ['long a, *b, c, d; a=*(b+c);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nADD @a $c\nSET @a $($a)\nFIN\n'], + ['long a, b, *c, d; a=*(b+c);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nADD @a $c\nSET @a $($a)\nFIN\n'], + ['long a, b, *c, d; a=*(5+c);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a #0000000000000005\nADD @a $c\nSET @a $($a)\nFIN\n'], + ['long *a, b, c, d; *(a+1)=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @r0 $a\nINC @r0\nSET @($r0) $b\nFIN\n'], + ['long a, b, c, d; a=(b*c)*d;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nMUL @a $c\nMUL @a $d\nFIN\n'], + ['long a, b, c, d; a=(b/c)/d;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nDIV @a $c\nDIV @a $d\nFIN\n'], + ['long a, b, c, d; a=~(0xFF<<8);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a #00000000000000ff\nSET @r0 #0000000000000008\nSHL @a $r0\nNOT @a\nFIN\n'], + ['long a, b, c, d; a=~(b/c)/d;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nDIV @a $c\nNOT @a\nDIV @a $d\nFIN\n'], + ['long a, b, c, d; a=(b/c)/~d;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nDIV @a $c\nSET @r0 $d\nNOT @r0\nDIV @a $r0\nFIN\n'], + ['long a, b, c, d; a=~(b/c/d);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nDIV @a $c\nDIV @a $d\nNOT @a\nFIN\n'], + ['long a, b, c, d, e; a=(b+c)*(d+e);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n\nSET @a $b\nADD @a $c\nSET @r0 $d\nADD @r0 $e\nMUL @a $r0\nFIN\n'], + ['long a, b, c, d, e; a=(b+c)/(d+e);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n\nSET @a $b\nADD @a $c\nSET @r0 $d\nADD @r0 $e\nDIV @a $r0\nFIN\n'], + ['long a, b, c, d, e; a%=1-((b+c)*(d+e));', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n\nSET @r0 $b\nADD @r0 $c\nSET @r1 $d\nADD @r1 $e\nMUL @r0 $r1\nSET @r1 #0000000000000001\nSUB @r1 $r0\nMOD @a $r1\nFIN\n'], + ['long a, b, c, d; a=--(b);', true, ''], + ['long a, b, c, d; a=(b+c)++;', true, ''], + ['long a, b, c, d; a=(b)[c];', true, ''], + ['long a, b, c, d; *(a+1)=b;', true, ''], + ['long a, b, c, d; *(a+c)=b;', true, ''], + ['long a, b, c, d; a=*(b+1);', true, ''], + ['long a, b, c, d; a=*(b+c);', true, ''], + // Arithmetic + comparisions + ['Arithmetic + comparisions;', null, 'div'], + ['long a, b, z; z=a==b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nBNE $a $b :__CMP_1_sF\nSET @z #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @z\n__CMP_1_end:\nFIN\n'], + ['long a, b, z; z=a!=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nBEQ $a $b :__CMP_1_sF\nSET @z #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @z\n__CMP_1_end:\nFIN\n'], + ['long a, b, z; z=2<=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nSET @z #0000000000000002\nBGT $z $b :__CMP_1_sF\nSET @z #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @z\n__CMP_1_end:\nFIN\n'], + ['long a, b, z; z=a<2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nSET @z #0000000000000002\nBGE $a $z :__CMP_1_sF\nSET @z #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @z\n__CMP_1_end:\nFIN\n'], + ['long a, b, z; z=a>=b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nBLT $a $b :__CMP_1_sF\nSET @z #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @z\n__CMP_1_end:\nFIN\n'], + ['long a, b, z; z=a>b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nBLE $a $b :__CMP_1_sF\nSET @z #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @z\n__CMP_1_end:\nFIN\n'], + ['long a, b, z; z=!a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nBNZ $a :__NOT_1_sF\nSET @z #0000000000000001\nJMP :__NOT_1_end\n__NOT_1_sF:\nCLR @z\n__NOT_1_end:\nFIN\n'], + ['long a, b, z; z=!!a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nBZR $a :__NOT_1_sF\nSET @z #0000000000000001\nJMP :__NOT_1_end\n__NOT_1_sF:\nCLR @z\n__NOT_1_end:\nFIN\n'], + ['long a, b, z; z=a&&b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nBZR $a :__CMP_1_sF\nBZR $b :__CMP_1_sF\nSET @z #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @z\n__CMP_1_end:\nFIN\n'], + ['long a, b, z; z=a||b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare z\n\nBNZ $a :__CMP_1_sT\nBNZ $b :__CMP_1_sT\nCLR @z\nJMP :__CMP_1_end\n__CMP_1_sT:\nSET @z #0000000000000001\n__CMP_1_end:\nFIN\n'], + ['long a, b, c; a=2+b==c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nINC @a\nINC @a\nBNE $a $c :__CMP_1_sF\nSET @a #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @a\n__CMP_1_end:\nFIN\n'], + ['long a, b, c; a=2+(b==c);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nBNE $b $c :__CMP_1_sF\nSET @a #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @a\n__CMP_1_end:\nINC @a\nINC @a\nFIN\n'], + ['long a, b, c; a=b==~c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $c\nNOT @a\nBNE $b $a :__CMP_1_sF\nSET @a #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @a\n__CMP_1_end:\nFIN\n'], + ['long a, b, c; a=b==!c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nBNZ $c :__NOT_2_sF\nSET @a #0000000000000001\nJMP :__NOT_2_end\n__NOT_2_sF:\nCLR @a\n__NOT_2_end:\nBNE $b $a :__CMP_1_sF\nSET @a #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @a\n__CMP_1_end:\nFIN\n'], + ['long a, b, c; a=!b==c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nBNZ $b :__NOT_2_sF\nSET @a #0000000000000001\nJMP :__NOT_2_end\n__NOT_2_sF:\nCLR @a\n__NOT_2_end:\nBNE $a $c :__CMP_1_sF\nSET @a #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @a\n__CMP_1_end:\nFIN\n'], + ['long a, b, c; a=!b==!c;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nBNZ $b :__NOT_2_sF\nSET @a #0000000000000001\nJMP :__NOT_2_end\n__NOT_2_sF:\nCLR @a\n__NOT_2_end:\nBNZ $c :__NOT_3_sF\nSET @r0 #0000000000000001\nJMP :__NOT_3_end\n__NOT_3_sF:\nCLR @r0\n__NOT_3_end:\nBNE $a $r0 :__CMP_1_sF\nSET @a #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @a\n__CMP_1_end:\nFIN\n'], + ['long a, b, c; a=!(b+c);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @a $b\nADD @a $c\nBNZ $a :__NOT_1_sF\nSET @a #0000000000000001\nJMP :__NOT_1_end\n__NOT_1_sF:\nCLR @a\n__NOT_1_end:\nFIN\n'], + ['long a, b, c; a=!(b==c);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nBEQ $b $c :__NOT_1_sF\nSET @a #0000000000000001\nJMP :__NOT_1_end\n__NOT_1_sF:\nCLR @a\n__NOT_1_end:\nFIN\n'], + ['long a, b, c, d; a=!(b==c)==d;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nBEQ $b $c :__NOT_2_sF\nSET @a #0000000000000001\nJMP :__NOT_2_end\n__NOT_2_sF:\nCLR @a\n__NOT_2_end:\nBNE $a $d :__CMP_1_sF\nSET @a #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @a\n__CMP_1_end:\nFIN\n'], + ['long a, b, c, d, z; z=1+((a&&b)||(c&&d));', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare z\n\nBZR $a :__OR_2_next\nBZR $b :__OR_2_next\nJMP :__CMP_1_sT\n__OR_2_next:\nBZR $c :__CMP_1_sF\nBZR $d :__CMP_1_sF\nJMP :__CMP_1_sT\n__CMP_1_sF:\nCLR @z\nJMP :__CMP_1_end\n__CMP_1_sT:\nSET @z #0000000000000001\n__CMP_1_end:\nINC @z\nFIN\n'], + ['long a, b, c, d, z; z=1+!((a&&b)||(c&&d));', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare z\n\nBZR $a :__OR_2_next\nBZR $b :__OR_2_next\nJMP :__NOT_1_sF\n__OR_2_next:\nBZR $c :__NOT_1_sT\nBZR $d :__NOT_1_sT\nJMP :__NOT_1_sF\n__NOT_1_sT:\nSET @z #0000000000000001\nJMP :__NOT_1_end\n__NOT_1_sF:\nCLR @z\n__NOT_1_end:\nINC @z\nFIN\n'], + ['long a, b, c, d; a=b+(++c==d++);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nINC @c\nBNE $c $d :__CMP_1_sF\nSET @a #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @a\n__CMP_1_end:\nADD @a $b\nINC @d\nFIN\n'], + ['long a, b, c, d; a=b+(++c&&d++);', true, ''], + ['long a, b, c, d, z; z=1+((a||b)&&(c||d));', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare z\n\nBNZ $a :__AND_2_next\nBNZ $b :__AND_2_next\nJMP :__CMP_1_sF\n__AND_2_next:\nBNZ $c :__CMP_1_sT\nBNZ $d :__CMP_1_sT\nJMP :__CMP_1_sF\n__CMP_1_sT:\nSET @z #0000000000000001\nJMP :__CMP_1_end\n__CMP_1_sF:\nCLR @z\n__CMP_1_end:\nINC @z\nFIN\n'], + ['long a, b, c, d, z; z=1+!((a||b)&&(c||d));', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare z\n\nBNZ $a :__AND_2_next\nBNZ $b :__AND_2_next\nJMP :__NOT_1_sT\n__AND_2_next:\nBNZ $c :__NOT_1_sF\nBNZ $d :__NOT_1_sF\n__NOT_1_sT:\nSET @z #0000000000000001\nJMP :__NOT_1_end\n__NOT_1_sF:\nCLR @z\n__NOT_1_end:\nINC @z\nFIN\n'], + ['long a, b; a==b;', true, ''], + ['long a, b; a!=b;', true, ''], + ['long b; 2<=b;', true, ''], + ['long a; a<2;', true, ''], + ['long a, b; a>=b;', true, ''], + ['long a, b; a>b;', true, ''], + ['long a; !a;', true, ''], + ['long a, b; a&&b;', true, ''], + ['long a, b; a||b;', true, ''], + // Optimizations + ['Optimizations;', null, 'div'], + ['long a, b; a=b/a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @r0 $b\nDIV @r0 $a\nSET @a $r0\nFIN\n'], + ['long a, b, c; a=1+(b/(c/a));', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 $c\nDIV @r0 $a\nSET @r1 $b\nDIV @r1 $r0\nINC @r1\nSET @a $r1\nFIN\n'], + // MISC + ['MISC;', null, 'div'], + ['long a, *b; a=~-*b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @a\nSET @r0 $($b)\nSUB @a $r0\nNOT @a\nFIN\n'], + ['long a, *b; a=~-~-*b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @r0\nSET @a $($b)\nSUB @r0 $a\nNOT @r0\nCLR @a\nSUB @a $r0\nNOT @a\nFIN\n'], + ['long a, *b; a=~-~-*b+1;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @r0\nSET @a $($b)\nSUB @r0 $a\nNOT @r0\nCLR @a\nSUB @a $r0\nNOT @a\nINC @a\nFIN\n'], + ['long a, b, c, d, e; a=b+c/d-e;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n\nSET @a $c\nDIV @a $d\nADD @a $b\nSUB @a $e\nFIN\n'], + ['long a, b, c, d, e; a=b<>2) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 $a\nSET @r1 #0000000000000002\nSHR @r0 $r1\nBZR $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a; if (a|2) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #0000000000000002\nBOR @r0 $a\nBZR $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a; if (a^2) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #0000000000000002\nXOR @r0 $a\nBZR $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + // UnaryOperator + ['long a; if (!a) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nBNZ $a :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a; if (~a) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 $a\nNOT @r0\nBZR $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + // SetUnaryOperators + ['long a; if (++a) { a++; }', true, ''], + ['long a; if (a++) { a++; }', true, ''], + // Assignment SetOperator + ['long a; if (a=b) { a++; }', true, ''], + ['long a; if (a+=b) { a++; }', true, ''], + // Comparision + ['long a, b; if (a==b) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBNE $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (a!=b) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBEQ $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (a>=b) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBLT $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (a>b) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBLE $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (a<=b) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBGT $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (a>2)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 $a\nSET @r1 #0000000000000002\nSHR @r0 $r1\nBNZ $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a; if (!(a|2)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #0000000000000002\nBOR @r0 $a\nBNZ $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a; if (!(a^2)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #0000000000000002\nXOR @r0 $a\nBNZ $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + // UnaryOperator + ['long a; if (!!a) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nBZR $a :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a; if (!~a) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 $a\nNOT @r0\nBNZ $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + // Comparision + ['long a, b; if (!(a==b)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBEQ $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (!(a!=b)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBNE $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (!(a>=b)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBGE $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (!(a>b)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBGT $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (!(a<=b)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBLE $a $b :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b; if (!(a=b && c>=d) || (e!=f && g!=h)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n^declare f\n^declare g\n^declare h\n\nBLT $a $b :__OR_2_next\nBLT $c $d :__OR_2_next\nJMP :__if1_start\n__OR_2_next:\nBEQ $e $f :__if1_endif\nBEQ $g $h :__if1_endif\nJMP :__if1_start\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b, c, d, e, f, g, h; if ((a>=b&&c>=d)||!(e==f&&g==h)) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^declare e\n^declare f\n^declare g\n^declare h\n\nBLT $a $b :__OR_2_next\nBLT $c $d :__OR_2_next\nJMP :__if1_start\n__OR_2_next:\nBNE $e $f :__if1_start\nBNE $g $h :__if1_start\nJMP :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['long a, b, c, d, e, f, g, h; if ((a<=b||c0) goto label1; if (temp==0) goto label2; goto label3; label1: temp++; label2: temp++; label3: temp++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare temp\n\nSET @temp #0000000000000002\nCLR @r0\nBLE $temp $r0 :__if1_endif\n__if1_start:\nJMP :label1\n__if1_endif:\nBNZ $temp :__if2_endif\n__if2_start:\nJMP :label2\n__if2_endif:\nJMP :label3\nlabel1:\nINC @temp\nlabel2:\nINC @temp\nlabel3:\nINC @temp\nFIN\n'], + ['long a, b; a++; asm { PSH @a\nPOP @b } b++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nINC @a\nPSH @a\nPOP @b\nINC @b\nFIN\n'], + ['long a, b; a++; sleep 1;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nINC @a\nSET @r0 #0000000000000001\nSLP $r0\nFIN\n'], + ['long a, b; exit; a++; ', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nFIN\nINC @a\nFIN\n'], + ['halt;', false, '^declare r0\n^declare r1\n^declare r2\n\nSTP\nFIN\n'], + ['long a, b, c; if (a) { a++; if (b) { b++; if (c) { c++; } } }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nBZR $a :__if1_endif\n__if1_start:\nINC @a\nBZR $b :__if2_endif\n__if2_start:\nINC @b\nBZR $c :__if3_endif\n__if3_start:\nINC @c\n__if3_endif:\n__if2_endif:\n__if1_endif:\nFIN\n'], + ['long a, b, c; if (a) {\n a++;\n} else if (b) {\n b++;\n} else if (c) {\n c++;\n}', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nBZR $a :__if1_else\n__if1_start:\nINC @a\nJMP :__if1_endif\n__if1_else:\nBZR $b :__if2_else\n__if2_start:\nINC @b\nJMP :__if2_endif\n__if2_else:\nBZR $c :__if3_endif\n__if3_start:\nINC @c\n__if3_endif:\n__if2_endif:\n__if1_endif:\nFIN\n'], + ['long a, b; a=2; const b=5; a++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @a #0000000000000002\n^const SET @b #0000000000000005\nINC @a\nFIN\n'], + ['Empty conditions', null, 'div'], + ['long a; if () { a++; }', true, ''], + ['long a; if () { a++; } else { b--; }', true, ''], + ['long a; while () { a++; }', true, ''], + ['long a; for (;;) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\n__loop1_condition:\n__loop1_start:\nINC @a\n__loop1_continue:\nJMP :__loop1_condition\n__loop1_break:\nFIN\n'], + ['long a; do { a++; } while ();', true, ''], + // pointer Operation + ['Full tests;', null, 'div'], + ['Pointer Assignment;', null, 'div'], + [`long *pa, *pb, va, vb; +pa = pb; pa = &pb; pa = &vb; *pa= vb; +*pa= *pb; va = vb; va = *pb;`, false, '^declare r0\n^declare r1\n^declare r2\n^declare pa\n^declare pb\n^declare va\n^declare vb\n\nSET @pa $pb\nSET @pa #0000000000000004\nSET @pa #0000000000000006\nSET @($pa) $vb\nSET @r0 $($pb)\nSET @($pa) $r0\nSET @va $vb\nSET @va $($pb)\nFIN\n'], + ['long *pa, *pb, va, vb; pa=vb; pa=*pb; *pa=pb; *pa=&pb; *pa=&vb; va=pb; va=&pb; va=&vb;', true, ''], + [`#pragma warningToError false\nlong *pa, *pb, va, vb; + pa=vb; pa=*pb; *pa=pb; *pa=&pb; *pa=&vb; va=pb; va=&pb; va=&vb;`, false, '^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'], + ['long *pa, *pb, va, vb; pa+=vb;', false, '^declare r0\n^declare r1\n^declare r2\n^declare pa\n^declare pb\n^declare va\n^declare vb\n\nADD @pa $vb\nFIN\n'], + ['long *pa, *pb, va, vb; pa+=vb+1;', false, '^declare r0\n^declare r1\n^declare r2\n^declare pa\n^declare pb\n^declare va\n^declare vb\n\nSET @r0 $vb\nINC @r0\nADD @pa $r0\nFIN\n'], + ['long *pa, *pb, va, vb; pa-=vb;', false, '^declare r0\n^declare r1\n^declare r2\n^declare pa\n^declare pb\n^declare va\n^declare vb\n\nSUB @pa $vb\nFIN\n'], + ['long *pa, *pb, va, vb; pa=pa-vb;', false, '^declare r0\n^declare r1\n^declare r2\n^declare pa\n^declare pb\n^declare va\n^declare vb\n\nSET @r0 $pa\nSUB @r0 $vb\nSET @pa $r0\nFIN\n'], + ['long *pa, *pb, va, vb; va=*vb;', true, ''], + ['long *pa, *pb, va, vb; *va=vb;', true, ''], + ['long *a, *a+1=0;', true, ''], + ['long *a, *(a*3)=0;', true, ''], + ['Pointer/Array Assignment;', null, 'div'], + ['long a[4], *b, c; *b=a[0]; a[0]=*b; b=a; *b=a[c]; a[c]=*b;', false, '^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 @($b) $a_0\nSET @a_0 $($b)\nSET @b $a\nSET @r0 $($a + $c)\nSET @($b) $r0\nSET @r0 $($b)\nSET @($a + $c) $r0\nFIN\n'], + ['long a[4], *b, c; a=b;', true, ''], + ['long a[4], *b, c; b=&a; b=&a[0]; b=&c;', false, '^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 @b #0000000000000003\nSET @b #0000000000000004\nSET @b #0000000000000009\nFIN\n'], + ['long a[4], *b, c; c=&a; c=&a[0]; c=&c;', true, ''], + ['#pragma warningToError false\nlong a[4], *b, c; c=&a; c=&a[0]; c=&c;', false, '^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'], + ['long *a, b, c;\n*(a+1)=b; *(a+30)=b; *(a+c)=b;\nb=*(a+1); b=*(a+30); b=*(a+c);', false, '^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'], + ['long *a, b, Array[4]; a=Array+2; a=Array-b; a+=7; a++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare Array\n^const SET @Array #0000000000000006\n^declare Array_0\n^declare Array_1\n^declare Array_2\n^declare Array_3\n\nSET @a $Array\nINC @a\nINC @a\nSET @a $Array\nSUB @a $b\nSET @r0 #0000000000000007\nADD @a $r0\nINC @a\nFIN\n'], + ['long *a, b, Array[4]; a=Array*2;', true, ''], + // [ "", false,"" ], + // Array + // Memtable: Arr+ Assignment, Arr+ SetOperator, + ['Array', null, 'div'], + ['long a[4]; long b; long c; a[b]=c;', false, '^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 @($a + $b) $c\nFIN\n'], + ['long a[4]; long b; long c; a[0]=b;', false, '^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 @a_0 $b\nFIN\n'], + ['long a[4]; long b; long c; a[2]=b;', false, '^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 @a_2 $b\nFIN\n'], + ["long a[4]; a[]='aaaaaaaazzzzzzz';", false, '^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\nSET @a_0 #6161616161616161\nSET @a_1 #007a7a7a7a7a7a7a\nCLR @a_2\nCLR @a_3\nFIN\n'], + ['long a[4]; a[]=0x3333333333333333222222222222222211111111111111110000000000000000;', false, '^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\nCLR @a_0\nSET @a_1 #1111111111111111\nSET @a_2 #2222222222222222\nSET @a_3 #3333333333333333\nFIN\n'], + ['long a[4]; long b; long c; c=a[b];', false, '^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 $($a + $b)\nFIN\n'], + ['long a[4]; long b; long c; c=a[0];', false, '^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 $a_0\nFIN\n'], + ['long a[4]; long b; long c; c=a[3];', false, '^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 $a_3\nFIN\n'], + ['long a[4]; long b; long c; a[b]+=c;', false, '^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 @r0 $($a + $b)\nADD @r0 $c\nSET @($a + $b) $r0\nFIN\n'], + ['long a[4]; long b; long c; a[0]-=b;', false, '^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\nSUB @a_0 $b\nFIN\n'], + ['long a[4]; long b; long c; a[2]*=b;', false, '^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\nMUL @a_2 $b\nFIN\n'], + ['long a[4]; long b; long c; c/=a[b];', false, '^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 @r0 $($a + $b)\nDIV @c $r0\nFIN\n'], + ['long a[4]; long b; long c; c&=a[0];', false, '^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\nAND @c $a_0\nFIN\n'], + ['long a[4]; long b; long c; c^=a[3];', false, '^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\nXOR @c $a_3\nFIN\n'], + ['long a[4]; long b; long c[4]; long d; a[b]=c[d];', false, '^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^const SET @c #000000000000000a\n^declare c_0\n^declare c_1\n^declare c_2\n^declare c_3\n^declare d\n\nSET @r0 $($c + $d)\nSET @($a + $b) $r0\nFIN\n'], + ['long a[4]; long b; long c[4]; long d; a[b]+=c[d];', false, '^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^const SET @c #000000000000000a\n^declare c_0\n^declare c_1\n^declare c_2\n^declare c_3\n^declare d\n\nSET @r0 $($a + $b)\nSET @r1 $($c + $d)\nADD @r0 $r1\nSET @($a + $b) $r0\nFIN\n'], + // Arr+ Constant, Arr+ Operator + ['long a[4]; long b; a[b]=2;', false, '^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\nSET @r0 #0000000000000002\nSET @($a + $b) $r0\nFIN\n'], + ['long a[4]; a[0]=0xFF;', false, '^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\nSET @a_0 #00000000000000ff\nFIN\n'], + ['long a[4]; a[2]="Ho ho";', false, '^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\nSET @a_2 #0000006f68206f48\nFIN\n'], + ['long a, b[4], c, d; a=b[c]/b[d];', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^const SET @b #0000000000000005\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 $($b + $c)\nSET @r0 $($b + $d)\nDIV @a $r0\nFIN\n'], + ['long a, b[4], c, d; a=b[c]<passenger='Ze'; +pcar->driver=a; +pcar->driver=*c; +pcar->driver=d[1]; +pcar->driver=d[a]; +pcar->driver=pcar->collector;\n +a=pcar->collector; +*c=pcar->collector; +d[1]=pcar->collector; +d[a]=pcar->collector;`, false, '^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\nCLR @r0\nSET @($pcar + $r0) $a\nSET @r0 $($c)\nCLR @r1\nSET @($pcar + $r1) $r0\nCLR @r0\nSET @($pcar + $r0) $d_1\nSET @r0 $($d + $a)\nCLR @r1\nSET @($pcar + $r1) $r0\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nCLR @r1\nSET @($pcar + $r1) $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'], + [`struct KOMBI { long driver; long collector; long passenger[4]; } ; +struct KOMBI car, *pcar; +long a, b, *c, d[2]; +pcar=&car;\n +pcar->passenger[2]='Ze'; +pcar->passenger[2]=a; +pcar->passenger[2]=*c; +pcar->passenger[2]=d[1]; +pcar->passenger[2]=d[a]; +pcar->passenger[2]=pcar->collector; +pcar->passenger[2]=pcar->passenger[1];\n +a=pcar->passenger[2]; +*c=pcar->passenger[2]; +d[1]=pcar->passenger[2]; +d[a]=pcar->passenger[2];`, false, '^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'], + [`struct KOMBI { long driver; long collector; long passenger[4]; } ; +struct KOMBI car[2], *pcar; +long a, b, *c, d[2]; +pcar=&car[a];`, false, '^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^const SET @car_0_passenger #0000000000000007\n^declare car_0_passenger_0\n^declare car_0_passenger_1\n^declare car_0_passenger_2\n^declare car_0_passenger_3\n^declare car_1_driver\n^declare car_1_collector\n^declare car_1_passenger\n^const SET @car_1_passenger #000000000000000e\n^declare car_1_passenger_0\n^declare car_1_passenger_1\n^declare car_1_passenger_2\n^declare car_1_passenger_3\n^declare pcar\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #0000000000000017\n^declare d_0\n^declare d_1\n\nSET @r0 #0000000000000007\nMUL @r0 $a\nSET @r1 $car\nADD @r1 $r0\nSET @pcar $r1\nFIN\n'], + [`struct KOMBI { long driver; long collector; long passenger[4]; } ; +struct KOMBI car, *pcar; +long a, b, *c, d[2]; +pcar=&car;\n +pcar->passenger[a]='Ze'; +pcar->passenger[a]=a; +pcar->passenger[a]=*c; +pcar->passenger[a]=d[1]; +pcar->passenger[a]=d[a]; +pcar->passenger[a]=pcar->collector; +pcar->passenger[a]=pcar->passenger[b];\n +a=pcar->passenger[b]; +*c=pcar->passenger[b]; +d[1]=pcar->passenger[b]; +d[a]=pcar->passenger[b];`, false, '^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 $a\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @r1 #000000000000655a\nSET @($pcar + $r0) $r1\nSET @r0 $a\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @($pcar + $r0) $a\nSET @r0 $a\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @r1 $($c)\nSET @($pcar + $r0) $r1\nSET @r0 $a\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @($pcar + $r0) $d_1\nSET @r0 $a\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @r1 $($d + $a)\nSET @($pcar + $r0) $r1\nSET @r0 $a\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @r2 #0000000000000001\nSET @r1 $($pcar + $r2)\nSET @($pcar + $r0) $r1\nSET @r0 $a\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @r1 $b\nSET @r2 #0000000000000003\nADD @r1 $r2\nSET @r2 $($pcar + $r1)\nSET @($pcar + $r0) $r2\nSET @a $b\nSET @r0 #0000000000000003\nADD @a $r0\nSET @a $($pcar + $a)\nSET @r0 $b\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @r1 $($pcar + $r0)\nSET @($c) $r1\nSET @d_1 $b\nSET @r0 #0000000000000003\nADD @d_1 $r0\nSET @d_1 $($pcar + $d_1)\nSET @r0 $b\nSET @r1 #0000000000000003\nADD @r0 $r1\nSET @r1 $($pcar + $r0)\nSET @($d + $a) $r1\nFIN\n'], + ['struct KOMBI { long driver, collector, passenger; } *car; void * ptr; ptr = &car;', false, '^declare r0\n^declare r1\n^declare r2\n^declare car\n^declare ptr\n\nSET @ptr #0000000000000003\nFIN\n'], + ['Logical operations with arrays and structs', null, 'div'], + ['long a[2], b; if (a[b]) { b++; }', false, '^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 b\n\nSET @r0 $($a + $b)\nBZR $r0 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nFIN\n'], + ['long a[2], b; if (!(a[b])) { b++; }', false, '^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 b\n\nSET @r0 $($a + $b)\nBNZ $r0 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nFIN\n'], + ['long a[2], b; if (!a[b]) { b++; }', false, '^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 b\n\nSET @r0 $($a + $b)\nBNZ $r0 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nFIN\n'], + [`struct KOMBI { long driver; long collector; long passenger; } car; +long a, b; +if (car.driver=='Ze') { b++; } +if (a<=car.collector) { b--; }`, false, '^declare r0\n^declare r1\n^declare r2\n^declare car_driver\n^declare car_collector\n^declare car_passenger\n^declare a\n^declare b\n\nSET @r0 #000000000000655a\nBNE $car_driver $r0 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nBGT $a $car_collector :__if2_endif\n__if2_start:\nDEC @b\n__if2_endif:\nFIN\n'], + [`struct KOMBI { long driver; long collector; long passenger; } car[2]; +long a, b; +if (car[1].driver=='Ze') { b++; } +if (a<=car[0].collector) { b--; }`, false, '^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^declare b\n\nSET @r0 #000000000000655a\nBNE $car_1_driver $r0 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nBGT $a $car_0_collector :__if2_endif\n__if2_start:\nDEC @b\n__if2_endif:\nFIN\n'], + [`struct KOMBI { long driver; long collector; long passenger; } car[2]; +long a, b; +if (car[b].driver=='Ze') { b++; } +if (a<=car[b].collector) { b--; }`, false, '^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^declare b\n\nSET @r0 #0000000000000003\nMUL @r0 $b\nSET @r1 $($car + $r0)\nSET @r0 #000000000000655a\nBNE $r1 $r0 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nSET @r0 #0000000000000003\nMUL @r0 $b\nINC @r0\nSET @r1 $($car + $r0)\nBGT $a $r1 :__if2_endif\n__if2_start:\nDEC @b\n__if2_endif:\nFIN\n'], + [`struct KOMBI { long driver; long collector; long passenger[3]; } car; +long a, b; +if (car.passenger[0]=='Ze') { b++; } +if (a<=car.passenger[2]) { b--; }`, false, '^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 a\n^declare b\n\nSET @r0 #000000000000655a\nBNE $car_passenger_0 $r0 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nBGT $a $car_passenger_2 :__if2_endif\n__if2_start:\nDEC @b\n__if2_endif:\nFIN\n'], + [`struct KOMBI { long driver; long collector; long passenger[3]; } car[2]; +long a, b; +if (car[0].passenger[0]=='Ze') { b++; } +if (a<=car[b].passenger[2]) { b--; }`, false, '^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^const SET @car_0_passenger #0000000000000007\n^declare car_0_passenger_0\n^declare car_0_passenger_1\n^declare car_0_passenger_2\n^declare car_1_driver\n^declare car_1_collector\n^declare car_1_passenger\n^const SET @car_1_passenger #000000000000000d\n^declare car_1_passenger_0\n^declare car_1_passenger_1\n^declare car_1_passenger_2\n^declare a\n^declare b\n\nSET @r0 #000000000000655a\nBNE $car_0_passenger_0 $r0 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nSET @r0 #0000000000000006\nMUL @r0 $b\nSET @r1 #0000000000000003\nADD @r0 $r1\nINC @r0\nINC @r0\nSET @r1 $($car + $r0)\nBGT $a $r1 :__if2_endif\n__if2_start:\nDEC @b\n__if2_endif:\nFIN\n'], + [`struct KOMBI { long driver; long collector; long passenger[3]; } car[2]; +long a, b; +if (car[0].passenger[b]=='Ze') { b++; } +if (a<=car[b].passenger[a]) { b--; }`, false, '^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^const SET @car_0_passenger #0000000000000007\n^declare car_0_passenger_0\n^declare car_0_passenger_1\n^declare car_0_passenger_2\n^declare car_1_driver\n^declare car_1_collector\n^declare car_1_passenger\n^const SET @car_1_passenger #000000000000000d\n^declare car_1_passenger_0\n^declare car_1_passenger_1\n^declare car_1_passenger_2\n^declare a\n^declare b\n\nSET @r0 $($car_0_passenger + $b)\nSET @r1 #000000000000655a\nBNE $r0 $r1 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nSET @r0 #0000000000000006\nMUL @r0 $b\nSET @r1 #0000000000000003\nADD @r0 $r1\nADD @r0 $a\nSET @r1 $($car + $r0)\nBGT $a $r1 :__if2_endif\n__if2_start:\nDEC @b\n__if2_endif:\nFIN\n'], + [`struct KOMBI { long driver; long collector; long passenger; } ; +struct KOMBI car[2], *pcar; +long a, b; +pcar=&car[1]; +if (pcar->driver=='Ze') { b++; } +if (a<=pcar->collector) { b--; }`, false, '^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 pcar\n^declare a\n^declare b\n\nSET @pcar #0000000000000007\nCLR @r1\nSET @r0 $($pcar + $r1)\nSET @r1 #000000000000655a\nBNE $r0 $r1 :__if1_endif\n__if1_start:\nINC @b\n__if1_endif:\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nBGT $a $r0 :__if2_endif\n__if2_start:\nDEC @b\n__if2_endif:\nFIN\n'], + // [ "", false,"" ], + // MemTable: general + ['General', null, 'div'], + ['long a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFIN\n'], + ['long a=3;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #0000000000000003\nFIN\n'], + ['long a,b;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nFIN\n'], + ['long a,b=3;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @b #0000000000000003\nFIN\n'], + ['long * a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFIN\n'], + ['long a[3];', false, '^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\nFIN\n'], + ['long a[3]; a[0]=9;', false, '^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\nSET @a_0 #0000000000000009\nFIN\n'], + ['long a[3]; a=9;', true, ''], + ['Functions', null, 'div'], + ['long a; void main(void) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nJMP :__fn_main\n\n__fn_main:\nPCS\nINC @a\nFIN\n'], + ['long a; void main(void) { a++; return; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nJMP :__fn_main\n\n__fn_main:\nPCS\nINC @a\nFIN\n'], + ['long a; void main(void) { a++; return; a++; }', false, '^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'], + ['long a; void test(void) { a++; return; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFIN\n\n__fn_test:\nINC @a\nRET\n'], + ['long a; void test(void) { a++; return; a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFIN\n\n__fn_test:\nINC @a\nRET\nINC @a\nRET\n'], + ['long a; test(); void test(void) { a++; return; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nJSR :__fn_test\nFIN\n\n__fn_test:\nINC @a\nRET\n'], + ['long a; a=test(); void test(void) { a++; return; }', true, ''], + ['long a; void test2(long b) { b++; return; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare test2_b\n\nFIN\n\n__fn_test2:\nPOP @test2_b\nINC @test2_b\nRET\n'], + ['long a; long test2(long b) { b++; return b; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare test2_b\n\nFIN\n\n__fn_test2:\nPOP @test2_b\nINC @test2_b\nPSH $test2_b\nRET\n'], + ['long a=0; a=test2(a); long test2(long b) { b++; return b; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare test2_b\n\nCLR @a\nPSH $a\nJSR :__fn_test2\nPOP @r0\nSET @a $r0\nFIN\n\n__fn_test2:\nPOP @test2_b\nINC @test2_b\nPSH $test2_b\nRET\n'], + ['#pragma warningToError false\nlong a=0; test2(a); long test2(long b) { b++; return b; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare test2_b\n\nCLR @a\nPSH $a\nJSR :__fn_test2\nPOP @r0\nFIN\n\n__fn_test2:\nPOP @test2_b\nINC @test2_b\nPSH $test2_b\nRET\n'], + ['long a=0; void main(void){ a++; test2(a); exit; } void test2(long b) { b++; return; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare test2_b\n\nCLR @a\nJMP :__fn_main\n\n__fn_main:\nPCS\nINC @a\nPSH $a\nJSR :__fn_test2\nFIN\n\n__fn_test2:\nPOP @test2_b\nINC @test2_b\nRET\n'], + ['#include APIFunctions\nlong a;Set_A1(a);', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nFUN set_A1 $a\nFIN\n'], + ['#include APIFunctions\nSet_A1();', true, ''], + ['long *a; a=test(); long *test(void) { long b; return &b; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare test_b\n\nJSR :__fn_test\nPOP @a\nFIN\n\n__fn_test:\nSET @r0 #0000000000000004\nPSH $r0\nRET\n'], + ['long a; a=test(); long *test(void) { long b; return &b; }', true, ''], + ['long *a; a=test(); long *test(void) { long b; return b; }', true, ''], + ['struct KOMBI { long driver; long collector; long passenger; } car, car2; car = teste(); struct KOMBI teste(void){ return car; }', true, ''], + ['Declarations', null, 'div'], + ['long ,b;', true, ''], + ['Improvements v0.3', null, ''], + [ + // Function returning struct pointer + 'struct KOMBI { long driver, collector, passenger; } *val; val = teste(); val = test2(); struct KOMBI *teste(void) { struct KOMBI tt2; return &tt2; } struct KOMBI *test2(void) { struct KOMBI *stt2; return stt2; }', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare val\n^declare teste_tt2_driver\n^declare teste_tt2_collector\n^declare teste_tt2_passenger\n^declare test2_stt2\n\nJSR :__fn_teste\nPOP @r0\nSET @val $r0\nJSR :__fn_test2\nPOP @r0\nSET @val $r0\nFIN\n\n__fn_teste:\nSET @r0 #0000000000000004\nPSH $r0\nRET\n\n__fn_test2:\nPSH $test2_stt2\nRET\n' + ], + [ + // Error check Function return pointer + 'struct KOMBI { long driver, collector, passenger; } *val; val = teste(); struct KOMBI *teste(void) { struct KOMBI tt2; return tt2; }', + true, + '' + ], + [ + // Modifier on Function -> arr on pointer return value + 'long valLong = teste()[1]; long *teste(void) { long message[4]; return message; }', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare valLong\n^declare teste_message\n^const SET @teste_message #0000000000000005\n^declare teste_message_0\n^declare teste_message_1\n^declare teste_message_2\n^declare teste_message_3\n\nJSR :__fn_teste\nPOP @valLong\nSET @r0 #0000000000000001\nSET @valLong $($valLong + $r0)\nFIN\n\n__fn_teste:\nPSH $teste_message\nRET\n' + ], + // #include APIFunctions\n long *next; void Send_Message(void) { Set_B1(-next);} + // long *a[4], b, c; c = -a[b]; + [ + // Recursion with fibbonacci + 'long a; long counter = 0; a = fibbonacci(12); \nlong fibbonacci(long n) {if(n == 0){ return 0; } else if(n == 1) { return 1; } else { return (fibbonacci(n-1) + fibbonacci(n-2)); } }', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare counter\n^declare fibbonacci_n\n\nCLR @counter\nSET @a #000000000000000c\nPSH $a\nJSR :__fn_fibbonacci\nPOP @a\nFIN\n\n__fn_fibbonacci:\nPOP @fibbonacci_n\nBNZ $fibbonacci_n :__if1_else\n__if1_start:\nCLR @r0\nPSH $r0\nRET\nJMP :__if1_endif\n__if1_else:\nSET @r0 #0000000000000001\nBNE $fibbonacci_n $r0 :__if2_else\n__if2_start:\nSET @r0 #0000000000000001\nPSH $r0\nRET\nJMP :__if2_endif\n__if2_else:\nPSH $fibbonacci_n\nSET @r0 $fibbonacci_n\nDEC @r0\nPSH $r0\nJSR :__fn_fibbonacci\nPOP @r0\nPOP @fibbonacci_n\nPSH $fibbonacci_n\nPSH $r0\nSET @r1 $fibbonacci_n\nSET @r2 #0000000000000002\nSUB @r1 $r2\nPSH $r1\nJSR :__fn_fibbonacci\nPOP @r1\nPOP @r0\nPOP @fibbonacci_n\nADD @r0 $r1\nPSH $r0\nRET\n__if2_endif:\n__if1_endif:\nCLR @r0\nPSH $r0\nRET\n' + ], + [ + // void pointer assigment + 'struct KOMBI { long driver; long collector; long passenger; } *pstruct; long *plong; void * pvoid; pvoid = plong; plong = pvoid; pvoid = pstruct; pstruct = pvoid;', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare pstruct\n^declare plong\n^declare pvoid\n\nSET @pvoid $plong\nSET @plong $pvoid\nSET @pvoid $pstruct\nSET @pstruct $pvoid\nFIN\n' + ], + [ + // void pointer function arguments and return + `struct KOMBI { long driver; long collector; long passenger; } *pstruct; long *plong; void * pvoid; +teste(pvoid, pvoid); teste(pvoid, pstruct); plong = ret(pvoid, plong); pstruct = ret(pvoid, plong); +void teste( long *aa, void *bb) { aa++, bb++; } +void *ret(long *aa, void *bb) { aa++; return aa; }`, + false, + '^declare r0\n^declare r1\n^declare r2\n^declare pstruct\n^declare plong\n^declare pvoid\n^declare teste_aa\n^declare teste_bb\n^declare ret_aa\n^declare ret_bb\n\nPSH $pvoid\nPSH $pvoid\nJSR :__fn_teste\nPSH $pstruct\nPSH $pvoid\nJSR :__fn_teste\nPSH $plong\nPSH $pvoid\nJSR :__fn_ret\nPOP @r0\nSET @plong $r0\nPSH $plong\nPSH $pvoid\nJSR :__fn_ret\nPOP @r0\nSET @pstruct $r0\nFIN\n\n__fn_teste:\nPOP @teste_aa\nPOP @teste_bb\nINC @teste_aa\nINC @teste_bb\nRET\n\n__fn_ret:\nPOP @ret_aa\nPOP @ret_bb\nINC @ret_aa\nPSH $ret_aa\nRET\n' + ], + [ + // return of null pointer and return of long_ptr at function returning void pointer. Assign this return value to a long_ptr + 'long *plong; plong = malloc(); void *malloc(void) { if (current >=20) { return NULL; } long current++; long mem[20]; return (&mem[current]); }', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare plong\n^declare malloc_current\n^declare malloc_mem\n^const SET @malloc_mem #0000000000000006\n^declare malloc_mem_0\n^declare malloc_mem_1\n^declare malloc_mem_2\n^declare malloc_mem_3\n^declare malloc_mem_4\n^declare malloc_mem_5\n^declare malloc_mem_6\n^declare malloc_mem_7\n^declare malloc_mem_8\n^declare malloc_mem_9\n^declare malloc_mem_10\n^declare malloc_mem_11\n^declare malloc_mem_12\n^declare malloc_mem_13\n^declare malloc_mem_14\n^declare malloc_mem_15\n^declare malloc_mem_16\n^declare malloc_mem_17\n^declare malloc_mem_18\n^declare malloc_mem_19\n\nJSR :__fn_malloc\nPOP @plong\nFIN\n\n__fn_malloc:\nSET @r0 #0000000000000014\nBLT $malloc_current $r0 :__if1_endif\n__if1_start:\nCLR @r0\nPSH $r0\nRET\n__if1_endif:\nINC @malloc_current\nSET @r0 $malloc_mem\nADD @r0 $malloc_current\nPSH $r0\nRET\n' + ], + [ + // Array of pointers and double deference + 'long a, b, *c, *d[2]; c = d[1]; a = *d[1]; a++; c = d[b]; a = *d[b]; a++; d[0]=c; *d[1]=a; a++; d[b]=c; *d[b]=a;', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #0000000000000007\n^declare d_0\n^declare d_1\n\nSET @c $d_1\nSET @a $d_1\nSET @a $($a)\nINC @a\nSET @c $($d + $b)\nSET @a $($d + $b)\nSET @a $($a)\nINC @a\nSET @d_0 $c\nSET @r0 $d_1\nSET @($r0) $a\nINC @a\nSET @($d + $b) $c\nSET @r0 $($d + $b)\nSET @($r0) $a\nFIN\n' + ], + [ + // Muldimensional arrays inside structs + 'struct KOMBI { long driver, collector, passenger[3][3]; } car; long a, b, c; car.passenger[2][2]=c; car.passenger[a][2]=c; car.passenger[2][b]=c; car.passenger[a][b]=c; c=car.passenger[2][2]; c=car.passenger[a][2]; c=car.passenger[2][b]; c=car.passenger[a][b];', + false, + '^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 car_passenger_4\n^declare car_passenger_5\n^declare car_passenger_6\n^declare car_passenger_7\n^declare car_passenger_8\n^declare a\n^declare b\n^declare c\n\nSET @car_passenger_8 $c\nSET @r0 #0000000000000003\nMUL @r0 $a\nINC @r0\nINC @r0\nSET @($car_passenger + $r0) $c\nSET @r0 $b\nSET @r1 #0000000000000006\nADD @r0 $r1\nSET @($car_passenger + $r0) $c\nSET @r0 #0000000000000003\nMUL @r0 $a\nADD @r0 $b\nSET @($car_passenger + $r0) $c\nSET @c $car_passenger_8\nSET @c #0000000000000003\nMUL @c $a\nINC @c\nINC @c\nSET @c $($car_passenger + $c)\nSET @c $b\nSET @r0 #0000000000000006\nADD @c $r0\nSET @c $($car_passenger + $c)\nSET @c #0000000000000003\nMUL @c $a\nADD @c $b\nSET @c $($car_passenger + $c)\nFIN\n' + ], + [ + // Get property LENGTH of an array + 'long a, b[4], c[3][3]; struct KOMBI { long driver; long passenger[6]; } car[2]; a=b.length; a=c.length; a=car.length; a=car[1].passenger.length; a=car[a].passenger.length;', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^const SET @b #0000000000000005\n^declare b_0\n^declare b_1\n^declare b_2\n^declare b_3\n^declare c\n^const SET @c #000000000000000a\n^declare c_0\n^declare c_1\n^declare c_2\n^declare c_3\n^declare c_4\n^declare c_5\n^declare c_6\n^declare c_7\n^declare c_8\n^declare car\n^const SET @car #0000000000000014\n^declare car_0_driver\n^declare car_0_passenger\n^const SET @car_0_passenger #0000000000000016\n^declare car_0_passenger_0\n^declare car_0_passenger_1\n^declare car_0_passenger_2\n^declare car_0_passenger_3\n^declare car_0_passenger_4\n^declare car_0_passenger_5\n^declare car_1_driver\n^declare car_1_passenger\n^const SET @car_1_passenger #000000000000001e\n^declare car_1_passenger_0\n^declare car_1_passenger_1\n^declare car_1_passenger_2\n^declare car_1_passenger_3\n^declare car_1_passenger_4\n^declare car_1_passenger_5\n\nSET @a #0000000000000004\nSET @a #0000000000000009\nSET @a #0000000000000002\nSET @a #0000000000000006\nSET @a #0000000000000006\nFIN\n' + ], + [ + // Get property LENGTH of an array in a struct pointer + 'long a; struct KOMBI { long driver; long passenger[6]; } *pcar; a=pcar->passenger.length;', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare pcar\n\nSET @a #0000000000000006\nFIN\n' + ], + [ + // Recursive struct pointer declaration + 'struct KOMBI { long driver; long collector; struct KOMBI *next; } ; struct KOMBI car, *pcar, *pnext; pcar=&car; pnext=pcar->next->next;', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare car_driver\n^declare car_collector\n^declare car_next\n^declare pcar\n^declare pnext\n\nSET @pcar #0000000000000003\nSET @r1 #0000000000000002\nSET @r0 $($pcar + $r1)\nSET @r1 #0000000000000002\nSET @pnext $($r0 + $r1)\nFIN\n' + ], + [ + // Special function 'catch' + '#program activationAmount 0\nlong table[20];\nvoid main (void) { const long a = 0; while (true) { table[a] = fibbonacci(a); halt; a++; } }\nlong fibbonacci(long n) { if(n == 0){ return 0; } else if(n == 1) { return 1; } else { return (fibbonacci(n-1) + fibbonacci(n-2)); } }\nvoid catch(void) { long a++; }', + false, + '^program activationAmount 0\n^declare r0\n^declare r1\n^declare r2\n^declare table\n^const SET @table #0000000000000004\n^declare table_0\n^declare table_1\n^declare table_2\n^declare table_3\n^declare table_4\n^declare table_5\n^declare table_6\n^declare table_7\n^declare table_8\n^declare table_9\n^declare table_10\n^declare table_11\n^declare table_12\n^declare table_13\n^declare table_14\n^declare table_15\n^declare table_16\n^declare table_17\n^declare table_18\n^declare table_19\n^declare main_a\n^declare fibbonacci_n\n^declare catch_a\n\nERR :__fn_catch\nJMP :__fn_main\n\n__fn_main:\nPCS\n^const SET @main_a #0000000000000000\n__loop1_continue:\n__loop1_start:\nPSH $main_a\nJSR :__fn_fibbonacci\nPOP @r0\nSET @($table + $main_a) $r0\nSTP\nINC @main_a\nJMP :__loop1_continue\n__loop1_break:\nFIN\n\n__fn_fibbonacci:\nPOP @fibbonacci_n\nBNZ $fibbonacci_n :__if2_else\n__if2_start:\nCLR @r0\nPSH $r0\nRET\nJMP :__if2_endif\n__if2_else:\nSET @r0 #0000000000000001\nBNE $fibbonacci_n $r0 :__if3_else\n__if3_start:\nSET @r0 #0000000000000001\nPSH $r0\nRET\nJMP :__if3_endif\n__if3_else:\nPSH $fibbonacci_n\nSET @r0 $fibbonacci_n\nDEC @r0\nPSH $r0\nJSR :__fn_fibbonacci\nPOP @r0\nPOP @fibbonacci_n\nPSH $fibbonacci_n\nPSH $r0\nSET @r1 $fibbonacci_n\nSET @r2 #0000000000000002\nSUB @r1 $r2\nPSH $r1\nJSR :__fn_fibbonacci\nPOP @r1\nPOP @r0\nPOP @fibbonacci_n\nADD @r0 $r1\nPSH $r0\nRET\n__if3_endif:\n__if2_endif:\nCLR @r0\nPSH $r0\nRET\n\n__fn_catch:\nPCS\nINC @catch_a\nFIN\n' + ], + [ + // Special function 'catch': no main function + 'long b, a = 0; while (true) { a++; } void catch(void) { long a++; }', + false, + '^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' + ], + [ + // 'catch' with return statement + 'long b, a = 0; void catch(void) { if (a) return; a++; }', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare b\n^declare a\n\nERR :__fn_catch\nCLR @a\nFIN\n\n__fn_catch:\nPCS\nBZR $a :__if1_endif\n__if1_start:\nFIN\n__if1_endif:\nINC @a\nFIN\n' + ], + [ + // Optimization with const nX variables + 'const long n233 = 233; long a, b[2]; b[a]=233; b[0]=233; while (a<233) { a++; };', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare n233\n^declare a\n^declare b\n^const SET @b #0000000000000006\n^declare b_0\n^declare b_1\n\n^const SET @n233 #00000000000000e9\nSET @($b + $a) $n233\nSET @b_0 $n233\n__loop1_continue:\nBGE $a $n233 :__loop1_break\n__loop1_start:\nINC @a\nJMP :__loop1_continue\n__loop1_break:\nFIN\n' + ], + [ + // Optimization with const nX variables + 'const long n2 = 2; struct KOMBI { long driver; long collector; long passenger; } *pcar; pcar->passenger="Ze";', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare n2\n^declare pcar\n\n^const SET @n2 #0000000000000002\nSET @r0 #000000000000655a\nSET @($pcar + $n2) $r0\nFIN\n' + ], + [ + // Macro codeStackPages test + '#program codeStackPages 10 \nlong a; void test(void) { a++; return; a++; }', + false, + '^program codeStackPages 10\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' + ], + [ + // Macro userStackPages test + '#program codeStackPages 0 \n#program userStackPages 5\n long a; void test(long aa) { a++; return; a++; }', + false, + '^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' + ], + [ + // Macro codeStackPages error test + '#program codeStackPages a\n#program userStackPages 0\nlong a; void test(void) { a++; return; a++; }', + true, + '' + ], + [ + // Macro outputSourceLineNumber test + '#pragma outputSourceLineNumber\nlong a=5;\nif (a==6){\na--;\n}\n', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare a\n\n^comment line 2\nSET @a #0000000000000005\n^comment line 3\nSET @r0 #0000000000000006\nBNE $a $r0 :__if1_endif\n__if1_start:\n^comment line 4\nDEC @a\n__if1_endif:\nFIN\n' + ], + /* + [ + // C + '', + false, + '' + ], + */ + // globalOptimization + ['globalOptimization', null, 'div'], + ['#pragma globalOptimization\nlong a,b; for (a=0;a<10;a++) { b++; } b--;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nCLR @a\n__loop1_condition:\nSET @r0 #000000000000000a\nBGE $a $r0 :__loop1_break\nINC @b\nINC @a\nJMP :__loop1_condition\n__loop1_break:\nDEC @b\nFIN\n'], + ['#pragma globalOptimization\nlong a,b; while (b) {a++; while (1) { if (a) break; } } a++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\n__loop1_continue:\nBZR $b :__loop1_break\nINC @a\n__loop2_continue:\nBZR $a :__loop2_continue\nJMP :__loop1_continue\n__loop1_break:\nINC @a\nFIN\n'], + ['#pragma globalOptimization\nlong a,b; if (!b) {a++; } b++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBNZ $b :__if1_endif\nINC @a\n__if1_endif:\nINC @b\nFIN\n'], + ['#pragma globalOptimization\nlong a,b; void main (void) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\n\nPCS\nINC @a\nFIN\n'], + ['#pragma globalOptimization\nlong a,b; if (!b) {a++; } else { b++;} ', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nBNZ $b :__if1_else\nINC @a\nFIN\n__if1_else:\nINC @b\nFIN\n'], + ['#pragma globalOptimization\nlong a,b; test(); void test (void) { if (a) a++; else b++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nJSR :__fn_test\nFIN\n\n__fn_test:\nBZR $a :__if1_else\nINC @a\nRET\n__if1_else:\nINC @b\nRET\n'], + ['#pragma globalOptimization\nlong a,b; test(); exit; a++; void test (void) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nJSR :__fn_test\nFIN\n\n__fn_test:\nINC @a\nRET\n'], + ['#pragma globalOptimization\nlong a,b; test(); void test (void) { return; a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nJSR :__fn_test\nFIN\n\n__fn_test:\nRET\n'], + ['#pragma globalOptimization\nlong a,b; test(); void test (void) { if (a) a++; else b++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nJSR :__fn_test\nFIN\n\n__fn_test:\nBZR $a :__if1_else\nINC @a\nRET\n__if1_else:\nINC @b\nRET\n'], + ['#pragma globalOptimization\nlong a, b, c, d; a=(b*c)*d;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n\nSET @a $b\nMUL @a $c\nMUL @a $d\nFIN\n'], + ['#pragma globalOptimization\nlong a[4][2], *b, c,d; b=&a[c][d];', false, '^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 a_4\n^declare a_5\n^declare a_6\n^declare a_7\n^declare b\n^declare c\n^declare d\n\nSET @r0 #0000000000000002\nMUL @r0 $c\nADD @r0 $d\nSET @b $a\nADD @b $r0\nFIN\n'], + ['#pragma globalOptimization\n long a; a=0; void test(void){ a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nCLR @a\nFIN\n\n'], + [`#pragma globalOptimization\nstruct KOMBI { long driver; long collector; long passenger; } ;struct KOMBI car, *pcar;long a, b, *c, d[2],z;pcar=&car; +pcar->passenger='Ze'; +pcar->driver=a; +b+=-a; +a=0; +d[a]=5; +for (a=0;a<10;a++) d[a]=1;\n +pcar->driver=*c;pcar->driver=d[1];pcar->driver=d[a];pcar->driver=pcar->collector; +a=pcar->collector;z++;*c=pcar->driver;d[1]=pcar->collector;d[a]=pcar->collector;`, false, '^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^declare z\n\nSET @pcar #0000000000000003\nSET @r0 #000000000000655a\nSET @r1 #0000000000000002\nSET @($pcar + $r1) $r0\nSET @($pcar) $a\nCLR @r0\nSUB @r0 $a\nADD @b $r0\nSET @r0 #0000000000000005\nSET @($d) $r0\nCLR @a\n__loop1_condition:\nSET @r0 #000000000000000a\nBGE $a $r0 :__loop1_break\nSET @r0 #0000000000000001\nSET @($d + $a) $r0\nINC @a\nJMP :__loop1_condition\n__loop1_break:\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)\nINC @z\nSET @r0 $($pcar)\nSET @($c) $r0\nSET @r0 #0000000000000001\nSET @d_1 $($pcar + $r0)\nSET @r1 #0000000000000001\nSET @r0 $($pcar + $r1)\nSET @($d + $a) $r0\nFIN\n'], + ['#pragma globalOptimization\n long d[2]; d[1]=d[1]+1;', false, '^declare r0\n^declare r1\n^declare r2\n^declare d\n^const SET @d #0000000000000004\n^declare d_0\n^declare d_1\n\nINC @d_1\nFIN\n'], + [`#pragma globalOptimization\n#pragma maxConstVars 3\nstruct KOMBI { long driver; long collector; long passenger; } ;struct KOMBI car, *pcar;long a, b, *c, d[2],z;pcar=&car; +pcar->passenger='Ze'; +pcar->driver=a; +b+=-a; +a=0; +d[a]=5; +for (a=0;a<10;a++) d[a]=1;\n +pcar->driver=*c;pcar->driver=d[1];pcar->driver=d[a];pcar->driver=pcar->collector; +a=pcar->collector;z++;*c=pcar->driver;d[1]=pcar->collector;d[a]=pcar->collector;`, false, '^declare r0\n^declare r1\n^declare r2\n^declare n1\n^const SET @n1 #0000000000000001\n^declare n2\n^const SET @n2 #0000000000000002\n^declare n3\n^const SET @n3 #0000000000000003\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 #000000000000000e\n^declare d_0\n^declare d_1\n^declare z\n\nSET @pcar #0000000000000006\nSET @r0 #000000000000655a\nSET @($pcar + $n2) $r0\nSET @($pcar) $a\nCLR @r0\nSUB @r0 $a\nADD @b $r0\nSET @r0 #0000000000000005\nSET @($d) $r0\nCLR @a\n__loop1_condition:\nSET @r0 #000000000000000a\nBGE $a $r0 :__loop1_break\nSET @($d + $a) $n1\nINC @a\nJMP :__loop1_condition\n__loop1_break:\nSET @r0 $($c)\nSET @($pcar) $r0\nSET @($pcar) $d_1\nSET @r0 $($d + $a)\nSET @($pcar) $r0\nSET @r0 $($pcar + $n1)\nSET @($pcar) $r0\nSET @a $($pcar + $n1)\nINC @z\nSET @r0 $($pcar)\nSET @($c) $r0\nSET @d_1 $($pcar + $n1)\nSET @r0 $($pcar + $n1)\nSET @($d + $a) $r0\nFIN\n'], + ['#pragma globalOptimization\n#pragma maxConstVars 3\nlong a, b, c; teste(a, 2); void teste(long aa, long bb) { aa=bb;} ', false, '^declare r0\n^declare r1\n^declare r2\n^declare n1\n^const SET @n1 #0000000000000001\n^declare n2\n^const SET @n2 #0000000000000002\n^declare n3\n^const SET @n3 #0000000000000003\n^declare a\n^declare b\n^declare c\n^declare teste_aa\n^declare teste_bb\n\nPSH $n2\nPSH $a\nJSR :__fn_teste\nFIN\n\n__fn_teste:\nPOP @teste_aa\nPOP @teste_bb\nSET @teste_aa $teste_bb\nRET\n'], + ['#pragma globalOptimization\n#pragma maxConstVars 3\nsleep 1;', false, '^declare r0\n^declare r1\n^declare r2\n^declare n1\n^const SET @n1 #0000000000000001\n^declare n2\n^const SET @n2 #0000000000000002\n^declare n3\n^const SET @n3 #0000000000000003\n\nSLP $n1\nFIN\n'], + ['#pragma globalOptimization\n long a,b; if ( a==4 && (b || a )) { a++; a=4;} b++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nSET @r0 #0000000000000004\nBNE $a $r0 :__if1_endif\nBNZ $b :__if1_start\nBZR $a :__if1_endif\n__if1_start:\nINC @a\nSET @a #0000000000000004\n__if1_endif:\nINC @b\nFIN\n'], + ['#pragma globalOptimization\n long a,b, c; if ( a==4 && (b || a>c )) { a++; a=4; } b++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n\nSET @r0 #0000000000000004\nBNE $a $r0 :__if1_endif\nBNZ $b :__if1_start\nBLE $a $c :__if1_endif\n__if1_start:\nINC @a\nSET @a #0000000000000004\n__if1_endif:\nINC @b\nFIN\n'], + ['#pragma globalOptimization\n long a; a=~a;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nNOT @a\nFIN\n'], + ['#pragma globalOptimization\n long a, b; tt(teste(b)); long teste(long c){ return ++c; } void tt(long d){ d++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare teste_c\n^declare tt_d\n\nPSH $b\nJSR :__fn_teste\nJSR :__fn_tt\nFIN\n\n__fn_teste:\nPOP @teste_c\nINC @teste_c\nPSH $teste_c\nRET\n\n__fn_tt:\nPOP @tt_d\nINC @tt_d\nRET\n'], + ['#pragma globalOptimization\n long a, b; /* No opt: interference with reuseVariable */ a=teste(teste(b)); tt(a); long teste(long c){ return ++c; } void tt(long d){ d++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare teste_c\n^declare tt_d\n\nPSH $b\nJSR :__fn_teste\nPOP @a\nPSH $a\nJSR :__fn_teste\nPOP @a\nPSH $a\nJSR :__fn_tt\nFIN\n\n__fn_teste:\nPOP @teste_c\nINC @teste_c\nPSH $teste_c\nRET\n\n__fn_tt:\nPOP @tt_d\nINC @tt_d\nRET\n'], + // [ "", false, "" ], + // const keyword + ['const keyword', null, 'div'], + ['struct KOMBI { long driver; long collector; long passenger[4]; } car[2]; long a, b, *c, d[2];\nconst a=353; const d[1]=354; const car[1].driver=355; const car[0].passenger[1]=356;', false, '^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^const SET @car_0_passenger #0000000000000007\n^declare car_0_passenger_0\n^declare car_0_passenger_1\n^declare car_0_passenger_2\n^declare car_0_passenger_3\n^declare car_1_driver\n^declare car_1_collector\n^declare car_1_passenger\n^const SET @car_1_passenger #000000000000000e\n^declare car_1_passenger_0\n^declare car_1_passenger_1\n^declare car_1_passenger_2\n^declare car_1_passenger_3\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #0000000000000016\n^declare d_0\n^declare d_1\n\n^const SET @a #0000000000000161\n^const SET @d_1 #0000000000000162\n^const SET @car_1_driver #0000000000000163\n^const SET @car_0_passenger_1 #0000000000000164\nFIN\n'], + ['long a, b, *c, d[2]; a++; const long e=5; a++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #0000000000000007\n^declare d_0\n^declare d_1\n^declare e\n\nINC @a\n^const SET @e #0000000000000005\nINC @a\nFIN\n'], + ['long a, b, *c, d[2]; a++; const long e=5; a++; e++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #0000000000000007\n^declare d_0\n^declare d_1\n^declare e\n\nINC @a\n^const SET @e #0000000000000005\nINC @a\nINC @e\nFIN\n'], + ['long a, b, *c, d[2]; a++; const long e=5; const a=2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #0000000000000007\n^declare d_0\n^declare d_1\n^declare e\n\nINC @a\n^const SET @e #0000000000000005\n^const SET @a #0000000000000002\nFIN\n'], + ['long a, b, *c, d[2]; a++; const b=3+3+4;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #0000000000000007\n^declare d_0\n^declare d_1\n\nINC @a\n^const SET @b #000000000000000a\nFIN\n'], + ['long a, b, *c, d[2]; const d=&a;', true, ''], + ['long a, b, *c, d[2]; const long e=3; a++; const e=4;', true, ''], + ['long a, b, *c, d[2]; a++; const b=3+3+4;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare c\n^declare d\n^const SET @d #0000000000000007\n^declare d_0\n^declare d_1\n\nINC @a\n^const SET @b #000000000000000a\nFIN\n'], + // when const declaration in multilong assigment, change CLR to SET #0 + ["long a[3]; const a[]='alow'; a[]='tchau';", false, '^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\n^const SET @a_0 #00000000776f6c61\n^const SET @a_1 #0000000000000000\n^const SET @a_2 #0000000000000000\nSET @a_0 #0000007561686374\nCLR @a_1\nCLR @a_2\nFIN\n'], + // After above optimizations, this code was being executed wrong + ['long a, b[5]; b[]=&a;', true, ''], + // [ "", false, "" ], + // program macro + ['macro program', null, 'div'], + ['#program name tEst2\n long a; a++;', false, '^program name tEst2\n^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n'], + ['#program description test teste tesssttt\n long a; a++;', false, '^program description test teste tesssttt\n^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n'], + ['#program activationAmount 100000\n long a; a++;', false, '^program activationAmount 100000\n^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n'], + ['#program name test-2\n long a; a++;', true, ''], + ['#program name test2 d\n long a; a++;', true, ''], + ['#program activationAmount 0xff\n long a; a++;', true, ''], + // allow _ in activationAmount + ['#program activationAmount 5_0000_0000', false, '^program activationAmount 500000000\n^declare r0\n^declare r1\n^declare r2\n\nFIN\n'], + // [ "", false, "" ], + // define macro + ['macro define', null, 'div'], + ['#define MAX 4\nlong a; a=MAX; long MAXimus=2;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare MAXimus\n\nSET @a #0000000000000004\nSET @MAXimus #0000000000000002\nFIN\n'], + ['#define MAX 4\n long a; a=MAX;\n #define MAX 2\n long MAXimus=MAX;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare MAXimus\n\nSET @a #0000000000000004\nSET @MAXimus #0000000000000002\nFIN\n'], + ['#define MAX 4\n long a; a=MAX;\n #define MAX \n long MAXimus=MAX;', true, ''], + ['#define 444 4\nlong a; a=444;\n #undef 444\nlong MAXimus=444;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare MAXimus\n\nSET @a #0000000000000004\nSET @MAXimus #00000000000001bc\nFIN\n'], + ['#define MAX 4\n#define MAX1 (MAX + 1)\n long a; if (a > MAX1) a++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #0000000000000005\nBLE $a $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + ['#define MAX 4\n#define MAX1 (MAX + 1)\n#undef MAX\n long a; if (a > MAX1) a++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @r0 #0000000000000005\nBLE $a $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + // [ "", false, "" ], + // ifdef macro + [ + 'macro ifdef', + null, + 'div' + ], + [ + '#define debug\n#ifdef debug\n#pragma maxAuxVars 1\n#endif\nlong a; a++;', + false, + '^declare r0\n^declare a\n\nINC @a\nFIN\n' + ], + [ + '#ifdef debug\n#pragma maxAuxVars 1\n#endif\nlong a; a++;', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nINC @a\nFIN\n' + ], + [ + '#ifdef debug\n#pragma maxAuxVars 1\n#else\n#pragma maxAuxVars 5\n#endif\nlong a; a++;', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare r3\n^declare r4\n^declare a\n\nINC @a\nFIN\n' + ], + [ + '#define A1\n#define A2\n\n#ifdef A1\nlong a1;\n# ifdef A2\nlong a2;\n# endif\n#endif\n\n#ifdef A1\na1++;\n#endif\n\n#ifdef A2\na2++;\n#endif', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare a1\n^declare a2\n\nINC @a1\nINC @a2\nFIN\n' + ], + [ + '#define A1\n\n#ifdef A1\nlong a1;\n# ifdef A2\nlong a2;\n# endif\n#endif\n\n#ifdef A1\na1++;\n#endif\n\n#ifdef A2\na2++;\n#endif', + false, + '^declare r0\n^declare r1\n^declare r2\n^declare a1\n\nINC @a1\nFIN\n' + ], + [ + '#ifdef A1\nlong a1;\n# ifdef A2\nlong a2;\n# endif\n#endif\n\n#ifdef A1\na1++;\n#endif\n\n#ifdef A2\na2++;\n#endif', + false, + '^declare r0\n^declare r1\n^declare r2\n\nFIN\n' + ], + [ + '#define A2\n\n#ifdef A1\nlong a1;\n# ifdef A2\nlong a2;\n# endif\n#endif\n\n#ifdef A1\na1++;\n#endif\n\n#ifdef A2\na2++;\n#endif', + true, + '' + ], + // bugfixes + ['Bug fixes', null, 'div'], + // bug 1, goto failed with undeclared variable + ['void teste(long ret) { long temp = 2; goto newlabel; ret = temp; newlabel: temp++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare teste_ret\n^declare teste_temp\n\nFIN\n\n__fn_teste:\nPOP @teste_ret\nSET @teste_temp #0000000000000002\nJMP :newlabel\nSET @teste_ret $teste_temp\nnewlabel:\nINC @teste_temp\nRET\n'], + // bug 2, failed when declaring pointer on function declaration + ['void teste(long * ret) { long temp = 2; goto newlabel; ret[temp] = temp; newlabel: temp++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare teste_ret\n^declare teste_temp\n\nFIN\n\n__fn_teste:\nPOP @teste_ret\nSET @teste_temp #0000000000000002\nJMP :newlabel\nSET @($teste_ret + $teste_temp) $teste_temp\nnewlabel:\nINC @teste_temp\nRET\n'], + ['void teste(long * ret) { long temp = 2; goto newlabel; *(ret+temp) = temp; newlabel: temp++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare teste_ret\n^declare teste_temp\n\nFIN\n\n__fn_teste:\nPOP @teste_ret\nSET @teste_temp #0000000000000002\nJMP :newlabel\nSET @r0 $teste_ret\nADD @r0 $teste_temp\nSET @($r0) $teste_temp\nnewlabel:\nINC @teste_temp\nRET\n'], + // bug 3, ReuseAssignedVar not working inside a function. + ["#pragma maxAuxVars 2\nlong itoa(long val) {\n long ret, temp;\n if (val >= 0 && val <= 99999999) { ret = (ret << 8) + temp; return ret; }\n return '#error';\n}", false, '^declare r0\n^declare r1\n^declare itoa_val\n^declare itoa_ret\n^declare itoa_temp\n\nFIN\n\n__fn_itoa:\nPOP @itoa_val\nCLR @r0\nBLT $itoa_val $r0 :__if1_endif\nSET @r0 #0000000005f5e0ff\nBGT $itoa_val $r0 :__if1_endif\nJMP :__if1_start\n__if1_start:\nSET @r0 $itoa_ret\nSET @r1 #0000000000000008\nSHL @r0 $r1\nADD @r0 $itoa_temp\nSET @itoa_ret $r0\nPSH $itoa_ret\nRET\n__if1_endif:\nSET @r0 #0000726f72726523\nPSH $r0\nRET\n'], + // bug 4, Double declaration causing array pointer to point wrong location. + ['long a=0; long b; a++; long a=3;', true, ''], + ['long a=0; long b; a++; void test(void) { a++; } long tt(void) { a++;} long test(void) {a++; return a; }', true, ''], + ['long a=0; long b; a++; void test(void) { a++; } long tt(void) { a++;} long test(void) {a++; return a; }', true, ''], + ['long a=0; void Get_B1(void) { a++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nCLR @a\nFIN\n\n__fn_Get_B1:\nINC @a\nRET\n'], + ['#include APIFunctions\nlong a=0; void Get_B1(void) { a++; }', true, ''], + ['long a=0; mylabel: a++; void temp(void) { a++; mylabel: a++; }', true, ''], + // bug 5, reuseAssignedVar not working inside functions. + ['void test(void) { long t, a; t = a+1; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare test_t\n^declare test_a\n\nFIN\n\n__fn_test:\nSET @test_t $test_a\nINC @test_t\nRET\n'], + // bug 6, removed warning when function returning long had no assignment. (removed failed case from other testcase + // bug 7, array type definition not found when declaring array inside functions. + ['void test(void) { long t[2], a; t[a] = 1; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare test_t\n^const SET @test_t #0000000000000004\n^declare test_t_0\n^declare test_t_1\n^declare test_a\n\nFIN\n\n__fn_test:\nSET @r0 #0000000000000001\nSET @($test_t + $test_a) $r0\nRET\n'], + // bug 8, wrong order of stack for function call + ['long ga, gb, gc; test(ga, gb, gc); void test(long a, long b, long c) { a+=b+c; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare ga\n^declare gb\n^declare gc\n^declare test_a\n^declare test_b\n^declare test_c\n\nPSH $gc\nPSH $gb\nPSH $ga\nJSR :__fn_test\nFIN\n\n__fn_test:\nPOP @test_a\nPOP @test_b\nPOP @test_c\nSET @r0 $test_b\nADD @r0 $test_c\nADD @test_a $r0\nRET\n'], + // optimization: array with constant index now used for reuseAssignedVar + ['long a[2], b; a[1]=b+1;', false, '^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 b\n\nSET @a_1 $b\nINC @a_1\nFIN\n'], + // Support for array notation on pointer variable. + ['long b; void teste(long * poper) { poper[3]=0; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare b\n^declare teste_poper\n\nFIN\n\n__fn_teste:\nPOP @teste_poper\nCLR @r0\nSET @r1 #0000000000000003\nSET @($teste_poper + $r1) $r0\nRET\n'], + // Support for check variable types on function calls + ['long a, b; teste(a, b); void teste(long *fa, long fb) { fb++; }', true, ''], + ['long * a, b; teste(a, b); void teste(long *fa, long fb) { fb++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare teste_fa\n^declare teste_fb\n\nPSH $b\nPSH $a\nJSR :__fn_teste\nFIN\n\n__fn_teste:\nPOP @teste_fa\nPOP @teste_fb\nINC @teste_fb\nRET\n'], + ['long * a, b; teste(a, *a); void teste(long *fa, long fb) { fb++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare teste_fa\n^declare teste_fb\n\nSET @r0 $($a)\nPSH $r0\nPSH $a\nJSR :__fn_teste\nFIN\n\n__fn_teste:\nPOP @teste_fa\nPOP @teste_fb\nINC @teste_fb\nRET\n'], + ['long * a, b; teste(&b, b); void teste(long *fa, long fb) { fb++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare teste_fa\n^declare teste_fb\n\nPSH $b\nSET @r0 #0000000000000004\nPSH $r0\nJSR :__fn_teste\nFIN\n\n__fn_teste:\nPOP @teste_fa\nPOP @teste_fb\nINC @teste_fb\nRET\n'], + ["struct KOMBI { long driver; long collector; long passenger; } ;struct KOMBI car, *pcar;long a, b;pcar=&car;\n teste(car);\n void teste(struct KOMBI * value) { value->driver = 'Zé'; }", true, ''], + ["struct KOMBI { long driver; long collector; long passenger; } ;struct KOMBI car, *pcar;long a, b;pcar=&car;\n teste(pcar);\n void teste(struct KOMBI * value) { value->driver = 'Zé'; }", false, '^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 teste_value\n\nSET @pcar #0000000000000003\nPSH $pcar\nJSR :__fn_teste\nFIN\n\n__fn_teste:\nPOP @teste_value\nSET @r0 #0000000000a9c35a\nCLR @r1\nSET @($teste_value + $r1) $r0\nRET\n'], + // Support for check variable types on API Function calls + ['#include APIFunctions\nlong * a;Set_A1(a);', true, ''], + // Support SetUnaryOperator in struct members, but not if it is an array + ['struct KOMBI { long driver; long collector; long passenger[4]; } car; long a, b; ++car.driver; a=car.collector++;', false, '^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 a\n^declare b\n\nINC @car_driver\nSET @a $car_collector\nINC @car_collector\nFIN\n'], + ['struct KOMBI { long driver; long collector; long passenger[4]; } car; long a, b; ++car.passenger[a]; ', true, ''], + // bug 9, missing comma before if, while and for keywords lead to no error and statement being ignored. + ['long a, b; test2() if (a) a++; long test2(void) { b++; return b; }', true, ''], + ['long a, b; test2(); if (a) a++; long test2(void) { b++; return b; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n\nJSR :__fn_test2\nPOP @r0\nBZR $a :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n\n__fn_test2:\nINC @b\nPSH $b\nRET\n'], + // bug 10, functions calls destroying content of registers. Implemented saving them in user stack + ["long a[5], b, c; b=atoi(c); a[b+1]=atoi('2'); a[b+1]=(b*2)/atoi('2'); long atoi(long val){return val+1;}", false, '^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 a_4\n^declare b\n^declare c\n^declare atoi_val\n\nPSH $c\nJSR :__fn_atoi\nPOP @b\nSET @r0 $b\nINC @r0\nPSH $r0\nSET @r1 #0000000000000032\nPSH $r1\nJSR :__fn_atoi\nPOP @r1\nPOP @r0\nSET @($a + $r0) $r1\nSET @r0 $b\nINC @r0\nSET @r1 #0000000000000002\nMUL @r1 $b\nPSH $r1\nPSH $r0\nSET @r2 #0000000000000032\nPSH $r2\nJSR :__fn_atoi\nPOP @r2\nPOP @r0\nPOP @r1\nDIV @r1 $r2\nSET @($a + $r0) $r1\nFIN\n\n__fn_atoi:\nPOP @atoi_val\nSET @r0 $atoi_val\nINC @r0\nPSH $r0\nRET\n'], + // bug 11, function calls inside array brackets lead to error. + ['long a[5], b, c; b=a[atoi("2")+1]; long atoi(long val){ return val+1;}', false, '^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 a_4\n^declare b\n^declare c\n^declare atoi_val\n\nSET @b #0000000000000032\nPSH $b\nJSR :__fn_atoi\nPOP @b\nINC @b\nSET @b $($a + $b)\nFIN\n\n__fn_atoi:\nPOP @atoi_val\nSET @r0 $atoi_val\nINC @r0\nPSH $r0\nRET\n'], + // bug 13, optimization deleting assembly compiler directives + ['#pragma globalOptimization\nwhile (1) halt; const long n8=8, n10=10, n0xff=0xff; long atoi(long val) { return 3; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare n8\n^declare n10\n^declare n0xff\n^declare atoi_val\n\n__loop1_continue:\nSTP\nJMP :__loop1_continue\n^const SET @n8 #0000000000000008\n^const SET @n10 #000000000000000a\n^const SET @n0xff #00000000000000ff\n\n'], + // bug 14, (more) optimization deleting assembly compiler directives + ['#pragma globalOptimization\n teste(); exit; const long n0xff=0xff; void teste(void) { const long b=5; b++; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare n0xff\n^declare teste_b\n\nJSR :__fn_teste\nFIN\n^const SET @n0xff #00000000000000ff\n\n__fn_teste:\n^const SET @teste_b #0000000000000005\nINC @teste_b\nRET\n'], + // bug 15, Need to be more restrictive on globalOptimization for PSH and POP + ['#pragma globalOptimization\n long a, b; a=b;insertPlayer(a); void insertPlayer(long address) { long id; id=(address >> 27); }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare b\n^declare insertPlayer_address\n^declare insertPlayer_id\n\nSET @a $b\nPSH $a\nJSR :__fn_insertPlayer\nFIN\n\n__fn_insertPlayer:\nPOP @insertPlayer_address\nSET @insertPlayer_id $insertPlayer_address\nSET @r0 #000000000000001b\nSHR @insertPlayer_id $r0\nRET\n'], + // bug 16, Could not return or sleep with array and variable index + ['long a, slot[4]; sleep slot[a];', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare slot\n^const SET @slot #0000000000000005\n^declare slot_0\n^declare slot_1\n^declare slot_2\n^declare slot_3\n\nSET @r0 $($slot + $a)\nSLP $r0\nFIN\n'], + // bug 17 Could not start program with function + ['XOR_A_with_B();\n#include APIFunctions', false, '^declare r0\n^declare r1\n^declare r2\n\nFUN XOR_A_with_B\nFIN\n'], + // bug 18 Fix infinite recursion loop with wrong code + ['Send_To_Address_In_B(sendEachBlockNQT) sleep SLP_BLOCKS;', true, ''], + // bug19 Optimization creating wrong code. Removed optimization on double SET instruction + ['#pragma globalOptimization\nlong _idx, uCount; _idx = ~(_idx+uCount); uCount = _idx; _idx++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare _idx\n^declare uCount\n\nSET @r0 $_idx\nADD @r0 $uCount\nNOT @r0\nSET @_idx $r0\nSET @uCount $_idx\nINC @_idx\nFIN\n'], + // bug 20 Fixes some wrong translation of RS-accounts + ["long a='S-D3HS-T6ML-SJHU-2R5R2';", false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nSET @a #005c77c9272585f8\nFIN\n'], + // bug 21 Memory was not assigned when declaring struct after a struct pointer + ['struct KOMBI { long driver; long collector; long passenger; }; void teste(void) { struct KOMBI tt2, *stru, tt, *stru2; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare teste_tt2_driver\n^declare teste_tt2_collector\n^declare teste_tt2_passenger\n^declare teste_stru\n^declare teste_tt_driver\n^declare teste_tt_collector\n^declare teste_tt_passenger\n^declare teste_stru2\n\nFIN\n\n__fn_teste:\nRET\n'], + // bug 22 Function/API Functions on conditional not being evaluated + ['long a=0; if (test2(a)){ a++; } long test2(long b) { b++; return b; }', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n^declare test2_b\n\nCLR @a\nPSH $a\nJSR :__fn_test2\nPOP @r0\nBZR $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n\n__fn_test2:\nPOP @test2_b\nINC @test2_b\nPSH $test2_b\nRET\n'], + ['#include APIFunctions\n long a=0; if (Get_A1()){ a++;} ', false, '^declare r0\n^declare r1\n^declare r2\n^declare a\n\nCLR @a\nFUN @r0 get_A1\nBZR $r0 :__if1_endif\n__if1_start:\nINC @a\n__if1_endif:\nFIN\n'], + // bug 23 Parser trying to get property of undefined variable + ['long a, b; *a + 1 = b', true, ''], + // bug 24 Wrong code being sucessfull compiled: ASM and Label disregarding information before them. + ['void teste(long ret) { long temp = 2; if (temp==2){ goto div_end: } ret = temp; div_end: temp++; }', true, ''], + ['long a, b; a++; long c, asm { PSH $a\nPOP @b } b++;', true, ''], + // bug 25 Incompatibility with const keyword and optimization for contants values + ['#pragma maxConstVars 3\nconst long a = 1;const long n256 = 256;const long ac = 256;long ad = 256, ae = ac;', false, '^declare r0\n^declare r1\n^declare r2\n^declare n1\n^const SET @n1 #0000000000000001\n^declare n2\n^const SET @n2 #0000000000000002\n^declare n3\n^const SET @n3 #0000000000000003\n^declare a\n^declare n256\n^declare ac\n^declare ad\n^declare ae\n\n^const SET @a #0000000000000001\n^const SET @n256 #0000000000000100\n^const SET @ac #0000000000000100\nSET @ad $n256\nSET @ae $ac\nFIN\n'], + // bug 26 Keyword substring in variable name causing error + ['long doing; doing++;', false, '^declare r0\n^declare r1\n^declare r2\n^declare doing\n\nINC @doing\nFIN\n'], + // [ "", false, "" ], + ['End of tests!', null, ''] + ]; + // params: asmcode, expect error?, bytecode, bytedata + const bytecodeTests = [ + ['FIN\n', false, '28', ''], + ['SET @a #0000000000000100\nSET @b $a\nCLR @b\nINC @b\nDEC @a\nADD @a $b\nSUB @a $b\nMUL @a $b\nDIV @a $b\nBOR @a $b\nAND @a $b\nXOR @a $b\nSET @a $b\nNOT @a\nSET @a $($b)\nSET @a $c\nADD @a $b\nSET @a $($b + $c)\nPSH $b\nJSR :__fn_teste\nPOP @a\nSET @($a) $b\nSET @($a + $b) $c\nMOD @a $b\nSHL @a $b\nSHR @a $b\nSLP $a\nJMP :__fn_main\n\n__fn_teste:\nPOP @teste_d\nSET @r0 $teste_d\nINC @r0\nPSH $r0\nRET\n\n__fn_main:\nPCS\nINC @a\nFIN', + false, + '010000000000010000000000000201000000000000000301000000040100000005000000000600000000010000000700000000010000000800000000010000000900000000010000000a00000000010000000b00000000010000000c00000000010000000200000000010000000d000000000e00000000010000000200000000020000000600000000010000000f000000000100000002000000100100000012e400000011000000001400000000010000001500000000010000000200000016000000000100000017000000000100000018000000000100000025000000001afd0000001103000000020400000003000000040400000010040000001330040000000028', + ''], + ['BZR $a :__if1_endif\nINC @b\n__if1_endif:\nBNZ $a :__if2_endif\nINC @b\n__if2_endif:\nBLE $a $b :__if3_endif\nINC @b\n__if3_endif:\nBGE $a $b :__if4_endif\nINC @b\n__if4_endif:\nBLT $a $b :__if5_endif\nINC @b\n__if5_endif:\nBGT $a $b :__if6_endif\nINC @b\n__if6_endif:\nBNE $a $b :__if7_endif\nINC @b\n__if7_endif:\nBEQ $a $b :__if8_endif\nINC @b\n__if8_endif:\nFIN\n', + false, + '1b000000000b04010000001e000000000b04010000002200000000010000000f04010000002100000000010000000f04010000002000000000010000000f04010000001f00000000010000000f04010000002400000000010000000f04010000002300000000010000000f040100000028', + ''], + ['BZR $a :__if1_endif\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\n__if1_endif:\nINC @b\nFIN\n', + false, + '1e000000000b1a8d00000001000000000100000000000000010000000001000000000000000100000000010000000000000001000000000100000000000000010000000001000000000000000100000000010000000000000001000000000100000000000000010000000001000000000000000100000000010000000000000001000000000100000000000000040100000028', + ''], + ['__loop1_continue:\nBZR $a :__loop1_break\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nJMP :__loop1_continue\n__loop1_break:\nINC @b\nFIN\n', + false, + '1e000000000b1a850000000100000000010000000000000001000000000100000000000000010000000001000000000000000100000000010000000000000001000000000100000000000000010000000001000000000000000100000000010000000000000001000000000100000000000000010000000001000000000000001a00000000040100000028', + ''], + ['__loop1_continue:\nSET @a #0000000000000001\nBNZ $a :__loop1_continue\n__loop1_break:\nINC @b\nFIN', + false, + '010000000001000000000000001e00000000f3040100000028', + ''], + ['__loop1_continue:\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nSET @a #0000000000000001\nBNZ $a :__loop1_continue\n__loop1_break:\nINC @b\nFIN', + false, + '010000000001000000000000000100000000010000000000000001000000000100000000000000010000000001000000000000000100000000010000000000000001000000000100000000000000010000000001000000000000000100000000010000000000000001000000000100000000000000010000000001000000000000001b000000000b1a00000000040100000028', + ''], + ['FUN clear_A_B\nFUN set_A1 $a\nFUN set_A1_A2 $a $b\nFUN @a check_A_equals_B\nFUN @a add_Minutes_to_Timestamp $b $c\nFIN\n', + false, + '3222013310010000000034140100000000010000003527010000000037060400000000010000000200000028', + ''], + ['SET @a #0000000000000005\nSET @b #0000000000000004\n^const SET @c #9887766554433221\nINC @a\nFIN\n', + false, + '0100000000050000000000000001010000000400000000000000040000000028', + '000000000000000000000000000000002132435465768798'], + ['^declare r0\n^declare a\n^declare b\n^declare c\n\n^const SET @c #9887766554433221\nSET @a #0000000000000005\nSET @b #0000000000000004\nINC @a\nFIN\n', + false, + '0101000000050000000000000001020000000400000000000000040100000028', + '0000000000000000000000000000000000000000000000002132435465768798'], + ['^declare r0\n^declare a\n^declare b\n^declare c\n\n^const SET @c #9887766554433221\n^const SET @a #000000000000fafe\nSET @b #0000000000000004\nINC @a\nFIN\n', + false, + '01020000000400000000000000040100000028', + '0000000000000000fefa00000000000000000000000000002132435465768798'], + ['FIZ $a\nSTZ $a\nERR :__error\nINC @a\nNOP\nNOP\n__error:\nDEC @a', + false, + '260000000027000000002b1600000004000000007f7f0500000000', + ''], + // BUG 12: bug when changing branch with offset overflow (this code causes error on v0.1) + ['BNE $var1 $var15 :lab_aa6\nBNE $var1 $var15 :lab_de2\nlab_aa6:\nSET @var02 #2065726120756f59\nSET @var02 #2065726120756f59\nSET @var02 #2065726120756f59\nSET @var02 #656e776f20746f6e\nSET @var02 #65746920666f2072\nSET @var02 #0000000000002e6d\nFIN\nlab_af3:\nSET @var02 #65746920666f2072\nSET @var02 #65746920666f2072\nSET @var02 #65746920666f2072\nlab_de2:\nFIN\n', + false, + '240000000001000000192300000000010000000f1a8f0000000102000000596f7520617265200102000000596f7520617265200102000000596f75206172652001020000006e6f74206f776e65010200000072206f662069746501020000006d2e00000000000028010200000072206f6620697465010200000072206f6620697465010200000072206f662069746528', + ''] + /* [ "", + false, + "", + "" ], +*/ + ]; + let code; + let result = ''; + let itemPass = 0; + let itemFail = 0; + result += '

Assembly tests

'; + for (let i = 0; i < bytecodeTests.length; i++) { + try { + result += '
Test ' + i + ' '; + code = bytecode(bytecodeTests[i][0]); + if (bytecodeTests[i][1] === false) { + if (code.ByteCode === bytecodeTests[i][2] && code.ByteData === bytecodeTests[i][3]) { + result += 'Pass! (run OK)'; + itemPass++; + } + else { + result += `Fail... (run OK) Code: ${encodedStr(bytecodeTests[i][0])} +Exp ByteCode: ${bytecodeTests[i][2]} +Exp ByteData: ${bytecodeTests[i][3]} +GOT Bytecode: ${code.ByteCode} +GOT ByteData: ${code.ByteData}`; + itemFail++; + } + } + else { + result += `Fail... (run OK) Code: ${encodedStr(bytecodeTests[i][0])} +Expected to throw exception but +GOT Bytecode: ${code.ByteCode} +GOT ByteData: ${code.ByteData}`; + itemFail++; + } + } + catch (e) { + if (bytecodeTests[i][1] === true) { + result += 'Pass! (failed)'; + itemPass++; + } + else { + result += `Fail... (failed) Code: ${encodedStr(bytecodeTests[i][0])} +GOT: ${e}`; + itemFail++; + } + } + } + result += '

Full tests

'; + tests.forEach((currentTest, index) => { + try { + if (currentTest[1] === null) { + result += '\n

' + currentTest[0] + '

'; + return; + } + result += `
Test ${index} `; + code = generate(syntaxProcess(shape(parse(tokenize(preprocess(currentTest[0])))))); + if (currentTest[1] === false) { + if (code === currentTest[2]) { + result += `Pass! (run OK) Code: ${encodedStr(currentTest[0])}`; + itemPass++; + } + else { + result += `Fail... (run OK) Code: ${encodedStr(currentTest[0])} Expected: +${currentTest[2]} +GOT +${code}`; + itemFail++; + } + } + else { + result += `Fail... (run OK) Code: ${encodedStr(currentTest[0])} +Expected to throw exception +GOT +${code}`; + itemFail++; + } + } + catch (e) { + if (currentTest[1] === true) { + result += `Pass! (failed) Code: ${encodedStr(currentTest[0])}
${e}`; + itemPass++; + } + else { + result += `Fail... (failed) Code: ${encodedStr(currentTest[0])}
GOT
${e}`; + itemFail++; + } + } + }); + return `Tests completed: ${itemPass} Passed; ${itemFail} Failed.

${result}`; +} diff --git a/v0.3/out/tokenizer.js b/v0.3/out/tokenizer.js new file mode 100644 index 0000000..c45c2d9 --- /dev/null +++ b/v0.3/out/tokenizer.js @@ -0,0 +1,221 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/** + * Transforms input source code into an array of pre tokens. + * This array is not recursive. + * @param input source code + * @returns array of pre tokens + */ +// eslint-disable-next-line no-unused-vars +function tokenize(input) { + const singleTokensSpecs = [ + { char: '=', tokenType: 'equal' }, + { char: '*', tokenType: 'star' }, + { char: '!', tokenType: 'not' }, + { char: '[', tokenType: 'bracket' }, + { char: ']', tokenType: 'bracket' }, + { char: '-', tokenType: 'minus' }, + { char: '+', tokenType: 'plus' }, + { char: '\\', tokenType: 'backslash' }, + { char: '/', tokenType: 'forwardslash' }, + { char: '.', tokenType: 'dot' }, + { char: '<', tokenType: 'less' }, + { char: '>', tokenType: 'greater' }, + { char: '|', tokenType: 'pipe' }, + { char: '&', tokenType: 'and' }, + { char: '%', tokenType: 'percent' }, + { char: '^', tokenType: 'caret' }, + { char: ',', tokenType: 'comma' }, + { char: ';', tokenType: 'semi' }, + { char: '~', tokenType: 'tilde' }, + { char: '`', tokenType: 'grave' }, + { char: '(', tokenType: 'paren' }, + { char: ')', tokenType: 'paren' }, + { char: ':', tokenType: 'colon' }, + { char: '{', tokenType: 'curly' }, + { char: '}', tokenType: 'curly' }, + { char: '#', tokenType: 'SPECIAL' } + ]; + const regexSingleTokensSpecs = [ + { + start: /^(\/\/.*)/, + tokenType: 'NONE', + addLength: 0 + }, + { + start: /^(\s+)/, + tokenType: 'NONE', + addLength: 0 + }, + { + start: /^(\d[\d_]*\b)/, + tokenType: 'numberDec', + addLength: 0 + }, + { + start: /^0[xX]([\da-fA-F][\da-fA-F_]*\b)/, + tokenType: 'numberHex', + addLength: 2 + }, + { + start: /^(break|const|continue|do|else|exit|for|goto|halt|if|long|return|sleep|void|while)\b/, + tokenType: 'keyword', + addLength: 0 + }, + { + start: /^(asm)/, + tokenType: 'ASM', + addLength: 0 + }, + { + start: /^(struct)/, + tokenType: 'STRUCT', + addLength: 0 + }, + { + start: /^(\w+)/, + tokenType: 'variable', + addLength: 0 + } + ]; + const regexDoubleTokensSpecs = [ + { + start: /^\/\*/, + end: /^([\s\S]*?\*\/)/, + tokenType: 'NONE', + startLength: 2, + removeTrailing: 0, + errorMsg: "Missing '*/' to end comment section." + }, + { + start: /^"/, + end: /^([\s\S]*?")/, + tokenType: 'string', + startLength: 1, + removeTrailing: 1, + errorMsg: "Missing '\"' to end string." + }, + { + start: /^'/, + end: /^([\s\S]*?')/, + tokenType: 'string', + startLength: 1, + removeTrailing: 1, + errorMsg: "Missing \"'\" to end string." + } + ]; + let currentChar, remainingText; + let current = 0; + const preTokens = []; + let currentLine = 1; + while (current < input.length) { + currentChar = input.charAt(current); + remainingText = input.slice(current); + // Resolve double regex preTokens + const found = regexDoubleTokensSpecs.find(ruleN => { + const startParts = ruleN.start.exec(remainingText); + if (startParts != null) { + const endParts = ruleN.end.exec(remainingText.slice(ruleN.startLength)); + current += ruleN.startLength; + if (endParts !== null) { + if (ruleN.tokenType === 'NONE') { + currentLine += (endParts[1].match(/\n/g) || '').length; + current += endParts[1].length; + return true; // breaks find function + } + preTokens.push({ type: ruleN.tokenType, value: endParts[1].slice(0, -ruleN.removeTrailing), line: currentLine }); + currentLine += (endParts[1].match(/\n/g) || '').length; + current += endParts[1].length; + return true; // breaks find function + } + throw new TypeError(`At line: ${currentLine}. ${ruleN.errorMsg}`); + } + return false; + }); + if (found !== undefined) { + // item already processed + continue; + } + // Resolve single regex preTokens + const found2 = regexSingleTokensSpecs.find(ruleN => { + const startParts = ruleN.start.exec(remainingText); + if (startParts != null) { + if (ruleN.tokenType === 'NONE') { + currentLine += (startParts[1].match(/\n/g) || '').length; + current += startParts[1].length + ruleN.addLength; + return true; // breaks find function + } + if (ruleN.tokenType === 'ASM') { + const asmParts = /^(asm[^\w]*\{)([\s\S]*)/.exec(remainingText); + if (asmParts === null) { + throw new TypeError('At line:' + currentLine + ' Error parsing `asm { ... }` keyword'); + } + const endLocation = asmParts[2].indexOf('}'); + if (endLocation === -1) { + throw new TypeError(`At line: ${currentLine}. Ending '}' not found for 'asm { ... }' keyword.`); + } + const asmText = asmParts[2].slice(0, endLocation); + const asmCode = asmParts[1] + asmText + '}'; + preTokens.push({ type: 'keyword', value: 'asm', line: currentLine, extValue: asmText }); + currentLine += (asmCode.match(/\n/g) || '').length; + current += asmCode.length; + return true; // breaks find function + } + if (ruleN.tokenType === 'STRUCT') { + const structParts = /^(struct\s+(\w+))/.exec(remainingText); + if (structParts === null) { + throw new TypeError(`At line: ${currentLine}. 'struct' keyword must be followed by a type name`); + } + preTokens.push({ type: 'keyword', value: 'struct', line: currentLine, extValue: structParts[2] }); + currentLine += (structParts[1].match(/\n/g) || '').length; + current += structParts[1].length; + return true; // breaks find function + } + preTokens.push({ type: ruleN.tokenType, value: startParts[1], line: currentLine }); + currentLine += (startParts[1].match(/\n/g) || '').length; + current += startParts[1].length + ruleN.addLength; + return true; // breaks find function + } + return false; + }); + if (found2 !== undefined) { + continue; + } + // Resolve all single preTokens + const search = singleTokensSpecs.find(charDB => charDB.char === currentChar); + if (search !== undefined) { + if (search.tokenType === 'NONE') { + current++; + continue; + } + else if (search.tokenType === 'SPECIAL') { + if (search.char === '#') { + current++; + const lines = input.slice(current).split('\n'); + let i = 0; + let val = ''; + for (; i < lines.length; i++) { + val += lines[i]; + current += lines[i].length + 1; // newline! + currentLine++; + if (lines[i].endsWith('\\')) { + val = val.slice(0, -1); + continue; + } + break; + } + preTokens.push({ type: 'macro', value: val, line: currentLine - i - 1 }); + continue; + } + throw new TypeError(`At line: ${currentLine}. SPECIAL rule not implemented in tokenizer().`); + } + preTokens.push({ type: search.tokenType, value: currentChar, line: currentLine }); + current++; + continue; + } + throw new TypeError(`At line: ${currentLine}. Forbidden character found: '${currentChar}'.`); + } + return preTokens; +} diff --git a/v0.3/out/utils.js b/v0.3/out/utils.js new file mode 100644 index 0000000..6782554 --- /dev/null +++ b/v0.3/out/utils.js @@ -0,0 +1,294 @@ +"use strict"; +// Author: Rui Deleterium +// Project: https://github.com/deleterium/SmartC +// License: BSD 3-Clause License +/* global TOKEN AST MEMORY_SLOT DECLARATION_TYPES */ +/** + * Simple functions that do not depend external variables. + */ +// eslint-disable-next-line no-unused-vars +const utils = { + /** Creates a constant Memory Object */ + createConstantMemObj(value = '') { + let param; + if (typeof (value) === 'number') { + if (value % 1 !== 0) { + throw new TypeError('Only integer numbers in createConstantMemObj().'); + } + param = value.toString(16).padStart(16, '0').slice(-16); + } + else if (typeof (value) === 'string') { + param = value.padStart(16, '0').slice(-16); + } + else { + throw new TypeError('Unknow value arrived at createConstantMemObj().'); + } + return { + address: -1, + name: '', + asmName: '', + type: 'constant', + scope: '', + size: 1, + declaration: 'long', + isDeclared: true, + hexContent: param + }; + }, + /** Creates a constant Memory Object */ + createVoidMemObj() { + return { + address: -1, + name: '', + asmName: '', + type: 'void', + scope: '', + size: 0, + declaration: 'void', + isDeclared: true + }; + }, + genMulToken(line = -1) { + return { type: 'Operator', precedence: 3, value: '*', line: line }; + }, + genAddToken(line = -1) { + return { type: 'Operator', precedence: 4, value: '+', line: line }; + }, + genSubToken(line = -1) { + return { type: 'Operator', precedence: 4, value: '-', line: line }; + }, + genAssignmentToken() { + return { type: 'Assignment', precedence: 9, value: '=', line: -1 }; + }, + genIncToken() { + return { type: 'SetUnaryOperator', precedence: 2, value: '++', line: -1 }; + }, + genDecToken() { + return { type: 'SetUnaryOperator', precedence: 2, value: '--', line: -1 }; + }, + genNotEqualToken() { + return { type: 'Comparision', precedence: 6, value: '!=', line: -1 }; + }, + genAPICallToken(line, name) { + if (name === undefined) { + throw new TypeError('Wrong APICall name in genAPICallToken'); + } + return { type: 'APICall', precedence: 0, value: name, line: line }; + }, + genPushToken(line) { + return { type: 'Push', precedence: 12, value: '', line: line }; + }, + mulHexContents(param1, param2) { + let n1, n2; + if (typeof (param1) === 'number') { + n1 = BigInt(param1); + } + else if (typeof (param1) === 'string') { + n1 = BigInt('0x' + param1); + } + else + throw new TypeError('Wrong type in mulHexContents'); + if (typeof (param2) === 'number') { + n2 = BigInt(param2); + } + else if (typeof (param2) === 'string') { + n2 = BigInt('0x' + param2); + } + else + throw new TypeError('Wrong type in mulHexContents'); + return (n1 * n2).toString(16).padStart(16, '0').slice(-16); + }, + divHexContents(param1, param2) { + let n1, n2; + if (typeof (param1) === 'number') { + n1 = BigInt(param1); + } + else if (typeof (param1) === 'string') { + n1 = BigInt('0x' + param1); + } + else + throw new TypeError('Wrong type in divHexContents'); + if (typeof (param2) === 'number') { + n2 = BigInt(param2); + } + else if (typeof (param2) === 'string') { + n2 = BigInt('0x' + param2); + } + else + throw new TypeError('Wrong type in divHexContents'); + return (n1 / n2).toString(16).padStart(16, '0').slice(-16); + }, + addHexContents(param1, param2) { + let n1, n2; + if (typeof (param1) === 'number') { + n1 = BigInt(param1); + } + else if (typeof (param1) === 'string') { + n1 = BigInt('0x' + param1); + } + else + throw new TypeError('Wrong type in addHexContents'); + if (typeof (param2) === 'number') { + n2 = BigInt(param2); + } + else if (typeof (param2) === 'string') { + n2 = BigInt('0x' + param2); + } + else + throw new TypeError('Wrong type in addHexContents'); + return (n1 + n2).toString(16).padStart(16, '0').slice(-16); + }, + subHexContents(param1, param2) { + let n1, n2; + if (typeof (param1) === 'number') { + n1 = BigInt(param1); + } + else if (typeof (param1) === 'string') { + n1 = BigInt('0x' + param1); + } + else + throw new TypeError('Wrong type in addHexContents'); + if (typeof (param2) === 'number') { + n2 = BigInt(param2); + } + else if (typeof (param2) === 'string') { + n2 = BigInt('0x' + param2); + } + else + throw new TypeError('Wrong type in addHexContents'); + let sub = n1 - n2; + if (sub < 0) { + sub += 18446744073709551616n; + } + return sub.toString(16).padStart(16, '0').slice(-16); + }, + /** Splits an AST into array of AST based on delimiters */ + splitASTOnDelimiters(Obj) { + const ret = []; + function recursiveSplit(recursiveAST) { + if (recursiveAST.type === 'endASN' || recursiveAST.type === 'lookupASN') { + ret.push(recursiveAST); + return; + } + if (recursiveAST.type === 'binaryASN' && recursiveAST.Operation.type === 'Delimiter') { + recursiveSplit(recursiveAST.Left); + recursiveSplit(recursiveAST.Right); + } + else { + ret.push(recursiveAST); + } + } + recursiveSplit(Obj); + return ret; + }, + /** + * Checks declaration types and return true if operation is not allowed. + * @param desiredDeclaration Should be this type + * @param MemoObj Object to test + * @returns return true if operation is NOT allowed. + */ + isNotValidDeclarationOp(desiredDeclaration, MemoObj) { + const memoDeclaration = this.getDeclarationFromMemory(MemoObj); + if (desiredDeclaration === 'void' || memoDeclaration === 'void') { + return true; + } + if (desiredDeclaration === memoDeclaration) { + return false; + } + if (desiredDeclaration === 'void_ptr' && (memoDeclaration === 'long_ptr' || memoDeclaration === 'struct_ptr')) { + return false; + } + if (memoDeclaration === 'void_ptr' && (desiredDeclaration === 'long_ptr' || desiredDeclaration === 'struct_ptr')) { + return false; + } + if (desiredDeclaration.includes('_ptr') && MemoObj.type === 'constant') { + // manual pointer assignment or multi byte array assignment. + return false; + } + return true; + }, + getDeclarationFromMemory(MemoObj) { + if (MemoObj.Offset === undefined) { + return MemoObj.declaration; + } + else { + return MemoObj.Offset.declaration; + } + }, + setMemoryDeclaration(MemoObj, dec) { + if (MemoObj.Offset === undefined) { + MemoObj.declaration = dec; + } + else { + MemoObj.Offset.declaration = dec; + } + }, + /** + * Simple otimization: + * 1) Remove unused labels + * 2) Removed unreachable jumps + * 3) Remove dummy jumps (jumps to next instruction) + */ + miniOptimizeJumps(asmCode) { + let tmpCodeLines = asmCode.split('\n'); + let optimizedLines; + do { + const jumpToLabels = []; + optimizedLines = tmpCodeLines.length; + // Collect information + tmpCodeLines.forEach(value => { + const jmp = /^.+\s:(\w+)$/.exec(value); + if (jmp !== null) { + jumpToLabels.push(jmp[1]); + } + }); + // remove labels without reference + tmpCodeLines = tmpCodeLines.filter((value) => { + const lbl = /^(\w+):$/.exec(value); + if (lbl !== null) { + if (jumpToLabels.indexOf(lbl[1]) !== -1) { + return true; + } + else { + return false; + } + } + else { + return true; + } + }); + // remove unreachable jumps + tmpCodeLines = tmpCodeLines.filter((value, index, array) => { + const jmp = /^JMP\s+.+/.exec(value); + if (jmp !== null) { + if (/^JMP\s+.+/.exec(array[index - 1]) !== null) { + return false; + } + else { + return true; + } + } + else { + return true; + } + }); + // remove meaningless jumps + tmpCodeLines = tmpCodeLines.filter((value, index, array) => { + const jmpto = /^.+\s:(\w+)$/.exec(value); + if (jmpto !== null) { + const lbl = /^(\w+):$/.exec(array[index + 1]); + if (lbl === null) { + return true; + } + if (jmpto[1] === lbl[1]) { + return false; + } + return true; + } + return true; + }); + optimizedLines -= tmpCodeLines.length; + } while (optimizedLines !== 0); + return tmpCodeLines.join('\n'); + } +}; diff --git a/v0.3/style.css b/v0.3/style.css new file mode 100644 index 0000000..8c6be32 --- /dev/null +++ b/v0.3/style.css @@ -0,0 +1,241 @@ +/* GENERAL config */ +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; + font-family: sans-serif; + background: #ddd; + display: grid; + grid-gap: 1em; +} + +h1 { + padding: 1vh; + background: #da7; + color: #114; + margin: 0 0 3vh 0; + text-align: center; + border-bottom: 0.5vh double #114; +} + +fieldset { + border: solid 0.5vh #ccc; + padding: 1em; + margin: 0em 1em; + border-radius: 2vh; +} + +legend, .tooltip { + background-color: #da7; + color: #114; + padding: 3px 6px; + border: 0.2vh solid #114; + transition: all ease 0.3s; +} +legend:hover { + background: #eb8; +} +legend { + cursor: pointer; +} + +#deployment_iframe { + border: 1px solid #114; + width: 97%; + height: 33vh; +} + +pre { + padding: 0; + margin: 0; + min-height: 50px; + font-family: "Lucida Console", "Courier New", monospace; + white-space: pre-wrap; + word-wrap: anywhere; + max-width: 100%; +} + +/* Buttons */ +.btn_class { + padding: 0.5em; + font-size: 100%; + text-align: center; + background: #fff; + transition: all ease 0.3s; + border: solid 0.5vh #ccc; + border-radius: 1vh; + margin: 0; +} + +#source_is_c:checked ~ #bt1 { background: #ccf; } +#debug:checked ~ #bt2 { background: #fdd; } +label:hover { background: #ddf; } +#debug:hover ~ #bt2 { background: #fcc; } +#test:hover { background: #fdd; } +.btn_class:hover{ background: #dfd; } + +/* CLASSES and IDs configs */ +.tooltip { + position: sticky; + white-space: nowrap; + display: inline; + bottom: 1%; + left: 100%; +} + +#source-code { + resize: none; + overflow-y: hidden; + overflow-x: auto; + outline: none; +} +#color_code { + position: absolute; + top: 0px; + left: 0px; + z-index: -1; +} +#source-code, #color_code { + background: #fafbfc; + border: 1px black solid; + border-radius: 5px; + font-size: 100%; + color: #114; + margin: 0; + padding: 1em; + font-family: "Lucida Console", "Courier New", monospace; + width: 100%; +} + +.transp { + opacity: 0; + transition: all ease 3s; +} +.opaque { + opacity: 1; + transition: all ease 0.2s; +} + +.asmInstruction { + color: mediumblue +} +.asmVariable { + color: purple; +} +.asmComment { + color: darkgreen; +} +.asmDirective { + color: brown; + font-weight: bold; +} +.asmLabel { + color: sienna; +} +.asmNumber { + color: red; +} +.asmError { + background-color: pink; +} + +::selection { + color: #114; + background: rgb(202, 202, 238); +} + +.msg_failure { + font-weight: bold; + color: red; +} +.msg_success { + font-weight: bold; + color: green; +} + +/* general lay-out */ +.table { + width: 100%; + display: table; +} +.div_row { + display: table-row; +} +.div_cell { + display: table-cell; + width: 50%; + vertical-align: middle; +} + +/*Grid for page (desktop) */ +@media only screen and (min-width: 960px) { + .gdrow1 { grid-row-start: 1; } + .gdrow2 { grid-row-start: 2; } + .gdrow3 { grid-row-start: 3; } + .gdrow4 { grid-row-start: 4; } + .gdcol1 { grid-column-start: 1; } + .gdcol2 { grid-column-start: 2; } + .gdcol12 { grid-column-start: 1; grid-column-end: 3; } +} + +/*Grid for page (mobile) */ +@media only screen and (max-width: 960px) { + .gmrow1 { grid-row-start: 1; } + .gmrow2 { grid-row-start: 2; } + .gmrow3 { grid-row-start: 3; } + .gmrow4 { grid-row-start: 4; } + .gmrow5 { grid-row-start: 5; } + .gmrow6 { grid-row-start: 6; } +} + +/*Grid for buttons */ +.grow1 { grid-row-start: 1; } +.grow2 { grid-row-start: 2; } +.gcol1 { grid-column-start: 1; } +.gcol2 { grid-column-start: 2; } +.gcol3 { grid-column-start: 3; } +.gcol4 { grid-column-start: 4; } +.gcol12 { grid-column-start: 1; grid-column-end: 3;} + +.div_window_source { + padding: 0px; + position: relative; +} +.div_window_normal { + padding: 1vh; +} +.div_window_grid { + padding: 1vh; + display: grid; + grid-gap: 1em; + /* justify-content: center; */ +} + +.inc_height { min-height: 150px; } + +.winbox { + background: linear-gradient(90deg, #228, #114); + border-radius: 12px 12px 0 0; + box-shadow: none; +} + +.winbox.min { + border-radius: 0; +} + +.wb-body { + /* the width of window border: */ + margin: 4px; + background: #ddd; +} + +.wb-title { + font-size: 13px; + text-transform: uppercase; + font-weight: 600; +} + +.wb-full { display: none } From cde0ef6d264d251a1eb74e729edab69daa6d19ac Mon Sep 17 00:00:00 2001 From: deleterium Date: Sat, 16 Oct 2021 10:09:01 -0300 Subject: [PATCH 4/4] Adjustments in released files dev -> v0.3 --- README.md | 2 +- v0.3/index.html | 4 ++-- v0.3/out/shaper.js | 4 ++-- v0.3/out/testcases.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 62738de..0014d3a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Write C smart contracts for signum network. Compile in your browser. Written in To empower developers, allowing them to create complex and highly optimized smart contracts. ## Usage -[Run on gitpages](https://deleterium.github.io/SmartC/v0.2/index.html) latest stable version. Optionally download project to your computer and open file `index.html` of desired version in your browser. After compiling, information to create the AT is presented and it is possible to deploy on your own local node. +[Run on gitpages](https://deleterium.github.io/SmartC/v0.3/index.html) latest stable version. Optionally download project to your computer and open file `index.html` of desired version in your browser. After compiling, information to create the AT is presented and it is possible to deploy on your own local node. ## FAQ Some information and explanation about the project for users that aren't programmers can be found on [Non-Technical FAQ](https://deleterium.github.io/SmartC/docs/Non-Technical-FAQ); diff --git a/v0.3/index.html b/v0.3/index.html index 0234cae..49ad82b 100644 --- a/v0.3/index.html +++ b/v0.3/index.html @@ -3,7 +3,7 @@ - SmartC - C Compiler for Signum - dev + SmartC - C Compiler for Signum - v0.3 @@ -28,7 +28,7 @@ -

SmartC - C Compiler for Signum - dev

+

SmartC - C Compiler for Signum - v0.3

❐ Source code:
diff --git a/v0.3/out/shaper.js b/v0.3/out/shaper.js index c034a23..4958279 100644 --- a/v0.3/out/shaper.js +++ b/v0.3/out/shaper.js @@ -22,14 +22,14 @@ function shape(tokenAST) { typesDefinitions: [], // Default configuration for compiler Config: { - compilerVersion: 'dev', + compilerVersion: '0.3', enableRandom: false, enableLineLabels: false, globalOptimization: false, maxAuxVars: 3, maxConstVars: 0, reuseAssignedVar: true, - version: 'dev', + version: '', warningToError: true, APIFunctions: false, PName: '', diff --git a/v0.3/out/testcases.js b/v0.3/out/testcases.js index 7b3238a..8494b29 100644 --- a/v0.3/out/testcases.js +++ b/v0.3/out/testcases.js @@ -920,7 +920,7 @@ void *ret(long *aa, void *bb) { aa++; return aa; }`, // Macro outputSourceLineNumber test '#pragma outputSourceLineNumber\nlong a=5;\nif (a==6){\na--;\n}\n', false, - '^declare r0\n^declare r1\n^declare r2\n^declare a\n\n^comment line 2\nSET @a #0000000000000005\n^comment line 3\nSET @r0 #0000000000000006\nBNE $a $r0 :__if1_endif\n__if1_start:\n^comment line 4\nDEC @a\n__if1_endif:\nFIN\n' + '^declare r0\n^declare r1\n^declare r2\n^declare a\n\n^comment line 3\nSET @a #0000000000000005\n^comment line 4\nSET @r0 #0000000000000006\nBNE $a $r0 :__if1_endif\n__if1_start:\n^comment line 5\nDEC @a\n__if1_endif:\nFIN\n' ], /* [ @@ -1235,7 +1235,7 @@ GOT: ${e}`; return; } result += `
Test ${index} `; - code = generate(syntaxProcess(shape(parse(tokenize(preprocess(currentTest[0])))))); + code = generate(syntaxProcess(shape(parse(tokenize(preprocess('#pragma version 0.3\n'+currentTest[0])))))); if (currentTest[1] === false) { if (code === currentTest[2]) { result += `Pass! (run OK) Code: ${encodedStr(currentTest[0])}`;