Viết một thông dịch viên gà!


8

Bạn phải viết một thông dịch viên cho một ngôn ngữ tuyệt vời gọi là !

Bạn nên đọc chương trình Gà từ một tệp, đầu vào tiêu chuẩn, đối số chương trình hoặc chức năng hoặc bất cứ điều gì thuận tiện nhất cho ngôn ngữ của bạn, cũng như đầu vào cho chương trình.

Bạn nên in hoặc trả về kết quả phiên dịch chương trình theo đặc tả ngôn ngữ Gà.

Mô tả thêm về ngôn ngữ .


Tổng quan về chương trình gà

Gà hoạt động trên một ngăn xếp duy nhất, bao gồm toàn bộ mô hình bộ nhớ của nó. Khi các hướng dẫn được thực thi, chương trình sẽ đẩy và bật các giá trị từ ngăn xếp, nhưng cũng có các hướng dẫn cho phép chương trình sửa đổi các phần khác của ngăn xếp theo ý muốn.

Có ba phân đoạn trong ngăn xếp:

  1. Các thanh ghi, tại các chỉ số 0 và 1. Chỉ mục 0 là tham chiếu đến chính ngăn xếp và chỉ mục 1 là tham chiếu đến đầu vào của người dùng. Chủ yếu được sử dụng cho hướng dẫn 6 (xem bên dưới).
  2. Mã được tải: đối với mỗi dòng mã có ô trong phân đoạn này chứa số "gà" trong dòng. Điều này được đệm bằng 0 (opcode để kết thúc chương trình) ở cuối.
  3. Ngăn xếp chương trình thực tế, nơi các giá trị được đẩy / bật khi chương trình chạy. Lưu ý rằng các phân đoạn không bị cô lập, điều đó có nghĩa là có thể tạo mã tự sửa đổi hoặc thực thi mã từ phân đoạn này của không gian ngăn xếp.

Gà ISA

Tập lệnh của gà dựa trên số lần từ "gà" xuất hiện trên mỗi dòng của chương trình. Một dòng trống kết thúc chương trình và in giá trị trên cùng trong ngăn xếp.

Bộ hướng dẫn Gà, theo số lượng "gà" trên mỗi dòng:

  1. Đẩy chuỗi chữ "gà" vào ngăn xếp
  2. Thêm hai giá trị ngăn xếp hàng đầu dưới dạng số tự nhiên và đẩy kết quả.
  3. Trừ hai giá trị hàng đầu dưới dạng số tự nhiên và đẩy kết quả.
  4. Nhân hai giá trị hàng đầu dưới dạng số tự nhiên và đẩy kết quả.
  5. So sánh hai giá trị hàng đầu cho đẳng thức, đẩy 1 nếu chúng bằng nhau và 0 khác.
  6. Xem hướng dẫn tiếp theo để xác định nguồn nào sẽ tải từ: 0 tải từ ngăn xếp, 1 tải từ đầu vào của người dùng. Đầu các điểm xếp chồng đến địa chỉ / chỉ mục để tải từ nguồn đã cho; tải giá trị đó và đẩy nó lên ngăn xếp. Vì đây là một lệnh rộng gấp đôi, con trỏ lệnh bỏ qua lệnh được sử dụng để xác định nguồn.
  7. Đầu các điểm xếp chồng đến địa chỉ / chỉ mục để lưu trữ. Giá trị bên dưới sẽ được bật và lưu trữ trong ngăn xếp tại chỉ mục đã cho.
  8. Đầu ngăn xếp là một phần bù tương đối để nhảy tới. Nếu giá trị dưới đây là trung thực, thì chương trình sẽ nhảy bằng bù.
  9. Giải thích đỉnh của ngăn xếp là ascii và đẩy ký tự tương ứng.
  10. (10 + N) Đẩy số bằng chữ n-10 lên ngăn xếp.

Thí dụ

Giả sử chương trình là:

chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken
chicken chicken chicken chicken chicken chicken
(an empty line)

(Một chương trình mèo. Lưu ý rằng dòng trống là bắt buộc vì dòng trước có 6 "gà".)

Đầu vào cung cấp cho chương trình Gà

Chicken

Đầu ra

Chicken

Việc triển khai tham chiếu Chicken.js .


Phát hiện lỗi

Trình thông dịch sẽ để lại một lỗi và chấm dứt khi có bất kỳ từ nào không phải là "gà" trong nguồn.


Chúc may mắn!


3
Bạn cần sao chép các thông số kỹ thuật ngôn ngữ vào câu hỏi. Các câu hỏi không nên dựa vào các liên kết bên ngoài.
mbomb007


Chà, tại sao không tự làm điều đó quá?

