Dicas para jogar golfe no Postscript?


Respostas:


3

Decodificador incorporado

Um programa Postscript possui uma capacidade exclusiva (?) De ler o próprio texto do programa como dados. Este é normalmente utilizado pelo imageoperador que recebe uma aquisição procedimento de dados como entrada, e este procedimento utiliza frequentemente currentfileseguido por readline, readstring, ou readhexstring. Mas, visto de outra maneira, imageé apenas mais um operador de loop, para que qualquer loop possa ler adiante . Um exemplo é o emulador de impressora de linha do Livro Verde.

O uso do tokenoperador chama o scanner em um arquivo ou sequência, retirando um número ou espaço - (ou não -: veja outra resposta) - nome delimitado.

Um simples intérprete PS no PS:

{currentfile token not {exit} if dup type /arraytype ne {exec} if }loop

Decodificador de cadeia de operador binário

Como não consigo fazer com que os tokens binários brutos funcionem para mim (veja outra resposta), usei a idéia de "decodificação incorporada" para explorar o mecanismo de token binário para empacotar o código em cadeias de caracteres de 8 bits e, em seguida, manipular e analisar os comandos da cadeia de caracteres em tempo real .

/.{
    <920>  % two-byte binary-encoded name template with 0x92 prefix
    dup 1 4 3 roll put  % insert number into string
    cvx exec  % and execute it
}def
/${
    //.   %the /. procedure body defined above
    73 .  %"forall" (by code number)
}def

O .procedimento pega um número da pilha e o insere como o segundo byte em uma cadeia de dois bytes, o primeiro byte sendo o prefixo-byte de um token binário, especificando um nome de sistema executável. Salvamos um byte na cadeia de caracteres hexadecimal usando uma regra do scanner de que um número ímpar de petiscos na cadeia hexadecimal é preenchido com um petisco 0 adicional, de modo que 3 petiscos hexadecimais produzem uma cadeia de 2 bytes. A sequência é marcada como executável e chamada com a execqual chama o scanner, produz o nome do sistema executável desejado e, em seguida, carrega o nome e executa o operador. A $faz isso em cada byte de uma cadeia na pilha, utilizando o .procedimento duas vezes , uma vez que o corpo do laço, e, em seguida, executar o operador looping forall, em número.

De forma mais compacta, esses procedimentos são assim:

 /.{<920>dup 1 4 3 roll put cvx exec}def/${//. 73 .}def
%123457890123456789012345678901234567890123456789012345
%        1         2         3         4         5

Portanto, 55 caracteres compram seqüências de token binárias. Ou, para 6 caracteres (talvez 7, se você o encerrar com um espaço), você pode carregar a biblioteca G com a (G)runqual define .e $como acima (+ algumas outras para estender o intervalo de códigos ascii-alcançáveis).

Mais ilustrado na minha resposta de palavras cruzadas .


1
Na verdade, se o postscript estiver no formato codificado, isso exige que você seja extremamente cuidadoso ao comparar coisas. Especificamente, se você estiver olhando para operadores, deverá analisá-lo como um token e, em seguida, comparar o nome do token com o valor de destino.
AJMansfield

2

Ao gerar saída gráfica e saída do console, não importa, use em =vez de pop.


2

Substitua hexstrings por ASCII85

Provavelmente notícias antigas, mas eu apenas as aprendi. :)

Você pode fazer isso usando o interpretador postscript interativamente com um filtro de codificação e recortar e colar. Mas vou mostrar como usá dc-lo "manualmente".

Então, aqui está uma string hexadecimal. Dividimos em pedaços de 4 bytes.

95 20 6e d8   d0 59 49 35   50 74 ba c5   08 2d

Iniciando o dc, os inserimos como números de 32 bits (não assinados) de ordem de big endian e byte. Então mod -off base-85 dígitos (deve haver 5 até chegar a 0).

0> dc
16i
95206ED8
Ai
d85% n85 /
82
d85% n85 /
83
d85% n85 /
82
d85% n85 /
78
d85% n85 /
47
d85% n85 /
0 0                    

