Всякие разные штуки сомнительной полезности сделанные в свободное от работы время.
Велосипед с гиперкубическими колесами
Для того, чтобы кататься в четвертом измерении, естественно.
Четвертое измерение можно воспринимать как своего рода надстройку над известными нам тремя (геометрическими, речь не идет о времени, как измерении, но, если ты хочешь, любознательный читатель, то ты можешь его тоже добавить).
Если вы посмотрите на написание программы, как на проектирование изображений в трехмерном измерении, то, создание программы, которая создает программы будет, по-своему, четвертым измерением. Написание программ которые пишут другие программы часто называют мета-программированием. Как правило, практические задачи решаемые мета-программированием сводятся к минимизации нудной работы - автоматизации создания служебного кода, который не интересно писать вручную. Но, это всего лишь маленькая толика тех задач, которые способно решать мета-программирование. В любой не-тривиальной программе направлений на получение ввода от пользователя (а не другой программы) рано или поздно возникает задача автоматизации. А именно:
- Автоматическое тестирование, которое включает имитацию действий которые мог бы совершить пользователь во время работы с программой.
- Макросы, самый минималистический макрос - запись всех действий пользователя и воспроизведение их в необходимом порядке (undo). Но помимо этого программа может предоставлять интерфейс для выполнения последовательностей операций ее составляющих. Наверняка вы знаете о том, что во Flash IDE есть JSFL, в FlashDevelop можно писать на C# Script, для Эклипса есть множество разных вариантов, включая реализации Lua, Python и Lisp на Java.
То, что я сейчас делаю, и собираюсь вам показать, это совсем только начало, наверняка с кучей недостатков и будет еще сто раз переделываться, но, тем не менее результаты кое-каие уже есть. Начал я этим заниматься не совсем от безделья, а по причине невозможности как-то автоматизировать тестирование приложения, написанного задолго до того, как меня приняли на работу и работающего из рук вон плохо... Естественно, приложение не маленькое, и написанное в худших традиция AS3 "фреймворков" с кучей синглтонов и кросс-референсов, которые делают модульное тестирование невозможным. Но нужно латать дыры и в уже существующей программе, потому, что переписать всю, за короткое время нет возможности. Латание дыр уже само по себе муторная задача, но гораздо тяжелее отыскивать нештатным ситуации. Это, в том числе связано с тем, что какие-то ситуации могут происходить только по истечении значительного количества времени, или, активная продолжительная деятельность пользователя может внезапно что-то сломать - посадить программиста воспроизводить баг, на воспроизведение которого нужно более часа - расточительство, да и сама программа так никогда не будет написана.
Вот так и появилась идея написания скриптового языка, который бы мог управлять любой программой, ну, на сколько это возможно. А еще лучше с возможностью передачи команд в реальном времени, изменении хода программы во время выполнения и, желательно, полностью автономного, т.е. такого, что не нужно было бы менять существующий код, для того, чтобы его можно было тестировать.
Ниже - буквально один из первых тестов. Язык очень похож на Лисп, но хуже и мало чего еще умеет, но я надеюсь по-тихоньку расти и совершенствовать. Одной из интересных задач было написание декларации функций. В процессе я понял "зачем", например, в PHP существует global для аргументов. Даже не сколько "зачем", а почему это плохо и очень примитивно с точки зрения языкостроительства
Исходники тут: http://code.google.com/p/e4xu/source...s%2Fautomation
Пример, вот он:
package examples { import flash.display.Sprite; import org.wvxvws.automation.ParensParser; public class TestZinScript extends Sprite { private var _parser:ParensParser = new ParensParser(); public function TestZinScript() { super(); this.test(); } private function test():void { // Makes all public methods accessible by names so // we don't need to use utils:slot-value all the time. this._parser.pushContext(this, "test-runner"); this._parser.read( <![CDATA[ (test-runner:testMethod) (test-runner:testMethodWithParameters 100 "Hello from outer space") (utils:print "Running ZinScript, highly experimental version...") (lang:defvar "test-var" 42) (utils:dotimes (math:- 2 1) (lang:getvar "time:interval") ; This is not ideal, will need to pass a list instead (lang:getvar "test-runner:testMethodWithParameters") 1000 (lang:setvar "test-var" (math:++ (lang:getvar "test-var"))) "Hello...") (utils:dotimes (math:+ 0 2 -10 9) (lang:getvar "time:timeout") ; Just same as above, need body form for loops and functions (lang:getvar "test-runner:testMethod") 1000 5) (bool:if (math:> (math:random 10) 5) (utils:print "Executing true condition") (utils:print "Executing false condition")) ; This is line comment (utils:print (string:+ "foo" "|" "bar")) (lang:defun "test-function" (lang:arguments "param1" "param2") (lang:body-form '(utils:print param1) '(utils:print param2))) (utils:print (lang:getvar "test-function")) (test-function "hello" "world") (lang:defpackage "tests") (lang:inpackage "tests") (lang:defvar "test-sprite" (lang:new (utils:resolve-class "flash.display.Sprite"))) (lang:defvar "graphics" (utils:slot-value (lang:getvar "test-sprite") "graphics")) (utils:funcall (utils:slot-value (lang:getvar "graphics") "beginFill") 255) (utils:funcall (utils:slot-value (lang:getvar "graphics") "drawRect") 0 0 100 200) (utils:funcall (utils:slot-value (lang:getvar "graphics") "endFill")) (test-runner:addChild (lang:getvar "test-sprite")) (utils:print "debugging..." (lang:package) (utils:slot-value (lang:package) "name")) (utils:print "moar..." (lang:getvar "test-sprite") (lang:getvar "tests:test-sprite")) (lang:extern "test-sprite") (lang:defun "test-handler" (lang:arguments "event") (lang:body-form '(utils:print "I am an enterFrame handler" event) '(utils:set-slot (lang:getvar "tests:test-sprite") "x" (math:++ (utils:slot-value (lang:getvar "tests:test-sprite") "x"))))) (test-runner:addEventListener "enterFrame" (lang:getvar "test-handler")) ]]>.toString()); } public function testMethod():void { trace("I am testMethod"); } public function testMethodWithParameters(foo:int, bar:String):void { trace("I am testMethodWithParameters", foo, bar); } } }
Код:
I am testMethod I am testMethodWithParameters 100 Hello from outer space Running ZinScript, highly experimental version... Executing false condition foo|bar function Function() {} hello world debugging... [object ParensPackage] tests moar... [object Sprite] [object Sprite] I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2] I am testMethodWithParameters 43 Hello... I am testMethod I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2] I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2] I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2] I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2] [SWF] /home/wvxvw/Projects/e4xu/bin-debug/TestZinScript.swf - 36,318 bytes after decompression I am an enterFrame handler ... eventPhase=2] I am testMethodWithParameters 43 Hello... I am testMethod I am an enterFrame handler ... eventPhase=2] I am testMethodWithParameters 43 Hello... I am testMethod
Всего комментариев 6
Комментарии
16.04.2011 16:29 | |
Вы, Батюшка, знаток извращений
|
16.04.2011 18:12 | |
Похожая проблема - после предыдущего проекта в стиле "добавляем фитчу - появляются 2 бага, которые выявляются спустя пару дней - латаем - добавляем 3 бага и т.д."
Т.к. в юнит-тестировании до сих пор много чего не понимаю, а слово "интеграционный тест" повергает в ужос - решил налепить скриптовый движок на клиент (на базе hscript). Но из-за отсутвия опыта применение нашлось только одно - автоматизация читов. Ну т.е. чтобы каждый итем не добавлять в инвентарь по отдельности - пишем скрипт "добавить 1000 итемов в инвентарь" и т.д. Чтобы в коде клиента не мусорить. Со скриптом в тестировании непонятны следующие моменты: - как лучше протащить в парсер доступ ко всем данным из модели? - как обеспечить парсер возможностью заставить клиент сделать всё что надо? - как обеспечить парсер отслеживанием всех нужных событий на клиенте? Всмысле как это сделать, не изуродовав код клиента и при этом не сильно завязавшись на струкруру клиента. Ведь порефакторили клиент - скрипты поотвалились (а ведь они, сцуко, не типизированные - компилятор не ткнет даже где надо поправить) wvxvw, можете просветить, или ссылку на статью какую-нть дать? P.S. Надо же, по сравнению с hscript (который еще пришлось заставлять работать в виде либы swc для as3) код парсера так просто выглядит P.S.2. А, это я на импорты еще не посмотрел - скока там всего используется. |
|
Обновил(-а) expl 16.04.2011 в 18:19
|
17.04.2011 03:22 | |
мне кажется было бы логичнее использовать хурлантовский движок для написания скриптов (т.е. as3)
|
17.04.2011 10:30 | |
Да, я думал об этом, и, в принципе, это могло бы быть решением. Но есть еще такой момент как NIH syndrome. Кроме того AS3 не очень подходит для консоли, кроме того, поскольку он скомпилируется во все тот же AS3 байткод, то выполнять его будет сам плеер, а это значит, что нужно будет жить по правилам плеера - например, нельзя переопределить класс или функцию в классе и т.п. Не то чтобы критично, конечно.
|
Последние записи от wvxvw
- Dired - текстовый проводник по файловой системе (29.06.2013)
- Навигация по HTML с WASD (09.06.2012)
- JavaScript, все не так плохо (07.06.2012)
- Что такое tarball и чем его пакуют (11.04.2012)
- Критика Presentation Model (18.02.2012)