Hacer un verificador de sintaxis finky


8

Debe crear un programa que pueda verificar la sintaxis de los programas de su mismo idioma. Por ejemplo, si lo hace en python, verifica la sintaxis de python. Su programa recibirá un programa en la entrada estándar y verificará si su sintaxis es correcta o no. Si es correcto, la salida es "verdadera" en la salida estándar. Si no, la salida es simplemente "falsa" en la salida estándar.

Sin embargo, su programa debe ser incorrecto en una instancia. Si se alimenta su propio código fuente, solo generará "falso" en la salida estándar. Este es el código de golf, ¡así que el programa más corto gana!

Nota: Aunque esto no es técnicamente una quine, debe seguir las reglas de quine, lo que significa que no puede acceder a su código fuente a través del sistema de archivos o lo que sea.

Nota: No puede reclamar un programa que no se pudo ejecutar debido a un error de sintaxis que resuelve este desafío, ya que debe ser ejecutable.


1
¿Se permite el uso de bibliotecas estándar y / o de terceros? (específicamente, para usar un lexer / analizador de trabajo para su idioma)
Martin Ender

Está try:exec(raw_input())...permitido?
user80551

@ user80551 Eso no funcionará para una entrada sintácticamente correcta que se repita para siempre. También es un ligero riesgo de seguridad.
Martin Ender

¿No debería tener la etiqueta quine?
Sylwester

1
@ m.buettner ¿Qué pasa si sangramos raw_input () en una nueva función y la ejecutamos para que la función nunca se llame realmente? Por cierto, ¿a quién le importa los riesgos de seguridad en code-golf?
user80551

Respuestas:


9

Ruby 2.0, 65 76 164 caracteres

eval r="gets p;$<.pos=0;`ruby -c 2>&0`;p$?==0&&$_!='eval r=%p'%r"

Esto utiliza el verificador de sintaxis incorporado de Ruby ( ruby -c) para verificar la sintaxis de la entrada, lo que significa que el código no será evaluado.

Ejemplo de uso básico:

ruby syntax.rb <<< foo
true
ruby syntax.rb <<< "'"
false
ruby syntax.rb < synxtax.rb # assumes the file was saved without trailing newline
false

Explicación

Esta solución está (estaba) basada en el estándar Ruby quine:

q="q=%p;puts q%%q";puts q%q

%pes el especificador de formato para arg.inspect, que se puede comparar con uneval: cuando se evalingresa la cadena devuelta arg.inspect, (generalmente) obtiene el valor original nuevamente. Por lo tanto, al formatear la qcadena consigo misma como argumento, el %pinterior de la cadena se reemplazará con la cadena citada (es decir, obtenemos algo así "q=\"q=%p;puts q%%q\";puts q%q").

Generalizar este tipo de quine lleva a algo como lo siguiente:

prelude;q="prelude;q=%p;postlude";postlude

Sin embargo, este enfoque tiene un gran inconveniente (al menos en ): todo el código debe duplicarse. Afortunadamente, evalse puede usar para evitar esto:

eval r="some code;'eval r=%p'%r"

Lo que sucede aquí es que el código pasado a eval se almacena dentro r antes de que eval se llame. Como resultado, evalse puede obtener el código fuente completo de la declaración 'eval r=%p'%r. Si hacemos esto dentro del evalcódigo d y nos aseguramos de que el nivel superior de nuestro consiste solo en una evaldeclaración, esa expresión en realidad nos da el código fuente completo de nuestro programa, ya que cualquier código adicional pasado evalya está almacenado dentro r.

Nota al margen: este enfoque en realidad nos permite escribir un quine Ruby en 26 caracteres: eval r="puts'eval r=%p'%r"

Ahora, en esta solución, el código adicional ejecutado en su interior evalconsta de cuatro declaraciones:

gets p

Primero, leemos todas las entradas de STDIN y las guardamos implícitamente en ellas $_.

$<.pos=0

Luego, rebobinamos STDIN para que la entrada esté disponible nuevamente para el subproceso que comenzamos en el siguiente paso.

`ruby -c 2>&0`

