HP Forums
RPL stackrobatics decoder - Printable Version

+- HP Forums (https://www.hpmuseum.org/forum)
+-- Forum: HP Calculators (and very old HP Computers) (/forum-3.html)
+--- Forum: General Forum (/forum-4.html)
+--- Thread: RPL stackrobatics decoder (/thread-11027.html)



RPL stackrobatics decoder - Claudio L. - 07-09-2018 04:14 PM

This thread has the repeated argument of how hard it is to read RPL code.

This brings the question: could it be possible to build an RPL converter to another language, or to pseudocode, or anything a bit more readable?

With a few rules, it should be possible for a simple parser to read RPL and output something more human. This would assist people in the conversion of the vast library or RPL programs to other platforms (Prime?).

The decoder would scan an RPL program and output text. It would define tons of names for functions and local variables (in the example below, I used 'lxxx' for locals, and hope they don't conflict with other variables declared within the program).
The rules could be very simple:
* We need to define a text output for each function/command/token we want to decode
* Any objects put on the stack declare new local variables, and the local variable name gets pushed on a stack instead
* Any command that takes N arguments and leaves M values on the stack, will reassign the N local variables taken as arguments with the results, and declaring new locals if M>N

As an example,I took a piece of code from that same thread(which derives from this post), and "mentally" scanned it with the hypothetical decoder using the rules above:
Code:

 « → n « --> OUTPUT 'function fcn1(n) {'
 0 --> OUTPUT 'l1=0', PUSH 'l1' TO THE STACK
 n --> OUTPUT 'l2=n', PUSH 'l2' to the stack
 1 --> 'l3=1', PUSH 'l3'
 - --> 'l2=l2-l3', PUSH 'l2', DROP l3,l2
WHILE ==> while(
 DUP --> 'l4=l2', PUSH 'l4'
 2 --> 'l5=2', PUSH 'l5'
MOD --> 'l4=l4 MOD l5', DROP l4,l5, PUSH 'l4'
NOT  --> l4= NOT l4' , DROP l4, PUSH 'l4'
REPEAT --> output 'l4', DROP 'l4' ) { (TO CLOSE THE WHILE EXPRESSION)
SWAP
--> HERE STACK CONTAINS 'l2' 'l1'
1 --> l6 = 1
+ --> l1=l1+l6, PUSH 'l1' DROP 'l1',l6
SWAP --> HERE STACK CONTAINS 'l1' 'l2'
2 --> 'l7 = 2'
/ --> 'l2 = l2 / l7', PUSH l2, DROP l7,l2
END --> }
--> HERE THE STACK CONTAINS 'l1' 'l2'
 → s d « --> OUTPUT 's=l1', OUTPUT 'd=l2' OUTPUT '{', DROP l1,l2
 { 2 13 23 1662803 } --> 'l8= { ... }', PUSH l8
IFERR --> OUTPUT try {
1 -->    'l9=1', PUSH l9
4 -->   'l10=4' PUSH l10
FOR i --> OUTPUT 'for i=l9 to l10 {' DROP l9,l10
DUP --> OUTPUT 'l11=l8', PUSH 'l11'
i --> 'l12= i'
GET --> 'l11=GET(l11,l12)', DROP l11,l12, PUSH l11
s --> l13=s, PUSH l13
d --> l14=d, PUSH l14
n --> l15=n, PUSH l15
COMPOSITE? --> OUTPUT 'l11=COMPOSITE?(l11,l13,l14,l15)' (FUNCTION COMPOSITE? MUST'VE BEEN DEFINED BEFORE IN THE DECODER, SOMEHOW), DROP l11,l13,l14,l15, PUSH l11
NOT --> 'l11=NOT l11', DROP l11, PUSH l11
INV --> 'l11=1/l11', DROP l11, PUSH l11
DROP --> OUTPUT discard(l11) , DROP l11
NEXT --> } next
THEN --> } catch {
--> THE DECODER HAS NO WAY OF KNOWING WHERE THE ERROR WOULD OCCUR, SO THIS CANNOT BE FIXED
SWAP DROP --> output as-is
ELSE DROP 1 --> output as-is
END --> OUTPUT }
» --> OUTPUT }
» --> OUTPUT }

After this first scan, we'd get the following output:
Code:

function fcn1(n) {
l1=0
l2=n
l3=1
l2=l2-l3
while( l4=l2; l5=2; l4=l4 mod l5; l4=not l4; l4 ) {
l6=1
l1=l1+l6
l7=2
l2=l2/l7
}
s=l1 d=l2 {
l8={ ... }
try {
l9=1
l10=4
for i=l9 to l10 {
l11=l8
l12=i
l11=GET(l11,l12)
l13=s
l14=d
l15=n
l11=COMPOSITE?(l11,l13,l14,l15)
l11=NOT l11
l11=1/l11
discard(l11)
} next
} catch {
SWAP DROP
ELSE DROP 1
}
}
}

Is that any better than RPL? Not yet. It needs tons of cleanup. Now we need a parser that does a second pass through that source with a few more rules:
* In a variable definition or an expression: Check if any of the variables in the expression is used in any statements after this one. If not, then replace the variable with its value. If being used later, or if being assigned to in this same statement, just leave it as-is
* In a variable definition: If a variable name being assigned to is never used afterwards (this includes the expression being assigned and all statements after), remove the whole statement, otherwise leave it.
* In a variable definition: If the previous statement assigns to the same variable name, then replace the value from previous statement into the current expression, and remove the previous statement


Let's see what happens to the example above when we apply these rules, pass after pass until there are no more changes:

Code:

FIRST PASS:
function fcn1(n) {
l1=0
l2=n
l3=1
l2=l2-1 (L3 NOT USED AFTERWARDS, REPLACE by value)
while( l4=l2; l5=2; /*l4=l4 mod 2; l5 is replaced, then whole statement is discarded by merge*/ /*l4=not (l4 mod 2); replaced and later DISCARDED BY MERGE*/  not (l4 mod 2) ) { // l4 IS NOT USED AFTERWARDS, SO IT WAS REPLACED BE ITS VALUE
l6=1
l1=l1+1 // l6 replaced
l7=2
l2=l2/2 // l7 replaced
}
s=l1 d=l2 {
l8={ ... }
try {
l9=1
l10=4
for i=1 to 4 {    //(l9,l10 replaced by value)
l11=l8
l12=i
l11=GET(l11,i)    // l12 replaced
l13=s
l14=d
l15=n
/*l11=COMPOSITE?(l11,s,d,n)*/     removed by merge after replacement of l13,l14,l15
l11=NOT COMPOSITE?(l11,s,d,n)        //NOT l11    --> MERGE 
l11=1/(NOT COMPOSITE?(l11,s,d,n)        //1/l11    --> MERGE
discard(l11)
} next
} catch {
SWAP DROP
ELSE DROP 1
}
}
}








// NEXT PASS

function fcn1(n) {
l1=0
l2=n
// l3 not used, remove whole statement
l2=l2-1
while( l4=l2; not (l2 mod 2) ) { --> l4 not used anymore, replace by value
// l6, l7 defined but not used, removed
l1=l1+1
l2=l2/2
}

s=l1 d=l2 {
l8={ ... }
try {
for i=1 to 4 {
/* l11=l8 */ // removed by merge
// l12 statement removed
/* l11=GET(l8,i) */ // 2 consecutive assignments after the l12 statement was removed, merge the two lines. Later, this line is removed by merging with the next!
// l13,l14,l15 statements removed
l11=1/(NOT COMPOSITE?(GET(l8,i),s,d,n)        // another consecutive assignment to the same variable, replace and merge
discard(l11)
} next
} catch {
SWAP DROP
ELSE DROP 1
}
}
}

// NEXT PASS

function fcn1(n) {
l1=0
l2=n-1
while( not (l2 mod 2) ) { // l4 statement removed
l1=l1+1
l2=l2/2
}

s=l1 d=l2 {
l8={ ... }
try {
for i=1 to 4 {
l11=1/(NOT COMPOSITE?(GET(l8,i),s,d,n)
discard(1/(NOT COMPOSITE?(GET(l8,i),s,d,n))    // l11 not used anymore, replaced    
} next
} catch {
SWAP DROP
ELSE DROP 1
}
}
}

// NEXT PASS

function fcn1(n) {
l1=0
l2=n-1
while( not (l2 mod 2) ) { 
l1=l1+1
l2=l2/2
}

s=l1 d=l2 {
l8={ ... }
try {
for i=1 to 4 {
// l11 statement removed, never used    
discard(1/(NOT COMPOSITE?(GET(l8,i),s,d,n)))
} next
} catch {
SWAP DROP
ELSE DROP 1
}
}
}

//     NO MORE CHANGES NEEDED ON NEXT PASS, DONE

The final result is not too bad:

Code:

function fcn1(n) {
l1=0
l2=n-1
while( not (l2 mod 2) ) { 
l1=l1+1
l2=l2/2
}

s=l1 d=l2 {
l8={ ... }
try {
for i=1 to 4 {
discard(1/(NOT COMPOSITE?(GET(l8,i),s,d,n)))
} next
} catch {
SWAP DROP
ELSE DROP 1
}
}
}

Any more rules or suggestions to improve on this?


RE: RPL stackrobatics decoder - Thomas Klemm - 07-09-2018 05:29 PM

(07-09-2018 04:14 PM)Claudio L. Wrote:  As an example, I took a piece of code from that same thread
I think you should have linked to post # 52.

Code:
MOD --> 'l4=l4 MOD l5', DROP l4,l5, PUSH 'l4'
+ --> l1=l1+l6, PUSH 'l1' DROP 'l1',l6
The inconsistent use of single quotes and the different order of PUSH and DROP makes it a bit hard to follow your reasoning.

Cheers
Thomas


RE: RPL stackrobatics decoder - Claudio L. - 07-09-2018 06:12 PM

(07-09-2018 05:29 PM)Thomas Klemm Wrote:  
(07-09-2018 04:14 PM)Claudio L. Wrote:  As an example, I took a piece of code from that same thread
I think you should have linked to post # 52.

Thanks, I updated the post with your suggestion.

(07-09-2018 05:29 PM)Thomas Klemm Wrote:  
Code:
MOD --> 'l4=l4 MOD l5', DROP l4,l5, PUSH 'l4'
+ --> l1=l1+l6, PUSH 'l1' DROP 'l1',l6
The inconsistent use of single quotes and the different order of PUSH and DROP makes it a bit hard to follow your reasoning.

Yes, sorry for I had very limited time, perhaps I'll fix that later. The idea is that there's a stack (only used for parsing/decoding) that carries only local variable names, functions DROP all their arguments from that stack, and PUSH all their results naming them the same name as the arguments they took when possible. Ignore the quotes, ignore the order of the PUSH/DROP which I wrote backwards in many places.
In that code snippet, MOD takes l4,l5 from the stack (always names), generates the output 'l4=l4 MOD l5', and leaves l4 (the name) on the stack. The + operator of course is supposed to also DROP first, then PUSH.


RE: RPL stackrobatics decoder - Thomas Klemm - 07-10-2018 07:17 AM

(07-09-2018 04:14 PM)Claudio L. Wrote:  Any more rules or suggestions to improve on this?

Not a suggestion but looking at Thomas Okken's program here:
Code:
<< DUP 1 - -> n n1
   << 1 n FOR i i INV NEXT
      n 1 + n n1 + FOR i n1 DUPN i INV NEXT
      n DUP 2 ->LIST ->ARRY
   >>
>>

I wonder how you would ever end up with something like this:
Code:
double[][] hilbert(int n) {
    double[][] h = new double[n][n];
    for (int j = 0; j < n; j++)
        h[0][j] = 1.0 / (j + 1);
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < n - 1; j++)
            h[i][j] = h [i - 1][j + 1];
        h[i][n - 1] = 1.0 / (i  + j + 1);
    }
    return h;
}

Commands like DUPN, ->LIST or ->ARRY consume or produce an arbitrary number of parameters. We know their exact numbers only at runtime.
Thus using local variables like in case of + or MOD wouldn't work.

And then there are idiomatic ways to do something like looping through an array.

C
Code:
const char* name = "Claudio";
size_t length = strlen(name);
for (int i = 0; i < length; i++) {
    printf("%c\n", name[i]);
}

Python
Code:
name = "Claudio"
for c in name:
    print c

Of course we could mimic the same behaviour in Python:
Code:
name = "Claudio"
length = len(name)
for i in range(length):
    print name[i]

But that would not be considered Pythonic.

Thus I'm not sure if it really helps to automatically translate RPL into other languages.
Most probably you end up with something that is not idiomatic and will be hard to understand.

However it could be helpful to automatically create stack-diagrams, e.g. when calculating the complex arccos function.

Cheers
Thomas


RE: RPL stackrobatics decoder - Claudio L. - 07-10-2018 04:42 PM

(07-10-2018 07:17 AM)Thomas Klemm Wrote:  Not a suggestion but looking at Thomas Okken's program here:
Code:
<< DUP 1 - -> n n1
   << 1 n FOR i i INV NEXT
      n 1 + n n1 + FOR i n1 DUPN i INV NEXT
      n DUP 2 ->LIST ->ARRY
   >>
>>

I wonder how you would ever end up with something like this:
Code:
double[][] hilbert(int n) {
    double[][] h = new double[n][n];
    for (int j = 0; j < n; j++)
        h[0][j] = 1.0 / (j + 1);
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < n - 1; j++)
            h[i][j] = h [i - 1][j + 1];
        h[i][n - 1] = 1.0 / (i  + j + 1);
    }
    return h;
}