Preencher o último pedaço com 00 00, produz (decimal), omitindo o mesmo número de bytes que preenchemos.

47 78 82 83 82   66 81 72 79 83   25 72 82 25 69  2 53 30 [2 53]

Adicione 33 para mudar para o intervalo imprimível de ASCII e poof! ASCII85.

80 111 115 116 115 99 114 105 112 116 58 105 115 58 102 35 86 63
que decodifica para: Postscript: is: f # V? %%% Ops! deve dizer "divertido"! Eu estraguei tudo em algum lugar. :)

Coloque-o em <~... ~>e o Postscript de nível 2 pode acessar dados de 8 bits, mais baratos que o hexadecimal.


2

Aqui está uma rápida: envolva várias definições [...>>beginpara eliminar a palavra-chave def(nb. [É igual a <<).

 def def
[>>begin

Então lembre-se: mais detrêsdois ... reunem-se ! ;)


A regra não deveria ser "mais de dois"? Compare /a 1 def/b 2 def/c 3 defcom <</a 1/b 2/c 3>>begin. Precisamos de mais espaços para def.
Thomas W.

Uau. Eu não tinha pensado nisso. Sim, o cálculo precisa ser aprimorado.
Luser droog

Na verdade, isso deve ser[/a 1/b 2/c 3>>begin
Thomas W.

palma da mão. . . .
Luser droog

1
Isso pode não se aplicar se você estiver nomeando algo que termina em um token auto-delimitante. Em /a{pop 2 mul}defou \b[2 3]def, o defúnico custa 3 caracteres, não 4.
AJMansfield 16/02

2

Enquanto a maioria dos operadores PostScript são sintaticamente identificadores (e, portanto, deve ser espaço- (ou otherwise-) delimitado), os nomes [, ], <<, e >>são auto-delimitação e scanner irá detectá-los sem intervir espaço. Pelo mesmo motivo, você não pode se referir a esses nomes com a /literalsintaxe usual (por exemplo, /[são dois tokens: um nome literal vazio equivalente a ()cvn cvlite o nome executável [equivalente a ([)cvn cvx exec).

Para redefinir esses nomes, que não podem ser mencionados pelo nome, podemos usar cadeias que são implicitamente convertidas em nomes quando usadas como chaves em um dicionário (conveniente!).

Este exemplo ilustra abusar desses operadores para executar aritmética.

%!
([)0 def
(])1 def
(<<){add}def
(>>){mul}def
 ]]<<]]]<<<<>> =
%1 1 add 1 1 1 add add mul = %prints 6

Também <<e [(e mark) todos significam a mesma coisa.


Meu próprio intérprete postscript, xpost , também disponibiliza a chave com algumas restrições. discussão


2
Além disso, /termina o token anterior para que você não precise de um espaço antes dele.
Geoff Reedy

1

Utilização repetida de fatores longos de nomes longos de operadores

Se você já estiver usando um <<>>begindicionário, haverá uma sobrecarga constante de /?{}4 caracteres por redefinição. Portanto, um operador de comprimento n repetido N vezes produzirá uma alteração na contagem de caracteres de
(4 + n ) - ( N * ( n - 1)).

Definir essa fórmula como 0 fornece a equação do ponto de equilíbrio . A partir disso, podemos resolver para cada variável em termos da outra, produzindo
n = - ( N - 4) / (1 - N ) e
N = (4 + n ) / ( n - 1).

Não, podemos responder perguntas como: "Para quantos usos de 'print' vale a pena abreviar?" n = 5, então N = 9/4. Pegue o teto, pois você não pode ligar efetivamente para impressão 1/4 vezes. Então, 3. 3 usos. E realmente,

print print print
/P{print}p p p

(supondo que você já pagou as despesas gerais <<>>beginpara ativar a definição, é claro).

Obviamente, os tokens binários fazem esse tipo de discussão, fornecendo os primeiros 255 nomes da tabela de nomes do sistema como 2 bytes: 0x92, 0x ?? E os tokens binários também são auto-delimitadores, não exigindo espaço em branco antes ou depois, pois o bit alto do primeiro byte está fora do intervalo ascii.


1

Tokens binários

Para o melhor zip-up de um programa PostScript, essa fronteira final é tokens binários que permite remover completamente os nomes longos de operadores, com o custo de não ter mais um programa de limpeza ASCII.

Então, começando com um bloco compactado de código postscript

[/T[70{R 0 rlineto}48{}49{}43{A rotate}45{A neg rotate}91{currentdict
end[/.[currentpoint matrix currentmatrix]cvx>>begin begin}93{. setmatrix
moveto currentdict end end begin}>>/S{dup B eq{T begin exch{load exec}forall
end}{exch{load exch 1 add S}forall}ifelse 1 sub }>>begin moveto 0 S stroke

Pesquisamos todos os nomes no final do PLRM (Apêndice F, pp. 795-797)

appearance
in
vim    dec  meaning

<92>   146  'executable system name' binary token prefix
^A     1    add
^M     13   begin
^^     30   currentdict
'      39   currentmatrix
(      40   currentpoint
2      50   cvx
8      56   dup
9      57   end
=      61   eq  !)
>      62   exch
?      63   exec
I      73   forall
U      85   ifelse
d      100  load
h      104  matrix
k      107  moveto
n      110  neg
<85>   133  rlineto
<88>   136  rotate
§      167 stroke
©      169 sub

E digite-os com o prefixo de um 146byte (decimal). ajuda do vim para inserir bytes arbitrários

Em seguida, no vim, o arquivo condensado pode ser digitado diretamente, portanto:

[/ T [70 {R 0 ^V146 ^V133} 48 {} 49 {} 43 {A ^V146 ^V136} 45 {A ^V146 ^V110 ^V146 ^V136} 91 { ^V146 ^V30 ^V146 ^V57 [/. [ ^V146 ^V40 ^V146 ^V104 ^V146 ^V39] ^V146 ^V50 >> ^V146 ^V13 ^V146 ^V13} 93 {. ^V146 ^V156 ^V146 ^V107 ^V146 ^V30 ^V146 ^V57 ^V146 ^V57 ^V146 ^V13} >> / S { ^V146 ^V56 B ^V146 ^V61 {T ^V146 ^V13 ^V146 ^V62 { ^V146 ^V100 ^V146^V 63}^V146^V73 ^V146 ^V57} { ^V146 ^V62 { ^V146 ^V100 ^V146 ^V62

... você precisa inserir um espaço aqui para finalizar o ^V-62 e iniciar o 1, mas pode fazer backup e excluí-lo mais tarde ...

1 ^V146 ^V1S} ^V146 ^V73} ^V146 ^V85

... tem que inserir um espaço aqui para finalizar o ^V-85 e iniciar o 1, mas você pode fazer backup e excluí-lo mais tarde ...

1 ^V146 ^V169} >> ^V146 ^V13 ^V146 ^V107

... o terceiro dígito do código de três dígitos termina a entrada de bytes, de modo que o seguinte 0aqui é normal, convenientemente ...

0 S ^V146 ^V167

Que ficará assim na tela (no vim):

[/T[70{R 0<92><85>}48{}49{}43{A<92><88>}45{A<92>n<92><88>}
91{<92>^^<92>9[/.[<92>(<92>h<92>']<92>2>>
<92>^M<92>^M}93{.<92><9c><92>k<92>^^<92>9<92>9<92>^M}
>>/S{<92>8B<92>={T<92>^M<92>>{<92>d<92>?}<92>I<92>9}{<92>>
{<92>d<92>>1<92>^AS}<92>I}<92>U1<92>©}>><92>^M
<92>k0 S<92>§

Este geralmente pode ser omitido inteiramente se o objetivo é apenas mostrar uma imagem. O Ghostscript pinta a maioria das coisas na tela sem a necessidade showpage.

¡    161   showpage

[ Isso realmente não está funcionando. O Ghostscript está me dando undefinede syntaxerrorpor esses tokens. Talvez haja algum modo que eu precise ativar. ]


Talvez seja algo sobre este programa. O compactador online também não gosta.
Luser droog

1

Alterar rolos negativos para positivos

Rolos negativos sempre podem ser alterados para positivos .

3 -1 roll
3 2 roll

5 -2 roll
5 3 roll

Qual é mais eficiente 3 -1 rollou 3 2 roll? No meu modelo mental, o primeiro deveria ser mais eficiente, porque é preciso apenas um movimento. Meu modelo mental está correto?
beija minha axila

Honestamente, não tenho certeza. Aqui está minha implementação , onde todos os rolos negativos são convertidos em positivos como a primeira etapa. Eu acho que ainda exigiria pelo menos 2 movimentos (mova o 3º valor para cima , mova 3 valores para baixo ) com uma implementação de matriz plana da pilha. Mas minha pilha é segmentada, de modo que o acesso passa por chamadas de função para gerenciar os segmentos; então certamente existem maneiras mais eficientes de implementar do que eu. ... Uma coisa que vejo agora: devo verificar o fluxo de pilha fora dos loops.
Luser droog

1
O arquivo de origem foi movido desde o meu último comentário. Aqui está minha implementação do rolloperador.
luser droog

0

Use minha biblioteca G

https://github.com/luser-dr00g/G

É um arquivo de texto. Sem extensão, para a sintaxe mais curta possível para carregá-lo.

Ele permite que este programa do Triângulo Sierpinksi de 203 caracteres

[48(0-1+0+1-0)49(11)43(+)45(-)/s{dup
0 eq{exch{[48{1 0 rlineto}49 1 index
43{240 rotate}45{120 rotate}>>exch
get exec}forall}{exch{load
exch 1 sub s}forall}ifelse 1 add}>>begin
9 9 moveto(0-1-1)9 s fill

ser reescrito em 151 bytes como

3(G)run $
{A - B + A + B - A}
{B B}

{A - B - B}7{[ex{du w{(>K?\2u)$}if}fora]}rep
cvx[/A{3 0 rl}/B 1 in/-{120 rot}/+{-120 rot}>>b
100 200(k?B9)$ showp

arquivo de trabalho com comentários

O uso do recurso abreviado de nomes de sistema 1(G)runremove completamente a carga de nomes longos de operadores. Um nome de operador precisa ser longo o suficiente para diferenciá-lo dos outros.

então

  • add torna-se ad
  • mul torna-se mu
  • index torna-se i
  • etc etc.

Use o apêndice F do PLRM para a tabela padrão de nomes de operadores.

E o recurso Operator Strings está disponível mesmo que os nomes abreviados não estejam selecionados. A biblioteca simples possui um "nível básico" selecionado adicionando simplesmente (G)runsem mais decorações.

O nível básico inclui uma nova função .que aceita o código inteiro para um operador (o mesmo Apêndice F mencionado acima) e o executa.

A nova função $itera através de uma string e chama .cada uma. Portanto, o código ascii seleciona diretamente o operador pelo número.

Uma nova função @permite chegar ao final da tabela no Apêndice F tratando o caractere de espaço (Ascii 0x20) como 0.

Uma nova função #permite que você chegue mais longe na tabela adicionando primeiro 95 (0x5F) para que o espaço 0x20 seja tratado como 127 (0x7F), o código seguinte após o último caractere ascii imprimível ~126 (0x7E).

Duas novas funções !permitem acessar uma estrutura profundamente aninhada de matrizes e / ou dictos com uma matriz de índice de índices / chaves, em vez de expressões tediosas de muitos get(e put) operadores.

(G)run 7 caracteres compra o nível básico.

1(G)run 8 caracteres compra esse AND abreviado nomes de sistema.

3(G)run $9 chars inicia imediatamente um bloco de procedimento implícito que varre as linhas de origem até a próxima linha em branco e, definindo a primeira linha como um procedimento chamado A, a próxima linha é definida como um procedimento chamado Betc. Isso deve remover a maioria dos defitens necessários para definir muitas coisas, sem precisar envolvê-las em um dicionário, nem mesmo dar nomes explicitamente.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.