Esto inicia Ruby en su modo de verificación de sintaxis incorporado, leyendo el código fuente de stdin. Si la sintaxis del script proporcionado (nombre de archivo o stdin) es correcta, se imprime Syntax OKen su stdout (que es capturado por el proceso principal), pero en caso de un error de sintaxis, se imprime una descripción del error en stderr , lo que ser visible, por lo que redirigimos eso a nirvana ( 2>&0) en su lugar.

p$?==0&&$_!='eval r=%p'%r

Luego, verificamos el código de salida del subproceso $?, que es 0 si la sintaxis era correcta. Por último, la entrada que leemos antes ( $_) se compara con nuestro propio código fuente (que, como describí anteriormente, se puede obtener con 'eval r=%p'%r).

Editar: ¡Salvé 14 caracteres gracias a @histocrat!


Creo que su respuesta es la única que realmente ha seguido las reglas hasta ahora.
PyRulez

1
¡Muy agradable! Creo que puedes reemplazar .write con <<y >>1<1con ==0.
histocrat

@histocrat Huh, de alguna manera se perdió la parte de ==los Process::Statusdocumentos. ¡Muchas gracias!
Ventero

6

Rebol - 69 o 7475 totalmente compatible con todas las reglas

Nuevas versiones de trabajo gracias a @rgchris! No estoy seguro si el primero falla el requisito de "no acceder a la fuente", ya que el intérprete contiene el código cargado y analizado que se ha pasado como un parámetro de línea cmd en el objeto del sistema ( system/options/do-arg) que se utiliza para reconocerse a sí mismo.

probe not any[error? i: try[load/all input]i = system/options/do-arg]

Este sigue todas las reglas:

do b:[i: input prin block? if i <> join "do b:" mold b [try [load/all i]]]

Ejemplo de uso:

Primero imprime un número entero válido, el segundo imprime un número entero inválido.

echo "rebol [] print 123" | rebol --do "probe not any[error? i: try[load/all input]i = system/options/do-arg]"
true
echo "rebol [] print 1w3" | rebol --do "probe not any[error? i: try[load/all input]i = system/options/do-arg]"
false
echo "probe not any[error? i: try[load/all input]i = system/options/do-arg]" | rebol --do "probe not any[error? i: try[load/all input]i = system/options/do-arg]"
false 

Versión totalmente compatible:

echo 'rebol [] 123' |r3 --do 'do b:[i: input prin block? if i <> join "do b:" mold b [try [load/all i]]
true
echo 'rebol [] 123a' |r3 --do 'do b:[i: input prin block? if i <> join "do b:" mold b [try [load/all i]]]'
false
echo 'do b:[i: input prin block? if i <> join "do b:" mold b [try [load/all i]]]' |r3 --do 'do b:[i: input prin block? if i <> join "do b:" mold b [try [load/all i]]
false

Explicación:

Primera versión

Esto utiliza la función integrada de Rebols loadpara analizar y cargar el código desde stdin, pero no lo ejecuta.

El trybloque detecta cualquier error de sintaxis y la error?función convierte el error en un simple booleano.

El i = system/options/do-argcompara la entrada de la entrada estándar (asignado a i) con el código aprobado en el do-argargumento (pero muy astuto de golf :).

anyes una gran función que devuelve truesi se anyevalúa algo en el bloque true(por ejemplo, any [ false false true ]volvería true).

notluego solo invierte el valor booleano para darnos la respuesta correcta y probemuestra el contenido del valor devuelto.

Versión totalmente compatible

Veamos esto en orden ...

Asigne la palabra bal bloque [] que sigue.

Use la dofunción para interpretar el dodialecto en el bbloque.

Dentro del bbloque ...

Establezca la palabra ipara referirse al contenido de stdin ( input).

Ahora, ifcolocamos joinla cadena "do b:" en el moldbloque 'ed by no es igual ( <>) a la entrada stdin, ientonces intentamos loadla entrada i.

Si el resultado es un, blockentonces hemos loadeditado los datos pasados ​​correctamente; de ​​lo contrario, recibiríamos un noneerror if.

Use prinpara mostrar el resultado del block?cual devuelve verdadero si el resultado es un bloque. Usar prinen lugar de printno muestra un retorno de carro después de la salida (y nos ahorra otro carácter).


Creo que puedes print not error? try[load/all input]
reducirlo

Eso verifica la sintaxis ... pero el problema estipula que devuelve falso en su propio código. Por casualidad, sin golf ...c:[c: compose/only [c: (c)]print not error? try[if c = load/all input[1 / 0]]]
HostileFork dice que no confíes en SE

