Escribir un intérprete para mi lenguaje esotérico Jumper

17

He pensado en el lenguaje esotérico Jumper. Más tarde verás por qué.

  • Funciona con memoria de acceso aleatorio con bytes como celdas. La RAM está indexada a cero y se llena inicialmente con ceros.
  • Al intentar acceder a celdas con índices negativos, se debe mostrar el error y finalizar el programa.
  • Al intentar leer en un índice mayor que el anterior, se debe devolver cero.
  • Al intentar escribir en un índice mayor que el anterior, la RAM debe aumentarse a múltiplos de 1024 y las celdas nuevas deben llenarse con ceros (técnicamente puede aumentar la RAM no a múltiplos de 1024, por lo que si aumenta el rendimiento, si le cuesta muchos caracteres, puede hacerlo no a múltiplo de 1024).
  • El programa también tiene un puntero a la celda en la RAM que inicialmente es cero
  • Cuando el programa comienza a ejecutar, se debe mostrar una solicitud de cadena de entrada (o tomar la entrada de los argumentos de la línea de comando, depende de usted). La cadena de entrada no debe contener caracteres nulos (byte cero). Luego, la cadena de entrada se escribe en la RAM comenzando en el índice cero.
  • Cuando el programa finaliza, se muestra un cuadro con la salida del programa: se excluye el contenido de la RAM desde el índice cero hasta el primer byte cero.

Ahora, la parte más interesante, la sintaxis.

El programa consta de comandos (operadores unarios-prefijos) y sus argumentos. Los comandos y argumentos pueden delimitarse con espacios o nuevas líneas, pero no son necesarios. Sin embargo, los espacios dentro de los argumentos no son válidos, por ejemplo, # 2 = 4es válido, pero # 2 = 4 4no lo es.
El programa puede tener comentarios entre (). Los comentarios no se pueden anidar, por ejemplo, en el (abc(def)ghi)comentario es (abc(def). Los comentarios se pueden colocar en cualquier lugar.

  • #123 establece el puntero RAM en 123 (cualquier entero decimal positivo o cero).
  • >123 incrementa el puntero RAM en 123 (cualquier entero decimal positivo).
  • <123 disminuye el puntero RAM en 123 (cualquier entero decimal positivo).
  • =123 escribe 123 (cualquier entero decimal de 8 bits sin signo) en la celda actual.
  • +123 agrega 123 (cualquier entero decimal de 8 bits sin signo) a la celda actual (módulo 256).
  • -123 resta 123 (cualquier entero decimal de 8 bits sin signo) de la celda actual (módulo 256).
  • :123- "goto" - va al comando número 123 (el primero es 0). Puede controlar el flujo de su programa solo con goto's, tiene que saltar, por eso decidí llamar a este lenguaje Jumper.

Si falta un argumento, piense que es 1 para ><+-comandos o 0 para #=:comandos.

Además, hay un modificador de comando: ?(prefijo al comando), ejecuta el siguiente comando solo si la celda actual no es cero; de lo contrario, omite ese comando. Se puede aplicar a cualquier comando.
Por ejemplo, ?:17- va al comando 17 si la celda actual no es cero.

Si el programa no es válido o se produce un error durante el tiempo de ejecución, se puede mostrar el mensaje "Error". Debido a esto es CodeGolf, un mensaje tan corto estará bien.

Tu tarea

Escriba el intérprete más corto para este idioma.

Algunos programas de prueba

(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
fuente
Escribiré algunos programas de prueba en Jumper después de un tiempo y un intérprete propio.
Somnium
"al primer byte cero excluido" Entonces, si todavía hay otros bytes después del primer byte cero, ¿no deberíamos generarlos?
ProgramFOX
Sí, no deberíamos. Esto se hace para no borrar toda la memoria usada, sino solo copiar la salida al comienzo.
Somnium
55
¿Podría darnos algunos programas de muestra y sus resultados?
arshajii
1
¿Podría especificar el mensaje de error exacto para los índices negativos? Creo que la diferencia entre las dos respuestas principales actuales es menor que la diferencia en sus mensajes de error, por lo que creo que sería más justo si esto se especificara con precisión.
Martin Ender

Respuestas:

6

Rubí, 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)*''

Toma tanto el programa como la entrada a través de argumentos de línea de comando.

EDITAR: se corrigieron algunos errores y se agregó compatibilidad con la sintaxis no válida a un costo de 40 bytes (al tiempo que se agregaron algunas otras optimizaciones).

Martin Ender
fuente
Demasiado gracioso Mi solución Ruby también pesaba 447 caracteres. Aunque estaba filmando a mediados de los 400, exactamente el mismo conteo de bytes fue una sorpresa.
Scott Leadley
@ScottLeadley Ha, es un empate interesante. ^^ Te daría un voto positivo si aún no lo hubiera hecho. ;)
Martin Ender
5

Pitó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]

En cuanto a ejecutar el programa:

  • 1er argumento: código de puente
  • Segundo argumento: cadena de inicialización

Ejemplo:

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

Probablemente hay algunas cosas que pasé por alto, así que por favor deje un comentario si intenta hacer algo que debería funcionar pero no funciona. Tenga en cuenta que esto está escrito en código Python 2.x.

arshajii
fuente
¿Cómo le das entrada?
Claudiu
@Claudiu Ver el ejemplo. Es un argumento de línea de comandos.
arshajii
Ese es el programa. Pero mira la séptima viñeta. Debería poder inicializar la matriz RAM inicial para, por ejemplo, "hola" a través de stdin o un argumento
Claudiu
Mi error, es la sexta viñeta: "Cuando el programa comienza a ejecutar, se debe mostrar una solicitud de cadena de entrada (o tomar la entrada de los argumentos de la línea de comando, depende de usted). La cadena de entrada no debe contener caracteres nulos (cero bytes). Luego ingrese la cadena se escribe en la RAM comenzando en el índice cero ".
Claudiu
@ Claudiu Ah, sabía que pasé por alto algo, gracias. Ahora, al ejecutar el programa, puede ingresar dicha cadena.
arshajii
4

