Невелика мова заслуговує на невеликого перекладача


21

Ось дуже просте визначення мови:

A Variable is any string that does not contain ^, <, >, !, or ?
The empty string is a valid variable identifier
The value of every variable starts at 0.
A Statement is one of (var is a Variable, P is a Program):
    var^   -> changes var to be equal to 1 more than itself
    var<P> -> while var > 0, changes var to be equal to 1 less than itself, then runs P
    var! -> output value of var
    var? -> ask for non-negative integer as input, increase var by that value
A Program is a concatenation of Statements, running a Program means running each Statement in order

Приклади програм (зауважте, що порожня рядок - це змінна, але я її використовуватиму вкрай для наочності, а деякі змінні нульові в програмі, коли вони зазвичай 0 за замовчуванням):

<>: sets the value of the empty string variable to 0
b<>b?b<a^>: asks for b, then adds the value stored in b to a, zeroing b in the process
b<>b?a<>b<a^>: asks for b, then sets a to the value of b, zeroing b in the process
a<>c<>b<a^c^>c<b^> : copies the value in b into a without zeroing it
b<>c<>a<c^c^c<b^>>b! : outputs a multiplied by 2
b^b<a<>a?a!b^> : outputs what you input, forever

Ваша мета - написати найменшого перекладача для цієї мови.

  1. Значення змінної може бути довільно великим і має бути обмежене лише загальною пам'яттю, до якої мова має доступ, теоретично, але вам потрібно лише обробляти значення до 2 ^ 256.

  2. Теоретично ваша програма повинна вміти обробляти довільно довгі програми, але вам потрібно буде працювати лише над програмами довжиною від 2 до 32 символів. Також потрібно обробляти вкладені петлі глибиною до 2 ^ 32.

  3. Ви можете припустити, що програма є дійсною програмою, і що ви коли-небудь отримаєте невід’ємні цілі числа лише тоді, коли попросите ввести. Ви також можете припустити, що у вхідний рядок включені лише символи для друку ASCII.

  4. Швидкість програми, яку ви інтерпретуєте, не має значення, вона вже буде болісно повільною для речей, простих як 5-розрядне множення, без оптимізації.

  5. Якщо ви хочете використовувати мову, яка не може розумно прийняти введення або вивести результат таким чином, як описаний цією мовою, використовуйте будь-яку інтерпретацію, яку ви хочете зробити можливою. Це стосується будь-якої причини, коли ваша мова не може реалізувати потрібну поведінку. Я хочу, щоб усі мови могли змагатися.

  6. Найкоротша програма виграє. Застосовуються стандартні лазівки.


Як побічний виклик я хочу побачити, наскільки коротка програма, яку я можу написати, яка виводить число 2016, але спочатку мені потрібно дочекатися написання перекладача, щоб я міг перевірити свій код.
Ніл

1
У мене є інтерпретатор в Python 2.7 тут .
Фрикативна диня

2
Як називається ця мова? Заслуговує на сайті esolangs.org
wizzwizz4

@Neil Мені вдалося це зробити в 72 символах
Fricative Melon

@FricativeMelon 72? Я можу це зробити у 43!
Ніл

Відповіді:


4

Рубін, 182 байти

$h=Hash.new 0
def r(c)c.scan(/(([^!?^<>]*)(<(\g<1>*)>|[!?^]))/){$4?($1=~/(.*?)<(.*)>/
($h[$1]-=1;r$2)while$h[$1]>0):$3<?"?p($h[$2]):$h[$2]+=$3<?@?STDIN.gets.to_i:
1}end
r IO.read *$*

Спробуйте так:

$ cat code
a?b<>c<>a<c^c^c<b^>>b!

$ ruby lynn.rb code
3                           <-- input
6                           <-- output

Як це працює

rФункція розмічає вхідні рядок і виконує кожен маркер:

def r(c)
    c.scan(/(([^!?^<>]*)(<(\g<1>*)>|[!?^]))/){
        ...
    }
