6 заметок с тегом

applescript

AppleScript и Тетрис

Тетрис (20.11КиБ)
Непритязательный внешний вид получившегося Тетриса

Я тут книгу читаю про хакеров (в первоначальном смысле слова) — «Хакеры. Герои компьютерной революции». Книга довольно занудная, но тот дух, который она пытается передать, я ещё очень хорошо помню. Ассемблер, такты, хаки, придуманные трудности — всё это было как специи в программировании. Сейчас как-то чем дальше, тем преснее.

И так тоскливо что-то мне стало, так захотелось написать что-нибудь долбанутое, чтобы почуствовать вкус того самого программирования, что как-то написался у меня в промежутках между делами в последние дни отпуска Тетрис на ЭплСкрипте.

Трудности, которые мне тут успешно самому себе удалось придумать, заключаются в том, что ЭплСкрипт в этой плоскости очень бедный язык. Если не сказать нищий. У него нет встроенных средств рисования чего-либо на экране или опроса клавиатуры, поэтому пришлось выкручиваться (те самые специи).

Для тех, кто не знает, надо наверное пояснить. ЭплСкрипт — это язык автоматизации пользовательских сценариев работы, он умеет управлять приложениями (с разной степенью успеха) и кое-какими вещами в «МакОСи».

Так как возможности что-то рисовать в нём нет, то стакан и фигуры сделаны из уменьшенных окошек приложения «TextEdit» — я их создаю по потребности и масштабирую к малому размеру. Получаются такие белые шарики, из которых всё и состоит.

Дольше всего я ломал голову над управлением, пока не придумал следующее — ЭплСкрипт умеет опрашивать значение громкости, так что клавиши уменьшения и увеличения я приспособил под сдвиг фигуры, а кнопку включения/выключения звука — под поворот.

Над скоростью игры я в некоторых местах поработал, но сильно углубляться не стал — ЭплСкрипт относительно шустрый, основную задержку вносит создание окон, это место вряд ли можно как-то ускорить — там несколько строк всего и всё адресовано приложению TextEdit, а оно уж открывает свои окна так быстро как может. Впрочем игровая скорость мне кажется приемлемой, только отрисовка самого стакана происходит дольше, чем хотелось бы.

Кажется это первая и единственная игра в реальном времени на ЭплСкрипте.

2017   applescript   программирование

Передача по ссылке в AppleScript’е

ЭплСкрипт, конечно, очень странный язык. С одной стороны, ребята явно старались сделать программирование похожим на написание текста на естественном (английском) языке, с другой стороны, все кишочки спрятать не удалось. Любимые мои грабли — это передача значений по ссылке.

У этой штуки есть одна особенность, которую надо знать. Код ниже перебирает список и выводит в консоль всякую информацию об единственном элементе, который в списке содержится:

repeat with itm in {false}
    log itm -- Выведет «false»
    log class of itm -- Выведет «boolean»
    log itm is false -- Выведет… «false», хм
    log itm is true -- И снова «false»
end repeat

Непонятно, да? Элемент булевского типа, но не «истина» и не «ложь», как так? Дело в том, что этот элемент — ссылка. И прежде чем его с чем-то сравнивать, надо получить его значение. Вот так нормально:

repeat with itm in {false}
    log contents of itm is false -- Выведет «true»
end repeat

Контринтуитивно и неясно какую пользу это приносит, но вот так сделано.

2017   applescript   программирование

99 бутылок: AppleScript, Applesoft BASIC

У меня тут мой ноут поперхнулся чаем, так что я неделю был на стареньком компике под Виндой, почувствовал себя себя как без рук. По этой же причине почти не писал ничего в блог, лишь одним вечером на ноуте жены написал очередные две «Песни про пиво» на языках AppleScript и Applesoft BASIC. Первый — современный язык для автоматизации мелочи на «Маках», второй — древний (1977 год) бейсик, когда-то так же использовавшийся на «Маках».