Commands like DUPN, ->LIST or ->ARRY consume or produce an arbitrary number of parameters. We know their exact numbers only at runtime.
Thus using local variables like in case of + or MOD wouldn't work.

Yes, those commands are trouble, one way to handle them could be for this decoder to explicitly output those "push/drop" that I was doing in the background, then have special rules to cancel them out and remove them in the cleanup phase. The stack would then be used as a vector, and whatever is not removed would look like the user storing elements in a vector.

Now the C code you show is not the target of this decoder, I wasn't thinking of specifically targetting constructs of a different language, but mostly to help the reader understand the algorithm by turning it into code in a language he's more familiar with. The decoder cannot rewrite an algorithm, merely try to present it in a way the programmer would be able to understand and "recode" properly in a language of choice.

Back to the example you presented, with explicit stack being output for example with a vector called 'stk' and an index called 'stkidx':

Code:

<< ----> output 'function fcn1() {'
DUP ----> Empty stack! The decoder will need to detect this and push 'arg1', while adding the argument to the function declaration above 'function fcn1(arg1)'. Now we make the pushes explicit: output 'stk[stkidx++]=arg1'. After that bit, output 'l1=arg1', 'stk[stkidx++]=l1'
1 -----> 'l2=1', 'stk[stkidx++]=l2'
- -----> output 'stkidx--', 'stkidx--' (drop 2 arguments explicitly), 'l1=l1-l2', 'stk[stkidx++]=l1'
-> n n1 << -----> output 'stkidx--','stkidx--', 'n=arg1', 'n1=l1'
1 -----> 'l3=1', 'stk[stkidx++]=l3'
n -----> 'l4=n', 'stk[stkidx++]=l4'
FOR i -----> 'stkidx--', 'stkidx--', 'for i= l3 to l4 {'
i -----> 'l5=i', 'stk[stkidx++]=l5'
INV -----> 'stkidx--', 'l5=1/l5', 'stk[stkidx++]=l5'
NEXT -----> output ' } '
n -----> 'l6=n', 'stk[stkidx++]=l6'
1 -----> 'l7=1', 'stk[stkidx++]=l7'
+ -----> 'stkidx--', 'stkidx--', 'l6=l6+l7', 'stk[stkidx++]=l6'
n -----> 'l8=n', 'stk[stkidx++]=l8
n1 -----> 'l9=n1', 'stk[stkidx++]=l9'
+ -----> 'stkidx--', 'stkidx--', 'l8=l8+l9', 'stk[stkidx++]=l8'
FOR i -----> 'stkidx--' 'stkidx--' 'for i= l6 to l8 {'
n1 -----> 'l10=n1', 'stk[stkidx++]=l10'
DUPN -----> 'stkidx--', here output NDUPN as a loop 'for tmp1=1 to l10 { stk[stkidx++]=stk[stkidx-l10] }'
i -----> 'l11=i', 'stk[stkidx++]=l11'
INV -----> 'stkidx--', l11=1/l11', 'stk[stkidx++]=l11'
NEXT -----> ' } '
n -----> 'l12=n', 'stk[stkidx++]=l12'
DUP -----> 'l13=l12', 'stk[stkidx++]=l13'
2 -----> 'l14=2', 'stk[stkidx++]=l14
->LIST -----> 'stkidx--', and implement it as a loop as well: ' l15={ }', 'for tmp2=0 to l14-1 { l15[tmp2+1]=stk[stkidx-l14+tmp2] } 'stkidx=stkidx-l14' 'stk[stkidx++]=l15'
->ARRY -----> 'stkidx--', and implement as a loop as well: 'l16=[][]', 'for tmp3=1 to l15[1] { for tmp4=1 to l15[2] { l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)] } } stkidx=stkidx-l15[1]*l15[2] stk[stkidx++]=l16'
>> -----> ' } '
>> -----> ' } '