Eché de menos el código no válido. ¿Qué tal esto? (prin none? attempt[load/all input] halt) 1a El entero inválido fallaría en la evaluación de la sintaxis, pero la detención detendría un error en la operación normal
johnk

"Todavía no se me ocurre una forma de no validar mi propio código por debajo de sumar el código y devolver falso si veo mi propio código". Si puede checksum/secureutilizar su programa e incluir el hash dentro de la propia fuente para fines de comparación, me gustaría incluir sus servicios en mi sindicato de delitos informáticos. Salvo eso, comience desde mi código, que funciona. :-)
HostileFork dice que no confíes en SE

1
D'oh! RebMu a las 49:do B[pb bl? iu a jn "do B" ml b [try [ld/all a]]]
rgchris

2

Javascript - 86 82

!function $(){b=(a=prompt())=='!'+$+'()';try{Function(a)}catch(e){b=1}alert(!b)}()

Pegue en la consola Javascript de su navegador para probar.
Explicación:

// Declare a function and negate it to call it immediately
// <http://2ality.com/2012/09/javascript-quine.html>
!function $ () { 
  // Concatenating $ with strings accesses the function source
  invalid = (input = prompt()) == '!' + $ + '()';
  try {
    // Use the Function constructor to only catch syntax errors
    Function(input)
  }
  catch (e) {
    invalid = 1
  }
  alert(!invalid)
// Call function immediately
}()

Nota: @ m.buettner trajo a colación el punto de que el programa regresa truepara una declaración de devolución desnuda, como return 0;. Debido a que Javascript no arroja un error de sintaxis para una declaración de devolución ilegal hasta que realmente se ejecute (lo que significa que el código como if (0) { return 0; }no arroja un error de sintaxis), no creo que haya alguna forma de solucionar este problema al escribir un analizador de Javascript en Javascript . Por ejemplo, considere el código:

while (1) {}
return 0;

Si se ejecuta el código, se bloqueará debido al bucle. Si el código no se ejecuta, no se generará ningún error para la declaración de devolución ilegal. Por lo tanto, esto es tan bueno como Javascript puede obtener para este desafío. Siéntase libre de descalificar Javascript si cree que esto no cumple el desafío lo suficiente.


2
Ahora no informará un error de sintaxis cuando se lo dé return 0.
Martin Ender

Mm, good one @ m.buettner
Abraham

2

Haskell - 222 bytes

Tenga en cuenta que esto utiliza un verdadero analizador. No depende de evalfunciones similares de lenguajes dinámicos.

import Language.Haskell.Parser
main=interact(\s->case parseModule s of ParseOk _->if take 89s=="import Language.Haskell.Parser\nmain=interact(\\s->case parseModule s of ParseOk _->if take"then"False"else"True";_->"False")

Esta solución no es particularmente bonita, pero funciona.


¿Falla por sí mismo?
PyRulez

Lo hace. La if take ...instrucción verifica si la entrada coincide con un literal de cadena que es la primera parte del programa.
gxtaillon

2

Creo que esto sigue las reglas:

JS (✖╭╮✖)

function f(s){if(s==f.toString())return false;try{eval(s)}catch(e){return false}return true}

El código será evaluado si es correcto.

Es necesario echar un vistazo a la notación de flecha para ver si no se puede acortar más.

!function f(){try{s=prompt();return"!"+f+"()"!=s?eval(s):1}catch(e){return 0}}()

Después de un par de intentos fallidos y revierte, ¡nueva versión!

!function f(){try{s=prompt();"!"+f+"()"!=s?eval(s):o}catch(e){return 1}}()

¡Y estoy de vuelta!

!function f(){try{s=prompt();"!"+f+"()"!=s?eval(s):o}catch(e){return !(e instanceof SyntaxError)}}()

Y me fui! Desafortunadamente, debido a la naturaleza de eval y gracias a @scragar (¡maldito seas @scragar!), Este enfoque no funcionará (ya que throw new SyntaxErrores un código JS válido, que marca este método), como tal, diría que es imposible crear un verificador de sintaxis (al menos usando eval o cualquier variación de los mismos)

(* ver los comentarios!)


