Friday, July 22, 2016

APEX IDE for Shakespeare Programming Language (SPL)

Recently I came across an esoteric programming language called The Shakespeare Programming Language (SPL) and become rather fascinated by it.  It's big, and it's clever, but it's not terribly useful or practical.  But this year is the 400th anniversary of Shakespeare's death, which adds some relevance I suppose.

Here is an example of an SPL program taken from the SPL docs.  All it does is receive a string as input and output the same string reversed.


Outputting Input Reversedly.

Othello, a stacky man.
Lady Macbeth, who pushes him around till he pops.


                    Act I: The one and only.

                    Scene I: In the beginning, there was nothing.

[Enter Othello and Lady Macbeth]

Othello:
 You are nothing!

                    Scene II: Pushing to the very end.

Lady Macbeth:
 Open your mind! Remember yourself.

Othello:
 You are as hard as the sum of yourself and a stone wall. Am I as horrid as a flirt-gill?

Lady Macbeth:
 If not, let us return to scene II. Recall your imminent death!

Othello:
 You are as small as the difference between yourself and a hair!

                    Scene III: Once you pop, you can't stop!

Lady Macbeth:
 Recall your unhappy childhood. Speak your mind!

Othello:
 You are as vile as the sum of yourself and a toad! Are you better than nothing?

Lady Macbeth:
 If so, let us return to scene III.

                    Scene IV: The end.

[Exeunt]

I picked this one to show here because it is much shorter than the Hello World example!

I'm sure you'll agree with me that this is a language worth learning.  I decided to try it out.  There is a compiler from SPL to C available on Github, but that didn't sound much fun to me as I'm not a C programmer these days and have no particular wish to revisit it.  So I thought, why not write my own SPL to PL/SQL "compiler"?  And having done that it seemed a logically step further to build a sort of IDE for building and running SPL programs - using Oracle APEX of course, because APEX is the hammer for every nail in my life,

I am therefore proud to present my Shakespeare Programming Language IDE (I'm toying with the name "SPL Developer"):

Using this I can enter the SPL code above, click compile, and get the following generated PL/SQL code:


-- Outputting Input Reversedly.
declare
-- Othello, a stacky man.
othello t_character := t_character();
-- Lady Macbeth, who pushes him around till he pops.
lady_macbeth t_character := t_character();
-- Act I: The one and only.
begin
spl.initialise(v('P4_INPUT'));
<<acti>> null;
-- Scene I: In the beginning, there was nothing.
<<acti_scenei>> null;
-- [Enter Othello and Lady Macbeth].
-- Othello: You are nothing.
lady_macbeth.becomes( (0 ));
-- Scene II: Pushing to the very end.
<<acti_sceneii>> null;
-- Lady Macbeth: Open your mind.
othello.open_mind();
-- Remember yourself.
othello.remember();
-- Othello: You are as hard as the sum of yourself and a stone wall.
lady_macbeth.becomes( (spl.sum (lady_macbeth.val , 2 ) ));
-- Am I as horrid as a flirt-gill.
if (othello.val )= (-1 ) then
-- Lady Macbeth: If not, let us return to scene II.
null;
else
spl.log_op;
goto acti_sceneii;
end if;
-- Recall your imminent death.
othello.recall();
-- Othello: You are as small as the difference between yourself and a hair.
lady_macbeth.becomes( (spl.difference (lady_macbeth.val , 1 ) ));
-- Scene III: Once you pop, you can't stop.
<<acti_sceneiii>> null;
-- Lady Macbeth: Recall your unhappy childhood.
othello.recall();
-- Speak your mind.
othello.speak_mind();
-- Othello: You are as vile as the sum of yourself and a toad.
lady_macbeth.becomes( (spl.sum (lady_macbeth.val , -1 ) ));
-- Are you better than nothing.
if (lady_macbeth.val )> (0 ) then
-- Lady Macbeth: If so, let us return to scene III.
spl.log_op;
goto acti_sceneiii;
end if;
-- Scene IV: The end.
<<acti_sceneiv>> null;
-- [Exeunt].
end;


I've retained the original SPL as comments, followed by the PL/SQL that implements it.

I can now run it:
As you can see, I ran it with "Blog post" as input, and it returned the output "tsop golB", as expected.

I know what you're thinking: "can I have a go?"  And the answer is yes, you can! You can view and run any of the programs there, and can even add and compile one of your own if you "register" (set up a username and password).  I've even set up a memorable link: tiny.cc/spl

I just hope Oracle can handle the traffic...

[Exeunt]

Wednesday, July 20, 2016

Conditional column linking in APEX

Sometimes there is a requirement to have a column in an APEX report that acts as a link to another page for some rows but not for others like this:
Here, only when a program's status is 'VALID' can we link to another page by clicking on the program name.

Until now I only knew a rather bad way of doing this, which would be to write code in the report query like:

select case when program_status='VALID'
       then '<a href="f?p=MYAPP:123:&SESSION.::&DEBUG.:123:P123_PROGRAM_NAME:' 
            || program_name || '">' || program_name || '</a>'
          else program_name
          end as program_name,

Not only is it ugly and hard to write, it has some other issues too:

  1. I need to change the column's "Display As" from the default to "Standard Report Column" to prevent the link HTML being escaped and displayed literally.  And this may be frowned upon as a potential security risk.
  2. If using session state protection (SSP) then I need to add a checksum to the URL by calling apex_utils.prepare_url.  Now I have a function call in the SELECT clause, which may cause performance issues.
I now have a different solution, based partly on Tyler Muth's Conditional Column Formatting post.

Here is the query for my report above:


select program_name
     , program_status
     , case when program_status != 'VALID' then 'nolink' end as class_name
  from spl_programs

The third column CLASS_NAME will be set to 'nolink' on all rows that do not have the 'VALID' status.  This column is then set to not show in the report.

In the column attributes for the PROGRAM_NAME column, I define the column link in the usual manner, except that I add class="#CLASS_NAME#" in the Link Attributes:


I can add some CSS to make "nolink" links look like plain text, and also not have the cursor change when hovering over the link:

a.nolink {
    color: #000;
    cursor: default;
}

But the user could still click on the link.  To prevent it redirecting to the other page I can change the onclick event for all "nolink" links with some "Execute when page loads" Javascript:

$('a.nolink').attr("onclick","return false;");

Now if the user clicks on the link, nothing happens.  But in theory they could disable Javascript and reinstate the default link action. Or they could view the page source or use Inspect Element to see the link's URL, and copy/paste it into the browser.  In some cases that may not matter, but if it does matter then it should be prevented, for example by adding an appropriate Authorization Scheme to the target page.

One possible issue (a colleague just reminded me) is accessibility - although it no longer looks like a link, and does nothing when clicked, to a screen reader program it is still a link, and the screen reader user may be mystified as to why it doesn't work. Perhaps instead of "return false;" it should have"alert('You cannot open this program as it is invalid');".  Or there may be a better solution?