end

Ми шукаємо $2відповідність змінних імен [^!?^<>]*, за якими слідує будь-яке

  • <...>де ...відповідає нулю або більше програм ( \gє рекурсією), у такому випадку $4- ніnil
  • А !, ?або ^персонаж, захоплений $3, у такому випадку $4є nil.

Тоді логіка для виконання маркера досить проста, коли її трохи відступати:

$4 ? (                                    # If it's a loop:
    $1 =~ /(.*?)<(.*)>/                   #   Re-match token*
    ($h[$1]-=1; r $2) while $h[$1] > 0    #   Recurse to run loop
) :                                       # Else:
    $3 < ?"                               #   If it's an !:
      ? p($h[$2])                         #     Print the var
      : $h[$2] +=                         #   Else, increment it by:
          $3 < ?@                         #     If it's a ?:
              ? STDIN.gets.to_i           #       User input
              : 1                         #     Else: 1

* There's an oniguruma bug, I think, that keeps me from simply using $3 here.

Мені дуже цікаво, як це працює.
Джеррі Єремія

1

JavaScript (ES6) 184 194 209

Спрощене редагування (використання функціональних параметрів для введення та виведення здавалося приємною ідеєю, але цього не було), ще 1 байт збережено thx @ ӍѲꝆΛҐӍΛПҒЦꝆ

Редагувати 2 Змінений аналіз. Логіка збільшення / введення запозичена з відповіді @ Lynn

F=(p,i=0,v={},n='')=>eval("for(;c='>?^!<'.indexOf(q=p[i++]||'');n=~c?'':n+q)if(c>3){for(;v[n]--;)F(p,i,v);i=F(p,i,v[n]=0)}else~c&&v?c>2?alert(v[n]|0):v[n]=~~v[n]+(--c||+prompt()):0;i")

Менше гольфу

