Partial tq interpreter

2

In this task you are expected to provide a list output given an input tq program. The tq programs will not contain whitespace inside them.

What is tq, in the first place?

tq is a lazy-evaluated language that is designed with the idea that array items should be accessable during the definition of them. In tq, there is no explicit separator of an array, only special arrangements of monadic/dyadic functions and nilads.

The following program is a program printing [123] (Pretend that tq doesn't support strings because we aren't dealing with them in this case):

123

This defines a list with the first item being the number 123, after which all items in the list will be outputted inside a list.

In tq, numbers are supported to allow multiple-digits. So this defines a list with 2 items:

12+12,5

In this test case, you are expected to output the list [24,5]. Let's explain it step by step.

12+12   # This evaluates 12 + 12 in the current item in the list, returning 24
     ,  # A separator. This separates two items when they could be potentially
        # ambiguous when they are applied without a separator.
      5 # This evaluates 5 in the current item in the list, returning 5
        # The comma is simply a no-op that doesn't require parsing.

So you think that tq is not hard at all to implement? Well, remember that tq also has a special feature of accessing the items in an array before the array is defined!

555th123

We introduce two new atoms:

  • t (tail) means access the last item in the list
  • h (head) means access the first item in the list

Therefore our list is going to yield:

[555,123,555,123]

Now take a look at this program:

555ps123

We introduce 2 more atoms:

  • p Yield the next item before (previous) the current position
  • s Yield the next item after (succeeding)the current position

This yields the list:

[555,555,123,123]

A quick reference of the tq language

Just assume that you only have two operands for the operators.

  • [0-9] starts a number. Numbers will only be positive integers, i.e. no decimals and negative numbers.
  • , This is a separator of different items when it is given that two consecutive indexes will be ambiguous with each other without a separator. In tq all of the remaining characters can act as a separator, but in this case it is a good idea to implement only , for the ease of your implementation.
  • + and * These are arithmetic operators. Usually in tq, they may be applied multiple times, e.g. 1+2+3, but in your implementation, input will be provided so that this will not happen and only 1+2 will happen (there will not be applications multiple times).
  • t return the last item in the list. If the code is t1,2,3 it shall return [3,1,2,3].
  • h return the first item in the list. If the code is 1,2,3h it shall return [1,2,3,1].
  • p returns the item before the current item. If the code is 0,1,p,3,4 the code shall return [0,1,1,3,4].
  • s returns the item after the current item. If the code is 0,1,s,3,4 the code shall return [0,1,3,3,4].

Additional rules (with some examples)

  • You also have to implement multiple hops. E.g. 1th should yield [1,1,1]
  • If you know that something is going to form a loop, e.g. 1sp2, the cells that form the loop should be removed. Therefore the previous example will yield [1,2].
  • Out of bounds indexing will yield the closest index of the indexed item that is a number. E.g. 1,2s should yield [1,2,2]
  • If any possible indexing fails, you may do whatever operation you prefer.

More test cases

  • 4p*p will yield [4,16]
  • 1p+s2 will yield [1,3,2]
  • 1,2,3h+t4,5,6 will yield [1,2,3,7,4,5,6]
  • 3ppss6 will yield [3,3,3,6,6,6]

user85052

Posted 2020-01-04T13:48:58.947

Reputation:

Is an expression such as t+3 possible? – Lyxal – 2020-01-05T04:19:14.433

Also, is an expression such as 3+t possible? – Lyxal – 2020-01-05T04:22:00.253

Answers

2

Python 3, 536 522 bytes

def f(c):
 D={"t":"o[n]","h":"o[0]","p":"o[i-1]","s":"o[i+1]"};o=[];T="";A=o.append;l=type;n=-1
 for C in c:
  if C==",":A(T);T=""
  elif C in"0123456789":
   if"o"in T:
    A(T);T=C
   else:T+=C
  elif C in"htps":
   if T and T[n]in"+*":T+=D[C]
   else:
    if T:A(T)
    T=D[C]
  else:
   T+=C
 if T:A(T)
 if D['s']in o[n]:o[n]=o[n].replace("i+1","i-1")
 while not all([l(m)==int for m in o]):
  for i in range(len(o)):
   x=o[i]
   try:x=eval(str(o[i]))
   except:pass
   if l(x)is str:continue
   else:o[i]=x
 return o

Try it online!

Ungolfed:

def tq(code):
    corresp = {"t":"output[-1]","h":"output[0]","p":"output[i-1]","s":"output[i+1]"}
    output = []
    temp = ""
    for char in code:
        if char == ",":
            output.append(temp)
            temp = ""

        elif char in "0123456789":
            if "h" in temp or "t" in temp or "p" in temp or "s" in temp:
                output.append(temp)
                temp = char
            else:
                temp += char

        elif char in "htps":
            if temp and temp[-1] in "+*":
                temp += corresp[char]
            else:
                if temp:
                    output.append(temp)
                temp = corresp[char]


        else:
            temp += char
    if temp:
        output.append(temp)
    if 'output[i+1]' in output[-1]:
        output[-1] = output[-1].replace("i+1","i-1")
    while not all([type(m) == int for m in output]):
        for i in range(len(output)):
            x = output[i]
            try: x = eval(str(output[i]))
            except: pass
            if type(x) is str:
                continue
            else:
                output[i] = x
    return output

Somehow, by fixing a bug, I was able to remove 14 bytes. I've well and truly broken code golf now.

Anyhow, this takes the program, parses it into evalable chunks and then evals them.

Lyxal

Posted 2020-01-04T13:48:58.947

Reputation: 5 253

True, but then it would be a longer answer. – Lyxal – 2020-01-05T06:15:50.380

1Fails on t5p*p – Nick Kennedy – 2020-01-05T19:32:11.977

Seems to fail on t2p*2 – Arnauld – 2020-01-05T23:41:33.830

2

Jelly, 131 bytes

+Ø+o+2$};;1N;ؽ“spth+*”żF
ṣ”,µeþØD,Øa¤Ḥ2¦SSƝ>2k)Ẏµe€⁾+*oḊ$k)µJç€LyⱮ"ƲVḢf?€€ØDAƑƇ.ị;@Ʋµṙ-+;×ɗị@ƭ/$¹L’$?¹Nị⁸¹L’$?ɗAƑ?€AƑ?F¥€`ÐLAƑƇFṖṖ

Try it online!

A full program taking a tq program string as its input and returning a list of integers. Still needs more golfing, but seems to work ok. Doesn’t use eval except for converting numbers from string to integer (which wouldn’t be an eval in most languages).

Nick Kennedy

Posted 2020-01-04T13:48:58.947

Reputation: 11 829

2

JavaScript (ES6),  269 263  262 bytes

v=>(F=r=>r+''==(h=(i,d)=>r[i]?1/r[i]?i:h(i+d,d):h,L=r.length-1,r=r.map((s,i)=>1/(x=eval((s+'').replace(/[h-t]/g,c=>` +r[${c<'i'?0:c>'s'?L:c>F?++i<L?i:h(L,-1):i?i-1:h(0,1)}]`)))?x:s))?r.filter(x=>1/x):F(r))(v.split(/,|((?:\d+|.)(?:[+*](?:\d+|.))?)/).filter(v=>v))

Try it online!

Test cases borrowed from Nick's answer.

How?

We first split the input into tokens with the following regular expression:

+---------------------------> either a comma (discarded)
|                             or the following group (captured):
|        +------------------>   1st operand:
|        |                        a number or a single non-numeric character
|        |                      which may be followed by:
|        |       +---------->     an operator
|        |       |       +-->     and a 2nd operand
|     ___|_    __|_   ___|_
,|((?:\d+|.)(?:[+*](?:\d+|.))?)

Example:

"12,3p*4h" --> [ '12', undefined, '3', 'p*4', 'h' ]

Empty or undefined elements are removed with filter(v => v).

The result is passed to the main function \$F\$ which recursively attempts to replace expressions with numeric values until no further modification is possible.

F = r =>                             // F is a function taking a list of tokens r[]
  r + '' == (                        // test whether r[] is modified by this iteration
    h = (i, d) =>                    // h is a function taking i = position, d = direction
      r[i] ?                         //   if r[i] is defined:
        1 / r[i] ? i                 //     if r[i] is numeric, return i
                 : h(i + d, d)       //     otherwise, add d to i and try again
      :                              //   else:
        h,                           //     return a non-numeric value
      L = r.length - 1,              // L = index of last element in r[]
      r = r.map((s, i) =>            // for each element s at position i in r[]:
        1 / (                        //
          x = eval((s + '').replace( //   evaluate s
            /[h-t]/g,                //   where letters are replaced with
            c => ` +r[${             //   " +r[index]" where the index is:
              c < 'i' ? 0 :          //     'h' -> 0
              c > 's' ? L :          //     't' -> L
              c > F ?                //     's' ->
                ++i < L ? i          //       i + 1 if this is not the last item
                        : h(L, -1)   //       or the first preceding number
              :                      //     'p' ->
                i ? i - 1            //       i - 1 if this is not the first item
                  : h(0, 1)          //       or the first following number
            }]`))                    //
        ) ? x : s                    //   yield x if it's numeric or s otherwise
      )                              // end of map()
  ) ?                                // if r[] is unchanged:
    r.filter(x => 1 / x)             //   return it with non-numeric items removed
  :                                  // else:
    F(r)                             //   do a recursive call

Arnauld

Posted 2020-01-04T13:48:58.947

Reputation: 111 334