Як одну з менш популярних мов, важко знайти літературу про авангард поскриптських хакерів. Отже, які відкриття тут зробили гольфісти для використання моделі стека (або інших особливостей) для подолання притаманної багатослівності Postcript?
Як одну з менш популярних мов, важко знайти літературу про авангард поскриптських хакерів. Отже, які відкриття тут зробили гольфісти для використання моделі стека (або інших особливостей) для подолання притаманної багатослівності Postcript?
Відповіді:
Програма Postscript має унікальну (?) Здатність читати власний текст програми як дані. Це , як правило , використовується image
оператор , який приймає конверсію-процедуру передачі даних в якості вхідних даних, і ця процедура часто використовує currentfile
слідують readline
, readstring
або readhexstring
. Але по-іншому, image
це лише ще один циклічний оператор, тому будь-який цикл може читати заздалегідь . Прикладом є емулятор лінійного принтера із Зеленої книги.
Використання token
оператора викликає сканер у файл або рядок, витягуючи число чи пробіл (або іншим способом: див. Іншу відповідь) - обмежене ім'я.
Простий інтерпретатор PS в PS:
{currentfile token not {exit} if dup type /arraytype ne {exec} if }loop
Оскільки я не можу отримати необроблені бінарні маркери для мене (див. Іншу відповідь), я використав ідею "вбудованого декодування" для використання механізму двійкового маркера для упаковки коду у 8-бітні рядки, а потім маніпулювати та розбирати команди з рядка на льоту .
/.{
<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
.
Процедура займає кілька з стека і вставляє його в якості другого байта в рядку два байта, перший байт є префікс байтами для довічних маркерів, який визначає виконується ім'я системи. Ми зберігаємо байт у шістнадцятковій стрічці, використовуючи правило сканера, що непарна кількість гризків у шістнадцятковій смужці додаткова 0 зайвих ніб, тому 3 шістнадцяткових кусання створюють 2-байтну рядок. Потім рядок позначається виконуваним і викликається, за допомогою exec
якого викликається сканер, виробляється потрібне ім'я виконуваної системи, а потім завантажується ім'я та виконується оператором. $
Робить це на кожних байтах рядка в стеку, використовуючи .
процедуру два рази , один раз в якості тіла циклу, а потім виконати зациклення оператора forall
за номером.
Більш компактно, ці процедури виглядають приблизно так:
/.{<920>dup 1 4 3 roll put cvx exec}def/${//. 73 .}def
%123457890123456789012345678901234567890123456789012345
% 1 2 3 4 5
Так, 55 символів купує двійкові рядки лексем. Або на 6 (можливо, 7, якщо ви закінчуєте це пробілом) символів, ви можете завантажити G-бібліотеку, з (G)run
якою визначено .
і $
як вище (+ кілька інших, щоб розширити діапазон кодів, доступних ascii).
Далі проілюстровано у моїй відповіді на кросворд .
Напевно, стара новина, але я просто дізнався її. :)
Ви можете зробити це за допомогою інтерпретатора постскрипту інтерактивно з кодуючим фільтром та вирізати та вставити. Але я покажу, як dc
це зробити, щоб зробити це «вручну».
Отже, ось шістнадцятковий рядок Ми розділили його на 4-байтні шматки.
95 20 6e d8 d0 59 49 35 50 74 ba c5 08 2d
Запускаючи постійний струм, ми вводимо їх як 32-бітні (непідписані) номери великого ендіанського байтового порядку. Тоді модифікація - відмінна база-85 цифр (має бути 5, поки ви не отримаєте 0).
0> DC 16і 95206ED8 Ай d85% n85 / 82 d85% n85 / 83 d85% n85 / 82 d85% n85 / 78 d85% n85 / 47 d85% n85 / 0
Пробиваючи останній шматок 00 00
, виходить (десятковий), опускаючи стільки ж байтів, що і ми.
47 78 82 83 82 66 81 72 79 83 25 72 82 25 69 2 53 30 [2 53]
Додайте 33, щоб перейти в діапазон друку ASCII і пуф! ASCII85.
80 111 115 116 115 99 114 105 112 116 58 105 115 58 102 35 86 63
який розшифровується до: Postscript: є: f # V? %%% На жаль! слід сказати "весело"! Я кудись накрутив. :)
Згорніть його в <~
... ~>
, а Postcript Level-2 може отримати доступ до 8-бітових даних, дешевших ніж шістнадцятковий.
Ось швидке повідомлення: введіть кілька визначень, [...>>begin
щоб усунути ключове слово def
(nb. Те [
саме, що <<
).
def def
[>>begin
Тож пам’ятайте: більшетридвоє ... стікаються разом ! ;)
/a 1 def/b 2 def/c 3 def
з <</a 1/b 2/c 3>>begin
. Нам потрібно більше пробілів для def.
[/a 1/b 2/c 3>>begin
/a{pop 2 mul}def
або \b[2 3]def
, то def
варто тільки 3 -х символів, а НЕ 4.
У той час як більшість постскріпта операторів синтаксично ідентифікатори (і , отже , повинен бути пробілу (або otherwise-) з роздільниками), імена [
, ]
, <<
, і >>
є саморазгранічни і сканер виявить їх без втручання простору. З цієї ж причини ви не можете посилатися на ці імена зі звичайним /literal
синтаксисом (наприклад, /[
це два лексеми: порожнє буквальне ім'я, еквівалентне ()cvn cvlit
, і ім'я виконуваної програми, [
еквівалентне ([)cvn cvx exec
).
Для того щоб переосмислити ці імена, які неможливо згадати по імені, ми можемо використовувати рядки, які неявно перетворюються на імена, коли вони використовуються як ключі в словнику (зручно!).
Цей приклад ілюструє зловживання цими операторами для виконання арифметики.
%!
([)0 def
(])1 def
(<<){add}def
(>>){mul}def
]]<<]]]<<<<>> =
%1 1 add 1 1 1 add add mul = %prints 6
Також <<
і [
(і mark
) все означають те саме.
Мій власний перекладач поштових скриптів , xpost , також робить правильну фігурну фігурну дужку доступною з деякими обмеженнями. обговорення
/
закінчується попередній маркер, тому вам не потрібно місця перед ним.
Якщо ви вже використовуєте <<>>begin
словник, постійно визначається накладні витрати у /?{}
4 символи на кожне змінення. Таким чином, оператор довжини n повторних N разів призведе до зміни числа символів
(4 + n ) - ( N * ( n - 1)).
Установка цієї формули , яка дорівнює 0 дає рівняння беззбитковості точки. З цього ми можемо вирішити для кожної змінної через іншу, отримуючи
n = - ( N - 4) / (1 - N ) і
N = (4 + n ) / ( n - 1).
Ні, ми не можемо відповісти на запитання на кшталт: "Для скільки вживань" print "варто скоротити?" n = 5, тому N = 9/4. Візьміть стелю, оскільки ви не можете ефективно викликати друк 1/4 рази. Отже, 3. 3 використання. І справді,
print print print
/P{print}p p p
(якщо припустити, ви вже заплатили накладні витрати, <<>>begin
щоб активувати визначення, звичайно).
Звичайно, бінарні лексеми створюють подібний тип спот, даючи вам перші 255 імен із таблиці імен системи у вигляді 2-байт: 0x92, 0x ??. А двійкові жетони також саморозмежовуються, не вимагаючи пробілів до або після, оскільки високий біт першого байта знаходиться поза діапазоном ascii.
Для остаточного завантаження програми PostScript, остання межа - це двійкові маркери, які дозволяють повністю видалити довгі імена операторів, за рахунок того, що більше не має ASCII-чистої програми.
Отже, починаючи з ущільненого блоку коду постскрипту
[/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
Ми шукаємо всі назви в задній частині PLRM (Додаток F, с. 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
А потім введіть їх у префіксі через 146
(десятковий) байт. Довідка vim для введення довільних байтів
Тоді в vim, стислий файл можна вводити безпосередньо, так:
[/ 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 ^V63}^V146 ^V73 ^V146 ^V57} { ^V146 ^V62 { ^V146 ^V100 ^V146 ^V62
... тут потрібно ввести пробіл, щоб скасувати ^V
-62 та запустити 1, але пізніше можна створити резервну копію та видалити ...
1 ^V146 ^V1S} ^V146 ^V73} ^V146 ^V85
... тут потрібно ввести пробіл, щоб скасувати ^V
-85 та почати 1, але ви можете створити резервну копію та видалити пізніше ...
1 ^V146 ^V169} >> ^V146 ^V13 ^V146 ^V107
... Третя цифра 3-значного коду припиняє введення байтів, тому наступне 0
тут нормально, зручно ...
0 S ^V146 ^V167
Як це буде виглядати так на екрані (in 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>§
Цю часто можна повністю опустити, якщо мета - лише показати картину. Ghostscript малює більшість речей на екрані без потребиshowpage
.
¡ 161 showpage
[ Це насправді не працює. Ghostscript дає мені undefined
і syntaxerror
ці жетони. Можливо, мені потрібен режим, який я повинен увімкнути. ]
Негативні рулони завжди можуть бути змінені на позитивні .
3 -1 roll
3 2 roll
5 -2 roll
5 3 roll
3 -1 roll
або 3 2 roll
? У моїй ментальній моделі колишня повинна бути ефективнішою, оскільки вона займає лише один крок. Чи правильна моя ментальна модель?
roll
оператора.
https://github.com/luser-dr00g/G
Це текстовий файл. Немає розширення для найкоротшого можливого синтаксису для завантаження.
Це дозволяє цю 203-програмову програму Sierpinksi Triangle
[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
повинні бути переписані в 151 байт як
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
Використання функції скорочених імен систем 1(G)run
повністю знімає тягар довгих імен операторів. Ім’я оператора повинно бути досить довгим, щоб відрізняти його від інших.
Так
add
стає ad
mul
стає mu
index
стає i
Використовуйте додаток F PLRM для стандартної таблиці імен операторів.
А функція "Операторські рядки" доступна, навіть якщо не вибрано скорочені імена. Гола бібліотека має "базовий рівень", вибраний простим додаванням(G)run
без додаткових прикрас.
Базовий рівень включає нову функцію, .
яка приймає цілий код для оператора (той же Додаток F, згаданий вище) та виконує його.
Нова функція $
повторюється через рядок та дзвінки.
кожну з них. Так код ascii безпосередньо вибирає оператора за номером.
Нова функція @
дозволяє дістатися до нижньої частини таблиці в Додатку F, розглядаючи пробільний символ (Ascii 0x20) як 0.
Нова функція #
дозволяє проникати далі в таблицю, спочатку додаючи 95 (0x5F), тому пробіл 0x20 трактується як 127 (0x7F), самий наступний код після останнього символу для друку ascii~
126 для 126 (0x7E).
Дві нові функції !
дозволяють отримати доступ до глибоко вкладеної структури масивів та / або диктів з індексним масивом індексів / клавіш, а не нудними виразами багатьох get
(і put
) операторів.
(G)run
7 символів купує базовий рівень.
1(G)run
8 символів купує, що І скорочені імена системи.
3(G)run $
9 символів негайно починає блок неявної процедури сканування рядків джерел до наступного порожнього рядка, і визначаючи перший рядок як називається процедура A
, наступний рядок визначається як процедура, що викликається B
, і т. Д. Це повинно видалити більшість def
s, необхідних для визначення багато матеріалів, не потребуючи загортання їх у словник, а також явно не даючи їм імен.