Viết một thông dịch viên cho ngôn ngữ bí truyền của tôi Jumper


17

Tôi đã nghĩ lên Jumper ngôn ngữ bí truyền. Sau này bạn sẽ thấy tại sao.

  • Nó hoạt động với bộ nhớ truy cập ngẫu nhiên với byte là các ô. RAM không được lập chỉ mục và ban đầu chứa đầy số không.
  • Khi thử các ô truy cập có lỗi chỉ mục âm sẽ được hiển thị và chương trình kết thúc.
  • Khi cố gắng đọc ở chỉ số lớn hơn lần trước, nên trả về 0.
  • Khi thử ghi ở chỉ số lớn hơn lần trước, RAM nên được tăng lên nhiều 1024 và các ô mới chứa đầy số không (về mặt kỹ thuật bạn có thể tăng RAM không lên nhiều 1024, lý do là tăng hiệu suất, vì vậy nếu bạn tốn nhiều ký tự có thể làm điều đó không phải là bội số của 1024).
  • Chương trình cũng có con trỏ tới ô trong RAM mà ban đầu là 0
  • Khi chương trình bắt đầu thực hiện lời nhắc cho chuỗi đầu vào sẽ được hiển thị (hoặc lấy đầu vào từ các đối số dòng lệnh, tùy thuộc vào bạn). Chuỗi đầu vào không được chứa ký tự null (byte không). Sau đó, chuỗi đầu vào được ghi vào RAM bắt đầu từ chỉ số 0.
  • Khi chương trình kết thúc thực thi một hộp có đầu ra chương trình được hiển thị - nội dung của RAM từ chỉ số 0 đến byte đầu tiên không bao gồm.

Bây giờ, phần thú vị nhất, cú pháp.