Ruby 2 - 540 447 420 caracteres

Ejecutar como "ruby2.0 jumper.rb 'instrucciones' 'datos de inicialización'". 1.x Ruby no funcionará (sin método String.bytes).


Se agregaron comandos y comentarios de varias líneas y mejoré mi colocación.


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

Aquí hay un conjunto de pruebas con algunas pruebas de dispersión. La forma más fácil de usarlo es introducir el código en t / jumper.t y ejecutar "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;

Versión sin golf.


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

Un proto-ensamblador 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
fuente
2

Clojure - 585 577 bytes

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

No se utilizan trucos especiales de golf, porque no conozco ninguno para Clojure. El intérprete es puramente funcional. Viene empaquetado con un buen mensaje de error en caso de una dirección RAM negativa (se genera un error, pero no se producen excepciones ni errores).

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

Ejemplos:

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

(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
fuente
Mi programa en Jumper que agrega un "!" ¡trabajos! Es un poco difícil de programar en Jumper ..
Somnium
Es un buen concepto el que tienes.
seequ
@ user2992539 Agregué un ejemplo de un sumador simple.
seequ
En general, es similar a Brainfuck, sin embargo, no tiene bucles, goto's y if's y tiene parámetros de comando.
Somnium
1
Si pones el -al final de la clase de personaje de tu expresión regular, no necesitas escapar de ella. -1 personaje.
tomsmeding
2

CoffeeScript (465)

El primer cuadro de aviso es para el programa y el segundo cuadro de aviso es de entrada. Pruébelo en 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.*/,"")

Esto todavía se juega al golf, pero comentó:

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

Editar : Agregar espacios tomó más bytes de lo que pensaba. Este intérprete arrojará un error en la sintaxis no válida, el otro tiene un comportamiento no especificado en la sintaxis no vá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
fuente
Aparentemente esto no funciona ?:2 :4 >1 :0 = 33 <10 =0(el programa append-! Con un espacio extra)
vea el
@Sieg Debe ser <1no <10.
soktinpk
@Sieg no importa, lo actualizaré cuando tenga tiempo
soktinpk
@Sieg funciona ahora, trabajaré más en el golf
soktinpk
1
Tomé la postura de "no se declaró, por lo que es un comportamiento no especificado", por lo que diría que está bien. Pero de nuevo, no soy el usuario poderoso.
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.*/,""))

Esto obtiene el programa y la entrada a través de cuadros de diálogo. Pegar esto en la consola Javascript de su navegador funcionará, así como incluirlo en un archivo, pegarlo antes del código <!DOCTYPE html>, nueva línea <html><head><script>y después del código </script></head><body></body></html>, y guardar el archivo resultante como "swagger.html".

Este es mi primer intento en esto, y ya me gusta el idioma. Un poco Sin embargo, realmente necesita etiquetas de texto, en lugar de este etiquetado de índice de instrucciones de estilo BÁSICO.

Versión sin golf (un poco):

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
fuente
Si solo el reemplazo de la cadena de Clojure no fueraclojure.string/replace
verqu
1
Además, algo que noté, no importa en absoluto si aumentas la memoria a múltiplos de 1024 o no;)
consulta el
1
No creo que su script pueda manejar ?:2 :4 >1 :0 = 33 <10 =0(el apéndice-! Con un espacio extra) Sin embargo, no se ha probado.
seequ
@Sieg definitivamente no puede. ¿Deberia? Puedo filtrar todo el espacio en blanco si es necesario ...
tomsmeding
1
"Los comandos y argumentos pueden estar delimitados con espacios o nuevas líneas, pero no son necesarios. Sin embargo, los espacios dentro de los argumentos no son válidos". Si entiendo eso correctamente, los argumentos pueden ir precedidos de espacios en blanco. @ user2992539 ¿Tienes alguna palabra sobre esto?
seequ
1

C 687 GCC 4.9.0 y Visual C ++ 2013 (si los finales de línea cuentan como 1)

Editar: Gracias a Dennis ahora es mucho más corto (y funciona en GCC para arrancar)

La versión de golf

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

Una versión un poco menos golfizada:

#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
fuente
1. No sé sobre otros compiladores, pero esto no se compilará en GCC. Esto se puede solucionar mediante la sustitución de la segunda R[z]=xen P(x)con (R[z]=x). 2. GCC no requiere ninguna de las declaraciones de inclusión. 3. char C-> U C.
Dennis
@ Dennis Lo estaba probando con Visual C ++ 2013. Lo probaré con GCC mañana.
Jerry Jeremiah
0

Groovy 582

versión sin golf:

Creo que hay un error con los comentarios, que no se reconoce correctamente, que podría ser causado por la expresión estúpida que utilicé, pero los 2 programas se ejecutan como deberían:

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
fuente
0

Haskell: una cantidad impía de personajes

Muy bien, en este momento esto es algo que podría o no jugarse golf en breve. Es increíblemente enorme por lo que es, con un montón de código descuidadamente escrito (hacía bastante tiempo desde la última vez que toqué a Haskell). Pero fue divertido escribir.

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)
Fors
fuente
0

Haskell, 584

El programa de entrada y puente se proporcionan como las dos primeras líneas de entrada de 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

Publicaré una versión no golfizada más tarde, pero mientras tanto quería dejar esto como un rompecabezas para el lector:

¿Dónde están los comandos de puente como '#', etc.?

¡Que te diviertas!

Matt Noonan
fuente