Faça um verificador de sintaxe finky

8

Você deve criar um programa que possa verificar a sintaxe dos programas do mesmo idioma. Por exemplo, se você fizer isso em python, ele verifica a sintaxe do python. Seu programa receberá um programa com entrada padrão e verifique se a sintaxe está correta ou não. Se estiver correto, a saída é apenas "verdadeira" na saída padrão. Caso contrário, envie apenas "false" na saída padrão.

Seu programa deve estar incorreto em uma instância. Se alimentado com seu próprio código-fonte, ele apenas emitirá "false" na saída padrão. Isso é código de golfe, e o programa mais curto vence!

Nota: Embora isso não seja tecnicamente adequado, você deve seguir as regras do quine, o que significa que não pode acessar seu código-fonte através do sistema de arquivos ou o que for.

Nota: Você não pode reivindicar um programa que não pôde ser executado devido a um erro de sintaxe resolve esse desafio, pois ele precisa ser executável.

PyRulez
fonte
1
É permitido o uso de bibliotecas padrão e / ou de terceiros? (especificamente, para usar um trabalho lexer / analisador para seu idioma)
Martin Ender
É try:exec(raw_input())...permitido?
user80551
@ user80551 Isso não funciona para entradas sintaticamente corretas, que são repetidas para sempre. Também é um pequeno risco de segurança.
Martin Ender
Não deveria ter a etiqueta quine?
21413 Sylwester
1
@ m.buettner E se recuarmos raw_input () em uma nova função e executá-la para que a função nunca seja realmente chamada. BTW quem ele se importa com os riscos de segurança no código-golfe?
precisa saber é o seguinte

Respostas:

9

Ruby 2.0, 65 76 164 personagens

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

Isso usa o verificador de sintaxe interno do Ruby ( ruby -c) para verificar a sintaxe da entrada, o que significa que o código não será avaliado.

Exemplo 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

Explicação

Esta solução é (foi) baseada no padrão Ruby quine:

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

%pé o especificador de formato para o arg.inspectqual pode ser comparado uneval: ao evalinserir a string retornada por arg.inspect, você (normalmente) obtém o valor original novamente. Assim, ao formatar a qstring como argumento, o %pinterior da string será substituído pela própria string citada (ou seja, obtemos algo parecido "q=\"q=%p;puts q%%q\";puts q%q").

A generalização desse tipo de quine leva a algo como o seguinte:

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

Essa abordagem tem uma enorme desvantagem (pelo menos no ): Todo o código precisa ser duplicado. Felizmente, evalpode ser usado para contornar isso:

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

O que acontece aqui é que o código passado para eval é armazenado dentro r antes de eval ser chamado. Como resultado, o código fonte completo da evalinstrução pode ser obtido com 'eval r=%p'%r. Se fizermos isso dentro do evalcódigo d e garantirmos que o nível superior de nosso consistir apenas em uma evalinstrução, essa expressão realmente nos fornecerá o código fonte completo do nosso programa, pois qualquer código adicional passado evaljá está armazenado r.

Nota lateral: Essa abordagem nos permite escrever um Ruby Quine em 26 caracteres: eval r="puts'eval r=%p'%r"

Agora, nesta solução, o código adicional executado dentro evalconsiste em quatro instruções:

gets p

Primeiro, lemos toda a entrada do STDIN e a salvamos implicitamente $_.

$<.pos=0

Em seguida, rebobinamos o STDIN para que a entrada fique novamente disponível para o subprocesso que iniciaremos na próxima etapa.

`ruby -c 2>&0`

Isso inicia o Ruby no modo de verificação de sintaxe embutido, lendo o código fonte do stdin. Se a sintaxe do script fornecido (nome do arquivo ou stdin) estiver correta, ela será impressa Syntax OKno seu stdout (que é capturado pelo processo pai), mas no caso de um erro de sintaxe, uma descrição do erro será impressa no stderr - o que seria fique visível, então redirecionamos isso para o nirvana ( 2>&0).

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

Depois, verificamos o código de saída do subprocesso $?, que é 0 se a sintaxe estiver correta. Por fim, a entrada que lemos anteriormente ( $_) é comparada com nosso próprio código-fonte (que, como descrevi anteriormente, pode ser obtido com 'eval r=%p'%r).