Which results in:
Code:

/*function fcn1() {*/
function fcn1(arg1) {
stk[stkidx++]=arg1
l1=arg1
stk[stkidx++]=l1
l2=1
stk[stkidx++]=l2
stkidx--
stkidx--
l1=l1-l2
stk[stkidx++]=l1
stkidx--
stkidx--
n=arg1
n1=l1
l3=1
stk[stkidx++]=l3
l4=n
stk[stkidx++]=l4
stkidx--
stkidx--
for i= l3 to l4 {
l5=i
stk[stkidx++]=l5
stkidx--
l5=1/l5
stk[stkidx++]=l5
}
l6=n
stk[stkidx++]=l6
l7=1
stk[stkidx++]=l7
stkidx--
stkidx--
l6=l6+l7
stk[stkidx++]=l6
l8=n
stk[stkidx++]=l8
l9=n1
stk[stkidx++]=l9
stkidx--
stkidx--
l8=l8+l9
stk[stkidx++]=l8
stkidx--
stkidx--
for i= l6 to l8 {
l10=n1
stk[stkidx++]=l10
stkidx--
for tmp1=1 to l10 { stk[stkidx++]=stk[stkidx-l10] }
l11=i
stk[stkidx++]=l11
stkidx--
l11=1/l11
stk[stkidx++]=l11
}
l12=n
stk[stkidx++]=l12
l13=l12
stk[stkidx++]=l13
l14=2
stk[stkidx++]=l14
stkidx--
l15={ }
for tmp2=0 to l14-1 { l15[tmp2+1]=stk[stkidx-l14+tmp2] }
stkidx=stkidx-l14
stk[stkidx++]=l15
stkidx--
l16=[][]
for tmp3=1 to l15[1] { for tmp4=1 to l15[2] { l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)] } }
stkidx=stkidx-l15[1]*l15[2]
stk[stkidx++]=l16
 } 
 }

Now applying the same 3 previous rules + the new "stack" rule for cleanup.
* Whenever stkidx-- is found, search for a previous stk[stkidx++]=... statement, but do not cross { } boundaries. If found, remove both statements.
For clarity, let's apply the new rule first:

Code:

function fcn1(arg1) {
/*stk[stkidx++]=arg1*/ // CANCELED OUT
l1=arg1
/*stk[stkidx++]=l1*/ CANCELED OUT
l2=1
/*stk[stkidx++]=l2
stkidx--*/ // BOTH CANCEL OUT TOGETHER
/*stkidx--*/ // cancels out with the l1
l1=l1-l2
/*stk[stkidx++]=l1
stkidx--*/  // CANCELED OUT
/*stkidx--*/ // CANCELS OUT THE arg1 PUSH
n=arg1
n1=l1
l3=1
/*stk[stkidx++]=l3*/
l4=n
/*stk[stkidx++]=l4
stkidx--*/
/*stkidx--*/ // CANCELS OUT THE l3 PUSH
for i= l3 to l4 {
l5=i
/*stk[stkidx++]=l5
stkidx--*/  // CANCEL OUT TOGETHER
l5=1/l5
stk[stkidx++]=l5
}
l6=n
/*stk[stkidx++]=l6*/
l7=1
/*stk[stkidx++]=l7
stkidx--*/
/*stkidx--*/  // CANCELS OUT THE l6 PUSH
l6=l6+l7
/*stk[stkidx++]=l6*/
l8=n
/*stk[stkidx++]=l8*/
l9=n1
/*stk[stkidx++]=l9
stkidx--*/
/*stkidx--*/ // CANCELS OUT THE l8 PUSH
l8=l8+l9
/*stk[stkidx++]=l8
stkidx--*/
/*stkidx--*/ // CANCELS OUT THE l6 PUSH
for i= l6 to l8 {
l10=n1
/*stk[stkidx++]=l10
stkidx--*/
for tmp1=1 to l10 { stk[stkidx++]=stk[stkidx-l10] }
l11=i
/*stk[stkidx++]=l11
stkidx--*/
l11=1/l11
stk[stkidx++]=l11
}
l12=n
stk[stkidx++]=l12
l13=l12
stk[stkidx++]=l13
l14=2
/*stk[stkidx++]=l14
stkidx--*/
l15={ }
for tmp2=0 to l14-1 { l15[tmp2+1]=stk[stkidx-l14+tmp2] }
stkidx=stkidx-l14
/*stk[stkidx++]=l15
stkidx--*/
l16=[][]
for tmp3=1 to l15[1] { for tmp4=1 to l15[2] { l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)] } }
stkidx=stkidx-l15[1]*l15[2]
stk[stkidx++]=l16
 } 
 }

