Escreva um intérprete para o meu idioma esotérico Jumper

17

Eu pensei em linguagem esotérica Jumper. Mais tarde você verá o porquê.

  • Opera com memória de acesso aleatório com bytes como células. A RAM é zero indexada e inicialmente preenchida com zeros.
  • Ao tentar acessar células com índices negativos, o erro deve ser exibido e o programa finalizado.
  • Ao tentar ler em um índice maior que o anterior, zero deve ser retornado.
  • Ao tentar escrever em um índice maior que o anterior, a RAM deve ser aumentada para múltiplos de 1024 e as novas células preenchidas com zeros (tecnicamente você pode aumentar a RAM para não múltiplos de 1024, o motivo foi o aumento de desempenho, por isso, se lhe custar muitos caracteres, pode fazê-lo para não múltiplo de 1024).
  • O programa também possui ponteiro para célula na RAM, que inicialmente é zero
  • Quando o programa começar a executar, um prompt para a string de entrada deve ser exibido (ou receber entrada dos argumentos da linha de comando, é com você). A sequência de entrada não deve conter caracteres nulos (zero byte). Em seguida, a sequência de entrada é gravada na RAM, iniciando no índice zero.
  • Quando o programa termina, é exibida uma caixa com saída do programa - excluindo o conteúdo da RAM do índice zero até o primeiro byte zero.

Agora, parte mais interessante, sintaxe.

Programa consiste em comandos (operadores-prefixos unários) e seus argumentos. Comandos e argumentos podem ser delimitados com espaços ou novas linhas, mas não são necessários. No entanto, os espaços dentro dos argumentos são inválidos, por exemplo, # 2 = 4são válidos, mas # 2 = 4 4não são.
Programa pode ter comentários entre (). Os comentários não podem ser aninhados - por exemplo, no (abc(def)ghi)comentário é (abc(def). Os comentários podem ser colocados em qualquer lugar.

  • #123 define o ponteiro de RAM para 123 (qualquer número inteiro decimal positivo ou zero).
  • >123 incrementa o ponteiro de RAM em 123 (qualquer número inteiro decimal positivo).
  • <123 diminui o ponteiro de RAM em 123 (qualquer número inteiro decimal positivo).
  • =123 grava 123 (qualquer número inteiro decimal de 8 bits não assinado) na célula atual.
  • +123 adiciona 123 (qualquer número inteiro decimal de 8 bits não assinado) à célula atual (módulo 256).
  • -123 subtrai 123 (qualquer número inteiro decimal de 8 bits não assinado) da célula atual (módulo 256).
  • :123- "goto" - vai para o número de comando 123 (o primeiro é 0). Você pode controlar o fluxo do seu programa apenas com o goto - ele precisa pular -, por isso decidi chamar esse idioma de Jumper.

Se o argumento estiver ausente - pense em 1 para ><+-comandos ou 0 para #=:comandos.

Além disso, existe o modificador de comando - ?(prefixo ao comando), ele executa o próximo comando apenas se a célula atual não for zero, caso contrário, ignora esse comando. Pode ser aplicado a qualquer comando.
Por exemplo, ?:17- vai para o comando 17 se a célula atual não for zero.

Se o programa for inválido ou ocorrer um erro durante o tempo de execução, pode ser exibida a mensagem "Erro". Por esse motivo, é CodeGolf, essa mensagem curta ficará boa.

Sua tarefa

Escreva o intérprete mais curto para esse idioma.

Alguns programas de teste

(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
Somnium
fonte
Depois de algum tempo, escreverei alguns programas de teste no Jumper e interpreto próprio.
Somnium
"para o primeiro byte zero excluindo" Então, se ainda houver outros bytes após o primeiro byte zero, não devemos produzi-los?
ProgramFOX
Sim, não deveríamos. Isso é feito para não limpar toda a memória usada, mas apenas copiar a saída para o início.
Somnium
5
Você poderia nos dar alguns exemplos de programas e suas saídas?
Arshajii
11
Você poderia especificar a mensagem de erro exata para índices negativos? Acho que a diferença entre as duas respostas principais atualmente é menor do que a diferença nas mensagens de erro, então acho que seria mais justo se isso fosse especificado com precisão.
Martin Ender

Respostas:

6

Ruby, 447 bytes

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)*''

Pega o programa e a entrada por meio de argumentos da linha de comando.

EDIT: corrigido alguns bugs e adicionado suporte para sintaxe inválida ao custo de 40 bytes (ao adicionar algumas outras otimizações).

Martin Ender
fonte
Muito engraçado. Minha solução Ruby também pesava 447 caracteres. Embora eu estivesse gravando em meados dos 400, exatamente a mesma contagem de bytes foi uma surpresa.
Scott Leadley
@ ScottLeadley Ha, isso é um empate interessante. ^^ Eu te daria uma votação se ainda não o tivesse feito. ;)
Martin Ender
5