52. AppleScript — встроенный в операционную систему «МакОС» язык программирования, используемый для мелкой автоматизации. По программе этой не очень очевидно, но язык максимально приближен к английскому, причём многие вещи можно формировать разнообразными способами — так же, как бы это сделал носитель английского языка.

В остальном, это обычный динамический интерпретируемый язык со слабой типизацией. Как видно в одном месте мне пришлось сделать преобразование типа. Вообще-то ЭплСкрипт делает это и сам, но в случае присоединения к строке, он число присоединит через запятую.

(* Песня про пиво. Автор — Евгений Степанищев *)
repeat with beer from 99 to 1 by -1
     set b to bottles(beer)
     log b & " on the wall, " & b & "."
     log "Take one down and pass it around, " & bottles(beer - 1) & " of beer on the wall."
     log
end repeat

log "No more bottles of beer on the wall, no more bottles of beer."
log "Go to the store and buy some more, 99 bottles of beer on the wall."

on bottles(beer)
     if beer = 0 then
 	        return "no bottles"
     else if beer > 1 then
 	        return (beer as string) & " bottles"
     else
 	        return "1 bottle"
     end if
end bottle


51. Applesoft BASIC — Бейсик разработанный компанией Майкрософт, использовавшийся в компьютерах фирмы Эпл, начиная с 1977 года. Ничего особо примечательного, вполне полнофункциональный язык, хоть и не без недостатков тех ранних «бейсиков» — вызов функций по номеру строки, параметров нет, локальных переменных нет, в общем, все прелести.

Я специально использовал конструкцию «ON…GOTO», хоть в ней и не было необходимости — хотел показать аналог конструкции switch. SGN тут — встроенная функция нахождения знака выражения.

000 REM Песня про пиво, автор — Евгений Степанищев
001 HOME: TEXT
010 FOR beer = 99 TO 1 STEP -1
020 b = beer: GOSUB 100
030 ? bottles$; " on the wall, "; bottles$; "."
035 b = beer - 1: GOSUB 100
040 ? "Take one down and pass it around, "; bottles$; " of beer on the wall."
050 ?
060 NEXT
070 ? "No more bottles of beer on the wall, no more bottles of beer."
080 ? "Go to the store and buy some more, 99 bottles of beer on the wall."
090 END
099 REM
100 ON SGN(b-1)+2 GOTO 120, 130, 110
110 LET bottles$ = STR$(b) + " bottles": RETURN
120 LET bottles$ = "no bottles": RETURN
130 LET bottles$ = "1 bottle": RETURN
2014   99   applescript   программирование

Как мне AppleScript

Миша, мой коллега, под постом, где я написал про интерпретатор Brainfuck на AppleScript с беспокойством осведомился, не подсел ли я на AppleScript.

Краткий ответ: нет.

Длинный ответ таков.

Мне непонятно текущее состояние AppleScript. Очевидно, что концепция «пользователь пишет программы на английском» вполне себе ничего. Для непрограммистов это был бы выход. А для программистов такой язык жутко неудобен. По понятными причинам. Программисты привыкли писать много, а на велиречивом AppleScript писать не каждому понравится (не пишу «никому», потому что существует же Objective C). Все эти сокращения в языке — «собачки», доллары и разные скобочки, их нужно помнить, но они здорово экономят время при написании программы и при её чтении. Потому что они короткие и их больше помещается на экране.

Текущее состояние непонятно, потому что цели своей авторы языка не достигли. До сих пор нужно писать «set Var to Var + 1» и нельзя «set Var to Var plus one», какой же это английский? При этом язык уже полтора года не развивается.

То есть проблема «иногда быстро писать мелкие программы» явно пока не решена. Так как для этого «иногда» надо иметь память робота, чтобы вспомнить через несколько месяцев как в этом языке что-то проверить или присвоить. Сам язык ещё ничего, запоминающийся. Но если хочется чего-то чуть более серьёзного, то приходится обращаться к специальному API.