Now let's do the other rules with the cleaner output:

Code:

// CODE BEFORE THE FIRST PASS, AFTER THE STACK CLEANUP RULE ABOVE:
function fcn1(arg1) {
l1=arg1
l2=1
l1=l1-l2
n=arg1
n1=l1
l3=1
l4=n
for i= l3 to l4 {
l5=i
l5=1/l5
stk[stkidx++]=l5
}
l6=n
l7=1
l6=l6+l7
l8=n
l9=n1
l8=l8+l9
for i= l6 to l8 {
l10=n1
for tmp1=1 to l10 { stk[stkidx++]=stk[stkidx-l10] }
l11=i
l11=1/l11
stk[stkidx++]=l11
}
l12=n
stk[stkidx++]=l12
l13=l12
stk[stkidx++]=l13
l14=2
l15={ }
for tmp2=0 to l14-1 { l15[tmp2+1]=stk[stkidx-l14+tmp2] }
stkidx=stkidx-l14
l16=[][]
for tmp3=1 to l15[1] { for tmp4=1 to l15[2] { l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)] } }
stkidx=stkidx-l15[1]*l15[2]
stk[stkidx++]=l16
 } 
 }

// FIRST PASS OTHER RULES
function fcn1(arg1) {
l1=arg1
l2=1
l1=l1-1 // l2 never used, replace
n=arg1
n1=arg1-1 // l1 never used after this, recursively replace by value since first replacement included l1 again
l3=1
l4=n
for i= 1 to n { // l3 and l4 never used after this, replace by value
/*l5=i*/
l5=1/i // CONSECUTIVE ASSIGNMENT, MERGE
stk[stkidx++]=1/i // l5 never used after
}
l6=n
l7=1
l6=l6+1  // l6 is still used, only l7 can be replaced
l8=n
l9=n1
l8=l8+n1 // l8 still used, only replace l19
for i= n+1 to n+n1 { // recursively replace l6 and l8, never used after here
l10=n1
for tmp1=1 to l10 { stk[stkidx++]=stk[stkidx-n1] } // replace l10, never used
/*l11=i*/
l11=1/i // CONSECUTIVE ASSIGNMENT, MERGE
stk[stkidx++]=1/i // l11 replaced, never used after here
}
l12=n
stk[stkidx++]=l12
l13=n // l12 replaced, never used
stk[stkidx++]=l13
l14=2
l15={ }
for tmp2=0 to l14-1 { l15[tmp2+1]=stk[stkidx-l14+tmp2] }
stkidx=stkidx-2 // l14 replaced
l16=[][]
for tmp3=1 to l15[1] { for tmp4=1 to l15[2] { l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)] } }
stkidx=stkidx-l15[1]*l15[2]
stk[stkidx++]=l16
 } 
 }


// SECOND PASS OTHER RULES
function fcn1(arg1) {
l1=arg1
/*l2=1*/ // never used, remove
/*l1=l1-1*/ // never used, remove
n=arg1
n1=arg1-1
/*l3=1*/ // never used, remove
/*l4=n*/ // never used, remove
for i= 1 to n {
/*l5=1/i*/ // never used, remove
stk[stkidx++]=1/i
}
l6=n
/*l7=1*/ // never used, remove
/*l6=l6+1 */ // never used, remove (after replacing l6 with n but doesn't matter)
l8=n
/*l9=n1*/ // never used, remove
/*l8=l8+n1*/ // never used, remove
for i= n+1 to n+n1 {
l10=n1
for tmp1=1 to n1 { stk[stkidx++]=stk[stkidx-n1] } // replace other l10, never used
/*l11=1/i*/ // never used, remove
stk[stkidx++]=1/i
}
l12=n
stk[stkidx++]=n // l12 replaced, never used after
l13=n
stk[stkidx++]=n // l13 never used after this, replace
l14=2
l15={ }
for tmp2=0 to l14-1 { l15[tmp2+1]=stk[stkidx-2+tmp2] } // l14 replaced, never used after
stkidx=stkidx-2 
l16=[][]
for tmp3=1 to l15[1] { for tmp4=1 to l15[2] { l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)] } }
stkidx=stkidx-l15[1]*l15[2]
stk[stkidx++]=l16
 } 
 }