Python (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]

Quanto à execução do programa:

  • 1º argumento: código de jumper
  • 2º argumento: sequência de inicialização

Exemplo:

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

Provavelmente há algumas coisas que eu ignorei, então deixe um comentário se você tentar algo que deve funcionar, mas não funciona. Observe que isso está escrito no código Python 2.x.

arshajii
fonte
Como você dá informações?
Claudiu
@ Claudiu Veja o exemplo. É um argumento de linha de comando.
Arshajii
Esse é o programa. Mas olhe para o sétimo ponto de bala. Você deve ser capaz de inicializar a matriz RAM inicial para, digamos, "Olá" via stdin ou um argumento
Claudiu
Meu erro, é o sexto marcador: "Quando o programa começa a executar um prompt para a string de entrada deve ser exibido (ou recebe a entrada dos argumentos da linha de comando, é com você). A string de entrada não deve conter caracteres nulos (zero byte). Em seguida, insira string é gravada na RAM começando no índice zero ".
Claudiu 24/07
@ Claudiu Ah, eu sabia que tinha esquecido alguma coisa, obrigado. Agora, ao executar o programa, você pode inserir a referida string.
Arshajii
4

Ruby 2 - 540 447 420 caracteres

Execute como "ruby2.0 jumper.rb 'instruções' 'dados de inicialização'". 1.x Ruby não funcionará (nenhum método String.bytes).


Adicionado comandos e comentários de várias linhas e melhorado minha colocação.


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*')

Aqui está uma suíte de testes com alguns testes de dispersão. A maneira mais fácil de usá-lo é inserir o código em t / jumper.t e executar "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;

Versão não destruída.


#
#       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*')

Um proto-montador rápido.


#
#       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
Scott Leadley
fonte
2

Clojure - 585 577 bytes

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

Não foram usados ​​truques especiais de golfe, porque não conheço nenhum de Clojure. O intérprete é puramente funcional. Vem com uma boa mensagem de erro em caso de endereço negativo da RAM (um erro é gerado, mas nenhuma exceção ou erro é gerada).

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

Exemplos:

(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!"

Código original pouco destruído:

(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))))))
seequ
fonte
Meu programa no Jumper que adiciona um "!" trabalho! É um pouco difícil de programa no Jumper ..
Somnium
É um conceito legal que você tem.
seequ
@ user2992539 Adicionei um exemplo de um somador simples.
25414
Geralmente, é semelhante ao Brainfuck, no entanto, não possui loops, goto e if e possui parâmetros de comando.
Somnium
11
Se você colocar o -no final da classe de caracteres do seu regex, não precisará escapar dele. -1 caractere.
tomsmeding 26/07
2

CoffeeScript (465)

A primeira caixa de prompt é para o programa e a segunda caixa de prompt é inserida. Teste-o em http://coffeescript.org .

Original :

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.*/,"")

Isso ainda é praticado, mas comentou:

# 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.*/,"")

Edit : Adicionar espaços levou mais bytes do que eu pensava. Este intérprete lançará um erro na sintaxe inválida, o outro possui um comportamento não especificado na sintaxe inválida.

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.*/,"")
soktinpk
fonte
Aparentemente, isso não funciona para ?:2 :4 >1 :0 = 33 <10 =0(o programa append- com um espaço extra!)
seequ
@Sieg Deve ser <1não <10.
26614 Soktinpk
@Sieg Nunca mente, eu vou atualizá-lo quando eu tiver tempo
soktinpk
@ Sieg funciona agora, eu vou trabalhar no golfe ainda #
soktinpk
11
Adotei a posição de "não foi declarado, portanto é um comportamento não especificado", portanto diria que está tudo bem. Mas, novamente, eu não sou o poderoso usuário.
seequ
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.*/,""))

Isso obtém o programa e a entrada via caixas de prompt. Colar isso no console Javascript do seu navegador funcionará, além de jogá-lo em um arquivo, colar antes do código <!DOCTYPE html>, nova linha <html><head><script>e depois do código </script></head><body></body></html>e salvar o arquivo resultante como "swagger.html".

Esta é a minha primeira tentativa e já gosto da linguagem. Meio. No entanto, ele realmente precisa de rótulos de texto, em vez da identificação do índice de instruções no estilo BASIC.

Versão ungolfed (tipo):

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.*/,""));
tomsmeding
fonte
Se apenas do Clojure cadeia de substituir não seriaclojure.string/replace
seequ
11
Além disso, algo que eu notei, não importa em tudo se você aumentar a memória para múltiplos de 1024 ou não;)
seequ
11
Eu não acho que seu script possa suportar ?:2 :4 >1 :0 = 33 <10 =0(o anexo -! Com um espaço extra) Não testado embora.
seequ
@ Sieg, definitivamente não pode. Deveria? Eu posso apenas filtrar todo o espaço em branco, se necessário ...
tomsmeding
11
"Comandos e argumentos podem ser delimitados com espaços ou novas linhas, mas não são necessários. No entanto, os espaços dentro dos argumentos são inválidos". @ user2992539 Tem uma palavra sobre isso?
26/07/14
1

C 687 GCC 4.9.0 e Visual C ++ 2013 (se as terminações de linha contarem como 1)

Edit: Graças a Dennis, agora é muito mais curto (e funciona no GCC para inicializar)

A versão golfada

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

Uma versão um pouco menos golfe:

#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);
}
Jerry Jeremiah
fonte
1. Não conheço outros compiladores, mas isso não será compilado no GCC. Isto pode ser corrigido através da substituição do segundo R[z]=xem P(x)com (R[z]=x). 2. O GCC não requer nenhuma das instruções de inclusão. 3. char C-> U C.
Dennis
@ Dennis Eu estava testando com o Visual C ++ 2013. Vou testá-lo com o GCC amanhã.
Jerry Jeremiah
0

Groovy 582

versão não destruída:

Eu acho que há um erro nos comentários, que não são reconhecidos corretamente, que podem ser causados ​​pelo regex estúpido que eu usei, mas os 2 programas são executados como deveriam:

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 ]
    }
}
markusw
fonte
0

Haskell: uma quantidade ímpia de caracteres

Tudo bem, agora isso é algo que pode ou não ser jogado em breve. É assustadoramente grande para o que é, com muitos códigos mal escritos (fazia algum tempo desde que eu toquei Haskell pela última vez). Mas foi divertido escrever.

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)
Para s
fonte
0

Haskell, 584

O programa de entrada e jumper é fornecido como as duas primeiras linhas de entrada do 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

Postarei uma versão não-gasta mais tarde, mas, enquanto isso, quero deixar isso como um quebra-cabeça para o leitor:

Onde estão os comandos do jumper, como '#' etc?

Diverta-se!

Matt Noonan
fonte