1
Đó là câu hỏi của bạn. Bạn xác định thông số kỹ thuật.
mbomb007

5
Đầu vào tập tin hạn chế thách thức này đối với các ngôn ngữ cụ thể. Ví dụ, nó không thể tạo ra câu trả lời cho gà, điều mà tôi chắc chắn rằng bạn sẽ đồng ý là đáng thất vọng.
Aaron

Câu trả lời:


1

Ruby, 335 byte

Lấy tên tệp đầu vào làm đối số dòng lệnh và lấy đầu vào của người dùng (đối với lệnh # 6) từ STDIN.

Do Ruby "thật" (mọi thứ ngoại trừ falsenil) khác với "tính trung thực" của Javascript (cộng với sự thật của Ruby 0, chuỗi trống, v.v.), có thể có một số trường hợp cạnh mà các chương trình hoạt động tốt trên trình thông dịch JS không thành công vì hướng dẫn số 8, chẳng hạn như nếu ""ở trên ngăn xếp. Tôi đã sửa trường hợp lớn nhất, tuy nhiên, đó là giả 0.

Hoạt động với chương trình thử nghiệm và chương trình Hello World trên trang web Chicken.

+(/^(#{c='chicken'}|\s)*$/m=~f=$<.read+"

")
s=[0,STDIN.read]+f.lines.map{|l|l.split.size};s[0]=s;i=1
s<<(s[i]<10?[->{c},->{x,y=s.pop 2;x+y},->{x,y=s.pop 2;x-y},->{s.pop*s.pop},->{s.pop==s.pop},->{s[s[i+=1]][s.pop]},->{s[s.pop]=s.pop;s.pop},->{l,k,j=s.pop 3;i+=j if k&&k!=0;l},->{s.pop.chr}][s[i]-1][]:s[i]-10)while s[i+=1]>0
$><<s.pop

Giải trình

Trình thông dịch bắt đầu ngay lập tức bằng cách chạy một trận đấu regex /^(chicken|\s)*$/mvới toàn bộ tệp ( $<.read), điều này đảm bảo tệp không chứa gì ngoài chickenkhoảng trắng. Trong Ruby, toán tử này trả về chỉ mục cho trận đấu hoặc nilnếu không tìm thấy.

Hai thủ thuật tiết kiệm byte được sử dụng ở đây: thay vì khớp trực tiếp chicken, toán tử thay thế chuỗi #{}được sử dụng thay vào đó để gán chuỗi đó cho một biến cho sau này (lưu 1 byte) và khi lưu trữ nội dung của tệp vào một biến để xử lý , nó nối thêm hai dòng mới để cho phép lineschức năng sau đó tự nhiên nối thêm 0vào phần cuối của tập lệnh. (Hai là cần thiết vì bỏ qua dòng mới, cần thiết cho chương trình Gà.)

Lỗi được sử dụng là NoMethodError: undefined method '+@' for nil:NilClass, được thực hiện bằng cách gói kết hợp biểu thức chính quy trong parens và đặt +phía trước. Nếu tệp khớp với mẫu, bạn nhận được +0, sẽ đánh giá 0và tiến hành bình thường.

Tiếp theo, ngăn xếp được lắp ráp. Danh sách ban đầu phải được tạo trước khi có thể chỉ định tự tham chiếu đến ngăn xếp, do đó, một trình giữ chỗ được sử dụng và sau đó được thay thế. Con trỏ lệnh được đặt thành 1thay 2vì bởi vì các toán tử tăng sau không tồn tại trong Ruby.

Cuối cùng, nó sử dụng thủ thuật lambda từ @BassdropCumberwubwubwub để xác định những gì cần đẩy vào ngăn xếp tiếp theo. Nếu một thao tác không đẩy bất cứ thứ gì lên ngăn xếp, trình thông dịch chỉ cần bật một giá trị bổ sung để ngăn xếp giữ nguyên. (Điều này giúp tiết kiệm byte bằng cách thêm thao tác đẩy vào mỗi lambda.)

Mã bị đánh cắp:

f = $<.read + "\n\n"
+(/^(chicken|\s)*$/m =~ f)
s = [0, STDIN.read] + f.lines.map{|l|l.split.size}
s[0] = s
i = 1

while s[i += 1] > 0
    if s[i] < 10
        s.push [
            ->{'chicken'},
            ->{
                x,y = s.pop 2
                x+y
                },
            ->{
                x,y = s.pop 2
                x-y
                },
            ->{s.pop*s.pop},
            ->{s.pop==s.pop},
            ->{s[s[i+=1]][s.pop]},
            ->{s[s.pop]=s.pop;s.pop},
            ->{
                l,k,j=s.pop 3
                i+=j if k&&k!=0
                l
                },
            ->{s.pop.chr}
        ][s[i] - 1][]
    else
        s.push(s[i] - 10)
    end
end

print s.pop

Trên thực tế, tôi không nghĩ rằng tôi có thể làm điều này ngắn. (+1)

4

Javascript ES6, 398 byte

Cho đến nay, môn golf dài nhất tôi từng làm, tôi chắc chắn điều này có thể được cải thiện nhưng bộ não của tôi không nhận ra bất cứ điều gì khác ngoài chickenthời điểm này.

(a,b)=>{for(c='chicken',s=[j=0,b,...A=a.split`
`.map(m=>m.split(c).length-1)],i=A.length+2;j<A.length;([_=>s[++i]=c,_=>s[--i]=s[i]+s[i+1],_=>s[--i]=s[i]-s[i+1],_=>s[--i]=s[i]*s[i+1],_=>s[--i]=s[i]==s[i+1],_=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],_=>s[s[i--]]=s[i--],_=>j+=s[--i]?s[--i+2]:0,_=>s[i]=String.fromCharCode(s[i])][s[j+2]-1]||(_=>s[++i]=s[j+1]-10))(j++));return /[^chicken \n]\w/g.test(a)?0:s[i]}

Tôi sẽ chỉnh sửa lời giải thích khi não tôi bắt đầu hoạt động trở lại. Bây giờ đây là một phiên bản hơi vô dụng.
Xuất ra giá trị falsey (0) cho mọi thứ không phải làchicken

(a,b)=>{
    for(c='chicken',s=[j=0,b,...A=a.split`
    `.map(m=>m.split(c).length-1)],i=A.length+2; // loop init
    j<A.length; // loop condition
    ( // everything else
        [
            _=>s[++i]=c,
            _=>s[--i]=s[i]+s[i+1],
            _=>s[--i]=s[i]-s[i+1],
            _=>s[--i]=s[i]*s[i+1],
            _=>s[--i]=s[i]==s[i+1],
            _=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],
            _=>s[s[i--]]=s[i--],
            _=>j+=s[--i]?s[--i+2]:0,
            _=>s[i]=String.fromCharCode(s[i])
        ][s[j+2]-1]
        ||(_=>s[++i]=s[j+1]-10)
    )(j++)
);
return /[^chicken \n]\w/g.test(a)?0:s[i]}

Hãy thử nó ở đây

f=
  (a,b)=>{for(c='chicken',s=[j=0,b,...A=a.split`
`.map(m=>m.split(c).length-1)],i=A.length+2;j<A.length;([_=>s[++i]=c,_=>s[--i]=s[i]+s[i+1],_=>s[--i]=s[i]-s[i+1],_=>s[--i]=s[i]*s[i+1],_=>s[--i]=s[i]==s[i+1],_=>s[i]=s[2+j++]?b[s[i]]:s[s[i]],_=>s[s[i--]]=s[i--],_=>j+=s[--i]?s[--i+2]:0,_=>s[i]=String.fromCharCode(s[i])][s[j+2]-1]||(_=>s[++i]=s[j+1]-10))(j++));return /[^chicken \n]\w/g.test(a)?0:s[i]}

i.innerHTML = f(`chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken chicken
chicken chicken chicken chicken chicken chicken
`, 'Hello world!')
<pre id=i>


Vâng, hãy để tôi chờ thêm một số câu trả lời, và tìm ra ai là người chiến thắng.

Điều này không thành công "Phát hiện lỗi". Bạn có thể làm như vậy bằng cách thêm if(!/^(chicken\s?)+$/.test(a))throw'There are any words except "chicken".';ngay vào sự cầu xin của thông dịch viên.
Ismael Miguel

@Matthew, suy nghĩ của bạn về điều đó là gì? Có một số ngôn ngữ nhất định không có loại lỗi, những ngôn ngữ thường có thể tạo ra giá trị falsey thay thế. Điều này khá mơ hồ trong OP nên tôi cho rằng điều này là ổn.
Bassdrop Cumberwubwubwub

Bạn có thể xuất lỗi, nói với họ một cái gì đó sai.

1
@BassdropCumberwubwubwub Ý nghĩa của OP là gì, ví dụ, ném một ngoại lệ hoặc xuất ra một cái gì đó để stderrhoặc thoát khỏi chương trình với mã khác không. Một cái gì đó cho thấy rằng một cái gì đó không đúng. Trong Javascript, bạn có thể đưa ra một ngoại lệ, trả về một đối tượng Lỗi, hiển thị cảnh báo, ghi vào bảng điều khiển bằng cách sử dụng console.erro()hoặc bất cứ thứ gì tương tự.
Ismael Miguel
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.