Chương trình bao gồm các lệnh (toán tử đơn nguyên-tiền tố) và các đối số của chúng. Các lệnh và đối số có thể được phân định bằng dấu cách hoặc dòng mới nhưng không cần thiết. Tuy nhiên, không gian bên trong các đối số là không hợp lệ, ví dụ, # 2 = 4là hợp lệ, nhưng # 2 = 4 4không phải.
Chương trình có thể có ý kiến ​​giữa (). Nhận xét không thể được lồng nhau - ví dụ, trong (abc(def)ghi)nhận xét là (abc(def). Bình luận có thể được đặt ở bất cứ đâu.

  • #123 đặt con trỏ RAM thành 123 (bất kỳ số nguyên thập phân dương hoặc không).
  • >123 tăng con trỏ RAM lên 123 (bất kỳ số nguyên thập phân dương).
  • <123 giảm con trỏ RAM đi 123 (bất kỳ số nguyên thập phân dương).
  • =123 ghi 123 (bất kỳ số nguyên thập phân 8 bit không dấu) trong ô hiện tại.
  • +123 thêm 123 (bất kỳ số nguyên thập phân 8 bit không dấu) vào ô hiện tại (modulo 256).
  • -123 trừ 123 (mọi số nguyên thập phân 8 bit không dấu) khỏi ô hiện tại (modulo 256).
  • :123- "goto" - chuyển đến lệnh số 123 (đầu tiên là 0). Bạn chỉ có thể kiểm soát luồng chương trình của mình bằng goto - nó phải nhảy - đó là lý do tại sao tôi quyết định gọi ngôn ngữ này là Jumper.

Nếu đối số bị thiếu - hãy nghĩ rằng đó là 1 cho ><+-các lệnh hoặc 0 cho #=:các lệnh.

Ngoài ra, có sửa đổi lệnh - ?(tiền tố cho lệnh), nó chỉ thực hiện lệnh tiếp theo nếu ô hiện tại không bằng 0, khác bỏ qua lệnh đó. Có thể được áp dụng cho bất kỳ lệnh nào.
Ví dụ: ?:17- chuyển đến lệnh 17 nếu ô hiện tại không bằng không.

Nếu chương trình không hợp lệ hoặc xảy ra lỗi trong thời gian chạy, có thể hiển thị thông báo "Lỗi". Bởi vì đây là CodeGolf, tin nhắn ngắn như vậy sẽ ổn.

Nhiệm vụ của bạn

Viết thông dịch viên ngắn nhất cho ngôn ngữ này.

Một số chương trình kiểm tra

(prints "Hello world!" regardless of input)
=72>=101>=108>=108>=111>=32>=119>=111>=114>=108>=100>=33>=

(appends "!" to the end of input string)
?:2 :4 >1 :0 =33 >1 =0

Tôi sẽ viết một số chương trình thử nghiệm trong Jumper sau một thời gian và thông dịch viên riêng.
Somnium

"Không có byte đầu tiên loại trừ" Vì vậy, nếu vẫn còn các byte khác sau byte 0 đầu tiên, chúng ta có nên xuất chúng không?
Chương trìnhFOX

Vâng, chúng tôi không nên. Điều đó được thực hiện để không xóa toàn bộ bộ nhớ đã sử dụng mà chỉ sao chép đầu ra để bắt đầu.
Somnium

5
Bạn có thể cho chúng tôi một số chương trình mẫu và đầu ra của chúng?
arshajii

1
Bạn có thể chỉ định thông báo lỗi chính xác cho các chỉ số tiêu cực? Tôi nghĩ rằng sự khác biệt giữa hai câu trả lời hiện đang dẫn đầu ít hơn sự khác biệt trong thông báo lỗi của họ, vì vậy tôi nghĩ sẽ công bằng hơn nếu điều này được chỉ định chính xác.
Martin Ender

Câu trả lời:


6

Ruby, 447 byte

p,i=$*
l=(i||'').length
r=[0]*l
l.times{|j|r[j]=i[j].ord}
i=j=0
s=p.gsub(/\(.*?\)|\s/,'')
q=s.scan(/(\?)?([#<>=+:-])(\d*)/)
e=->{abort"Error"}
p[/\d\s+\d/]||q*''!=s ?e[]:(r+=[0]until i+1<r.length
c=q[j]
j+=1
f=c[1]
c[0]&&r[i]==0?next: a=c[2]==''? '><+-'[f]?1:0:c[2].to_i
'=+-'[f]&&a>255?e[]: f==?#?i=a :f==?>?i+=a :f==?<?i-=a :f==?=?r[i]=a :f==?+?r[i]+=a :f==?-?r[i]-=a :j=a
i<0?e[]:r[i]%=256)while j<q.length
puts r.first(r.index 0).map(&:chr)*''

Đưa cả chương trình và đầu vào thông qua các đối số dòng lệnh.

EDIT: Đã sửa một vài lỗi và thêm hỗ trợ cho cú pháp không hợp lệ với chi phí 40 byte (trong khi thêm một vài tối ưu hóa khác).


Quá buồn cười. Giải pháp Ruby của tôi chỉ nặng có 447 ký tự. Mặc dù tôi đã chụp vào giữa những năm 400, nhưng chính xác số byte tương tự là một bất ngờ.
Scott Leadley

@ScottLeadley Ha, đó là một chiếc cà vạt thú vị. ^^ Tôi sẽ cung cấp cho bạn một upvote nếu tôi đã không làm như vậy. ;)
Martin Ender

5

Con trăn (729)

import re,sys
R,p,i,T,q,g=[0]*1024,0,0,re.findall(r'\d+|[()#><=+:?-]',sys.argv[1]),lambda i:0<=i<len(T),lambda i,d:int(T[i])if q(i)and T[i].isdigit()else d
def z(p):
 global R;assert p>=0
 if p>=len(R):R+=[0]*1024
s=sys.argv[2]
R[0:len(s)]=map(ord,s)
while i<len(T):
 t=T[i]
 if t=='(': 
  while T[i]!=')':i+=1
  i+=1
  if not q(i):break
  t=T[i]
 i+=1
 if t=='#':p=g(i,0)
 if t=='>':p+=g(i,1)
 if t=='<':p-=g(i,1)
 if t=='=':z(p);R[p]=g(i,0)
 if t=='+':z(p);R[p]+=g(i,1);R[p]%=256
 if t=='-':z(p);R[p]-=g(i,1);R[p]%=256
 if t==':':
  v=int(T[i])
  i,c=-1,-1
  while c!=v:i+=1;c+=T[i]in'#><=+-:'
 if t=='?':
  assert p>=0
  if p<len(R)and R[p]==0:i+=1
 i+=q(i)and T[i].isdigit()
print''.join(chr(int(c))for c in R).split('\0')[0]

Đối với việc chạy chương trình:

  • Đối số thứ nhất: Mã nhảy
  • Đối số thứ 2: Chuỗi khởi tạo

Thí dụ:

$ python jumper.py "=97>>(this is a comment)=98>2=99#" "xyz123"
ayb1c3

Có lẽ có một vài điều tôi đã bỏ qua, vì vậy xin vui lòng để lại nhận xét nếu bạn tình cờ thử một cái gì đó nên hoạt động nhưng không. Lưu ý rằng điều này được viết bằng mã Python 2.x.


Làm thế nào để bạn cung cấp cho nó đầu vào?
Claudiu

@Claudiu Xem ví dụ. Đó là một đối số dòng lệnh.
arshajii

Đó là chương trình. Nhưng hãy nhìn vào điểm đạn thứ 7. Bạn sẽ có thể khởi tạo mảng RAM ban đầu thành "xin chào" thông qua stdin hoặc một đối số
Claudiu

Lỗi của tôi, đó là viên đạn thứ 6: "Khi chương trình bắt đầu thực hiện lời nhắc cho chuỗi đầu vào sẽ được hiển thị (hoặc lấy đầu vào từ các đối số dòng lệnh, tùy thuộc vào bạn). Chuỗi đầu vào không được chứa ký tự null (byte 0). chuỗi được ghi vào RAM bắt đầu từ chỉ số không. "
Claudiu

@Claudiu Ah, tôi biết tôi đã bỏ qua điều gì đó, cảm ơn. Bây giờ, khi chạy chương trình, bạn có thể nhập chuỗi đã nói.
arshajii

4

Ruby 2 - 540 447 420 ký tự

Chạy dưới dạng "ruby2.0 jumper.rb 'hướng dẫn' 'dữ liệu khởi tạo'". 1.x Ruby sẽ không hoạt động (không có phương thức String.bytes).


Đã thêm các lệnh và nhận xét nhiều dòng và cải thiện việc đặt của tôi.


i=$*[0].gsub(/\([^)]*\)/m,' ').scan(/(\??)\s*([#=:><+-])\s*(\d*)/m).map{|a|[a[0]!='?',a[1],a[2]==''?/[#=:]/=~a[1]?0:1:a[2].to_i]}
N=i.size
d=$*[1].bytes
r=p=0
while p<N
u,o,x=i[p]
p+=1
d[r]=0 if d[r].nil?
case o
when'#';r=x
when'>';r+=x
when'<';r-=x
when/[=+-]/;eval "d[r]#{o.tr'=',''}=x";d[r]%=256
when':';p=x;abort'Error'if p>=N
end if u||d[r]>0
abort'Error'if r<0
end
printf"%s\n",d.take_while{|v|v&&v!=0}.pack('C*')

Đây là một bộ thử nghiệm với một số thử nghiệm phân tán. Cách dễ nhất để sử dụng nó là nhét mã vào t / jumper.t và chạy "perl t / jumper.t".


#/usr/bin/perl
use strict;
use warnings;
#       timestamp: 2014 August 3, 19:00
#
# - Assume program takes machine code and initialization string as command
#       line options.
# - Assume all required errors reported as "Error\n".
# - Go with the flow and suffix output with \n. Merged terminal newlines are
#       unacceptable [I'm talkin' to YOU Ruby puts()!].
# - As per OP - jumping to > end-of-program must be an error.

use Test::More qw(no_plan);
# use Test::More tests => 4;

my $jumper = "jumper.rb";
#
#       "happy" path
#
# starter tests provided by OP
is( `$jumper '=72>=101>=108>=108>=111>=32>=119>=111>=114>=108>=100>=33>=' '' 2>&1`, "Hello world!\n", "hello world (from user2992539)");
is( `$jumper '?:2 :4 >1 :0 =33 >1 =0' 'a' 2>&1`, "a!\n", 'append !, #1 (from user2992539)');

# simple variations
is( `$jumper '?:2 :4 >1 :0 =33 >1 =0' '' 2>&1`, "!\n", 'append !, #2');
is( `$jumper '?:2 :4 >1 :0 =33' '' 2>&1`, "!\n", 'append !, #3, no NUL');

# comment delimiters don't nest
is( `$jumper "(()=" 'oops' 2>&1`, "\n", "() don't nest");
# comments and termination
is( `$jumper '(start with a comment)?(comment w/ trailing sp) # (comment w/ surrounding sp) 1 =98' 'a' 2>&1`, "ab\n", 'walk to exit');
is( `$jumper '(start with a comment)? (comment w/ leading sp)= (comment w/ surrounding sp) 97()' '' 2>&1`, "\n", 'skip to exit');
is( `$jumper '#1=0 (actually two instructions, but it scans well) :5 #=(truncate further if not jumped over)' 'a b' 2>&1`, "Error\n", 'truncate & jump to exit');

# is RAM pointer initialized to 0?
is( `$jumper '-103(g-g) ?:1025(exit) =103 #4=10' 'good' 2>&1`, "good\n\n", 'intial string in right place?');

# TBD, do jumps work?
# TBD, do conditional jumps work?
# jump right to a harder case, copy byte 0 to byte 3 and format, e.g. input="Y" output="Y=>Y"
is( `$jumper '#1=61#2=62#4=0#3=#10=#(11:)?:13:20(13:)#3+#10+#0-:11(20:)#10(21:)?:23:28(23:)#0+#10-:21(28:)#' 'Y' 2>&1`, "Y=>Y\n", 'copy a byte');


# test memory allocation by dropping 255s at increasingly large intervals
is( `$jumper '#16=511 #64=511 #256=511 #1024=511 #4096=511 #16384=511 #65536=511 #262144=511 #1048576=511 #65536-255 (20:)?:23(exit) #=' 'wrong' 2>&1`, "\n", 'test alloc()');

# upcase by subtraction
is( `$jumper '-32' 't' 2>&1`, "T\n", 'upcase via subtraction');
# 2 nested loops to upcase a character, like so: #0=2; do { #0--; #1=16; do { #1--; #2--; } while (#1); } while (#0);
is( `$jumper '#=2 (2:)#- #1=16 (6:)#1- #2- #1?:6 #0?:2 #=32 #1=32' '  t' 2>&1`, "  T\n", 'upcase via loops');
# downcase by addition
is( `$jumper '+32' 'B' 2>&1`, "b\n", 'downcase via addition');
# same thing with a loop, adjusted to walk the plank instead of jumping off it
is( `$jumper '#1 ?:3 :7 -<+ :0 #' 'B ' 2>&1`, "b\n", 'downcase via adder (from  Sieg)');
# base 10 adder with carry
is( `$jumper '#0-48#10=9#11=#5=#0(9:)?:11:22(11:)#10?:14:22(14:)-#11+#5+#0-:9(22:)#0?:110#11(25:)?:27:32(27:)#0+#11-:25(32:)#0+48>-43?:110=43>-48#10=9#11=#2(45:)?:47:58(47:)#10?:50:58(50:)-#11+#5+#2-:45(58:)#2?:110#11(61:)?:63:68(63:)#2+#11-:61(68:)#2+48>-61?:110=61>?:110=32#10=9#11=#5-10(83:)?:85:94(85:)#10?:88:94(88:)-#11+#5-:83(94:)#5?:99#4=49:100(99:)+10(100:)#11(101:)?:103:108(103:)#5+#11-:101(108:)#5+48' '1+1=' 2>&1`, "1+1= 2\n", 'base 10 adder, #1');
is( `$jumper '#0-48#10=9#11=#5=#0(9:)?:11:22(11:)#10?:14:22(14:)-#11+#5+#0-:9(22:)#0?:110#11(25:)?:27:32(27:)#0+#11-:25(32:)#0+48>-43?:110=43>-48#10=9#11=#2(45:)?:47:58(47:)#10?:50:58(50:)-#11+#5+#2-:45(58:)#2?:110#11(61:)?:63:68(63:)#2+#11-:61(68:)#2+48>-61?:110=61>?:110=32#10=9#11=#5-10(83:)?:85:94(85:)#10?:88:94(88:)-#11+#5-:83(94:)#5?:99#4=49:100(99:)+10(100:)#11(101:)?:103:108(103:)#5+#11-:101(108:)#5+48' '9+9=' 2>&1`, "9+9=18\n", 'base 10 adder, #2');

# order of assignment shouldn't affect order of print
is( `$jumper '#1=98 #0=97' '' 2>&1`, "ab\n", 'print order != assignment order');

# are chars modulo 256?
is( `$jumper '#10(#10 defaults to 0) +255+(#10 += 256) ?#(skip if #10==0) =' 'good' 2>&1`, "good\n", 'memory values limited to 0<x<255');
# go for the cycle;
is( `$jumper '(0:)+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ (256:)#4=10' 'BCID' 2>&1`, "ACID\n\n", 'cycle character less 1, PC>255');
# same thing with a loop;
is( `$jumper '#4=255(#4 = 255) (2:)#1+(#1++) #4-(#4--) ?:2(loop 255 times) #4=10(#4 = NL)' 'ADID' 2>&1`, "ACID\n\n", 'cycle character less 1, PC>255');


#       Exercise the program counter.
# PC > 255;
is( `$jumper '(0:)= (1:)############################################################################################################################################################################################################################################################### (256:)?:259 (257:)+ (258:):1 (259:)=97#3=10' 'a==' 2>&1`, "a==\n\n", 'program counter range >255');


#
#       "sad" path
#
#       Error checking required by the specification.
#
# simplest test case of PC going out of bounds
is( `$jumper ':2' '' 2>&1`, "Error\n", 'program counter too big by 1');
is( `$jumper ':1024' '' 2>&1`, "Error\n", 'program counter in space');
is( `$jumper ':1073741824' '' 2>&1`, "Error\n", 'program counter in hyperspace');
# try to drive program counter negative, if 32-bit signed integer
is( `$jumper ':2147483648(exit)' 'ridiculous speed' 2>&1`, "Error\n", 'program counter goes negative?, #1');
# try to drive program counter negative, if 64-bit signed integer
is( `$jumper ':9223372036854775808 (exit)' 'ludicrous speed' 2>&1`, "Error\n", 'program counter goes negative?, #2');

# spaces not allowed in operand; error or silently ignore (my choice)
isnt(`$jumper '#= #= #= #= #= +1 4 ' 'aops' 2>&1`, "oops\n", 'do not accept spaces in operands');
# ditto w/ a comment ; error or silently ignore (my choice)
isnt(`$jumper '#= #= #= #= #= +1(not valid)4 ' 'aops' 2>&1`, "oops\n", 'do not accept spaces in operands');

# RAM pointer error-checking; "Error" or "" are OK
isnt( `$jumper '<>=' 'oops' 2>&1 | grep -v Error`, "oops\n", 'unused negative RAM pointer behavior unspecified');
# RAM pointer negative and use it
is( `$jumper '<=' '' 2>&1`, "Error\n", 'cannot use negative RAM pointer, #1');
# check for RAM pointer wrap-around
is( `$jumper '<=' '0123456789' 2>&1`, "Error\n", 'cannot use negative RAM pointer, #2');

# The way I read this
#       "Commands and arguments may be delimited with spaces or new lines but
#       not necessary."
# multi-line commands are legit.
is( `$jumper "#4#?\n=" 'oops' 2>&1`, "\n", 'multi-line commands allowed');

# Multi-line comments would be consistent with multi-line commands, but I can't
# find something I can translate into a "must" or "must not" requirement in
#       "Program can have comments between (). ... Comments can be placed
#       anywhere."
# Until uncertainty resolved, no test case.


#
#       "bad" path
#
#       These tests violate the assumption that the instruction stream is wellll-farmed.
#
# characters not in the language; error or (my choice) silently skip
isnt(`$jumper 'x =' 'oops' 2>&1`, "oops\n", 'opcode discrimination');
# is ? accepted as an operator (vs operation modifier); error or (my choice) silently skip
is(`$jumper '(bad 0, good 0:)??0 (bad 1, good 0:):3 (bad 2, good 1:)#0' '' 2>&1`, "Error\n", '? not accepted as an opcode');

exit 0;

Phiên bản bị đánh cắp.


#
#       Turing Machine Mach 2.0.
#       Tape? Tape? We don't need no stinkin' tape! We gots RAM!
#
#       dM = data memory
#       iM = instruction memory
#       pC = program counter
#       rP = RAM pointer
#       u, o, x = current instruction being executed
#
#       N = number of instructions in instruction memory
#

#       instruction decoder
iM = $*[0].gsub(/\([^)]*\)/m,' ').scan(/(\??)\s*([#=:><+-])\s*(\d*)/m).map { |a|
    [
        a[0] != '?',
        a[1],
        (a[2] == '')  ?  (/[#=:]/ =~ a[1] ? 0 : 1)  :  a[2].to_i
    ]
}
pC = 0
N = iM.size

dM = $*[1].bytes
rP = 0

while pC < N do
    #   u, unconditional instruction,   execute if true || (dM[rP] > 0)
    #                                   skip if false && (dM[rP] == 0)
    #   o, operator
    #   x, operand
    (u, o, x) = iM[pC]
    pC += 1
    dM[rP] = 0  if dM[rP].nil?
    if u || (dM[rP] > 0)
        case o
        when '#'
            rP = x
        when '>'
            rP += x
        when '<'
            rP -= x
        when /[=+-]/
            eval "dM[rP]#{o.tr'=',''}=x"
            dM[rP] %= 256
        when ':'
            pC = x
            abort 'Error'  if pC >= N
        end
    end
    abort 'Error'  if rP < 0
end
printf "%s\n", dM.take_while{|v|v&&v!=0}.pack('C*')

Một proto-lắp ráp nhanh.


#
#       Jumper "assembler" - symbolic goto labels.
#
# what it does:
#       - translates labels/targets into absolute position
#               @label ?:good_exit
#               ...
#               :label
#
#       - a label is [a-zA-Z][a-zA-Z0-9_]*
#       - a target is @label
#       - one special label:
#               - "hyperspace" is last instruction index + 1
#       - strips out user comments
#               - everything from "//" to EOL is stripped
#               - jumper comments are stripped
#       - adds "label" comments of the form "(ddd:)"
# limitations & bugs:
#       - multi-line jumper comments aren't alway handled gracefully
#       - a target not followed by an instruction will reference
#               the previous instruction. this can only happen
#               at the end of the program. recommended idiom to
#               avoid this:
#                       @good_exit #
# what it doesn't do:
#       - TBD, simple error checking
#               - labels defined and not used
#       - TBD, symbolic memory names
#
# Example:
#
#   input -
#       (
#               adder from Sieg
#       )
#       @loop_head # 1  // while (*(1)) {
#       ?:continue
#       :good_exit
#
#       @continue -     //     *(1) -= 1;
#       <-           //     *(0) += 1;
#       +
#       :loop_head      // }
#       @good_exit #
#
#   output -
#       (0:) #1 ?:3 :7 (3:) - < + :0 (7:)#

rawSource = ARGF.map do |line|
  line.gsub(/\([^)]*\)/, ' ')   # eat intra-line jumper comments
    .gsub(/\/\/.*/, ' ')        # eat C99 comments
    .gsub(/^/, "#{$<.filename}@#{$<.file.lineno}\n") # add line ID
end.join
rawSource.gsub! /\([^)]*\)/m, '' # eat multi-line jumper comments
#
# Using example from above
#
# rawSource =
#       "sieg.ja@1\n \n" +
#       "sieg.ja@4\n@loop_head # 1\n"
#       ...
#       "sieg.ja@12\n@good_exit # \n"

instructionPattern = %r{
    (?<label> [[:alpha:]]\w* ){0}
    (?<operator> \??\s*[#=:><+-]) {0}
    (?<operand> \d+|[[:alpha:]]\w* ){0}

    \G\s*(@\g<label>\s*)?(\g<operator>\s*)?(\g<operand>)?
  }x
FAIL = [nil, nil, nil]
instructionOffset = 0
iStream = Array.new
target = Hash.new
targetComment = nil
for a in rawSource.lines.each_slice(2) do
  # only parse non-empty lines
  if /\S/ =~ a[1]
    m = nil
    catch( :parseError ) do
      chopped = a[1]
      while m = instructionPattern.match(chopped)
        if m.captures.eql?(FAIL) || (!m[:operator] && m[:operand])
          m = nil
          throw :parseError
        end
        if m[:label]
          if target.has_key?(m[:label].to_sym)
            printf $stderr, a[0].chomp + ": error: label '#{m[:label]}' is already defined"
            abort a[1]
          end
          target[ m[:label].to_sym ] = instructionOffset
          targetComment = "(#{instructionOffset}:)"
        end
        if m[:operator]
          iStream[instructionOffset] = [
              targetComment,
              m[:operator],
              /\A[[:alpha:]]/.match(m[:operand]) ? m[:operand].to_sym : m[:operand]
            ]
          targetComment = nil
          instructionOffset += 1
        end
        chopped = m.post_match
        if /\A\s*\Z/ =~ chopped
          # nothing parseable left
          break
        end
      end
    end
    if !m
      printf $stderr, a[0].chomp + ": error: parse failure"
      abort a[1]
    end
  end
end

# inject hyperspace label
target[:hyperspace] = instructionOffset

# replace operands that are labels
iStream.each do |instruction|
  if instruction[2]
    if !(/\A\d/ =~ instruction[2]) # its a label
      if target.has_key?(instruction[2])
        instruction[2] = target[instruction[2]]
      else
        abort "error: label '@#{instruction[2]}' is used but not defined"
      end
    end
  end
  puts instruction.join
end

2

Clojure - 585 577 byte

Edit:   I forgot modulo 256, of course
Edit 2: Now supports whitespace and comments anywhere. (585 -> 578)

Không có thủ thuật chơi gôn đặc biệt nào được sử dụng, vì tôi không biết gì về Clojure. Thông dịch viên là hoàn toàn chức năng. Đi kèm với một thông báo lỗi đẹp trong trường hợp địa chỉ RAM âm (lỗi được xuất ra, nhưng không có ngoại lệ hoặc lỗi được ném).

(defn j[o i](let[o(re-seq #"\??[#<>=+:-]\d*"(clojure.string/replace o #"\(.*?\)|\s"""))r(loop[c 0 p 0 m(map int i)](if-let[f(nth o c nil)](let[[c p m]((fn r[t](let[f(first t)s(if(next t)(apply str(next t))(case f(\#\=\:)"0"(\>\<\+\-)"1"))v(read-string s)a(nth m p 0)](case f\?(if(=(nth m p 0)0)[c p m](r s))\#[c v m]\>[c(+ p v)m]\<[c(- p v)m]\:[(dec v)p m][c p(assoc(vec(concat m(repeat(- p(count m))0)))p(mod({\+(+ a v)\-(- a v)}f v)256))])))f)](if(< p 0)(str"Negative index "p" caused by "f)(recur(inc c)p m)))m))](if(string? r)r(apply str(map char(take-while #(> % 0)r))))))

Ví dụ:

(j "=72>=101>=108>=108>=111>=32>=119>=111>=114>=108>=100>=33>=" "")
=> "Hello world!"
(j "?:2 :4 >1 :0 =33 >1 =0" "hi there")
=> "hi there!"
(j "#1 ?:3 :7 -<+ :0" "01") ; adder
=> "a"
(j "?:2 :4 >1 :0 =33 <10 =0" "hi there")
=> "Negative index -2 caused by <10"
(j "=72>=101>=108>=108>=111>=3(comment here <100)2>=119>=111>=114>=108>=100>=33>=" "")
=> "Hello world!"

Mã ban đầu hơi vô căn cứ:

(defn memory
  ([]
    (vec (repeat 1024 0)))
  ([m i v]
    (assoc (vec (concat m (repeat (- i (+ -1024 (mod i 1024)) (count m)) 0)))
           i v)))

(defn parse [c p m t]
  (let [f (first t)
        s (if-let [v (next t)]
            (apply str v)
            (case f
              (\#\=\:) "0"
              (\>\<\+\-) "1"))
        v (read-string s)
        a (nth m p 0)]
    (case f
      \? (if (= (nth m p 0) 0) [c p m] (parse c p m s))
      \# [c v m]
      \> [c (+ p v) m]
      \< [c (- p v) m]
      \: [(dec v) p m]
      [c p (memory m p (mod ({\+ (+ a v) \- (- a v)} f v) 256))])))

(defn jumper [o i]
  (let [o (re-seq #"\??[#<>=+:-]\d*" (clojure.string/replace o #"\(.*?\)|\s" ""))
        r (loop [c 0
                 p 0
                 m (map int i)]
            (if-let [f (nth o c nil)]
              (let [[c p m] (parse c p m f)]
                (if (< p 0)
                  (str "Negative index " p " caused by " (nth o c))
                  (recur (inc c) p m))) m))]
    (if (string? r)
      r
      (apply str (map char (take-while #(> % 0) r))))))

Chương trình của tôi trong Jumper có thêm "!" làm! Có một chút khó khăn khi lập trình trong Jumper ..
Somnium

Đó là một khái niệm gọn gàng bạn có.
xem

@ user2992539 Tôi đã thêm một ví dụ về một bộ cộng đơn giản.
xem

Nói chung, nó tương tự như Brainfuck, tuy nhiên nó không có vòng lặp, goto và nếu thay vào đó và có các tham số lệnh.
Somnium

1
Nếu bạn đặt -ở cuối lớp nhân vật trong regex của mình, bạn không cần phải thoát nó. -1 ký tự.
tomsmeding

2

CoffeeScript (465)

Hộp nhắc đầu tiên dành cho chương trình và hộp nhắc thứ hai là đầu vào. Kiểm tra nó tại http://coffeescript.org .

Bản gốc :

p=prompt().replace(/\(.*?\)|[\s\n\r]/g,"").match(/\??[^\d]\d*/g) ?[]
y=p[..]
i=prompt()
r=[].map.call i,(c)->c[0].charCodeAt()
n=0
m=[(d)->n=d
(d)->m[5] (r[n]+d)%%256
(d)->p=y[d..]
(d)->m[1] -d
(d)->n-=d
(d)->r[n]=d
(d)->n+=d]
while b=p.shift()
 if b[0]=="?"
  continue unless r[n]
  b=b[1..]
 d="><+-#=:".indexOf(b[0])//4
 ~d||throw "!badcmd '#{b[0]}'"
 m[b[0].charCodeAt()%7](+b[1..]||+!d)
 n<0&&throw "!ramdix<0"
alert String.fromCharCode(r...).replace(/\0.*/,"")

Đây vẫn là golf, nhưng nhận xét:

# Get program
p=prompt().replace(/\(.*?\)/g,"").match(/\??[^\s\d]\d*/g) ?[]
# Create a copy of the program (for goto)
y=p[..]
# Get input
i=prompt()
# Put the input in the ram
r=[].map.call i,(c)->c[0].charCodeAt()
# RAM pointer
n=0
# An array of commands
# Since each of "<>+-#=:" is a different
# value mod 7 (what a coincedence?!)
# So 0th value is "#" command because 
# "#".charCodeAt() % 7 === 0
m=[(d)->n=d
(d)->m[5] (r[n]+d)%%256
(d)->p=y[d..]
(d)->m[1] -d
(d)->n-=d
(d)->r[n]=d
(d)->n+=d]
# Iterate through commands
while b=p.shift()
 # If you find a "?" skip unless r[n] is > 0
 if b[0]=="?"
  continue unless r[n]
  b=b[1..]
 # Get the default value
 d="><+-#=:".indexOf(b[0])//4
 # If the command isn't good, throw an error
 throw "!badcmd '#{b[0]}'" if d==-1
 # Call the appropriate command
 # By computing the char code mod 7
 m[b[0].charCodeAt()%7](+b[1..]||+!d)
 # Make sure n is bigger than or equal to 0
 throw "!ramdix<0" if n<0
# Show output
alert String.fromCharCode(r...).replace(/\0.*/,"")

Chỉnh sửa : Thêm không gian mất nhiều byte hơn tôi nghĩ. Trình thông dịch này sẽ đưa ra một lỗi về cú pháp không hợp lệ, người khác có hành vi không xác định về cú pháp không hợp lệ.

p=prompt().replace(/\(.*?\)/g,"").match(/\??[^\d\s\r\n]\s*\n*\r*\d*/g) ?[]
y=p[..]
i=prompt()
r=[].map.call i,(c)->c[0].charCodeAt()
n=0
m=[(d)->n=d
(d)->m[5] (r[n]+d)%%256
(d)->p=y[d..]
(d)->m[1] -d
(d)->n-=d
(d)->r[n]=d
(d)->n+=d]
while b=p.shift()?.replace /^(.)(\s\r\n)*/,"$1"
 if b[0]=="?"
  continue if !r[n]
  b=b[1..]
 d="><+-#=:".indexOf(b[0])//4
 ~d||throw "!badcmd"
 m[b[0].charCodeAt()%7](+b[1..]||+!d)
 n<0&&throw "!ramdix<0"
alert String.fromCharCode(r...).replace(/\0.*/,"")

Rõ ràng điều này không hoạt động cho ?:2 :4 >1 :0 = 33 <10 =0(chương trình nối thêm!) Với một không gian thừa)
xem phần

@Sieg Nó phải là <1không <10.
soktinpk

@Sieg đừng bận tâm, tôi sẽ cập nhật nó khi có thời gian
soktinpk

@Sieg hoạt động ngay bây giờ, tôi sẽ tiếp tục chơi gôn
soktinpk

1
Tôi đã đưa ra lập trường "nó không được tuyên bố, vì vậy đó là hành vi không xác định", do đó tôi sẽ nói rằng nó ổn. Nhưng một lần nữa, tôi không phải là người dùng hùng mạnh.
xem

1

Javascript, 519

C=prompt().replace(/\(.*?\)/g,"").match(/\??[#><=+:-]\d*/g)
M=prompt().split("").map(function(c){return c.charCodeAt(0)})
R=0
f=function(I){T=I[0]
A=I.slice(1)
if(T=="?")return M[R]?f(A):P++
A=A==""?1:+A
if(T==">")R+=A
if(T=="<")R-=A
if("=+-".indexOf(T)+1){if(R<0)throw alert("ERR RAMidx<0")
while(R>=M.length)M.push(0)}
if(T=="+")M[R]=M[R]+A&255
if(T=="-")M[R]=M[R]-A&255
A=+I.slice(1)
if(T=="#")R=A
if(T=="=")M[R]=A
if(T==":")P=A;else++P}
for(P=0;C[P];)f(C[P])
alert(String.fromCharCode.apply(7,M).replace(/\0.*/,""))

Điều này nhận được chương trình và đầu vào thông qua các hộp nhắc nhở. Dán cái này trong bảng điều khiển Javascript của trình duyệt của bạn sẽ hoạt động, cũng như ném cái này vào một tệp, dán trước mã <!DOCTYPE html>, dòng mới <html><head><script>và sau mã </script></head><body></body></html>và lưu tệp kết quả là "swagger.html".

Đây là nỗ lực đầu tiên của tôi về điều này, và tôi đã thích ngôn ngữ này. Kinda. Nó thực sự cần nhãn văn bản, thay vì ghi nhãn chỉ mục theo kiểu BASIC này.

Phiên bản Ungolfed (kinda):

var C,M,R,P,f;
C=prompt().replace(/\(.*?\)/g,"").match(/\??[#><=+:-]\d*/g); //Code
M=prompt().split("").map(function(c){return c.charCodeAt(0)}); //Memory
R=0; //RAM pointer
f=function(I){ //parser function, Instruction
    var T,A;
    T=I[0]; //Type
    A=I.slice(1); //Argument
    if(T=="?")return M[R]?f(A):P++;
    A=A==""?1:+A;
    if(T==">")R+=A;
    if(T=="<")R-=A;
    if("=+-".indexOf(T)+1){
        if(R<0)throw alert("ERR RAMidx<0");
        while(R>=M.length)M.push(0);
    }
    if(T=="+")M[R]=M[R]+A&255;
    if(T=="-")M[R]=M[R]-A&255;
    A=+I.slice(1);
    if(T=="#")R=A;
    if(T=="=")M[R]=A;
    if(T==":")P=A;else++P;
}
for(P=0;C[P];f(C[P])); //Program pointer
alert(String.fromCharCode.apply(7,M).replace(/\0.*/,""));

Nếu chỉ thay thế chuỗi của Clojure sẽ khôngclojure.string/replace
hãy xem

1
Ngoài ra, một điều tôi nhận thấy, hoàn toàn không thành vấn đề nếu bạn tăng bộ nhớ lên bội số 1024 hay không;)
xem phần

1
Tôi không nghĩ rằng tập lệnh của bạn có thể xử lý ?:2 :4 >1 :0 = 33 <10 =0(phần phụ lục-! Có thêm khoảng trắng) Chưa được kiểm tra.
xem

@Sieg nó chắc chắn không thể. Có nên không? Tôi chỉ có thể lọc ra tất cả các khoảng trắng nếu cần ...
tomsmeding

1
"Các lệnh và đối số có thể được phân định bằng dấu cách hoặc dòng mới nhưng không cần thiết. Tuy nhiên, khoảng trắng bên trong đối số không hợp lệ" Nếu tôi hiểu chính xác, đối số có thể được đặt trước khoảng trắng. @ user2992539 Có một từ về điều này?
xem

1

C 687 GCC 4.9.0 và Visual C ++ 2013 (nếu kết thúc dòng được tính là 1)

Chỉnh sửa: Nhờ Dennis, giờ nó ngắn hơn nhiều (và hoạt động trong GCC để khởi động)

Phiên bản chơi gôn

#define S char*
#define Z (S)R
#define U unsigned char
#define M R=(U*)realloc(Z,r+1024),memset(Z+r,0,1024),r+=1024
#define J z<0?exit(puts("!")),0:z>r?
#define G J 0:R[z]
#define P(x)J M,R[z]=x:(R[z]=x);
#define O !*(C+1)?1:
#define V atoi(C+1)
#define I if(*C==
#define W while(*p&&*p<33)p++;
#define K (*q++=*p++)
#define Y return
U C[999][9];U*R=0;r=0,z=0,c=0;S T(S a){S p=a,*q=C[c++],*r;W if(K==63){W K;}W int i=strtol(p,&r,0);memcpy(q,p,r-p);q[r-p]=0;Y r;}int E(S C){I 63)Y G?E(C+1):0;I 35)z=V;I 62)z+=O V;I 60)z-=O V;I 61)P(V)I 43)P(G+O V-1)I 45)P(G-O V-1)I 58)c=V-1;}main(int u,S *v){M;u==3?strcpy(Z,v[2]):0;S X=v[1];while(*X)X=T(X);*C[c]=0;c=-1;while(*C[++c])E(C[c]);Y puts(Z);}

Một phiên bản ít chơi gôn hơn:

#include<stdlib.h>
#include<memory.h>
#include<string.h>
#include<stdio.h>
#define CHAR_STAR char*
#define CASTED_R (CHAR_STAR)RAM
#define UNSIGNED_CHAR unsigned char
#define INCREASE_MEMORY RAM=(UNSIGNED_CHAR*)realloc(CASTED_R,RAM_size+1024),memset(CASTED_R+RAM_size,0,1024),RAM_size+=1024
#define IF_ERROR current<0?exit(puts("!")),0:current>RAM_size?
#define GET_CELL IF_ERROR 0:RAM[current]
#define PUT_CELL(x) IF_ERROR INCREASE_MEMORY,RAM[current]=x:RAM[current]=x;
#define ONE_IF_EMPTY !*(command+1)?1:
#define VALUE atoi(command+1)
#define REMOVE_WHITESPACE while (*pointer&&*pointer<33)pointer++;
#define COPY_CHAR (*command++ = *pointer++)
#define RETURN return
char commands[999][9];
UNSIGNED_CHAR*RAM = 0;
int RAM_size = 0, current = 0, command_size = 0;
CHAR_STAR get_command(CHAR_STAR a)
{
    CHAR_STAR pointer = a, *command = commands[command_size++], *next;
    REMOVE_WHITESPACE
    if (COPY_CHAR == '?')
    {
        REMOVE_WHITESPACE
        COPY_CHAR;
    }
    REMOVE_WHITESPACE
    int i = strtol(pointer, &next, 0);
    memcpy(command, pointer, next - pointer);
    command[next - pointer] = 0;
    RETURN next;
}
void eval(CHAR_STAR command){
    if (*command == '?')RETURN GET_CELL ? eval(command + 1) : 0;
    if (*command == '#')current = VALUE;
    if (*command == '>')current += ONE_IF_EMPTY VALUE;
    if (*command == '<')current -= ONE_IF_EMPTY VALUE;
    if (*command == '=')PUT_CELL(VALUE)
    if (*command == '+')PUT_CELL(GET_CELL + ONE_IF_EMPTY VALUE - 1)
    if (*command == '-')PUT_CELL(GET_CELL - ONE_IF_EMPTY VALUE - 1)
    if (*command == ':')command_size = VALUE - 1;
}
int main(int argc, CHAR_STAR *argv)
{
    INCREASE_MEMORY;
    argc == 3 ? strcpy(CASTED_R, argv[2]) : 0;
    CHAR_STAR command = argv[1];
    while (*command) command = get_command(command);
    *commands[command_size] = 0; command_size = -1;
    while (*commands[++command_size]) eval(commands[command_size]);
    RETURN puts(CASTED_R);
}

1. Tôi không biết về các trình biên dịch khác, nhưng điều này sẽ không được biên dịch trong GCC. Điều này có thể được sửa chữa bằng cách thay thế thứ hai R[z]=xP(x)với (R[z]=x). 2. GCC không yêu cầu bất kỳ câu lệnh bao gồm nào. 3. char C-> U C.
Dennis

@Dennis Tôi đã thử nghiệm nó với Visual C ++ 2013. Tôi sẽ thử nghiệm nó với GCC vào ngày mai.
Jerry Jeremiah

0

Groovy 582

phiên bản không có bản quyền:

Tôi nghĩ rằng có một lỗi với các bình luận, không được nhận dạng chính xác, có thể do regex ngu ngốc mà tôi đã sử dụng, nhưng 2 chương trình chạy như họ nên:

class P {
    def c = 0
    def p = 0
    def m = []

    P(i="") {
        m = i.chars.collect { it }
        m << 0
    }

    def set(v) { m[p] = v }
    def add(v) { m[p] += v }
    def sub(v) { m[p] -= v }

    def eval(i) {
        while(c < i.size()) {
            if (i[c].p && m[p] == 0) {c++} 
            else { i[c].f(this,i[c].v) }
        }
        return m
    }
}


def parse(s) {
    def ops = [
       '#' : [{p, v -> p.p = v; p.c++}, "0"],
       '>' : [{p, v -> p.p += v; p.c++}, "1"],
       '<' : [{p, v -> p.p -= v; p.c++}, "1"],
       '=' : [{p, v -> p.set(v); p.c++}, "0"],
       '+' : [{p, v -> p.add(v); p.c++}, "1"],
       '-' : [{p, v -> p.sub(v); p.c++}, "1"],
       ':' : [{p, v -> p.c = v}, "0"]
    ]

    (s =~ /\(.*\)/).each {
        s = s.replace(it, "")
    }

    (s =~ /(\?)?([#><=+-:])([0-9]*)?/).collect {        
        def op = ops[it[2]]
        [f : op[0], v : Integer.parseInt(it[3] ?: op[1]), p : it[1] != null ]
    }
}

0

Haskell: một lượng nhân vật vô duyên

Được rồi, ngay bây giờ đây là một cái gì đó có thể hoặc không thể được chơi golf trong thời gian ngắn. Nó cực kỳ to lớn đối với những gì nó có, với rất nhiều mã được viết chậm chạp (đã khá lâu kể từ lần cuối tôi chạm vào Haskell). Nhưng đó là niềm vui để viết.

import Data.Char

parse [] p c a m i =
    if c == ' ' || c == '?' then
        []
    else
        (p ++ [(c, a, m)])

parse (h:t) p c a m i
    | i
        = parse t p c a m (h == ')')
    | isDigit h && a < 0
        = parse t p c (digitToInt h) m i
    | isDigit h
        = parse t p c (10 * a + (digitToInt h)) m i
    | elem h "#><=+-:?"
        = if c == ' ' || c == '?' then
            parse t p h a (c == '?') i
        else
            parse t (p ++ [(c, a, m)]) h (-1) False i
    | otherwise
        = case h of
            '(' -> parse t p c a m True
            ' ' -> parse t p c a m i
            _   -> []

run p pp r rp
    | pp >= length p
        = r
    | pp 0 || rp < 0
        = []
    | otherwise
        = if mr then
            case c of
                '#' -> run p (pp + 1) r pa
                '>' -> run p (pp + 1) r (rp + pa)
                '<' -> run p (pp + 1) r (rp - pa)
                '=' -> run p (pp + 1) (rh ++ ((chr pa) : rt)) rp
                '+' -> run p (pp + 1) (rh ++ (chr (mod ((ord h) + pa) 256) : rt)) rp
                '-' -> run p (pp + 1) (rh ++ (chr (mod ((ord h) - pa + 256) 256) : rt)) rp
                ':' -> run p pa r rp
        else
            run p (pp + 1) r rp
        where
            (c, a, m)
                = p !! pp
            (rh, h:rt)
                = splitAt rp r
            pa
                = if a < 0 then
                    if elem c "><+-" then
                        1
                    else
                        0
                else
                    a
            mr
                = ord (r !! rp) > 0 || not m

main = do
    p <- getLine
    let n = parse p [] ' ' (-1) False False
    if n == []
        then do
            putStrLn "Error"
        else do
            s <- getLine
            let r = run n 0 (s ++ (repeat (chr 0))) 0
            if r == []
                then do
                    putStrLn "Error"
                else do
                    putStrLn (takeWhile (/=(chr 0)) r)

0

Haskell, 584

Chương trình đầu vào và nhảy được cung cấp như hai dòng đầu vào đầu tiên từ stdin.

a g(i,n,x)=(i+1,n,take n x++((g$x!!n)`mod`256):drop(n+1)x)
b g(i,n,x)=(i+1,g n,x)
c=b.q:a.(+):g:a.(-):b.(-):a.q:b.(+):c
d=(%['0'..'9'])
e=fromEnum
f=0>1
g n(_,x,y)=(n,x,y)
h(x:_)=d x;h _=f
i g p@(j,n,m)|x$m!!n=g p|t=(j+1,n,m)
j=0:1:0:1:1:0:1:j
k=takeWhile
l[]=[];l(x:y)|x%") \n"=l y|x%"("=l$u(/=')')y|t=x:l y
main=v>>=(\y->v>>=putStr.map toEnum.k x.r(0,0,map e y++z).p.l)
o n s|h s=(read$k d s,u d s)|t=(n,s)
p[]=[];p(x:y)|x%"?"=w$p y|t=(c!!e x)n:p m where(n,m)=o(j!!e x)y
q=const
r s@(i,n,m)p|i<length p=r((p!!i)s)p|t=m
t=0<1
u=dropWhile
v=getLine
w(m:n)=i m:n
x=(/=0)
z=0:z
(%)=elem

Tôi sẽ đăng một phiên bản không có bản quyền sau đó, nhưng trong lúc này tôi muốn để nó như một câu đố cho người đọc:

Các lệnh nhảy như '#', v.v. ở đâu?

Chúc vui vẻ!

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.