A Bit of a Pickle

19

1

Python's pickle module is used for serialisation, allowing one to dump an object in a way such that it can be later reconstructed. For this, pickle uses a simple stack-based language.

To keep things simple, we will be dealing with a small subset of this language:

(              Push a mark to the stack
S'abc'\n       Push a string to the stack (here with contents 'abc')
l              Pop everything up to the last mark, wrapping all but the mark in a list
t              Pop everything up to the last mark, wrapping all but the mark in a tuple
.              Terminate the virtual machine

Your task is to implement this subset of the language. Note that \n is a literal newline here, and newlines are actually important to the language.

For those familiar with GolfScript or CJam-like languages, ( and l/t operate similarly to [ and ] respectively.

Input

To keep things simple, the input will always be valid. In particular, you may assume the following about the input:

  • Strings will only consist of lowercase letters and spaces [a-z ], and will always use single quotes.
  • There will be no extraneous characters, with all instructions being as specified above. For example, this means that newlines will only ever occur after strings.
  • Every l/t has a matching ( before it and every ( has a matching l/t after it. There will also be at least one (.
  • There will be exactly one ., and it will always be the final character.

You may take input via command line, STDIN or function argument. You may use a single newline-escaped string instead of a multiline string if you wish, but please specify this in your answer.

Output

Output should be a representation of the final object, printed to STDOUT or returned as a string. Specifically:

  • Strings are represented by opening and closing single quotes with content in between, e.g. S'abc' -> 'abc'. You may not use double quotes for this challenge, even though they are allowed in Python.

  • Lists are represented by comma-separated elements surrounded by [] (e.g. ['a','b','c']), while tuples are repsented by comma-separated elements surrounded by () (e.g. ('a','b','c')).

  • Spaces don't matter, e.g. ('a', 'b', 'c' ) is okay.
  • You cannot have a comma before the closing bracket. Note that this is intentionally different from Python syntax rules to make things easier for most languages, and also to make it harder to simply build the list/tuple in Python then output it, due to how the single-element tuple is represented (for this challenge, we need ('a') as opposed to ('a',)).

Examples

The above text might seem daunting, but the following examples should make things a bit clearer.

(l.

Possible output: []

(t.

Possible output: ()

(S'hello world'
l.

Possible output: ['hello world']

(S'string one'
S'string two'
S'string three'
t.

Possible output: ('string one', 'string two', 'string three')

(S'a'
(S'b'
S'c'
lt.

Possible output: ('a',['b','c'])

((S'a'
S'b'
(lS'c'
t(S'd'
tl.

Possible output: [('a', 'b', [], 'c'), ('d')]

((S'a'
((S'b'
t(S'c'
lS'd'
(((ltlS'e'
S'f'
lS'g'
tl.

Possible output: [('a',[('b'),['c'],'d',[([])],'e','f'],'g')]

Rules

  • This is , so the code in the fewest bytes wins.
  • Any functionality that is designed to work with Python pickles is not allowed.

Security note: In real code, only unpickle from sources you trust, or else you might get a nasty cos\nsystem\n(S'rm -rf'\ntR. surprise

Sp3000

Posted 2015-10-04T16:39:49.770

Reputation: 58 729

Does S'abc'\n push abc or 'abc'? – CalculatorFeline – 2017-06-22T02:31:19.040

Answers

4

CJam, 63

q{"Slt 1:T;L ]',*'[\+']+ ]',*'(\+')+ [
 0:T; C+"35/T=S/(C#=~}fC

Try it online

Explanation:

q        read the input
{…}fC    for each character C in the input
  "…"    push that long string, containing code to handle various cases
  35/    split it into (two) parts of length 35
  T=     get the T'th part; T is 1 when parsing a string and 0 otherwise
          (T is initially 0 by default)
  S/     split by space into an array of strings
  (      take out the first item (containing special characters to check)
  C#     find the index of C in that string
  =      get the corresponding string from the array
          (when C is not found, # returns -1 which gets the last array item)
  ~      execute that string

Now the long string with various pieces of code. Each part has a few characters to check and then a block for handling each one, and the default case.

First part: Slt 1:T;L ]',*'[\+']+ ]',*'(\+')+ [

Slt      special characters to check
######## first block, corresponding to character 'S'
1:T;     set T=1, causing the next characters to be processed with the 2nd part
L        push an empty string/array, which will be used to collect the string
######## second block, corresponding to character 'l'
]        end array
',*      join with commas
'[\+     prepend a '['
']+      append a ']'
######## third block, corresponding to character 't'
]        end array
',*      join with commas
'(\+     prepend a '('
')+      append a ')'
######## last block, corresponding to other characters (practically, '(' and '.')
[        start array

Second part: (newline) 0:T; C+

newline  special characters to check (only one)
######## first block, corresponding to newline
0:T;     set T=0, switching back to the first part
######## last block, corresponding to any other character (including apostrophe)
C+       append the character to the collecting string

aditsu quit because SE is EVIL

Posted 2015-10-04T16:39:49.770

Reputation: 22 326

3

Perl, 149 bytes

I have a bad feeling that this is a poor attempt, but here goes:

$/=$,;$"=",";@s=[];/^\(/?$s[@s]=[]:{$p=/S(.*')/?$1:/l|t/?($l="@{pop@s}")|/l/?"[$l]":"($l)":0,push@{$s[-1]},$p}for<>=~/([(lt]|S.*?\n)/g;print$s[0][0];

The script has to be saved in a file and it takes input from STDIN.

Explanation:

# Set the input record separator to undef so that <> reads all lines at
# once
$/=$,;
# Ensure that elements of lists printed in quotes are separated by commas
$"=",";

# The stack. Initialise the bottom element with an empty array
@s=[];

# Tokens are extracted in the for loop a few lines below. Copied here for
# clarity: Read the entire input and iterate over all valid tokens of the
# pickle language
# for <>=~/([(lt]|S.*?\n)/g;
# the token is a mark - push an empty array to the stack
/^\(/ ? $s[@s]=[]
      # token is a string, push it inside the stack top
      : {$p=/S(.*')/ ? $1
                     # otherwise, remove the top and create list or tuple
                     # from it and push it inside the top element
                     : /l|t/ ? ($l="@{pop@s}") | /l/ ? "[$l]"
                                                     : "($l)"
                             : 0 # dummy value
                             # pushing of the string/list/tuple actually
                             # happens here
                             , push@{$s[-1]},$p} 
# read the entire input at once and iterate over all valid tokens
for <>=~/([(lt]|S.*?\n)/g;

# in the end, the bottom element of the stack will be an array with just one
# element which is the string representation of the object
print$s[0][0];

svsd

Posted 2015-10-04T16:39:49.770

Reputation: 556

0

Julia + ParserCombinator.jl 306 240

With my latest set of revisions I no longer think a pure julia solution would be shorter.

using ParserCombinator
v=join
j(t)=v(t,",")
a=Delayed()
s=E"S'"+Star(p".")+Drop(Equal("'\n"))|>x->"'$(v(x))'"
i=Star(a)|E""
l=E"("+i+E"l"|>x->"[$(j(x))]"
t=E"("+i+E"t"|>x->"($(j(x)))"
a.matcher=s|l|t
f(x)=parse_one(x,a+E".")|>first

That was interesting. I think the coded is fairly eloquent.

  • Output formatting is done at generation
  • a l, i, t, and s are basically CFG rules
  • f is the function that is called it brings it all together.
  • the Drop(Equal("'\n")) is annoying -- that would ideally be written as E"\n" but the E string macro does not handle escape sequences.
  • Interestingly this can trivially be converted to returning julia data-structures, it is basically removing the transforms on the RHS of |>s and adding tuple for the t rule

Lyndon White

Posted 2015-10-04T16:39:49.770

Reputation: 1 021

Unfortunately, per the rules in our help center, golfing is a requirement for posting solutions to code golf challenges.

– Dennis – 2017-06-21T06:08:01.597

I'm not 100% I can do an a shorter one, however. This is golfed to the exent that any solution using this langauge/library combination "Julia + ParserCombinator.jl" can be golfed. But on the other hand, there is solid change that there is a shorter pure julia solution.... now I have to write it. – Lyndon White – 2017-06-21T06:11:06.623

You don't have to write an entirely different solution; getting the most out of your approach is enough. At least the comments should be removed though. – Dennis – 2017-06-21T06:17:12.927

I didn't count the comments (or blank lines) towards the byte-count. I figured that was convention, I guess I figured wrongly – Lyndon White – 2017-06-21T06:19:07.087

Yes, the code is scored as posted. You can always add an ungolfed/annotated version though.

– Dennis – 2017-06-21T06:21:13.363

Neat solution. As far as golfing it goes, I think you can at least save four bytes by using b inline rather than defining it. The same can probably be done with s, l, and t, at the cost of parentheses. – Julian Wolf – 2017-06-22T00:03:08.560

@JulianWolf The b for sure. the s l and t I guess so yes, it might save a couple of bytes. Depends how many based on how tightly | binds. I'd need to check. I just worked out how to save 66. So I'll be back maybe to play with this later. – Lyndon White – 2017-06-22T01:10:51.313

0

JavaScript (ES6), 199 bytes

s=>(q=x=>(o=[],x.slice(0,-1).map(v=>o=[...o,v.map?q(v):`'${v}'`]),x.pop()<"m"?`[${o}]`:`(${o})`),q(eval(s[r="replace"](/\(/g,"[")[r](/[tl](?![\w ]+'\n)/g,"'$&'],")[r](/S('.+')/g,"$1,").slice(0,-2))))

Runs several regex replaces on the input to turn it into valid JS code, then parses that.

Test Snippet

f=
s=>(q=x=>(o=[],x.slice(0,-1).map(v=>o=[...o,v.map?q(v):`'${v}'`]),x.pop()<"m"?`[${o}]`:`(${o})`),q(eval(s[r="replace"](/\(/g,"[")[r](/[tl](?![\w ]*'\n)/g,"'$&'],")[r](/S('.+')/g,"$1,").slice(0,-2))))
<select oninput="I.value=this.selectedIndex?this.value.replace(/\\n/g,'\n'):'';O.innerHTML=this.selectedIndex?f(I.value):''"><option>---Tests---<option>(l.<option>(t.</option><option>(S'hello world'\nl.<option>(S'string one'\nS'string two'\nS'string three'\nt.<option>(S'a'\n(S'b'\nS'c'\nlt.<option>((S'a'\nS'b'\n(lS'c'\nt(S'd'\ntl.<option>((S'a'\n((S'b'\nt(S'c'\nlS'd'\n(((ltlS'e'\nS'f'\nlS'g'\ntl.</select><br>
<textarea rows=10 cols=20 id=I></textarea><br><button onclick="O.innerHTML=f(I.value)">Run</button><br><pre id=O></pre>

Justin Mariner

Posted 2015-10-04T16:39:49.770

Reputation: 4 746

0

><>, 88 bytes

^"][">}r]
~rl?!;o11.
^0\!\
 &</\?[1&~?=1l","
 1/\ii:"'"=?v44.
>i9%0$.     >r]i~


 ")("\

Fun with jumps! Uses the fact that the ASCII codes for the 5 main commands involved, mod 9, are:

S -> 2
l -> 0
t -> 8
( -> 4
. -> 1

This allows each operation to be handled on its own line, which will be jumped to directly. Also uses the stack of stacks to construct each string and nested list/tuple separately before wrapping them in the required characters.

Sok

Posted 2015-10-04T16:39:49.770

Reputation: 5 592

Nice work, but unfortunately I don't seem to be getting the right output for most of the test cases (the brackets seem to be the wrong way around, for one thing) – Sp3000 – 2015-10-13T15:01:19.220