Например, чтобы вызвать текстовый редактор и написать там «Hello», нужно сделать следующее:

tell application "TextEdit"
    activate
    tell application "System Events" to keystroke "hello"
end tell

Я открываю приложение TextEdit (так встроенный в MacOS текстовый редактор называется) и прошу System Events понажимать в редактор несколько клавиш. Выделенное наклонным — это как раз пример такого API. Причём, если написать в кавычки «³˚¹⁄₃¹⁄₂», то программа либо не сделает ничего, либо напечатает в редактор ерунду. Всё устроено так, как будто System Events действительно жмёт клавиши на клавиатуре. А вот эти символы система не знает как ввести. То же будет с китайскими иероглифами, например.

Ещё, когда я только начал знакомиться с AppleScript, у меня было заблуждение, что AppleScript — язык общего назначения, вроде PowerShell или Bash. Ничего подобного, вместо bash в Mac OS есть bash. AppleScript толком не умеет даже получить данные от пользователя, скажем перехватить нажатие клавиши (и нет ничего встроенного, что может ему в этом помочь), а уж о том, как мало он умеет самостоятельно выводить и говорить нечего.

Так что симпатии к языку у меня не появится, думаю. Не для меня он. Но писать я на нём иногда буду, я и на CMD много странного писал в своё время.

2011   applescript   программирование

Brainfuck на AppleScript

Для развлечения написал интерпретатор языка Brainfuck на AppleScript.

Особенности: для ввода программы используется текстовое поле, которое выглядит как однострочное, но на деле понимает многострочный текст. Операция «,» у меня с буфером, это значит, что при запросе можно вводить сразу строку, при надобности интерпретатор сам будет брать оттуда значения. Если их не хватит, запросит ещё.

AppleScript медленный язык, следовательно и получившийся интерпретатор медленный (никаких оптимизаций я не делал), лучше всего на нём запускать небольшие программы. Например, «Hello, world»:

++++++++[>+++++++++<-]>.<+++++[>++++++<-]>-.+++++++..+++.< 
++++++++[>>++++<<-]>>.<<++++[>------<-]>.<++++[>++++++<-]> 
.+++.------.--------.>+.

Не уверен, что нигде не ошибся (всё-таки время четвёртый час ночи), но даже вложенные циклы на программах, которые я прогонял, работают нормально. 

#!/usr/bin/osascript
-- AppleScript Brainfuck interpreter
-- Evgeny Stepanischev 1 Jul 2011 http://bolknote.ru

set Cells to {}
repeat 30000 times
    set end of Cells to 0
end repeat

display dialog "Enter your BF program" default answer ""
set Bf to text items of text returned of result

set Pointer to 1
set CmdPointer to 1
set Output to ""
set Input to {}
set Stack to {}