Editar: salvou 14 caracteres graças a @histocrat!

Ventero
fonte
Eu acredito que sua resposta é a única que realmente seguiu as regras até agora.
PyRulez
1
Muito agradável! Eu acho que você pode substituir .write por <<e >>1<1com ==0.
histocrat
@histocrat Huh, de alguma forma perdeu a parte ==dos Process::Statusdocumentos. Muito obrigado!
Ventero
6

Rebol - 69 ou 7475 totalmente compatível com todas as regras

Novas versões de trabalho graças a @rgchris! Não tenho certeza se o primeiro falha, o requisito "não acesse a fonte", pois o intérprete retém o código carregado e analisado e passou como um parâmetro de linha cmd no objeto do sistema ( system/options/do-arg) que é usado para se reconhecer.

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

Este segue todas as regras:

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

Exemplo de uso:

Primeiro imprimindo um número inteiro válido, o segundo imprimindo um número inteiro 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 

Versão totalmente compatível:

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

Explicação:

Primeira versão

Isso usa a função incorporada do Rebols loadpara analisar e carregar o código do stdin, mas não o executa.

O trybloco captura qualquer erro de sintaxe e a error?função converte o erro em um booleano simples.

O i = system/options/do-argcompara a entrada de stdin (atribuída a i) com o código passado no do-argargumento (sorrateiro, mas muito golf :).

anyé uma ótima função que retorna truese any-thing no bloco é avaliado como true(por exemplo, any [ false false true ]retornaria true).

notem seguida, apenas inverte o booleano para fornecer a resposta correta e probeexibe o conteúdo do valor retornado.

Versão totalmente compatível

Vamos passar por isso em ordem ...

Atribua a palavra bao bloco [] a seguir.

Use a dofunção para interpretar o dodialeto no bbloco.

Dentro do bquarteirão ...

Defina a palavra ipara se referir ao conteúdo de stdin ( input).

Agora, iftemos joina string "do b:" no moldbloco 'ed be não é igual ( <>) à entrada stdin, ientão tentamos loada entrada i.

Se o resultado for um, blockentão nós loadeditamos os dados passados ​​corretamente, caso contrário, receberíamos um noneda falha if.

Use prinpara exibir o resultado do block?qual retorna true se o resultado for um bloco. Usar prinem oposição a printnão exibe um retorno de carro após a saída (e economiza outro caractere).

johnk
fonte
Eu acho que você pode raspar que até 36 caracteres comprint not error? try[load/all input]
draegtun
Isso verifica a sintaxe ... mas o problema estipula que ele retorne false em seu próprio código. Offhand, ungolfed ... #c:[c: compose/only [c: (c)]print not error? try[if c = load/all input[1 / 0]]]
HostileFork diz que não confie em SE
Perdi um pouco o código não válido. Que tal agora? (prin none? attempt[load/all input] halt) 1a O inteiro inválido falharia avaliação sintaxe, mas a parada iria parar um erro na operação normal
johnk
"Ainda não é possível encontrar uma maneira de falhar na validação do meu próprio código, sem somar o código e retornar falso se eu vir meu próprio código". Se você puder o checksum/secureseu programa e incluir o hash dentro da própria fonte para fins de comparação, gostaria de incluir seus serviços no meu sindicato de crimes cibernéticos. Salvo isso, comece pelo meu código, que funciona. :-)
HostileFork disse que não confia em SE
1
D'oh! RebMu em 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)}()

Cole no console Javascript do seu navegador para testar.
Explicação:

// 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 levantou a questão de que o programa retorna truepara uma declaração de retorno nu, como return 0;. Como o Javascript não gera um erro de sintaxe para uma declaração de retorno ilegal até que seja realmente executado (ou seja, código como if (0) { return 0; }não gera um erro de sintaxe), não creio que haja maneira de corrigir isso sem escrever um analisador Javascript em Javascript. . Por exemplo, considere o código:

while (1) {}
return 0;

