12
\$\begingroup\$

The Project

The golfing snippet serves as a test suite for solutions. The snippet provides a testbed for golfed code, providing a bytecount, an input field, and an output console. Users can edit the golfed code to test improvements and compare outputs with the original solution.

How to Use

Create a new stack snippet in your answer. Paste the snippet code into the HTML section, and paste your code golf solution into the JavaScript section.

The golfed code will automatically be visible in your answer.

The snippet code:

<!--                               Try the test suite below!                              --><strong id="bytecount" style="display:inline; font-size:32px; font-family:Helvetica"></strong><strong id="bytediff" style="display:inline; margin-left:10px; font-size:32px; font-family:Helvetica; color:lightgray"></strong><br><br><pre style="margin:0">Code:</pre><textarea id="textbox" style="margin-top:5px; margin-bottom:5px"></textarea><br><pre style="margin:0">Input:</pre><textarea id="inputbox" style="margin-top:5px; margin-bottom:5px"></textarea><br><button id="testbtn">Test!</button><button id="resetbtn">Reset</button><br><p><strong id="origheader" style="font-family:Helvetica; display:none">Original Code Output:</strong><p><div id="origoutput" style="margin-left:15px"></div><p><strong id="newheader" style="font-family:Helvetica; display:none">New Code Output:</strong><p><div id="newoutput" style="margin-left:15px"></div><script type="text/javascript" id="golfsnippet">var bytecount=document.getElementById("bytecount");var bytediff=document.getElementById("bytediff");var textbox=document.getElementById("textbox");var inputbox=document.getElementById("inputbox");var testbtn=document.getElementById("testbtn");var resetbtn=document.getElementById("resetbtn");var origheader=document.getElementById("origheader");var newheader=document.getElementById("newheader");var origoutput=document.getElementById("origoutput");var newoutput=document.getElementById("newoutput");textbox.style.width=inputbox.style.width=window.innerWidth-50+"px";var _originalCode=null;function getOriginalCode(){if(_originalCode!=null)return _originalCode;var allScripts=document.getElementsByTagName("script");for(var i=0;i<allScripts.length;i++){var script=allScripts[i];if(script.id!="golfsnippet"){originalCode=script.textContent.trim();return originalCode}}}function getNewCode(){return textbox.value.trim()}function getInput(){try{var inputText=inputbox.value.trim();var input=eval("["+inputText+"]");return input}catch(e){return null}}function setTextbox(s){textbox.value=s;onTextboxChange()}function setOutput(output,s){output.innerHTML=s}function addOutput(output,data){output.innerHTML+='<pre style="background-color:'+(data.type=="err"?"lightcoral":"lightgray")+'">'+escape(data.content)+"</pre>"}function getByteCount(s){return(new Blob([s],{encoding:"UTF-8",type:"text/plain;charset=UTF-8"})).size}function onTextboxChange(){var newLength=getByteCount(getNewCode());var oldLength=getByteCount(getOriginalCode());bytecount.innerHTML=newLength+" bytes";var diff=newLength-oldLength;if(diff>0){bytediff.innerHTML="(+"+diff+")";bytediff.style.color="lightcoral"}else if(diff<0){bytediff.innerHTML="("+diff+")";bytediff.style.color="lightgreen"}else{bytediff.innerHTML="("+diff+")";bytediff.style.color="lightgray"}}function onTestBtn(evt){origheader.style.display="inline";newheader.style.display="inline";setOutput(newoutput,"");setOutput(origoutput,"");var input=getInput();if(input===null){addOutput(origoutput,{type:"err",content:"Input is malformed. Using no input."});addOutput(newoutput,{type:"err",content:"Input is malformed. Using no input."});input=[]}doInterpret(getNewCode(),input,function(data){addOutput(newoutput,data)});doInterpret(getOriginalCode(),input,function(data){addOutput(origoutput,data)});evt.stopPropagation();return false}function onResetBtn(evt){setTextbox(getOriginalCode());origheader.style.display="none";newheader.style.display="none";setOutput(origoutput,"");setOutput(newoutput,"")}function escape(s){return s.toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}window.alert=function(){};window.prompt=function(){};function doInterpret(code,input,cb){var workerCode=interpret.toString()+";function stdout(s){ self.postMessage( {'type': 'out', 'content': s} ); }"+" function stderr(s){ self.postMessage( {'type': 'err', 'content': s} ); }"+" function kill(){ self.close(); }"+" self.addEventListener('message', function(msg){ interpret(msg.data.code, msg.data.input); });";var interpreter=new Worker(URL.createObjectURL(new Blob([workerCode])));interpreter.addEventListener("message",function(msg){cb(msg.data)});interpreter.postMessage({"code":code,"input":input});setTimeout(function(){interpreter.terminate()},1E4)}setTimeout(function(){getOriginalCode();textbox.addEventListener("input",onTextboxChange);testbtn.addEventListener("click",onTestBtn);resetbtn.addEventListener("click",onResetBtn);setTextbox(getOriginalCode())},100);function interpret(code,input){window={};alert=function(s){stdout(s)};window.alert=alert;console.log=alert;prompt=function(s){if(input.length<1)stderr("not enough input");else{var nextInput=input[0];input=input.slice(1);return nextInput.toString()}};window.prompt=prompt;(function(){try{var evalResult=eval(code);if(typeof evalResult=="function"){var callResult=evalResult.apply(this,input);if(typeof callResult!="undefined")stdout(callResult)}}catch(e){stderr(e.message)}})()};</script>