// THIRD PASS OTHER RULES
function fcn1(arg1) {
/*l1=arg1*/ // never used, remove
n=arg1
n1=arg1-1
for i= 1 to n {
stk[stkidx++]=1/i
}
/*l6=n*/ // never used, remove
/*l8=n*/ // never used, remove
for i= n+1 to n+n1 {
/*l10=n1*/ // never used, remove
for tmp1=1 to n1 { stk[stkidx++]=stk[stkidx-n1] }
stk[stkidx++]=1/i
}
/*l12=n*/ // never used, remove
stk[stkidx++]=n
/*l13=n*/ // never used, remove
stk[stkidx++]=n
l14=2
l15={ }
for tmp2=0 to 2-1 { l15[tmp2+1]=stk[stkidx-2+tmp2] } // l14 replaced, never used after
stkidx=stkidx-2 
l16=[][]
for tmp3=1 to l15[1] { for tmp4=1 to l15[2] { l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)] } }
stkidx=stkidx-l15[1]*l15[2]
stk[stkidx++]=l16
 } 
 }

// FOURTH PASS OTHER RULES
function fcn1(arg1) {
n=arg1
n1=arg1-1
for i= 1 to n {
stk[stkidx++]=1/i
}
for i= n+1 to n+n1 {
for tmp1=1 to n1 { stk[stkidx++]=stk[stkidx-n1] }
stk[stkidx++]=1/i
}
stk[stkidx++]=n
stk[stkidx++]=n
/*l14=2*/ // never used, remove
l15={ }
for tmp2=0 to 2-1 { l15[tmp2+1]=stk[stkidx-2+tmp2] }
stkidx=stkidx-2 
l16=[][]
for tmp3=1 to l15[1] { for tmp4=1 to l15[2] { l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)] } }
stkidx=stkidx-l15[1]*l15[2]
stk[stkidx++]=l16
 } 
 }

And this leads to the following final code:

Code:

// FINAL OUTPUT OTHER RULES
function fcn1(arg1)
{
    n=arg1
    n1=arg1-1
    for i= 1 to n {
        stk[stkidx++]=1/i
    }
    for i= n+1 to n+n1 {
        for tmp1=1 to n1 { stk[stkidx++]=stk[stkidx-n1] }
        stk[stkidx++]=1/i
    }
    stk[stkidx++]=n
    stk[stkidx++]=n
    l15={ }
    for tmp2=0 to 2-1 { l15[tmp2+1]=stk[stkidx-2+tmp2] }
    stkidx=stkidx-2 
    l16=[][]
    for tmp3=1 to l15[1] {
        for tmp4=1 to l15[2] {
            l16[tmp3][tmp4]=stk[stkidx-(l15[1]*l15[2])+l15[2]*(tmp3-1)+(tmp4-1)]
        }
    }
    stkidx=stkidx-l15[1]*l15[2]
    stk[stkidx++]=l16
}

It's a bit convoluted at the end, not sure if it helps as it is. To make it more like the actual code you would write, a more advanced decoder should detect that the loop 'for tmp2=0 to 1 ...' is determined at compile time, and should try to unroll the loop and simplify further, showing that l15[1]=l15[2]=n, replacing and removing it all. It would help a lot with readability in the last part, which is merely trying to fill up a matrix, the first couple of loops where the actual math is done came out pretty nice.
Anyway, this was a nice experiment as a proof of concept, perhaps one day I'll actually code it for newRPL giving it the power to decompile RPL into something like Lua or Python.


RE: RPL stackrobatics decoder - pier4r - 07-11-2018 11:24 AM

Nice challenge!