Se o código for executado, ele será interrompido por causa do loop. Se o código não for executado, nenhum erro será gerado para a declaração de retorno ilegal. Portanto, isso é tão bom quanto o Javascript pode obter para esse desafio. Sinta-se à vontade para desqualificar o Javascript se achar que isso não cumpre o desafio suficientemente.

Abraão
fonte
2
Agora, ele não relatará um erro de sintaxe ao fornecê-lo return 0.
Martin Ender
Mm, boa @ m.buettner
Abraham
2

Haskell - 222 bytes

Observe que isso usa um analisador verdadeiro. Não depende de evalfunções semelhantes de linguagens dinâmicas.

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 solução não é particularmente bonita, mas funciona.

gxtaillon
fonte
Falhar por si mesmo?
PyRulez
Faz. A if take ...instrução verifica se a entrada corresponde a uma string literal que é a primeira parte do programa.
Gxtaillon
2

Eu acho que isso segue as regras:

JS (✖╭╮✖)

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

O código será avaliado se estiver correto.

Precisa dar uma olhada na notação de seta para ver se ela não pode ser reduzida mais.

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

Depois de algumas tentativas fracassadas e reverte - nova versão!

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

E eu voltei!

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

E eu fui embora! Infelizmente, devido à natureza de eval e, graças a @scragar (maldição, @scragar!), Essa abordagem não funcionará (visto que throw new SyntaxErroré um código JS válido, que marca esse método) -, como tal, eu diria que é impossível criar um verificador de sintaxe (pelo menos usando eval ou qualquer variação dos mesmos)

(* veja os comentários!)

eithed
fonte
Suponha que a entrada seja um loop infinito? Eu sugiro que você useeval("x=function(){"+t+"}");
DankMemes 4/04
1
@ZoveGames Isso pode ser quebrado com entradas como }//ou };{.
Ventero 04/07/2014
Eu não sei se isso é válido ou não de acordo com as regras, uma vez que tem de ser um programa, não uma função (eu acho)
Abraham
@ZoveGames Good point! Embora a manipulação de scripts do navegador deva entrar em ação (contador de loop / tempo limite), ainda é fácil escrever um script que fará com que o "sistema" seja interrompido. Aguardarei a alteração até que o OP especifique a regra sobre isso.
eithed
1
@eithedog Desculpe, fiquei confuso em idiomas, Javascripts base throwable é realmente chamado Error, não Exception. throw new Error('')causa o comportamento incorreto.
scragar
1

Python (95)

c=raw_input()
try:compile('"'if sum(map(ord,c))==7860 else c,'n','exec');print 1
except:print 0
ɐɔıʇǝɥʇuʎs
fonte
Isso não funciona apenas para uma linha de entrada?
31568 Ian Ian Scott
2
Não funciona: c=u'#\u1e91'porqueord('#') + ord(u'\u1e91') == 7860
ThinkChaos
Tente um hash.
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

Comentário necessário para manter o 'hash' (uma cópia descarada de s, ɐɔıʇǝɥʇuʎs). Usando php -l / lint para verificar se há erros.

$ 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
Aurel Bílý
fonte
0

C 174

Explicação -Wall necessário para produzir erro do sistema enquanto ainda está sendo compilável. O erro de sintaxe é não. return 0;Para inserir via stdin no console do Windows, digite Ctrl-Z após colar e pressione enter.

Golfe

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

Ungolfed:

#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");
}
bacchusbeale
fonte
0

T-SQL - 110

Bastante simples, eu tenho vontade de tentar um desafio aqui há um tempo, finalmente consegui fazê-lo. Este não é o código mais chique, mas eu me diverti.

A versão 'golfed'.

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

Uma versão melhor formatada.

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

BEGIN CATCH 
    PRINT'FALSE'
END CATCH

É bastante auto-explicativo, usa SET NOEXEC, o que faz com que apenas analise a consulta em vez de retornar quaisquer resultados. o resto é principalmente a tentativa / captura que eu uso para determinar o que preciso imprimir.

Edição: Eu deveria ter adicionado que isso tecnicamente falhará por si mesmo. porque ele usa SQL dinâmico, qualquer aspas simples na entrada devem ser duplicadas '->' '

PenutReaper
fonte