Một ngôn ngữ nhỏ xứng đáng là một thông dịch viên nhỏ


21

Đây là một định nghĩa ngôn ngữ rất đơn giản:

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

Các chương trình ví dụ (lưu ý rằng chuỗi rỗng là một biến, nhưng tôi sẽ sử dụng nó một cách tiết kiệm vì mục đích rõ ràng và một số biến bị loại bỏ trong chương trình khi chúng thường là 0 theo mặc định):

<>: 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

Mục tiêu của bạn là viết trình thông dịch nhỏ nhất cho ngôn ngữ này.

  1. Giá trị của một biến có thể lớn tùy ý và chỉ nên bị giới hạn bởi tổng bộ nhớ mà ngôn ngữ của bạn có quyền truy cập, theo lý thuyết, nhưng bạn chỉ được yêu cầu xử lý các giá trị lên tới 2 ^ 256.

  2. Về mặt lý thuyết, chương trình của bạn sẽ có thể xử lý các chương trình dài tùy ý, nhưng bạn sẽ chỉ được yêu cầu làm việc trên các chương trình dài dưới 2 ^ 32 ký tự. Bạn cũng được yêu cầu xử lý các vòng lặp lồng nhau có độ sâu lên tới 2 ^ 32.

  3. Bạn có thể giả định rằng chương trình là một chương trình hợp lệ và bạn sẽ chỉ nhận được các số nguyên không âm khi bạn yêu cầu nhập liệu. Bạn cũng có thể giả sử rằng chỉ các ký tự có thể in ASCII được bao gồm trong chuỗi đầu vào.

  4. Tốc độ của chương trình bạn diễn giải không thành vấn đề, nó sẽ rất chậm đối với những thứ đơn giản như phép nhân 5 chữ số, không cần tối ưu hóa.

  5. Nếu bạn muốn sử dụng một ngôn ngữ không thể chấp nhận hợp lý đầu vào hoặc tạo đầu ra theo cách được mô tả bởi ngôn ngữ đó, hãy sử dụng bất kỳ giải thích nào bạn muốn làm cho nó có thể. Điều này áp dụng cho mọi lý do ngôn ngữ của bạn không thể thực hiện một số hành vi cần thiết. Tôi muốn tất cả các ngôn ngữ để có thể cạnh tranh.

  6. Chương trình ngắn nhất sẽ thắng. Tiêu chuẩn áp dụng.


Là một thách thức phụ tôi muốn xem chương trình tôi có thể viết ngắn đến mức nào cho ra số 2016, nhưng trước tiên tôi cần đợi một thông dịch viên được viết để tôi có thể kiểm tra mã của mình.
Neil

1
Tôi có một trình thông dịch trong Python 2.7 ở đây .
Dưa ma sát

2
Ngôn ngữ này được gọi là gì? Nó xứng đáng có một vị trí tại esolangs.org
wizzwizz4

@Neil Tôi quản lý để làm điều đó trong 72 ký tự
Fricative Melon

@FricativeMelon 72? Tôi có thể làm điều đó trong 43!
Neil

Câu trả lời:


4

Ruby, 182 byte

$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 *$*

Hãy thử nó như thế này:

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

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

Làm thế nào nó hoạt động

Các rchức năng tokenizes một chuỗi đầu vào và thực hiện mỗi mã thông báo:

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

Chúng tôi tìm kiếm một số $2khớp tên biến [^!?^<>]*, theo sau là một trong hai

  • <...>trong đó ...khớp với 0 hoặc nhiều chương trình ( \glà đệ quy), trong trường hợp đó $4khôngnil
  • A !, ?hoặc ^ký tự, được chụp bởi $3, trong trường hợp $4này là nil.

Sau đó, logic để thực thi mã thông báo khá đơn giản khi thụt lề một chút:

$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.

Tôi thực sự tò mò làm thế nào điều này hoạt động.
Jerry Jeremiah

1

JavaScript (ES6) 184 194 209

Chỉnh sửa Đơn giản hóa (sử dụng tham số chức năng cho đầu vào và đầu ra có vẻ là một ý tưởng hay, nhưng thực tế không phải vậy), thêm 1 byte được lưu thx @ ӍѲꝆΛҐӍΛNETҒЦꝆ

Chỉnh sửa 2 Phân tích cú pháp sửa đổi. Logic cho tăng / đầu vào được mượn từ câu trả lời của @ 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")

Ít chơi gôn

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)
}

KIỂM TRA Đoạn trích bắt đầu đánh giá năm 2016 bằng chương trình được đăng bởi @Neil. Kiên nhẫn...

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>


Là sử dụng evalđể tránh returnkhông phải là một lựa chọn?
Mama Fun Roll

@ ӍѲꝆΛҐӍΛNETҒЦꝆ có, eval tiết kiệm 1 byte. Tôi vẫn đang tìm kiếm thứ gì đó quan trọng hơn
edc65

0

Perl, 251 byte

@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++;} 

Phiên bản dễ đọc hơn:

# 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++;
}

Điều này làm lãng phí một loạt các byte cố định các vòng lặp để nhảy trực tiếp, nhưng quét ngược lại cho vòng lặp bắt đầu xúc phạm cảm giác thẩm mỹ của tôi.


0

C ++ chuẩn, 400 byte

Điều này biên dịch với 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]);}

Tôi có thể rút ngắn nó thêm một số. Nếu bạn có một số gợi ý xin vui lòng bình luận.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.