¿Y si la entrada es un bucle infinito? Le sugiero que useeval("x=function(){"+t+"}");
DankMemes

1
@ZoveGames Eso se puede romper con entradas como }//o };{.
Ventero

No sé si esto es válido o no de acuerdo con las reglas, ya que tiene que ser un programa, no una función (creo)
Abraham

@ZoveGames Buen punto! Si bien el manejo del script de los navegadores debería iniciarse (contador de bucle / tiempo de espera), aún es fácil escribir un script que hará que el "sistema" se bloquee. Esperaré con el cambio hasta que OP especifique la regla sobre esto.
Eithed

1
@eithedog Lo siento, me confundí con los idiomas, la base de javascripts se llama realmente Error, no Exception. throw new Error('')Causa el comportamiento incorrecto.
scragar

1

Pitón (95)

c=raw_input()
try:compile('"'if sum(map(ord,c))==7860 else c,'n','exec');print 1
except:print 0

¿Eso no funciona solo para una línea de entrada?
Ian D. Scott

2
No funciona: c=u'#\u1e91'porqueord('#') + ord(u'\u1e91') == 7860
ThinkChaos

Prueba un hash en su lugar.
PyRulez

1

PHP - 140

<?exec("echo ".escapeshellarg($argv[1])." | php -l",$A,$o);echo$o|(array_sum(array_map(ord,str_split($argv[1])))==77*150)?"false":"true";//Q

Comentario necesario para mantener el 'hash' (una copia descarada de s, ɐɔıʇǝɥʇuʎs). Usando php -l / lint para verificar errores.

$ php finky_syntax_check.php '<?php echo "good php code"; ?>' 
true
$ php finky_syntax_check.php '<?php echo bad--php.code.; ?>'
false
$ php finky_syntax_check.php '<?exec("echo ".escapeshellarg($argv[1])." | php -l",$A,$o);echo$o|(array_sum(array_map(ord,str_split($argv[1])))==77*150)?"false":"true";//Q'
false
$ php finky_syntax_check.php '<?exec("echo ".escapeshellarg($argv[1])." | php -l",$A,$o);echo$o|(array_sum(array_map(ord,str_split($argv[1])))==77*150)?"false":"true";//D'
true // note that the last character was changed

0

C 174

Explicación -Wall necesitaba producir un error del sistema sin dejar de ser compilable. El error de sintaxis es no return 0;Para ingresar mediante stdin en la consola de Windows, escriba Ctrl-Z después de pegar y presione Intro.

Golfed

char c[256];int i;int main(){FILE *f=fopen("a.c","w");while(fgets(c,256,stdin)!=NULL){fputs(c,f);}fclose(f);i=system("gcc a.c -o -Wall a.exe");printf("%s",i?"false":"true");}

Sin golf:

#include <stdio.h>
#include <stdlib.h>
char c[256];int i;
int main()
{
FILE *f=fopen("a.c","w");
while(fgets(c,256,stdin)!=NULL)
{
fputs(c,f);
}
fclose(f);
i=system("gcc a.c -o -Wall a.exe");
printf("%s",i?"false":"true");
}

0

T-SQL - 110

Bastante simple, he estado queriendo probar un desafío aquí por un tiempo, finalmente pude hacerlo. Este no es el código más elegante, pero no obstante me divertí.

La versión 'golfizada'.

BEGIN TRY DECLARE @ VARCHAR(MAX)='SET NOEXEC ON'+'//CODE GOES HERE//'EXEC(@)PRINT'TRUE'END TRY BEGIN CATCH PRINT'FALSE'END CATCH

Una versión mejor formateada.

BEGIN TRY 
    DECLARE @SQL VARCHAR(MAX)='SET NOEXEC ON'+'//CODE GOES HERE//'
    EXEC(@SQL)
    PRINT'TRUE'
END TRY 

BEGIN CATCH 
    PRINT'FALSE'
END CATCH

Es bastante autoexplicativo, utiliza SET NOEXEC, lo que hace que simplemente analice la consulta en lugar de devolver ningún resultado. el resto es principalmente el try / catch que uso para determinar lo que necesito imprimir.

EDITAR: Debería haber agregado que esto técnicamente fallará por sí mismo. porque utiliza SQL dinámico, las comillas simples en la entrada deben duplicarse '->' '

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.