Example

Here's an example usage of the snippet. Try running and editing the code! (Note: expects two strings as input, which can be inputted as "string 1", "string 2")

Features

  • Bytecount is automatically updated as code is modified.
  • Original code is preloaded into the code area.
  • Code is editable! Outputs for the original and modified code are shown side-by-side for the same input to allow for easy comparison.
  • All entered code is sandboxed by using webworkers. This means that entered code snippets cannot interfere with each other or with the main snippet.
  • A 10 second timeout is employed to handle infinite loops.


Details

These are specific for the JavaScript interprter for this snippet.

(these are irrelevant for interpreters written to run other languages)

  • Programs can be either a full program or a function.
  • Input can be taken through prompt or function arguments.
  • Output can be through alert, console.log, or function return.
  • If you are writing a function, the result of evaling your code must return the function. Notably, function a(){} cannot be your main function, but a=function(){} will work at no cost.
  • The list of input is determined by wrapping the input string in brackets [] and evaling it. Thus input box must contain a comma-separated list, in the same form as in declaring an array.


But why just JavaScript?

It doesn't have to be! At the end of this code, there is an interpret function that can be easily edited with an interpreter for another language, which would transform the snippet into using another language.

It seems that there is a decent interest in this snippet. I'm working on renovating this post to serve as a more complete guide. Unminified code and a guide to extending this to other languages is in the works!

Disclaimer: This is not a perfect snippet (yet!); it is very much a work-in-progress. Feedback is very much appreciated.

\$\endgroup\$
  • \$\begingroup\$ This question might interest you. (Your snippet looks a lot like my sample answer.) \$\endgroup\$ – Calvin's Hobbies Oct 5 '15 at 7:15
  • 1
    \$\begingroup\$ @Calvin'sHobbies True. I'd forgotten about that post! However, this snippet has many advantages. Code is sandboxed in a webworker and has a time limit. Thus, while(1); won't crash this snippet. Also, this sandboxing stops code from interfering with the interpreter, like a document.getElementById("...").style.display="none" type of code. This interpreter allows varying input forms, provides a bytecount, and errors are outputted to the display. (note: This was not intended to be criticism of your snippet - sorry if it's misinterpreted - just noting some differences. Thanks for the input! :D) \$\endgroup\$ – jrich Oct 6 '15 at 1:07
  • \$\begingroup\$ Chrome's mixed content blocking kicks in when viewing the example you linked over HTTPS. (This happened after I hit the "Test!" button.) \$\endgroup\$ – user2428118 Oct 8 '15 at 22:06
  • 1
    \$\begingroup\$ @user2428118 Interesting, I can replicate this behavior. Oddly, it works fine in FireFox. Not quite sure how to fix this, but thanks for the feedback! \$\endgroup\$ – jrich Oct 8 '15 at 22:48
  • \$\begingroup\$ Just for fun, I tried minifying/golfing the snippet while retaining all of the original formatting. I managed to get it from 4973 to 3428 using the usual techniques (rename variables/element IDs, remove extra whitespace from HTML/CSS, move creation of once-used functions, etc.), but I don't think I'd be able to get much more than another hundred off without reorganizing some stuff completely, which I'm not prepared to do right now. Just thought I'd let you know. ;) \$\endgroup\$ – ETHproductions Oct 9 '15 at 2:39

You must log in to answer this question.

Browse other questions tagged .