repeat while CmdPointer ≤ (count of Bf)
    set Cmd to item (CmdPointer) of Bf
    
    if Cmd is equal to ">" then
        set Pointer to Pointer + 1
        if Pointer > 30000 then set Pointer to 1
        
    else if Cmd is equal to "<" then
        set Pointer to Pointer - 1
        if Pointer < 1 then set Pointer to 30000
        
    else if Cmd is equal to "+" then
        set Val to (item (Pointer) of Cells) + 1
        if Val is greater than 255 then set Val to 0
        copy Val to item (Pointer) of Cells
        
    else if Cmd is equal to "-" then
        set Val to (item (Pointer) of Cells) - 1
        if Val is less than 0 then set Val to 255
        copy Val to item (Pointer) of Cells
        
    else if Cmd is equal to "." then
        set Output to Output & (ASCII character item (Pointer) of Cells)
        
    else if Cmd is equal to "," then
        if Output ≠ "" then
            display alert Output
            set Output to ""
        end if
        
        if Input is {} then
            display dialog "Enter one or more characters" default answer ""
            set Res to text returned of result
            
            set item (Pointer) of Cells to ASCII number of character 1 of Res
            
            try
                set Input to rest of text items of Res
            end try
            
        else
            set item (Pointer) of Cells to ASCII number of item 1 of Input
            set Input to rest of Input
        end if
        
    else if Cmd is equal to "[" then
        if item (Pointer) of Cells = 0 then
            set CmdPointer to CmdPointer + 1
            set Deep to 1
            
            repeat while CmdPointer ≤ (count of Bf)
                set Cmd to item (CmdPointer) of Bf
                
                if Cmd is equal to "[" then
                    set Deep to Deep + 1
                else if Cmd is equal to "]" then
                    set Deep to Deep - 1
                    
                    if Deep is 0 then exit repeat
                end if
                
                set CmdPointer to CmdPointer + 1
                
            end repeat
        else
            set Stack to (CmdPointer - 1) & Stack
        end if
    else if Cmd is equal to "]" then
        if item (Pointer) of Cells ≠ 0 then
            copy item (1) of Stack to CmdPointer
        end if
        set Stack to rest of Stack
    end if
    
    set CmdPointer to CmdPointer + 1
end repeat

if Output ≠ "" then
    display alert Output
end if
2011   applescript   bf   программирование

Запуск сайтов из Spotlight

Занялся потихоньку автоматизацией своих задач на «Маке».

Очень хотелось сделать так, чтобы по короткому имени, набираемому в Spotlight, открывались какие-то сайты. В интранете «Яндекса» очень много сайтов, которые я открываю десятки раз за день, вот я и подумал — было бы круто, если бы я набирал в Spotlight «wiki», нажимал Enter и мне открывался бы сайт wiki.yandex-team.ru.

Я знаю, что тут много ребят сидят на Windows, поэтому поясню. Spotlight (его аналог) есть на Windows 7 — это поисковая строка, куда можно вбить начальные буквы какого-то названия, а операционная система предложит к запуску список программ и/или файлов, имена или метаинформация которых наилучшим образом подходит под заданную пользователем строку.

В Mac OS X удобно, что это поисковое поле появляется по кнопке Ctrl+Пробел.

Написать плагин к Spotlight мне пока слабо́, а вот набросать на AppleScript программу, открывающую одноимённый сайт — пожалуйста.

Идея такова: кладу на диск одну и ту же программу в нескольких копиях, с разными именами — «wiki», «jira», «calendar» и так далее. Теперь их можно запускать откуда угодно, включая Spotlight. После запуска программа смотрит какое у неё имя, прибавляет к нему префикс/суффикс и проверяет существует ли получившийся домен, если да, то открывает его в браузере.

Программу надо сохранить при помощи редактора AppleScript (AppleScript Editor) с нужным именем (↑⌘S), формат файла должен быть «программа» («application»).

Вот сама программа (наверное маску «%.yandex-team.ru» нужно будет убрать, она вам будет мешать, если вы только не работаете в «Яндексе»):

-- Ищет по маскам хост, подставляя имя программы,
-- если найден, открывает его в браузере
-- bolknote.ru апрель 2011

tell application "System Events"
    set appname to the name of the current application
end tell

repeat with mask in {"%.yandex-team.ru", "www.%.ru", "www.%.com"}
    set AppleScript's text item delimiters to "%"
    set mask to text items of mask
    set AppleScript's text item delimiters to appname
    set hostname to "" & mask
    
    if resolve(hostname) then
        open location "http://" & hostname
        exit repeat
    end if
end repeat

on resolve(host)
    try
        do shell script "host '" & host & "'"
        return true
    on error
        return false
    end try
end resolve

Это пока мой первый опыт программирования в AppleScript, так что, возможно, какие-то вещи можно было сделать проще, напишите я поправлю.

Добавлено позднее: в комментариях один из читателей предложил более простой и изящный вариант. Ура!

2011   applescript   mac   программирование