F=(p,      // program 
   i = 0,  // initial instruction pointer  
   v = {}, // variables (default to empty) or if 0, flag of dummy execution
   n = ''    // name of current variable (has to be local for recursive calls)
{
  for(; c='>?^!<'.indexOf(q=p[i++]||''); )
  // q = current character
  // c = current command (int 0..4 or -1 id not recognized)
  //     note 0 end of subprogram or end of program
  {
    if(c>3) // 4='<' call subprogram - recursive
    {
      for(;v[n]--;)
        F(p,i,v); // conditional call, repeated - using real environment
      v[n] = 0; // Reset variable at loop end
      i=F(p,i,0) // one more unconditional dummy call, just to advance i
    }
    else
      ~c&&v? // if valid command (1..3) and not dummy
      c>2?
        alert(v[n]|0) // output, undefined becomes 0
        :v[n]=~~v[n]+(--c||+prompt()) // inc with 1 or user input
      :0     // not valid command or dummy, do nothing
    n=~c?'':n+q // reset or update current variable name
  }
  return i // return current istruction pointer (for recursive calls)
}

ТЕСТ Тест- фрагмент початку оцінювання 2016 року за допомогою програми, опублікованої @Neil. Будьте терплячі ...

F=(p,i=0,v={},n='')=>eval("for(;c='>?^!<'.indexOf(q=p[i++]||'');n=~c?'':n+q)if(c>3){for(;v[n]--;)F(p,i,v);i=F(p,i,v[n]=0)}else~c&&v?c>2?alert(v[n]|0):v[n]=~~v[n]+(--c||+prompt()):0;i")

// TEST
function definput(){  I.disabled = KI.checked; }
function defoutput(){  O.disabled = KO.checked; }

function run()
{
  var prog=P.value, irows = I.value.split('\n'), pi=0;
  var fout=x=>O.value+=x+'\n';
  var fin=x=>irows[pi++];
  var saveAlert=alert, savePrompt=prompt
  if (!KO.checked) alert=fout,O.value=''
  if (!KI.checked) prompt=fin
  
  F(prog);
  
  alert=saveAlert
  prompt=savePrompt
}

P.value="^^^^<a^a^>a<^^^^><a^b^>a<c<b^^>b<c^^>>!"

run()
Program <button onclick="run()">RUN</button><br>
<textarea id=P></textarea><br>
Input (or <input type=checkbox id=KI onclick="definput()"> interactive prompt)<br>
<textarea id=I>5</textarea><br>
Output (or <input type=checkbox id=KO onclick="defoutput()"> popup)<br>
<textarea id=O readonly></textarea><br>


Використовувати evalуникати returnне є варіантом?
Mama Fun Roll

@ ӍѲꝆΛҐӍΛПҒЦꝆ так, eval зберігає 1 байт. Я все ще шукаю щось більш істотне
edc65

0

Perl, 251 байт

@p=split/([<>!?^])/,<>;for$c(0..$#p){$_=$p[$c];/</&&push@j,$c;if(/>/){$a=pop@j;$p[$c]=">$a";$p[$a]="<$c";}}while($c<$#p){$_=$p[$c];/\^/&&$v{$l}++;/!/&&print$v{$l};/\?/&&($v{$l}=<>);/<(\d+)/&&($v{$l}?$v{$l}--:($c=$1));/>(\d+)/&&($c=$1-2);$l=$_;$c++;} 

Простіша для читання версія:

# treat the first line of input as a program

# split on punctuation keywords; @p will contain the program as a list
# of tokens (including whitespace between adjacent punctuation)
@p = split /([<>!?^])/, <>;

# rewrite jump addresses

# the interpreter could scan backwards to avoid this, but that idea
# makes me feel dirty
for $c (0..$#p) {
    $_ = $p[$c];
    # save loop-start address on stack
    /</ && push @j, $c;
    if (/>/) {
        # if we encounter a loop-end instruction, rewrite it and the
        # corresponding loop-start to include the address (of the
        # instruction---jumps have to offset from this)
        $a = pop @j;
        $p[$c] = ">$a";
        $p[$a] = "<$c";
    }
}

# execute the program

# our program is already in @p

# $c will contain our program counter

# $l will contain the name of the last-referenced variable

while ($c < $#p) {
    # move current instruction into $_ for shorter matching
    $_ = $p[$c];

    # increment instruction
    /\^/ && $v{$l}++;

    # output instruction
    /!/ && print $v{$l};

    # input instruction
    /\?/ && ($v{$l} = <>);

    # loop start, including address
    /<(\d+)/ && ($v{$l} ? $v{$l}-- : ($c = $1));

    # loop end, including address
    />(\d+)/ && ($c = $1-2);

    # copy current instruction into "last variable name"---this will
    # sometimes contain operators, but we have null-string
    # instructions between adjacent operators, so it'll be fine
    $l = $_;

    # advance the program counter
    $c++;
}

Це витрачає купу байтів, що фіксують петлі, щоб бути прямими стрибками, але сканування назад для початку циклу образило моє почуття естетики.


0

Стандартний C ++, 400 байт

Це компілюється з g++ -g test.cpp -Wall -Wextra -pedantic -std=gnu++11

#include<map>
#include<cstring>
#define b ;break;case
#define u unsigned long long
std::map<std::string,u>V;void r(char*s){char*p,*q,*e;for(u c;*s;s=p){p=strpbrk(s,"^<?!");c=*p;*p++=0;switch(c){b'^':V[s]++b'<':for(e=p,c=0;*e!='>'||c;e++)c+=(*e=='<')-(*e=='>');*e++=0;while(V[s]>0){V[s]--;r(q=strdup(p));free(q);}p=e;b'?':scanf("%llu",&V[s])b'!':printf("%llu",V[s]);}}}int main(int,char*v[]){r(v[1]);}

Я можу трохи більше скоротити його. Якщо у вас є якісь пропозиції, будь ласка, прокоментуйте.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.