/* * Copyright (c) 2014, Claudio Lapilli and the newRPL Team * All rights reserved. * This file is released under the 3-clause BSD license. * See the file LICENSE.txt that shipped with this distribution. */ #include "newrpl.h" #include "libraries.h" #include "hal.h" // THIS LIBRARY PROVIDES ONLY COMPILATION OF IDENTS AFTER ALL OTHER LIBRARIES // HAD A CHANCE TO IDENTIFY THEIR COMMANDS // ANY LAM COMMANDS HAVE TO BE IN A SEPARATE LIBRARY // THERE'S ONLY ONE EXTERNAL FUNCTION: THE LIBRARY HANDLER // ALL OTHER FUNCTIONS ARE LOCAL // MAIN LIBRARY NUMBER, CHANGE THIS FOR EACH LIBRARY #define LIBRARY_NUMBER 20 #define LIB_ENUM lib20enum #define LIB_NAMES lib20_names #define LIB_HANDLER lib20_handler #define LIB_NUMBEROFCMDS LIB20_NUMBEROFCMDS // LIST OF COMMANDS EXPORTED, CHANGE FOR EACH LIBRARY #define CMD_LIST \ CMD(LSTO), \ CMD(LRCL), \ CMD(ABND), \ CMD(NULLLAM) // ADD MORE OPCODES HERE // EXTRA LIST FOR COMMANDS WITH SYMBOLS THAT ARE DISALLOWED IN AN ENUM // THE NAMES AND ENUM SYMBOLS ARE GIVEN SEPARATELY #define CMD_EXTRANAME \ "->" #define CMD_EXTRAENUM \ NEWLOCALENV // THESE ARE SPECIAL OPCODES FOR THE COMPILER ONLY // THE LOWER 16 BITS ARE THE NUMBER OF LAMS TO CREATE, OR THE INDEX OF LAM NUMBER TO STO/RCL #define NEWNLOCALS 0x40000 // SPECIAL OPCODE TO CREATE NEW LOCAL VARIABLES #define GETLAMNEVAL 0x30000 // SPECIAL OPCODE TO RCL THE CONTENT OF A LAM AND EVAL (XEQ ITS CONTENT) #define GETLAMN 0x20000 // SPECIAL OPCODE TO RCL THE CONTENT OF A LAM #define PUTLAMN 0x10000 // SPECIAL OPCODE TO STO THE CONTENT OF A LAM // INTERNAL DECLARATIONS // CREATE AN ENUM WITH THE OPCODE NAMES FOR THE DISPATCHER #define CMD(a) a enum LIB_ENUM { CMD_LIST , CMD_EXTRAENUM , LIB_NUMBEROFCMDS }; #undef CMD // AND A LIST OF STRINGS WITH THE NAMES FOR THE COMPILER #define CMD(a) #a char *LIB_NAMES[]= { CMD_LIST , CMD_EXTRANAME }; #undef CMD // INTERNAL RPL PROGRAM THAT CALLS ABND WORD abnd_prog[]= { (WORD)MKOPCODE(LIBRARY_NUMBER,ABND), // JUST A WORD THAT WILL BE SKIPPED BY THE COMPILER (WORD)MKOPCODE(LIBRARY_NUMBER,ABND) // THIS IS THE WORD THAT WILL BE EXECUTED // SEMI NOT NEEDED SINCE ABND ALREADY DOES SEMI }; // INTERNAL SINT OBJECTS WORD lam_baseseco_bint[]= { (WORD)LAM_BASESECO }; // INTERNAL SINT OBJECTS WORD lam_errhandler_bint[]= { (WORD)LAM_ERRHANDLER }; // INTERNAL NULLLAM IDENT OBJECTS WORD nulllam_ident[]= { (WORD)MKOPCODE(LIBRARY_NUMBER,NULLLAM) }; extern BINT64 powersof10[20]; void LIB_HANDLER() { if(ISPROLOG(CurOpcode)) { // PROVIDE BEHAVIOR OF EXECUTING THE OBJECT HERE // NORMAL BEHAVIOR ON A IDENT IS TO PUSH THE OBJECT ON THE STACK: rplPushData(IPtr); if(LIBNUM(CurOpcode)==LIBRARY_NUMBER+1) { // UNQUOTED LAM, NEED TO ALSO DO XEQ ON ITS CONTENTS { WORDPTR val=rplGetLAM(rplPeekData(1)); if(!val) { val=rplGetGlobal(rplPeekData(1)); if(!val) { // INEXISTENT IDENT EVALS TO ITSELF, SO RETURN DIRECTLY return; } } rplOverwriteData(1,val); // REPLACE THE FIRST LEVEL WITH THE VALUE LIBHANDLER han=rplGetLibHandler(LIBNUM(*val)); // AND EVAL THE OBJECT if(han) { BINT SavedOpcode=CurOpcode; CurOpcode=MKOPCODE(LIB_OVERLOADABLE,OVR_XEQ); // EXECUTE THE OTHER LIBRARY DIRECTLY (*han)(); // RESTORE THE PREVIOUS ONE ONLY IF THE HANDLER DID NOT CHANGE IT if(CurOpcode==MKOPCODE(LIB_OVERLOADABLE,OVR_XEQ)) CurOpcode=SavedOpcode; } else { // THE LIBRARY DOESN'T EXIST BUT THE OBJECT DOES? // THIS CAN ONLY HAPPEN IF TRYING TO EXECUTE WITH A CUSTOM OBJECT // WHOSE LIBRARY WAS UNINSTALLED AFTER BEING COMPILED (IT'S AN INVALID OBJECT) Exceptions=EX_BADARGTYPE; ExceptionPointer=IPtr; } return; } } else return; } // PROCESS OVERLOADED OPERATORS FIRST if(LIBNUM(CurOpcode)==LIB_OVERLOADABLE) { if(ISUNARYOP(CurOpcode)) { // APPLY UNARY OPERATOR DIRECTLY TO THE CONTENTS OF THE VARIABLE // TODO: ADD SYMBOLIC OPERATION MODE switch(OPCODE(CurOpcode)) { case OVR_EVAL: // RCL WHATEVER IS STORED IN THE LAM AND THEN XEQ ITS CONTENTS // NO ARGUMENT CHECKS! THAT SHOULD'VE BEEN DONE BY THE OVERLOADED "EVAL" DISPATCHER { WORDPTR val=rplGetLAM(rplPeekData(1)); if(!val) { val=rplGetGlobal(rplPeekData(1)); if(!val) { // INEXISTENT IDENT EVALS TO ITSELF, SO RETURN DIRECTLY return; } } rplOverwriteData(1,val); // REPLACE THE FIRST LEVEL WITH THE VALUE CurOpcode=MKOPCODE(LIB_OVERLOADABLE,OVR_XEQ); LIBHANDLER han=rplGetLibHandler(LIBNUM(*val)); // AND EVAL THE OBJECT if(han) { // EXECUTE THE OTHER LIBRARY DIRECTLY (*han)(); } else { // THE LIBRARY DOESN'T EXIST BUT THE OBJECT DOES? // THIS CAN ONLY HAPPEN IF TRYING TO EXECUTE WITH A CUSTOM OBJECT // WHOSE LIBRARY WAS UNINSTALLED AFTER BEING COMPILED (IT'S AN INVALID OBJECT) Exceptions=EX_BADARGTYPE; ExceptionPointer=IPtr; CurOpcode=*IPtr; } } return; case OVR_XEQ: // JUST KEEP THE IDENT ON THE STACK, UNEVALUATED return; default: // PASS AL OTHER OPERATORS DIRECTLY TO THE CONTENTS { WORDPTR val=rplGetLAM(rplPeekData(1)); if(!val) { val=rplGetGlobal(rplPeekData(1)); if(!val) { // TODO: INEXISTENT IDENT SHOULD BE OPERATED ON AS A SYMBOLIC Exceptions=EX_VARUNDEF; ExceptionPointer=IPtr; return; } } // CHECK FOR CIRCULAR REFERENCE if(ISIDENT(*val)) { // TODO: IDENTS NEED TO BE OPERATED ON AS A SYMBOLIC OBJECT Exceptions=EX_BADOPCODE; ExceptionPointer=IPtr; return; } // FOR ALL OTHER OBJECT TYPES, JUST APPLY THE OPERATOR rplOverwriteData(1,val); rplCallOvrOperator(CurOpcode); return; } } } // END OF UNARY OPERATORS if(ISBINARYOP(CurOpcode)) { // APPLY BINARY OPERATORS DIRECTLY TO THE CONTENTS OF THE VARIABLE // TODO: ADD SYMBOLIC OPERATION MODE if(ISIDENT(*rplPeekData(1))) { WORDPTR val=rplGetLAM(rplPeekData(1)); if(!val) { val=rplGetGlobal(rplPeekData(1)); if(!val) { // INEXISTENT IDENT EVALS TO ITSELF, SO LEAVE ON STACK } } if(!val) { // TODO: APPLY THE OPERATOR AS A SYMBOLIC OBJECT Exceptions=EX_VARUNDEF; ExceptionPointer=IPtr; return; } // HERE val HAS THE CONTENTS OF THE NAMED VARIABLE rplOverwriteData(1,val); // REPLACE THE FIRST LEVEL WITH THE VALUE } if(ISIDENT(*rplPeekData(2))) { WORDPTR val=rplGetLAM(rplPeekData(2)); if(!val) { val=rplGetGlobal(rplPeekData(2)); if(!val) { // INEXISTENT IDENT EVALS TO ITSELF, SO LEAVE ON STACK } } if(!val) { // TODO: APPLY THE OPERATOR AS A SYMBOLIC OBJECT Exceptions=EX_VARUNDEF; ExceptionPointer=IPtr; return; } // HERE val HAS THE CONTENTS OF THE NAMED VARIABLE rplOverwriteData(2,val); // REPLACE THE SECOND LEVEL WITH THE VALUE } // PASS AL OTHER OPERATORS DIRECTLY TO THE CONTENTS rplCallOvrOperator(CurOpcode); return; } } // SPECIAL OPCODES if(OPCODE(CurOpcode)&0x70000) { // IT'S ONE OF THE COMPACT OPCODES BINT op=OPCODE(CurOpcode)>>16; BINT num=OPCODE(CurOpcode)&0xffff; if(num&0x8000) num|=0xFFFF0000; // GET NEGATIVE LAMS TOO! switch(op) { case 1: // PUTLAMn { if(rplDepthData()<1) { Exceptions=EX_BADARGCOUNT; ExceptionPointer=IPtr; return; } WORDPTR *local=rplGetLAMn(num); *local=rplPopData(); return; } case 2: // GETLAMn rplPushData(*rplGetLAMn(num)); return; case 3: // GETLAMnEVAL rplPushData(*rplGetLAMn(num)); //rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_XEQ)); return; case 4: // NEWNLOCALS // THIS ONE HAS TO CREATE 'N' LOCALS TAKING THE NAMES AND OBJECTS FROM THE STACK // AND ALSO HAS TO 'EVAL' THE NEXT OBJECT IN THE RUNSTREAM // THE STACK CONTAINS VAL1 VAL2 ... VALN LAM1 LAM2 ... LAMN { if(rplDepthData()<2*num) { Exceptions=EX_BADARGCOUNT; ExceptionPointer=IPtr; return; } // CHECK ALL ARGUMENTS BINT cnt=num; while(cnt) { if(!ISIDENT(*rplPeekData(cnt))) { Exceptions=EX_BADARGTYPE; ExceptionPointer=IPtr; return; } --cnt; } // CREATE A NEW LAM ENVIRONMENT FOR THIS SECONDARY nLAMBase=LAMTop; // POINT THE GETLAM BASE TO THIS SECONDARY'S LAMS rplCreateLAM(lam_baseseco_bint,rplPeekRet(1)); // PUT MARKER IN LAM STACK rplPushRet(abnd_prog); // PUT ABND IN THE STACK TO DO THE CLEANUP BINT offset=num; // NOW CREATE ALL LOCAL VARIABLES while(num) { rplCreateLAM(rplPeekData(num),rplPeekData(num+offset)); --num; } // CLEAN THE STACK rplDropData(2*offset); } return; } } switch(OPCODE(CurOpcode)) { case LSTO: { // STORE CONTENT INSIDE A LAM VARIABLE, CREATE A NEW VARIABLE IF NEEDED if(rplDepthData()<2) { Exceptions=EX_BADARGCOUNT; ExceptionPointer=IPtr; return; } // ONLY ACCEPT IDENTS AS KEYS (ONLY LOW-LEVEL VERSION CAN USE ARBITRARY OBJECTS) if(!ISIDENT(*rplPeekData(1))) { Exceptions=EX_BADARGTYPE; ExceptionPointer=IPtr; return; } // FIND LOCAL VARIABLE IN THE CURRENT SCOPE ONLY WORDPTR *val=rplFindLAM(rplPeekData(1),0); BINT neednewenv=rplNeedNewLAMEnv(); if(val && !neednewenv) { val[1]=rplPeekData(2); rplDropData(2); } else { // LAM WAS NOT FOUND, CREATE A NEW ONE if(neednewenv) { // A NEW LAM ENVIRONMENT NEEDS TO BE CREATED nLAMBase=LAMTop; rplCreateLAM(lam_baseseco_bint,rplPeekRet(1)); // AND PUSH THE AUTOMATIC CLEANUP ROUTINE rplPushRet(abnd_prog); } // CREATE THE NEW VARIABLE WITHIN THE CURRENT ENVIRONMENT rplCreateLAM(rplPeekData(1),rplPeekData(2)); rplDropData(2); } } return; case LRCL: { // RCL CONTENT FROM INSIDE A LAM VARIABLE if(rplDepthData()<1) { Exceptions=EX_BADARGCOUNT; ExceptionPointer=IPtr; return; } // ONLY ACCEPT IDENTS AS KEYS (ONLY LOW-LEVEL VERSION CAN USE ARBITRARY OBJECTS) if(!ISIDENT(*rplPeekData(1))) { Exceptions=EX_BADARGTYPE; ExceptionPointer=IPtr; return; } WORDPTR val=rplGetLAM(rplPeekData(1)); if(val) { rplOverwriteData(1,val); } else { Exceptions=EX_VARUNDEF; ExceptionPointer=IPtr; return; } } return; case ABND: // CLEANUP ALL LAMS AND DO SEMI { if(*(nLAMBase+1)==rplPeekRet(1)) { // THIS WILL BE TRUE 99.9% OF THE TIMES LAMTop=nLAMBase; } else { // THERE'S SOME OTHER LAM CONSTRUCT OR CORRUPTED MARKERS, SEARCH FOR THE CORRECT MARKER WORDPTR *val=LAMTop; while( (val=rplGetNextLAMEnv(val)) ) { // PURGE ALL LAMS AFTER THE MARKER if(*(val+1)==rplPeekRet(1)) { LAMTop=val; break; } } // SOMETHING BAD HAPPENED, THIS SECONDARY HAD NO LAM ENVIRONMENT BUT AN ABND WORD! if(!val) LAMTop=LAMs; } nLAMBase=rplGetNextLAMEnv(LAMTop); if(!nLAMBase) nLAMBase=LAMs; IPtr=rplPopRet(); // GET THE CALLER ADDRESS CurOpcode=*IPtr; // SET THE WORD SO MAIN LOOP SKIPS THIS OBJECT, AND THE NEXT ONE IS EXECUTED } return; case NEWLOCALENV: // THIS COMMAND DOES NOTHING, IT'S JUST A MARKER FOR THE COMPILER return; // ADD MORE OPCODES HERE // STANDARIZED OPCODES: // -------------------- // LIBRARIES ARE FORCED TO ALWAYS HANDLE THE STANDARD OPCODES case OPCODE_COMPILE: // COMPILE RECEIVES: // TokenStart = token string // TokenLen = token length // BlankStart = token blanks afterwards // BlanksLen = blanks length // CurrentConstruct = Opcode of current construct/WORD of current composite // COMPILE RETURNS: // RetNum = enum CompileErrors // LSTO NEEDS SPECIAL CONSIDERATION TO CREATE LAMS AT COMPILE TIME if((TokenLen==4) && (!strncmp((char *)TokenStart,"LSTO",4))) { // ONLY ACCEPT IDENTS AS KEYS (ONLY LOW-LEVEL VERSION CAN USE ARBITRARY OBJECTS) // CHECK IF THE PREVIOUS OBJECT IS A QUOTED IDENT? WORDPTR object,prevobject; if(ValidateTop<=RSTop) { // THERE'S NO ENVIRONMENT object=TempObEnd; // START OF COMPILATION } else { object=*(ValidateTop-1); // GET LATEST CONSTRUCT ++object; // AND SKIP THE PROLOG / ENTRY WORD } if(objectenv) break; prolog=**(env+1); // GET THE PROLOG OF THE SECONDARY if(ISPROLOG(prolog) && LIBNUM(prolog)==SECO) { // LAMS ACROSS << >> SECONDARIES HAVE TO BE COMPILED AS IDENTS rplCompileAppend(MKOPCODE(LIBRARY_NUMBER,LSTO)); RetNum=OK_CONTINUE; return; } env=rplGetNextLAMEnv(env); } while(env); } // SPECIAL CASE: WHEN A SECO DOESN'T HAVE ANY LOCALS YET // BUT LAMS FROM THE PREVIOUS SECO SHOULDN'T BE COMPILED TO GETLAMS // SCAN ALL CURRENT CONSTRUCTS TO FIND THE INNERMOST SECONDARY // THEN VERIFY IF THAT SECONDARY IS THE CURRENT LAM ENVIRONMENT // THIS IS TO FORCE ALL LAMS IN A SECO TO BE COMPILED AS IDENTS // INSTEAD OF PUTLAMS // LAMS ACROSS DOCOL'S ARE OK AND ALWAYS COMPILED AS PUTLAMS WORDPTR *scanenv=ValidateTop-1; while(scanenv>=RSTop) { if( (LIBNUM(**scanenv)==SECO)&& (ISPROLOG(**scanenv))) { // FOUND INNERMOST SECONDARY if(*scanenv>*(nLAMBase+1)) { // THE CURRENT LAM BASE IS OUTSIDE THE INNER SECONDARY rplCompileAppend(MKOPCODE(LIBRARY_NUMBER,LSTO)); if(rplNeedNewLAMEnvCompiler()) { // CREATE A NEW ENVIRONMENT IF NEEDED nLAMBase=LAMTop; rplCreateLAM(lam_baseseco_bint,*(ValidateTop-1)); } rplCreateLAM(prevobject,prevobject); RetNum=OK_CONTINUE; return; } break; } --scanenv; } // IT'S A KNOWN LOCAL VARIABLE, COMPILE AS PUTLAM CompileEnd=prevobject; BINT Offset=((BINT)(LAMptr-nLAMBase))>>1; rplCompileAppend(MKOPCODE(DOIDENT,PUTLAMN+(Offset&0xffff))); RetNum=OK_CONTINUE; return; } } rplCompileAppend(MKOPCODE(LIBRARY_NUMBER,LSTO)); RetNum=OK_CONTINUE; return; } if((TokenLen==4) && (!strncmp((char *)TokenStart,"LRCL",4))) { // ONLY ACCEPT IDENTS AS KEYS (ONLY LOW-LEVEL VERSION CAN USE ARBITRARY OBJECTS) // CHECK IF THE PREVIOUS OBJECT IS A QUOTED IDENT? WORDPTR object,prevobject; if(ValidateTop<=RSTop) { // THERE'S NO ENVIRONMENT object=TempObEnd; // START OF COMPILATION } else { object=*(ValidateTop-1); // GET LATEST CONSTRUCT ++object; // AND SKIP THE PROLOG / ENTRY WORD } if(objectenv) break; prolog=**(env+1); // GET THE PROLOG OF THE SECONDARY if(ISPROLOG(prolog) && LIBNUM(prolog)==SECO) { // LAMS ACROSS << >> SECONDARIES HAVE TO BE COMPILED AS IDENTS rplCompileAppend(MKOPCODE(LIBRARY_NUMBER,LRCL)); RetNum=OK_CONTINUE; return; } env=rplGetNextLAMEnv(env); } while(env); } // SPECIAL CASE: WHEN A SECO DOESN'T HAVE ANY LOCALS YET // BUT LAMS FROM THE PREVIOUS SECO SHOULDN'T BE COMPILED TO GETLAMS // SCAN ALL CURRENT CONSTRUCTS TO FIND THE INNERMOST SECONDARY // THEN VERIFY IF THAT SECONDARY IS THE CURRENT LAM ENVIRONMENT // THIS IS TO FORCE ALL LAMS IN A SECO TO BE COMPILED AS IDENTS // INSTEAD OF GETLAMS // LAMS ACROSS DOCOL'S ARE OK AND ALWAYS COMPILED AS GETLAMS WORDPTR *scanenv=ValidateTop-1; while(scanenv>=RSTop) { if( (LIBNUM(**scanenv)==SECO)&& (ISPROLOG(**scanenv))) { // FOUND INNERMOST SECONDARY if(*scanenv>*(nLAMBase+1)) { // THE CURRENT LAM BASE IS OUTSIDE THE INNER SECONDARY rplCompileAppend(MKOPCODE(LIBRARY_NUMBER,LRCL)); RetNum=OK_CONTINUE; return; } break; } --scanenv; } // IT'S A KNOWN LOCAL VARIABLE, COMPILE AS GETLAM CompileEnd=prevobject; BINT Offset=((BINT)(LAMptr-nLAMBase))>>1; rplCompileAppend(MKOPCODE(DOIDENT,GETLAMN+(Offset&0xffff))); RetNum=OK_CONTINUE; return; } } rplCompileAppend(MKOPCODE(LIBRARY_NUMBER,LSTO)); RetNum=OK_CONTINUE; return; } // THIS STANDARD FUNCTION WILL TAKE CARE OF COMPILATION OF STANDARD COMMANDS GIVEN IN THE LIST // NO NEED TO CHANGE THIS UNLESS CUSTOM OPCODES libCompileCmds(LIBRARY_NUMBER,LIB_NAMES,NULL,LIB_NUMBEROFCMDS); return; case OPCODE_DECOMPILE: // DECOMPILE RECEIVES: // DecompileObject = Ptr to prolog of object to decompile // DecompStringEnd = Ptr to the end of decompile string //DECOMPILE RETURNS // RetNum = enum DecompileErrors if(ISPROLOG(*DecompileObject)) { if(LIBNUM(*DecompileObject)==LIBRARY_NUMBER) // THIS IS A QUOTED IDENT rplDecompAppendChar('\''); BYTEPTR ptr=(BYTEPTR)(DecompileObject+OBJSIZE(*DecompileObject)); if(ptr[3]==0) // WE HAVE A NULL-TERMINATED STRING, SO WE CAN USE THE STANDARD FUNCTION rplDecompAppendString((BYTEPTR) (DecompileObject+1)); else rplDecompAppendString2((BYTEPTR)(DecompileObject+1),OBJSIZE(*DecompileObject)<<2); if(LIBNUM(*DecompileObject)==LIBRARY_NUMBER) // THIS IS A QUOTED IDENT rplDecompAppendChar('\''); RetNum=OK_CONTINUE; return; } switch(OPCODE(*DecompileObject)&0x70000) { case NEWNLOCALS: { rplDecompAppendString((BYTEPTR)"NEWLOCALS"); BINT result=OPCODE(*DecompileObject)&0xffff; BINT digit=0; char basechr='0'; while(result=powersof10[digit]) { ++basechr; result-=powersof10[digit]; } rplDecompAppendChar(basechr); ++digit; basechr='0'; } basechr+=result; rplDecompAppendChar(basechr); } RetNum=OK_CONTINUE; return; case GETLAMN: { rplDecompAppendString((BYTEPTR)"GETLAM"); BINT result=OPCODE(*DecompileObject)&0xffff; if(result&0x8000) result|=0xFFFF0000; if(result<0) { rplDecompAppendChar('u'); result=-result; } BINT digit=0; char basechr='0'; while(result=powersof10[digit]) { ++basechr; result-=powersof10[digit]; } rplDecompAppendChar(basechr); ++digit; basechr='0'; } basechr+=result; rplDecompAppendChar(basechr); } RetNum=OK_CONTINUE; return; case GETLAMNEVAL: { rplDecompAppendString((BYTEPTR)"GETLAM"); BINT result=OPCODE(*DecompileObject)&0xffff; if(result&0x8000) result|=0xFFFF0000; if(result<0) { rplDecompAppendChar('u'); result=-result; } BINT digit=0; char basechr='0'; while(result=powersof10[digit]) { ++basechr; result-=powersof10[digit]; } rplDecompAppendChar(basechr); ++digit; basechr='0'; } basechr+=result; rplDecompAppendChar(basechr); } rplDecompAppendString((BYTEPTR)"EVAL"); RetNum=OK_CONTINUE; return; case PUTLAMN: { rplDecompAppendString((BYTEPTR)"PUTLAM"); BINT result=OPCODE(*DecompileObject)&0xffff; if(result&0x8000) result|=0xFFFF0000; if(result<0) { rplDecompAppendChar('u'); result=-result; } BINT digit=0; char basechr='0'; while(result=powersof10[digit]) { ++basechr; result-=powersof10[digit]; } rplDecompAppendChar(basechr); ++digit; basechr='0'; } basechr+=result; rplDecompAppendChar(basechr); } RetNum=OK_CONTINUE; return; } // THIS STANDARD FUNCTION WILL TAKE CARE OF DECOMPILING STANDARD COMMANDS GIVEN IN THE LIST // NO NEED TO CHANGE THIS UNLESS THERE ARE CUSTOM OPCODES libDecompileCmds(LIB_NAMES,NULL,LIB_NUMBEROFCMDS); return; case OPCODE_VALIDATE: // VALIDATE RECEIVES OPCODES COMPILED BY OTHER LIBRARIES, TO BE INCLUDED WITHIN A COMPOSITE OWNED BY // THIS LIBRARY. EVERY COMPOSITE HAS TO EVALUATE IF THE OBJECT BEING COMPILED IS ALLOWED INSIDE THIS // COMPOSITE OR NOT. FOR EXAMPLE, A REAL MATRIX SHOULD ONLY ALLOW REAL NUMBERS INSIDE, ANY OTHER // OPCODES SHOULD BE REJECTED AND AN ERROR THROWN. // Library receives: // CurrentConstruct = SET TO THE CURRENT ACTIVE CONSTRUCT TYPE // LastCompiledObject = POINTER TO THE LAST OBJECT THAT WAS COMPILED, THAT NEEDS TO BE VERIFIED // VALIDATE RETURNS: // RetNum = OK_CONTINUE IF THE OBJECT IS ACCEPTED, ERR_INVALID IF NOT. RetNum=OK_CONTINUE; return; } // UNHANDLED OPCODE... // IF IT'S A COMPILER OPCODE, RETURN ERR_NOTMINE if(OPCODE(CurOpcode)>=MIN_RESERVED_OPCODE) { RetNum=ERR_NOTMINE; return; } // BY DEFAULT, ISSUE A BAD OPCODE ERROR Exceptions|=EX_BADOPCODE; ExceptionPointer=IPtr; return; }