Hi everybody
Is it possible to get a list of handlers from a scriptserver or/and library?
Thanks
Browser: Safari 531.21.10
Operating System: Mac OS X (10.6)
Hi everybody
Is it possible to get a list of handlers from a scriptserver or/and library?
Thanks
Browser: Safari 531.21.10
Operating System: Mac OS X (10.6)
Hello
At least you can have a list inside the ScriptServer/Library with its handlers.
I think you could have a handlder wich decompiled at least a copy of the scriptserver, to text, so that you could
use grep or similar to get that list of handlers.
Best Regards
McUsr
Thank you very much
This is what I do now during dev, but its not very charming.
Pitty that Applescript Editor doesn’t have a way to let it execute an applescript to read that current file in the editor and add some line’s on that scpt file, or does it?
Is there a sample of this or some writings on the matter? I couldn’t find anything then just a closed applescript app or a small textual part in Applescript book from O’Reilly
Thanks again.
Hello
Try googling for “AppleScript+Decompile” but you really shouldn’t have to do that now.
I think I found an easy way to do this, thinking that that Decompile method now was a remnant of the past. That is some months ago, and I never used it, so the method I thought I found didn’t “stick” with me.
One way (with some pain) is to edit .AppleScript files (text files) and use osacompile to convert them to scripts. At least some versions of Vi, and Xcode to (I think I used Xcode to edit .AppleScript files) have color syntax support for .AppleScript and you can certainly to the same with both ScriptEditor. I believe you can do it with ScriptDebugger too.
So the options are there if you care to step one step back, and work on text files that you then compile to the library. That way you should be able to automate the generation of the handler list for the script server via shell tools.
Yeah: And I want your shell scripts when they are tested:
Best Regards
McUsr
I think I check for the ‘Applescript editor’ service menu, and do a copy all, a handle collect action and then replace the selection with code to supply back the table.
XCode is also possible but then I have to create every time a obj-c/applescript project. Not knowing about XCode4.
Hello
It is really easy with Xcode, you are going to edit that script server for some time anyway (maybe).
At least for stuff like a script server or bigger lasting projects environments like Xcode really shines.
You can just reopen and continue where you left without further ado.
Xcode is much easier to use than you think.
You just create an empty project in Xcode under the “other” tab, nothing more difficult than that.
Then you just fill in with a new AppleScript.class, which makes a new .applescript file for you.
The IDE is also great for setting up workflows for extracting the handler names and such.
It would be cool to have your service for extracting handlers though.
Best Regards
McUsr
XCode, I use that often. But I don’t like things in my project that I don’t need.
To do things atomically, I have 15 or so scripts under the script menu.
Maybe I should create a template with just one .applescript file.
The only value I see here is that I can put it under SubVersion because its text .applescript and create a build action wich will/can do the table creation.
But on the otherhand, I use my own subversion scripts and app instead of Xcode’s, because of version differences. I save it as text from AppleScript Editor. But maybe I change tomorrow, easesly.
Good Night
It is nice to have nice environments. Dedicated but not many options and jitter.
Well I used to use RCS under Tiger, it was real pain, getting the encodings right, had to deal with three different.
RCS is Ok when you are just one guy on your own project. - Otherwise I prefer Github (But Subversion is ok).
Back to RCS: With the encoding scheme under Leopard and Snow Leopard that should work like a real breeze, so I guess I’l have to fix the Tiger version, I think I can fix the scripts to work without any flaws there with what I now know, fix the script for Snow Leopard, and XCode, it would also have been great to use it in Script Debugger.
Best Regards
McUsr
a quickie
osadecompile will decompile scripts to text.
Thanks
I knew I had seen something like that somewhere. How odd that I didn’t find it among the references and in the “see also” section when I perused the man pages of osascript, osalang and osacompile yesterday.
I will file that as a bug
Best Regards
McUsr
I know I am quite late in this, but this may help others.
I found the original script while searching for something else and had to “modernize” it to work in today’s level of software. I have Snow Leopard. You will see the attribution in the comments for the original source of the script and about 99% of what is there now. The change I did was to use osadecompile.
It will decompile a script and list out its handlers. I am working on a means of taking this basic functionality and providing a tool for use while scripting.
property |version| : 1.0
property author : "Pescados Software · [url=http://homepage.mac.com/julifos/soft]http://homepage.mac.com/julifos/soft"[/url]
property |date| : date "Sunday, March 21, 2004 12:00:00 "
property license : "freeware, open-source"
set thisScript to choose file
set thePath to thisScript as text
set poxPath to POSIX path of thisScript
set qfPoxPath to quoted form of poxPath
stop log
set scptScript to do shell script "/usr/bin/osadecompile " & qfPoxPath & "| /usr/bin/tr -d '\\000'"
start log
set scptAlias to thisScript
set myCode to scptScript
set allHandlers to {}
set entireHandlers to {}
--> look for handlers in first line
set firstLine to myCode's paragraph 1
set ignoreFirstChunkOn to true
set ignoreFirstChunkTo to true
if firstLine starts with "on " then
set ignoreFirstChunkOn to false
else if firstLine starts with "to " then
set ignoreFirstChunkTo to false
end if
--> extract "on" handlers
set AppleScript's text item delimiters to return & "on "
set onHandlers to myCode's text items
set AppleScript's text item delimiters to {""}
if ignoreFirstChunkOn then
set onHandlers to rest of onHandlers
else
set item 1 of onHandlers to (text 4 thru -1 of (item 1 of onHandlers))
end if
repeat with i in onHandlers
set i to i's first paragraph
set x to ((offset of "(" in i) - 1)
if x = -1 then set x to ((offset of " " in i) - 1)
set end of allHandlers to text 1 thru x of i
set end of entireHandlers to "on " & i
end repeat
--> extract "to" handlers
set AppleScript's text item delimiters to return & "to "
set toHandlers to myCode's text items
set AppleScript's text item delimiters to {""}
if ignoreFirstChunkTo then
set toHandlers to rest of toHandlers
else
set item 1 of toHandlers to (text 4 thru -1 of (item 1 of toHandlers))
end if
repeat with i in toHandlers
set i to i's first paragraph
set x to ((offset of "(" in i) - 1)
if x = -1 then set x to ((offset of " " in i) - 1)
set end of allHandlers to text 1 thru x of i
set end of entireHandlers to "to " & i
end repeat
-- «constant afdregfp» is "frontmost application"
--> Ok, handlers loaded
if allHandlers = {} then --> empty list
tell application (path to «constant afdregfp» as text) to display dialog "No handlers in \"" & name of (info for scptAlias) & "\"!" buttons {"OK"} default button 1 with icon note
else
tell application (path to «constant afdregfp» as text) to set userInput to (choose from list allHandlers with prompt "Available handlers in \"" & name of (info for scptAlias) & "\"" OK button name "To Clipboard" with multiple selections allowed and empty selection allowed)
if userInput ≠false and userInput ≠{} then
set stuffToClipboard to {}
repeat with i in userInput
set itemIndex to my IndexOfItem(i, allHandlers)
set theHandler to allHandlers's item itemIndex
set handlerEnd to "end " & theHandler
set handlerContents to text (offset of (entireHandlers's item itemIndex) in myCode) thru ((offset of handlerEnd in myCode) + (handlerEnd's length) - 1) of myCode
set end of stuffToClipboard to handlerContents & return
end repeat
set the clipboard to (text 1 thru -2 of (stuffToClipboard as string))
end if
end if
--end listHandlers
set current_Handler to {}
set foundHandler to false
repeat with counter1 from 1 to count paragraphs of myCode
set currentPara to paragraph counter1 of myCode
if currentPara starts with "on" or currentPara starts with "to" then
set x to ((offset of "(" in currentPara) - 1)
set end of current_Handler to return & "Handler: " & (text 4 thru x of currentPara) & ": " & tab
set foundHandler to true
else if foundHandler is true then
if currentPara does not start with "on" and currentPara does not start with "to" then
repeat with counter2 from 1 to length of allHandlers
if currentPara contains item counter2 of allHandlers and currentPara does not contain "end" then
set end of current_Handler to item counter2 of allHandlers
end if
end repeat
end if
end if
end repeat
log current_Handler
on IndexOfItem(theItem, theList) -- credits Emmanuel Levy
set text item delimiters to return
set theList to return & theList & return
set text item delimiters to {""}
try
-1 + (count (paragraphs of (text 1 thru (offset of (return & theItem & return) in theList) of theList)))
on error
0
end try
end IndexOfItem
Hello.
Here is a humble contribution to that script, it is really made by Nigel Garvey.
If you use this instead of the “dreaded date literal” then It can compile everywhere without any editing.
on setDateAndTime(intYear, intMonth, intDay, int23Hour, int59Minutes, int59Seconds) -- fixed by Nigel Garvey
tell (current date)
set {day, year, its month, day, time} to {1, intYear, intMonth, intDay, int23Hour * hours + int59Minutes * minutes + int59Seconds}
return it
end tell
end setDateAndTime
The date property looks so much better this way:
property |date| : ""
” first in the implicit run handler:
set |date| to setDateAndTime(2004,3,21,12,0,0)
Edit: How about adding it to my ScriptLIbrary loader?, It would be neat to have all the handlers from
library files into a database events database. I event think or are eager to find out if you can store a file alias in that database, -(the HFS id of the file). (That would be nice) only thing one had to do was some folder actions to update the contents of the database when items are added to and deleted from the library folder. And a cron job or similar to check for changed files.
And some LIbraries comes with documentation. It would have been nice to have access to that documentation from within the script Editor, by some smaller Safari window.
Think about it, and please test the ScriptLibraryLoader in the mean time.
It is by no means finished yet, but it does what it does in reasonable manner.
Hello
This is a worthy case for some scripting . It is not straight forward to get right. First of all: you can have handlers within script objects and script objects within script objects with handlers within which can contain script objects which can contain handlers which can contain . -That was a little bit off, but you get what I mean.
One can’t handle all situations. For starters it should suffice to list every handler which is either declared at the top level, together with the top level script objects and their handlers (indented). in a list.
It should be possible to get to the direct position of a wished for handler in that file in an editor.
It should also be possible to paste the full declaration of the handlers signature to the clipboard.
This is not that a heavy weight process, but it takes its toll. the best thing would have it stored somewhere, so it just can look it up, and keep library files locked, until they are to be edited, not so much to ensure their contents, more to ensure that unnecessary file updating don’t take place.
I have played a little bit with Julio’s script, and it doesn’t work that well, when it comes to script servers and the like, where one usually would want to have several script objects containing handlers, due to the fact that the handlers are indented by tabs.
”>What do we do if we have one single script object in a file containing everything?
” I suggest we leave that script object on the outside of the list, together with the filename.
If there are several script objects, then a tree structured view by indentation of a list box would be more appropriate. Maybe it would be convenient with a contracted view as well, with only the script object, if the scriptLibrary is a huge server.
For starters it would be nice to just come up with an “on the fly” solution, which tackles the top level entities and
any handlers within those entities, which should be challenging enough in its own right. So challenging given the tools applescript provides us, so that I will take “a stroll” and have a look for some solutions that may exist within the AppleScript community.
There would nee to be a database created on the fly with records containing… Type (Script, handler), Name(…), start paragraph(…), end paragraph(…), and possibly “parent” or “contained by” and then an associated list that includes what handlers or scripts this is referenced in. I would love to have the time then to set the script to automatically output this to OmniGraffle to draw a representation of the structure of a script. Would be useful for complex ones.
Anyway, I will craft up a draft script using the database and try it. Should be able to have recursive routines to drill down as far as needed to get scripts and or handlers.
John
Hello.
Yes, OmniGraffle would be it, but then it wouldn’t be for everybody, I hadn’t thought of that yet. But if you generate output for Omni Graffle,then you could also generate for dot and graphwiz, which are free - not alternatives to Omni Graffle (I own it - and it’s great.)
I was hoping to avoid writing a full blown parser in Apple Script So I’m more in for stripping all comments,
parsing the the top level handlers and script objects, then extracting handler within each script object, and see how usable that is, or what I want once I have gotten it that far.
I basically want to be able to paste in handler signatures into my scripts. There would also be nice to be able to get up a documentation page for a script object, but that can wait.
When it eventually comes to database events, then I at least want to have one or two categories with each script object, the containing file, (I think relations is a rather manual affaire in Database Events But I still want one file with file relevant info, and such. If a handler isn’t contained within a script object, then the file is the script object.
-And that is an ambiguity.
I’ll restrain myself, and check out how easy it is to build that cheap prototype on - the fly solution, which may or may not be usable.
Here is rev 5 (edited over 3 & 4).
Rev 5: added branch processing (it has to be worked on as duplicate branches show up in complex scripts)
Rev 4 adds timing and lines per sec calc for those interested.
Rev 3: Includes some bug fixes I ran into… flagged in the comments where done
Includes a handler to strip all comments that are mixed on lines with code, including the start of multi-line comments that start on a line of code. Is ‘smart enough’ to understand double quotes to ensure no quoted comment flags are detected.
The code now includes the To Do list as well, which can be tracked to see what is to be done.
-- Based on the initial concept of "Pescados Software · [url=http://homepage.mac.com/julifos/soft]http://homepage.mac.com/julifos/soft"[/url] in "explore Handlers"
(*
Work to be done...
1. Be able to make a map of the processes and their inter-reationships
2. Tracking and listing of Handlers and scripts in Libraries referenced under Tell blocks
3. Possible output to OmniGraffle to get a drawing of the handler/script layout map
4. Look into merging the comment elimination handlers (full line and partial line comments)
Changes versus rev 4
1. Added logic to ensure full line comments eliminated if in-line comment elimination is wanted
2. Misc comments added regaring passing information at the end of processing to another script or application
3. Added length variables for databases
4. One database added, contains the data based on the 'containing' routine, line number of instance, contained routine
5. Added searchForBranches handler to get all routine branches (delivers some duplicates that need to be addessed when handlers are cross-referenced and are in scripts that are in handlers) NOTE: this slows down the processing depending on number of routines and branches
Changes versus rev 3
1. Eliminated out of date comments
2. Modified attribution to reduce potential for confusion between scripts
3. Added timing to the script with a report to log. Note I get a crash using the timing script as set up by McUsr, see my work around
4. Made 'log of script stripped of all comments' a comment. This could be used to ship uncommented code out, if wanted
Changes versus rev 2
1. Bug fixes as flagged
2. Added handler to get rid of any comments at the end of lines, requires the eliminate comments value must be set to true
*)
------------------------------------------------------------------------------------------------
global HandlerDatabase
global ScriptDatabase
global CommentsDatabase
global EliminateCommentLines -- changed the name in rev 3 to this from EliminateComments
global EliminateCodeLineComments -- added rev 3 to get comments at end of lines of code
global ContainerDatabase -- added rev 5 to get the list of handlers branched form handlers...
global GetBranches
----------
-- TIMING Script
script timeTools
-- © McUsr 2010
-- property parent : AppleScript
property getMillisec : missing value
on firstMillisec()
local overhead
my realgetMillisec() -- gets stuff loaded into mem for later, this first call takes longer time.
set my getMillisec to my realgetMillisec
set _overhead to -(my realgetMillisec()) + 2 * (my realgetMillisec()) -- Nigel Garvey
return _overhead
end firstMillisec
on realgetMillisec()
local res
set res to do shell script "/Library/UnixApps/timetools -ums"
-- you must change path to your hardcoded path to timetools,
return res
end realgetMillisec
--on run
set my getMillisec to my realgetMillisec -- changed versus McUsr original (was: firstMillisec)
--end run
end script
------ items to modify to adjust results, could be set via dialogs
set EliminateCommentLines to true -- include comments increases procesing, try it, returns a list of all comments, the longer=slower
set EliminateCodeLineComments to true -- eliminates in-line comments that share a line with code
set GetBranches to true -- find referenced branches to handlers and scripts inside handlers and scripts
if GetBranches is true then
set EliminateCommentLines to true -- need all comments turned into blanks as a reference in a comment may cause a false match
set EliminateCodeLineComments to true
end if
------
set thisScript to choose file -- get the script to check
set poxPath to POSIX path of thisScript
set qfPoxPath to quoted form of poxPath
stop log
set ScrptCode to do shell script "/usr/bin/osadecompile " & qfPoxPath & "| /usr/bin/tr -d '\\000'" -- gets script and decompiles
start log
----------
set HandlerDatabase to {}
set ScriptDatabase to {}
set CommentsDatabase to {}
set db3 to {}
set theCleanScript to ""
----------
-- HandlerDatabase: Handlers- theName:, Start:, Finish:, ContainedBy:
-- ScriptDatabase: Scripts- theName:, Start:, Finish:, ContainedBy:
-- CommentsDatabase: Lines as comments- Start:, Finish, Contents:
-- ContainerDatabase: Referenced entities- theContainer, theLine, theBranch, theType -- where a handler has code that branches to another handler, done rev 5
----------
-- T I M I N G C O D E
tell timeTools to run
set t to timeTools's getMillisec()
set StartTime to t
----------------------------------- this is the list of handlers used to process the file and parse the results
set theCodeLength to count paragraphs of ScrptCode
set theCleanScript to EliminateTabs(theCodeLength, ScrptCode) -- gets rid of leading tabs
set theCleanScript to ProcessCommentLines(theCodeLength, theCleanScript) -- do comment lines first
if EliminateCodeLineComments is true and EliminateCommentLines is true then
set theCleanScript to ProcessCodeLineComments(theCodeLength, theCleanScript)
-- do comments at end of lines, requires full line comments be eliminated first
end if
set counter1 to 1 -- tracks the paragraph being analyzed
ProcessScrptsHandlrs(theCleanScript, counter1, theCodeLength, "Implied Run")
-- create databases of handlers & scripts, implied run means at top level and no 'on run' handler
set countHandlers to length of HandlerDatabase
set countScripts to length of ScriptDatabase
if GetBranches is true then
searchForBranches(theCleanScript, theCodeLength, countHandlers, countScripts)
end if
-- ------- T I M I N G C O D E
tell timeTools to run
set t to timeTools's getMillisec()
set EndTime to t
set theTimeResult to (EndTime - StartTime) / 1000
------------
-- Added this to provide a review of results, of course these could be passed to other scripts...
if EliminateCommentLines is false then
log "-- Comments --"
log "-- count: " & length of CommentsDatabase
log CommentsDatabase
end if
log "-o-"
log "Time to complete: " & theTimeResult & " seconds"
log "Lines of code/comments analyzed: " & theCodeLength
log "Lines/sec: " & theCodeLength / theTimeResult
log "-o-"
log "-- Handlers -- "
log "-- count: " & countHandlers
log HandlerDatabase
log "-- Scripts -- "
log "-- count: " & countScripts
log ScriptDatabase
if GetBranches is true then
log "Containers"
log ContainerDatabase
end if
----------
on EliminateTabs(theCodeLength, ScrptCode) -- find the tabs within the code and eliminate them
local ParaCounter
set ParaCounter to 1
set theCleanScript to ""
repeat -- this is to strip out tabs and, if eliminate comments is true, to make blanks of any lines that are space or tab character
set PrecursorText to paragraph ParaCounter of ScrptCode
repeat
if PrecursorText ≠"" and PrecursorText ≠" " and PrecursorText ≠" " then
if character 1 of PrecursorText = tab then
set PrecursorText to (characters 2 through -1 of PrecursorText) as string
else
if PrecursorText starts with "on error" then set PrecursorText to "xx error"
set theCleanScript to theCleanScript & PrecursorText & return
exit repeat
end if
else
if EliminateCommentLines is true and (PrecursorText = " " or PrecursorText = " ") then --fixed ≠to =
set PrecursorText to ""
set theCleanScript to theCleanScript & PrecursorText & return
exit repeat
end if
if PrecursorText starts with "on error" then set PrecursorText to "xx error"
set theCleanScript to theCleanScript & PrecursorText & return
exit repeat
end if
end repeat
set ParaCounter to ParaCounter + 1
if ParaCounter > theCodeLength then exit repeat
end repeat
return theCleanScript
end EliminateTabs
----------
on ProcessCommentLines(theCodeLength, theCleanScript) -- and eliminate if flag set
local ParaCounter
-- find the comment lines within the code
set ParaCounter to 1
set ScrptCode to ""
repeat -- parse for comments, then make blanks if eliminate comments is true
if paragraph ParaCounter of theCleanScript starts with "--" then
saveDBRecord(ParaCounter, ParaCounter, (paragraph ParaCounter of theCleanScript)) --Params: Start line, Finish line, Comment
set ScrptCode to CommentElimination(ParaCounter, ScrptCode, theCleanScript) -- see if comments are blanked, then save-- fixed as was missing from rev. 2
else if paragraph ParaCounter of theCleanScript starts with "#" then
saveDBRecord(ParaCounter, ParaCounter, (paragraph ParaCounter of theCleanScript))
set ScrptCode to CommentElimination(ParaCounter, ScrptCode, theCleanScript) -- see if comments are blanked, then save
else if paragraph ParaCounter of theCleanScript starts with "(*" then
if paragraph ParaCounter of theCleanScript contains "*)" then
saveDBRecord(ParaCounter, ParaCounter, (paragraph ParaCounter of theCleanScript))
set ScrptCode to CommentElimination(ParaCounter, ScrptCode, theCleanScript) -- see if comments are blanked, then save
else
set StartLine to ParaCounter
set TempComment to paragraph ParaCounter of theCleanScript & return
set ScrptCode to CommentElimination(ParaCounter, ScrptCode, theCleanScript) -- see if comments are blanked, then save
repeat with counter1 from (ParaCounter + 1) to theCodeLength
if paragraph counter1 of theCleanScript contains "*)" then
set TempComment to TempComment & paragraph counter1 of theCleanScript & return
saveDBRecord(StartLine, counter1, TempComment)
set ParaCounter to counter1
set ScrptCode to CommentElimination(ParaCounter, ScrptCode, theCleanScript) -- see if comments are blanked, then save
exit repeat
else -- the multi-line comment continues
set TempComment to TempComment & paragraph counter1 of theCleanScript & return
set ScrptCode to CommentElimination(ParaCounter, ScrptCode, theCleanScript) -- see if comments are blanked, then save
end if
end repeat
end if
else
set ScrptCode to ScrptCode & paragraph ParaCounter of theCleanScript & return
end if
if ParaCounter = theCodeLength then exit repeat
set ParaCounter to ParaCounter + 1
end repeat
set theCleanScript to ScrptCode -- was missing in rev 2, added to speed results when comments eliminated
(* Code for use when debugging
if EliminateCommentLines is true then
log "Code without tabs and full line comments: " & theCleanScript
else
log "Code without tabs but retaining full line comments: " & theCleanScript
end if
*)
return theCleanScript
end ProcessCommentLines
----------
on ProcessCodeLineComments(theCodeLength, theCleanScript)
-- this will find and eliminate any comments at the end of lines of code
local ParaCounter
-- find the comments within the code and elimiinate
-- lines of comments must be blanked by this point
set ParaCounter to 1
set NewScript to ""
stop log -- skips bogus log entry for the ASCII value find, start it if wanted for debugging
repeat
set QuoteActive to false
set CommentActive to false
set MultiLineActive to false
set theActualCode to paragraph ParaCounter of theCleanScript
set TempComment to ""
-- only scan text that may have a comment, notice no need to look for end of multi-line comment as done later
if theActualCode contains "#" or theActualCode contains "--" or theActualCode contains "(*" then --skip if no comments
repeat with counter1 from 1 to ((length of theActualCode) - 1)
set item1 to character counter1 of theActualCode
if item1 = (ASCII character (34)) then -- if this is a set of quoted text then wait for it to end
if QuoteActive is false and CommentActive is false and MultiLineActive is false then
set QuoteActive to true
else
set QuoteActive to false
-- like a semaphore on/off switch to detect when there is quoted text that may include a comment flag inside of it
end if
end if
if QuoteActive is false then
if item1 = "#" then
if MultiLineActive is false then
set theActualCode to characters 1 thru (counter1 - 1) of paragraph ParaCounter of theCleanScript as string
exit repeat
end if
else if item1 = "-" or item1 = "*" or item1 = "(" then
if counter1 ≤ ((length of theActualCode) - 1) then
set item2 to characters counter1 thru (counter1 + 1) of theActualCode as string
if item2 = "--" then
if MultiLineActive is false then
set CommentActive to true
set theActualCode to characters 1 thru (counter1 - 1) of paragraph ParaCounter of theCleanScript as string
exit repeat
end if
else if item2 = "*)" then
error number 0 -- should never get here
else if item2 = "(*" then
set CommentActive to true
set MultiLineActive to true
set theActualCode to characters 1 thru (counter1 - 1) of paragraph ParaCounter of theCleanScript as string
repeat with counter2 from (ParaCounter + 1) to theCodeLength
if paragraph counter2 of theCleanScript contains "*)" then
set TempComment to TempComment & "" & return
set ParaCounter to counter2
exit repeat
else -- the multi-line comment continues
set TempComment to TempComment & "" & return
end if
end repeat
exit repeat
end if
end if
end if
end if
end repeat
end if
set NewScript to NewScript & theActualCode & return & TempComment
if ParaCounter < theCodeLength then
set ParaCounter to ParaCounter + 1
else
--log NewScript
-- turn this on to see the script stripped of all comments in the log (you may like to use this version of the script)
exit repeat
end if
end repeat
start log
return NewScript -- script now cleansed of all comments
end ProcessCodeLineComments
----------
on ProcessScrptsHandlrs(theCleanScript, counter1, theCodeLength, ContainingRoutine)
-- find the handlers and scripts within the code
repeat
if EliminateCommentLines is false then
repeat with counter2 from 1 to length of CommentsDatabase
if counter1 ≥ (Start of (item counter2 of CommentsDatabase)) and counter1 ≤ (Finish of (item counter2 of CommentsDatabase)) then
-- this is inside a comment, so skip this counter1 paragraph
exit repeat
end if
-- this is for post comment verification
set counter1 to DetermineRoutine(theCleanScript, counter1, theCodeLength, counter2, ContainingRoutine)
end repeat
else -- commented lines have been converted to blanks
set counter1 to DetermineRoutine(theCleanScript, counter1, theCodeLength, 1, ContainingRoutine)
end if
if counter1 = theCodeLength then
exit repeat
else
set counter1 to counter1 + 1
end if
end repeat
end ProcessScrptsHandlrs
------
on handlerTracker(theCleanScript, lineID, theCodeLength, CommentsDone, ContainerHandler)
local counter1, FoundEnd, db1Record
set db1Record to {theName:"", Start:"", Finish:"", ContainedBy:""}
set textLine to paragraph lineID of theCleanScript
set HandlerName to word 2 of textLine --as string -- leaves the 'on' and 'to' off and gets handler name
set theName of db1Record to HandlerName -- the handler's name
set db1Record's Start to lineID
set db1Record's ContainedBy to ContainerHandler
set FoundEnd to false
set counter1 to lineID + 1
repeat
if EliminateCommentLines is false then
repeat with counter2 from CommentsDone to length of CommentsDatabase
if counter1 ≥ (Start of (item counter2 of CommentsDatabase)) as integer and counter1 ≤ (Finish of (item counter2 of CommentsDatabase)) as integer then
-- this is inside a comment, so skip this counter1 paragraph
exit repeat
end if
if paragraph counter1 of theCleanScript starts with "script" then
-- this starts a script, send to script tracker
set counter1 to scriptTracker(theCleanScript, counter1, theCodeLength, counter2, HandlerName)
else if paragraph counter1 of theCleanScript starts with "On" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, counter2, HandlerName)
else if paragraph counter1 of theCleanScript starts with "To" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, counter2, HandlerName)
else if paragraph counter1 of theCleanScript starts with "end " & db1Record's theName then
set db1Record's Finish to counter1
set FoundEnd to true
exit repeat
end if
end repeat
else --Comments have been made blanks
if paragraph counter1 of theCleanScript starts with "script" then
-- this starts a script, send to script tracker
set counter1 to scriptTracker(theCleanScript, counter1, theCodeLength, 1, HandlerName)
else if paragraph counter1 of theCleanScript starts with "On" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, 1, HandlerName)
else if paragraph counter1 of theCleanScript starts with "To" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, 1, HandlerName)
else if paragraph counter1 of theCleanScript starts with "end " & db1Record's theName then
set db1Record's Finish to counter1
set FoundEnd to true
exit repeat
end if
end if
if FoundEnd is true then exit repeat
if counter1 = theCodeLength then
exit repeat
else
set counter1 to counter1 + 1
end if
end repeat
set the end of HandlerDatabase to db1Record
return counter1
end handlerTracker
------
on scriptTracker(theCleanScript, lineID, theCodeLength, CommentsDone, ContainerHandler)
local counter1, FoundEnd, db3Record
set db3Record to {theName:"", Start:"", Finish:"", ContainedBy:""}
set textLine to paragraph lineID of theCleanScript
set ScriptName to word 2 of textLine --as string -- leaves the 'on' and 'to' off and gets handler name
set theName of db3Record to ScriptName -- the handler's name
set db3Record's Start to lineID
set db3Record's ContainedBy to ContainerHandler
set FoundEnd to false
set counter1 to lineID + 1
repeat
if EliminateCommentLines is false then
repeat with counter2 from CommentsDone to length of CommentsDatabase
if counter1 ≥ (Start of (item counter2 of CommentsDatabase)) as integer and counter1 ≤ (Finish of (item counter2 of CommentsDatabase)) as integer then
-- this is inside a comment, so skip this counter1 paragraph
exit repeat
end if
if paragraph counter1 of theCleanScript starts with "Script" then
-- this starts a script, send to script tracker
set counter1 to scriptTracker(theCleanScript, counter1, theCodeLength, counter2, ScriptName)
else if paragraph counter1 of theCleanScript starts with "On" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, counter2, ScriptName)
else if paragraph counter1 of theCleanScript starts with "To" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, counter2, ScriptName)
else if paragraph counter1 of theCleanScript starts with "end script" then
set db3Record's Finish to counter1
set FoundEnd to true
exit repeat
end if
end repeat
else --Comments have been made blanks
if paragraph counter1 of theCleanScript starts with "Script" then
-- this starts a script, send to script tracker
set counter1 to scriptTracker(theCleanScript, counter1, theCodeLength, 1, ScriptName)
else if paragraph counter1 of theCleanScript starts with "On" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, 1, ScriptName)
else if paragraph counter1 of theCleanScript starts with "To" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, 1, ScriptName)
else if paragraph counter1 of theCleanScript starts with "end script" then
set db3Record's Finish to counter1
set FoundEnd to true
exit repeat
end if
end if
if FoundEnd is true then exit repeat
if counter1 = theCodeLength then
exit repeat
else
set counter1 to counter1 + 1
end if
end repeat
set the end of ScriptDatabase to db3Record
return counter1
end scriptTracker
----------
-- there are duplicate branch entries being found that need to be resolved
----------
-- Looking for branches to handlers and scripts within handlers/scripts
on searchForBranches(theCleanScript, theCodeLength, countHandlers, countScripts)
-- uses the globals: handlerdatabase and scriptdatabase for info on handler and script locations
set ContainerDatabase to {}
if countHandlers > 0 then
repeat with HandlerCounter1 from 1 to countHandlers
repeat with HandlerCounter2 from 1 to countHandlers
repeat with counter1 from ((Start of (item HandlerCounter2 of HandlerDatabase)) + 1) to ((Finish of (item HandlerCounter2 of HandlerDatabase)) - 1) -- Start+1 due to need to skip the first line of routine ('on Handler()'), finish -1 for similar reason
if paragraph counter1 of theCleanScript contains ((theName of (item HandlerCounter1 of HandlerDatabase)) as text item) and paragraph counter1 of theCleanScript does not contain (((theName of (item HandlerCounter1 of HandlerDatabase)) as text item) & "'s") then
set db4Record to {theContainer:theName of (item HandlerCounter2 of HandlerDatabase), theLine:counter1, theBranch:theName of (item HandlerCounter1 of HandlerDatabase), theType:"HinH"}
set the end of ContainerDatabase to db4Record
end if
end repeat
end repeat
if countScripts > 0 then
repeat with ScriptCounter2 from 1 to countScripts
repeat with counter1 from ((Start of (item ScriptCounter2 of ScriptDatabase)) + 1) to ((Finish of (item ScriptCounter2 of ScriptDatabase)) - 1) -- Start+1 due to need to skip the first line of routine ('on Handler()'), finish -1 for similar reason
if paragraph counter1 of theCleanScript contains ((theName of (item HandlerCounter1 of HandlerDatabase)) as text item) and paragraph counter1 of theCleanScript does not contain (((theName of (item HandlerCounter1 of HandlerDatabase)) as text item) & "'s") then
set db4Record to {theContainer:theName of (item ScriptCounter2 of ScriptDatabase), theLine:counter1, theBranch:theName of (item HandlerCounter1 of HandlerDatabase), theType:"HinS"}
set the end of ContainerDatabase to db4Record
end if
end repeat
end repeat
end if
end repeat
end if
if countScripts > 0 then
repeat with ScriptCounter1 from 1 to countScripts
repeat with HandlerCounter2 from 1 to countHandlers
repeat with counter1 from ((Start of (item HandlerCounter2 of HandlerDatabase)) + 1) to ((Finish of (item HandlerCounter2 of HandlerDatabase)) - 1) -- Start+1 due to need to skip the first line of routine ('on Handler()'), finish -1 for similar reason
if paragraph counter1 of theCleanScript contains ((theName of (item ScriptCounter1 of ScriptDatabase)) as text item) and paragraph counter1 of theCleanScript does not contain (((theName of (item ScriptCounter1 of ScriptDatabase)) as text item) & "'s") then
set db4Record to {theContainer:theName of (item HandlerCounter2 of HandlerDatabase), theLine:counter1, theBranch:theName of (item ScriptCounter1 of ScriptDatabase), theType:"SinH"}
set the end of ContainerDatabase to db4Record
end if
end repeat
end repeat
if countHandlers > 0 then
repeat with ScriptCounter2 from 1 to countScripts
repeat with counter1 from ((Start of (item ScriptCounter2 of ScriptDatabase)) + 1) to ((Finish of (item ScriptCounter2 of ScriptDatabase)) - 1) -- Start+1 due to need to skip the first line of routine ('on Handler()'), finish -1 for similar reason
if paragraph counter1 of theCleanScript contains ((theName of (item ScriptCounter1 of ScriptDatabase)) as text item) and paragraph counter1 of theCleanScript does not contain (((theName of (item ScriptCounter1 of ScriptDatabase)) as text item) & "'s") then
set db4Record to {theContainer:theName of (item ScriptCounter2 of ScriptDatabase), theLine:counter1, theBranch:theName of (item ScriptCounter1 of ScriptDatabase), theType:"SinS"}
set the end of ContainerDatabase to db4Record
end if
end repeat
end repeat
end if
end repeat
end if
end searchForBranches
-- ========== Handler to place comments into database
on saveDBRecord(StartValue, FinishValue, CommentValue)
set db2Record to {Start:StartValue, Finish:FinishValue, Comment:CommentValue}
set end of CommentsDatabase to db2Record
end saveDBRecord
-- ========== Handler to check and then blank comments but retain lines
on CommentElimination(ParaCounter, ScrptCode, theCleanScript)
if EliminateCommentLines is true then
set ScrptCode to ScrptCode & "" & return
else
set ScrptCode to ScrptCode & paragraph ParaCounter of theCleanScript & return
end if
return ScrptCode
end CommentElimination
-- ========== Handler determines if code is a script or handler start
on DetermineRoutine(theCleanScript, counter1, theCodeLength, counter2, ContainingRoutine)
ignoring case
if paragraph counter1 of theCleanScript starts with "On" or paragraph counter1 of theCleanScript starts with "To" then
-- this starts a handler, send to the handler tracker
set counter1 to handlerTracker(theCleanScript, counter1, theCodeLength, counter2, ContainingRoutine)
else if paragraph counter1 of theCleanScript starts with "script" then
-- this starts a script, send to script tracker
set counter1 to scriptTracker(theCleanScript, counter1, theCodeLength, counter2, ContainingRoutine)
end if
end ignoring
return counter1
end DetermineRoutine
Model: MacBook (early 2009)
Browser: Safari 533.16
Operating System: Mac OS X (10.6)
Hello.
I’m sitting here and working on my version, it is hopefully soon bug free.
I won’t look at yours before mine is ready!
(But then I will look closely.)
Here it comes.
You better check this post once and twice, because I’m not finished with the description and hows and whys and such of it. Yet I have to go get some food :).
Great Thanks to oldmanegan for triggering me into this and to Julio for always being a guiding star.
My version handles any (so far) library. No matter how they are organized.
¢ It returns a list of the entities hierarchically in order to give you the overall structure.
- Handlers and Script objects that contains either handlers or script objects.
¢ It removes what I consider as implementation details as empty script objects - "o"
It doesn’t do much more than that at the moment, but I will soon start to add some features. -I have just cleaned up the code and want to play a little with it. But it gives a good overview of what a library contains. -And on my machine it runs fast enough to be used on the fly. I realize that this is not necessarily the case on all machines, and will optimize it. I have used Satimage.osax’s regexp package in order to create it, so you need to download that to test it. I have all position data intact from Satimage’s find text, but I haven’t collected any end position data, nor comments.
Read more about this later on.
I will include it in the library loader of mine.
Here comes the “proper text for the code” which aren’t edited to a finished state yet.
Edit Yours and mine are about the same length.
What remains to be done
1. At the moment the LibraryLister only deals with Library files, and not script servers, I will come back to that later on, as that parsing of a ScriptServer Applet is really a separate task. What I mean by script server is having an applet, which loads script libraries either on run or on demand.
2. I can’ generate output in “dot” for graphwiz of omnigraffle yet, as I haven’t collected or generated any containment information, but I will have this as a side task while I optimize.
3. Optimize it for what its worth.
4. Apply a sensible set on operations on the list, and such, I have think through this, and play with it first.
HOW AND WHY THIS THING WORKS AS IT DOES
The Objective
A parser that works with any kind of scripted Library/As file.
To generate a list of all the entities, but no more than those that are
interesting or should be interesting for a user of a Library. No matter how the
Library looks like or are organized, it should allways work properly and give
the desired result.
Any implementation details, like script objects without handlers, or properties
are washed away together with any handlers that are commented out. We are
dealing with the working code here. -If I want to know about these things I
figure I will open the source and have a gander anyway.
This is a rather sluggish implementation, where the main objective was to have
something that rather worked, than worked fast. There are wast opportunities for
optimization, which I will return to when I’m sure that I have got it “right”.
Comments in the form of handler descriptions is a feature I haven’t had in
mind during this phase of development as this is a rather big and unexplored
area at the moment with a phletoria of differing ways to comment scripts.
Showing dependencies among script objects should also be relatively easy to add.
Any thoughts of any form of database implementation is also lacking, but most of
the necessary data are retained in records in the final list before we generate
the textual output which is my main interest at the moment. (Playing)
This is rather a prototype, where thing at the moment are generated on the fly,
so that I can play with it. (I love to play!) Maybe I’ll discover something, or
get some new I ideas before I go further.
Foundation
This things uses the formatting of a compiled applescript as
the foundation for figuring out the hierarchy of the various entities* That is
the number of tabs that prepends an entity -Relative to the former level.
*(I regard either a Script Object or a handler as an entity in this context).
that is: you can allways figure out that a handler or script object is within
another handler or script object by it’s indentation, so I therefore needen’t to
consider the ending of any entities. It is also a fact that Script Libraries
and other AppleScript files this LibrarLister produces output for
has had to be compiled, before source text could end up here, which frees the
script for any considerations about correct syntax and structure.
Strategy for solving this task
The very first thing it does after having received the source text is to surveil all
the block comment positions. As we will wash out anything from within those.
I collect all top level entities first, since this is a level in its own right.
This level is also special in that this is the only place where handlers reside
side by side with script objects; I have chosen this view out of convenience,
because I then will never have to classify handlers and script objects, but can
allways regard them as entites and never being afraid of washing out any
handlers with the empty script objects.
This is really a special case of the general handler, since we will merge any
top lever handlers as a final step. Thereby being able to both retain the
handlers in the final product and at the same time been enabled to wash out
empty script objects without having to classify each and everyone of the
entities at the moment.
We then recurse down the wanted levels of the object hierarchy of the source
text. At the moment I feel that it is enough to go down and collect any
eventual handlers of the 5 level. We have covered the first level in our run
handler, so there is a depth of four more levels to cover, before we do the
final washing of any empty first level script objects, and merges this clean
list with both the handlers from the top level and the resulting clean list from
the recursive collector routine.
Summary
I have a tool that can visualize the contents of a single script library and that’s
about it. All data except end markers for the entities are present in the parsing
process. The end markers are Very easy to get. No operations on the
displayed list are so far implemented, nor any database solution for storing
parsed files. I will implement some basic features, forAs Editor, Script
Debugger and Smile and play a little with those. I also see the possibilites of
using it as an on the fly “Annotation tool” while scripting big scripts, and it is
usable for that as well, but I foresee it needs some optimization for comfortable
usage on old machines. I’m seriously considering to implement it as a stand
alone server for my ScriptLibraryLoader. And use it as a
Stand alone tool. I never enjoyed running scripts with a size of 1000 lines on my
old G4 powerbook. Speaking of: I will make it Tiger compatible with help by
Satimage/Smile for Tiger, which supports decompiling.
I will ponder constructing any database for a while as I really won’t spend time
implementing something before I fully understand my needs
A preliminary Comparison
We have headed in two different directions, where I have been focused on one thing
, and that has been to be able to parse everything and display it correctly. My
solution is doing far less than yours, which does everything, and deeper.
-I haven’t tested your’s yet, but I assume it will display everything correctly.
We both ended up with recursive routines. But here our solutions grossly differ.
I refuted to write a regular parser for starters; I have had some trouble with
that approach when it has come to presenting data earlier.
I have had me as an end user of a library in mind when I wrote, I have no or
should have no interest in implementation details I use.
But you have done this and even more, with nicely concerning comments, where I
have neglected them, you may even support cross referencing and ToDo’s!.
Your solution, is more for the Library builder with cross referencing and all
that, -all in all a complete tool. My tool is on the other hand easy to combine
with other tools to build bigger tools. And that is the path I’m going to take.
I haven’t tested your tool, nor timed it, but I will. Any comparision will, if
the favour mine, be unfair to you, since yours do so much more.
Here is the output of the Script when run on itself
run
getLibraryText(aRefToPxFile)
getScriptObjectEntities(tabIndent, theTextToPeruse, commentlist, callCount, MAXLEVEL)
mergeHandlersFindPossibleEmptyScriptObjects(HandlerList, ScriptObjectList, MergedList, washingCandidates, callCount, MAXLEVEL)
mergeCleanLists(parentList, childList)
washParentListForEmptyObjects(parentList, childList, origScrObjList, washList)
washMergeListForEmptyScriptObjects(mergeList, washList)
washOutBlockCommentedEntities(entityList, blockCommentList)
washOutOnErrorStatements(HandlerList)
makeEntityList(seachString, searchItemNr, theTextToPeruse)
prependIndent(tabIndent, HandlerList)
withinABlockComment(aPos, listOfBlockPos)
getBlockCommentPositions(theTextToPeruse, listOfBlockPos)
indexByNextAList(listToIndex)
Here is the output when run on Julio’s sfri Librariy
application "Safari"
modifyimage(imgID, |attribute|, newvalue)
sendcookie(nombre, valor, caduca)
currentcookies()
writedata(t)
openwindow given data:{x, y, Z, a}
setcolor(theelement, thecolor)
getcolor(theelement)
progressBar
endBar()
updateBar()
initBar()
resetBar()
Formularios
setvalue()
focus()
blur()
select
submit()
reset()
listforms()
wathvalue(fID, eID)
StringToList(k, tid)
statistics with allinfo
displayconfirm(msg)
displayprompt(msg, ans)
loadpage(wheretogo)
location(frame, theloc)
fullscreen()
resizewin to {w, H, centered}
movewin to {x, y}
scrollwin to {x, y}
focuswin(docObject)
aboutmonitor()
aboutnavigator()
aboutplugins()
aboutmimetype(num)
aboutimage(imgID)
aboutme()
extractBookmarks()
extractImages()
extractLinks()
ExtractObjects(obj, v)
ExtractObjects2(obj, v)
returnprashes(tid, off, txt)
SearchReplace(s, r, theText)
AbsolURL(bu, u)
StringToList(k)
offsetOf(theItem, theList)
Here is the output when run on Jon Pugh’s smartStrings library
SmartString
newString(aString) --> script object factory method
getString() --> string (only reads)
setString(aString) --> string
setList(aList, seperator) --> string
subString(x, y) --> string (only reads)
beforeString(aString) --> string (only reads)
afterString(aString) --> string (only reads)
betweenStrings(afterThis, beforeThis) --> string (only reads)
appendString(aString) --> string
prependString(aString) --> string
replaceString(thisStr, thatStr) -- syntax forgivenness so you don't have to remember if there is or isn't an s
replaceStrings(thisStr, thatStr) --> string
deleteString(aString) --> string
replaceBetween(frontTag, rearTag, newValue) --> string
insertBefore(beforeStr, thisStr) --> string
insertAfter(afterStr, thisStr) --> string
deleteBefore(beforeStr) --> string
deleteAfter(afterStr) --> string
deleteBetween(afterThis, beforeThis) --> string
keepBefore(beforeStr) --> string
keepAfter(afterStr) --> string
keepBetween(afterThis, beforeThis) --> string
deleteCharacters(x, y) --> string
convertListToString(aList, delim) --> string (only reads)
getTokens(delim) --> list (only reads)
setTokens(aList, delim) --> string
firstToken(delim) --> string (only reads)
lastToken(delim) --> string
NthToken(n, delim) --> string
deleteNthToken(n, delim) --> string
deleteFirstToken(delim) --> string
deleteLastToken(delim) --> string
keepFirstToken(delim) --> string
keepLastToken(delim) --> string
keepNthToken(n, delim) --> string
getTokenRange(startIndex, endIndex, delim) --> list (only reads)
deleteTokenRange(startIndex, endIndex, delim) --> string
keepTokenRange(startIndex, endIndex, delim) --> string
beforeToken(token, delim) --> string (only reads)
afterToken(token, delim) --> string (only reads)
deleteTokensAfter(token, delim) --> string
keepTokensAfter(token, delim) --> string
trimWhitespace() --> string
uppercase {} --> string
lowercase {} --> string
abs (n)
<drumroll(>
And here follows the code.
-- A library lister that returns a hierarchic view of script objects and handlers in an as Library file.
-- a nice feature would be to exclude an opened file from the recent items menu.
-- The Idea and implementation and any faults is totally mine. © McUsr 2010 and put in the Public Domain.
-- The usually guarrantees about nothing what so ever applies, use it at your own risk.
-- Read the documentation.
-- You are not allowed to post this code elsewhere, but may of course refer to the post at macscripter.net.
” macscripter.net/viewtopic.php?pid=131154#p131154
(*
TERMS OF USE.
This applies only to posting code, as long as you don't post it, you are welcome to do
whatever you want to do with it without any further permission.
Except for the following: Selling the code as is, or removing copyright statmentents and the embedded link in the code (without the http:// part) from the code.
You must also state what you eventually have done with the original source. This obviously doesn't matter if you distribure AppleScript as read only. I do not require you to embed any properties helding copyright notice for the code.
Credit for having contributed to your product would in all cases be nice!
If you use this code as part of script of yours you are of course welcome to post that code with my code in it here at macscripter.net. If you then wish to post your code elsewhere after having uploaded it to MacScripter.net please email me and ask for permission.
The ideal situation is however that you then refer to your code by a link to MacScripter.net
The sole reason for this, is that it is so much better for all of us to have a centralized codebase which are updated, than having to roam the net to find any usable snippets. Which some of us probabaly originated in the first hand.
I'm picky about this. If I find you to have published parts of my code on any other site without previous permission, I'll do what I can to have the site remove the code, ban you, and sue you under the jurisdiction of AGDER LAGMANNSRETT of Norway. Those are the terms you silently agree too by using this code.
The above paragraphs are also valid if you violate any of my terms.
If you use this or have advantage of this code in a professional setting, where professional setting means that you use this code to earn money by keeping yourself more productive. Or if you as an employee share the resulting script with other coworkers, enhancing the productivity of your company, then a modest donation to MacScripter.net would be appreciated.
*)
property MAXLEVEL : 4
on run
script o
property pxLibPath : ""
end script
local txtOfLibraryScript
local commentlist, level1ScriptObjects, level1Handlers, level2Handlers, scriptObjectEntities, washingCandidates, MergedList, childList
local callCount, startPattern, tabString
set {commentlist, level1ScriptObjects, level1Handlers, level2Handlers, scriptObjectEntities, washingCandidates, MergedList} to {{}, {}, {}, {}, {}, {}, {}}
set {callCount, tabString} to {0, ""}
set txtOfLibraryScript to getLibraryText(a reference to o's pxLibPath)
getBlockCommentPositions(txtOfLibraryScript, commentlist)
-- Collects level 1 script objects.
set level1ScriptObjects to makeEntityList(("^" & tabString & "(script|using terms from)[ ](.*$)"), "\\2", txtOfLibraryScript)
set level1ScriptObjects to washOutBlockCommentedEntities(level1ScriptObjects, commentlist)
-- collects level 1 handlers.
set level1Handlers to makeEntityList(("^" & tabString & "(on|to)[ ](.*$)"), "\\2", txtOfLibraryScript)
set level1Handlers to washOutBlockCommentedEntities(level1Handlers, commentlist)
set level1Handlers to washOutOnErrorStatements(level1Handlers)
if level1ScriptObjects is {} and level1Handlers is {} then
tell me
activate
display alert "There were no script objects nor handlers in in " & o's pxLibPath & "!"
error number -128
end tell
end if
set {tabString, level2Handlers} to {" ", {}} -- uses the new value for retrieving any level 2 script objects as well.
if level1ScriptObjects is not {} then
-- collect any level 2 handlers.
set level2Handlers to makeEntityList(("^" & tabString & "(on|to)[ ](.*$)"), "\\2", txtOfLibraryScript)
set level2Handlers to washOutBlockCommentedEntities(level2Handlers, commentlist)
set level2Handlers to washOutOnErrorStatements(level2Handlers)
set level2Handlers to prependIndent(tabString, level2Handlers)
-- Merge the Level2 handlers with the ScriptObjects List and create a Candidates list for washing at the same time.
set scriptObjectEntities to mergeHandlersFindPossibleEmptyScriptObjects(level2Handlers, level1ScriptObjects, scriptObjectEntities, washingCandidates, callCount, MAXLEVEL)
set level2Handlers to missing value
end if
if level1Handlers is not {} and scriptObjectEntities is not {} then
-- Merges level1Handlers with scriptObjectEntites before adding any children.
local HandlerFirstPos, HandlerItemNo, HandlerCount, ScriptObjectFirstPos, ScriptObjectItemNo, ScriptObjectCount
set {HandlerFirstPos, HandlerItemNo, HandlerCount} to {matchPos of item 1 of level1Handlers, 1, (count level1Handlers)}
set {ScriptObjectFirstPos, ScriptObjectItemNo, ScriptObjectCount} to {matchPos of item 1 of scriptObjectEntities, 1, (count scriptObjectEntities)}
repeat while ((ScriptObjectItemNo ≤ ScriptObjectCount) or (HandlerItemNo ≤ HandlerCount))
if (ScriptObjectItemNo > ScriptObjectCount) then
set end of MergedList to item HandlerItemNo of level1Handlers
set HandlerItemNo to HandlerItemNo + 1
else if (HandlerItemNo > HandlerCount) then
set end of MergedList to item ScriptObjectItemNo of scriptObjectEntities
set ScriptObjectItemNo to ScriptObjectItemNo + 1
else if (matchPos of contents of item HandlerItemNo of level1Handlers < matchPos of contents of item ScriptObjectItemNo of scriptObjectEntities) then
set end of MergedList to item HandlerItemNo of level1Handlers
set HandlerItemNo to HandlerItemNo + 1
else
set end of MergedList to item ScriptObjectItemNo of scriptObjectEntities
set ScriptObjectItemNo to ScriptObjectItemNo + 1
end if
end repeat
else if level1Handlers is {} then
set MergedList to scriptObjectEntities
else -- had to have handlers, -- we had to to get here.
set MergedList to level1Handlers
end if
-- we are now getting at level 2 script objects, i.e at the same level as the level2 handlers.
set childList to getScriptObjectEntities(tabString, txtOfLibraryScript, commentlist, callCount, MAXLEVEL)
-- following handlers check for an empty child list
if washingCandidates ≠{} then
set MergedList to washParentListForEmptyObjects(MergedList, childList, level1ScriptObjects, washingCandidates)
end if
set {level1ScriptObjects, washingCandidates} to {missing value, missing value}
set MergedList to mergeCleanLists(MergedList, childList)
set childList to missing value
-- extracts the result for viewing
set displayList to {}
repeat with anItem in MergedList
set end of displayList to matchResult of anItem
end repeat
tell me
activate
choose from list displayList default items (item 1 of displayList) with prompt o's pxLibPath
end tell
set {tids, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
set displayList to text items of displayList as text
set AppleScript's text item delimiters to tids
set test to "A ;-)"
end run
on getLibraryText(aRefToPxFile) ” Thanks to oldmanegan
-- returns text of Script library upon success
local theSourceText, thisScript, qfPoxPath
set thisScript to (choose file) as text
set contents of aRefToPxFile to POSIX path of thisScript
set qfPoxPath to quoted form of POSIX path of thisScript as text
try
set theSourceText to do shell script "/usr/bin/osadecompile " & qfPoxPath & "| /usr/bin/tr -d '\\000'"
on error e number n
display alert e & " : " & n
error number -128
end try
if theSourceText is "" then
display alert "The Script is empty, or left in debugging state"
error number -128
end if
return theSourceText
end getLibraryText
on getScriptObjectEntities(tabIndent, theTextToPeruse, commentlist, callCount, MAXLEVEL)
-- Collects a script object with its contents. returns a single indented list of items.
-- Emtpy script objects washed away.
-- CONTEXT: The first level of both handlers and script objects and the second level of handlers is already parsed
local tabCount, originalIndent, ScriptObjectList, HandlerList
set {tabCount, originalIndent, ScriptObjectList, callCount} to {1, tabIndent, {}, callCount + 1}
-- Collects script objects.
set ScriptObjectList to makeEntityList(("^" & tabIndent & "(script|using terms from)[ ](.*$)"), "\\2", theTextToPeruse)
set ScriptObjectList to washOutBlockCommentedEntities(ScriptObjectList, commentlist)
set ScriptObjectList to prependIndent(tabIndent, ScriptObjectList)
if ScriptObjectList is not {} then -- END CONDITON FOR RECURSION
-- Finds any handlers in the script objects so far.
set {tabIndent, HandlerList} to {(tabIndent & " "), {}}
set HandlerList to makeEntityList(("^" & tabIndent & "(on|to)[ ](.*$)"), "\\2", theTextToPeruse)
set HandlerList to washOutBlockCommentedEntities(HandlerList, commentlist)
set HandlerList to washOutOnErrorStatements(HandlerList)
set HandlerList to prependIndent(tabIndent, HandlerList)
if HandlerList is {} and callCount is MAXLEVEL then return {}
else
return {}
end if
-- Merges the eventual outcome of previous gathering into single lists, marks possible empty script objects for washing.
local MergedList, washingCandidates
set {MergedList, washingCandidates} to {{}, {}}
set MergedList to mergeHandlersFindPossibleEmptyScriptObjects(HandlerList, ScriptObjectList, MergedList, washingCandidates, callCount, MAXLEVEL)
if HandlerList is {} then
set washingCandidates to indexByNextAList(ScriptObjectList)
end if
set HandlerList to missing value
if callCount is MAXLEVEL then
-- washes out any candidates from findings in mergeHandlersFindPossibleEmptyScriptObjects
set MergedList to washMergeListForEmptyScriptObjects(MergedList, washingCandidates)
return MergedList
else
-- Washes away empty script objects with our aquired facts.
set tabIndent to originalIndent & " "
set childList to getScriptObjectEntities(tabIndent, theTextToPeruse, commentlist, callCount, MAXLEVEL)
if childList is not {} then
-- following handlers check for an empty child list
set MergedList to washParentListForEmptyObjects(MergedList, childList, ScriptObjectList, washingCandidates)
else
-- just removes any items of the washlist one bye one.
set MergedList to washMergeListForEmptyScriptObjects(MergedList, washingCandidates)
end if
set {ScriptObjectList, washingCandidates} to {missing value, missing value}
set MergedList to mergeCleanLists(MergedList, childList)
set childList to missing value
return MergedList
end if
end getScriptObjectEntities
on mergeHandlersFindPossibleEmptyScriptObjects(HandlerList, ScriptObjectList, MergedList, washingCandidates, callCount, MAXLEVEL)
-- returns a list with merged handlers also tags any script objects empty so far.
local HandlerFirstPos, HandlerItemNo, HandlerCount, ScriptObjectFirstPos, ScriptObjectItemNo, ScriptObjectCount, lastWasScriptObject
if HandlerList is not {} then
set {HandlerFirstPos, HandlerItemNo, HandlerCount} to {matchPos of item 1 of HandlerList, 1, (count HandlerList)}
set {ScriptObjectFirstPos, ScriptObjectItemNo, ScriptObjectCount} to {matchPos of item 1 of ScriptObjectList, 2, (count ScriptObjectList)}
set {MergedList, end of MergedList, lastWasScriptObject} to {{}, item 1 of ScriptObjectList, true}
repeat while ((ScriptObjectItemNo ≤ ScriptObjectCount) or (HandlerItemNo ≤ HandlerCount))
if (ScriptObjectItemNo > ScriptObjectCount) then
set lastWasScriptObject to false -- because: there isn't any other script object to add at this moment.
set end of MergedList to item HandlerItemNo of HandlerList
set HandlerItemNo to HandlerItemNo + 1
else if HandlerItemNo > HandlerCount then
if lastWasScriptObject is true then
set end of washingCandidates to {ScriptObjectItemNo, item -1 of MergedList}
set lastWasScriptObject to false
else
set end of MergedList to item ScriptObjectItemNo of ScriptObjectList
set end of washingCandidates to {(ScriptObjectItemNo + 1), item ScriptObjectItemNo of ScriptObjectList}
set ScriptObjectItemNo to ScriptObjectItemNo + 1
end if
else if (matchPos of contents of item HandlerItemNo of HandlerList < matchPos of contents of item ScriptObjectItemNo of ScriptObjectList) then
set lastWasScriptObject to false
set end of MergedList to item HandlerItemNo of HandlerList
set HandlerItemNo to HandlerItemNo + 1
else -- Script object has a lower position value
if lastWasScriptObject is false then
set end of MergedList to item ScriptObjectItemNo of ScriptObjectList
else -- two script objects in a row
set end of washingCandidates to {ScriptObjectItemNo, item -1 of MergedList}
set end of MergedList to item ScriptObjectItemNo of ScriptObjectList
end if
set ScriptObjectItemNo to ScriptObjectItemNo + 1
set lastWasScriptObject to true
end if
end repeat
if lastWasScriptObject is true then set end of washingCandidates to {ScriptObjectItemNo, item -1 of MergedList}
set HandlerList to missing value
else
set MergedList to ScriptObjectList
end if
return MergedList
end mergeHandlersFindPossibleEmptyScriptObjects
on mergeCleanLists(parentList, childList)
-- Merges two lists, by order of matchpos, returns result
-- the ParentList is higher up in the hierarchy, therefore contains preceding elements.
if not childList is {} then -- superfluos but
local resultList, parentItemNo, parentCount, childItemNo, childCount
set {parentItemNo, parentCount, childItemNo, childCount} to {2, (count parentList), 1, (count childList)}
set {resultList, end of resultList} to {{}, item 1 of parentList}
-- Safely assume that first item in merg list has lower pos than first in child list
repeat while ((parentItemNo ≤ parentCount) or (childItemNo ≤ childCount))
if (parentItemNo > parentCount) then
set end of resultList to item childItemNo of childList
set childItemNo to childItemNo + 1
else if (childItemNo > childCount) then
set end of resultList to item parentItemNo of parentList
set parentItemNo to parentItemNo + 1
else if (matchPos of contents of item childItemNo of childList < matchPos of contents of item parentItemNo of parentList) then
set end of resultList to item childItemNo of childList
set childItemNo to childItemNo + 1
else
set end of resultList to item parentItemNo of parentList
set parentItemNo to parentItemNo + 1
end if
end repeat
return resultList
else
return parentList
end if
end mergeCleanLists
on washParentListForEmptyObjects(parentList, childList, origScrObjList, washList)
-- compares elements in parent list with child list and washlist.
-- draws a conclusion wether parent element is really empty, then removes it.
local parentItemNo, parentCount, childItemNo, washItemNo, washCount, washFirstPos, washLastPos, foundLaterChild, curChildPos
set {parentItemNo, parentCount, childItemNo, childCount, washItemNo, washCount} to {1, (count parentList), 1, (count childList), 1, (count washList)}
if washList ≠{} and childList ≠{} then
set washFirstPos to matchPos of item 2 of item washItemNo of washList
set washLastPos to matchPos of item (item 1 of item washItemNo of washList) of origScrObjList
repeat while washItemNo ≤ washCount
set foundLaterChild to false
repeat with i from childItemNo to childCount
set curChildPos to matchPos of item i of childList
if curChildPos > washFirstPos and curChildPos < washLastPos then
set washItemNo to washItemNo + 1
set childItemNo to childItemNo + 1
set foundLaterChild to true
exit repeat -- found sibling keeps this candidate
else if curChildPos > washLastPos then -- must be less than, positions are unique
set foundLaterChild to true -- we bypassed the candidate
repeat with j from parentItemNo to parentCount
if matchPos of item j of parentList = washFirstPos then
set item j of parentList to missing value
set mergeItemNo to j + 1
exit repeat
end if
end repeat
set washItemNo to washItemNo + 1
set foundLaterChild to true
exit repeat
end if
end repeat
if foundLaterChild is false then
-- all there is to delete every member of washingList from parentList
repeat with i from washItemNo to washCount
repeat with j from parentItemNo to contents of parentCount
if matchPos of item j of parentList = washFirstPos then
set item j of parentList to missing value
set parentItemNo to j + 1
exit repeat
end if
-- set washFirstPos to matchPos of item 2 of item (i + 1) of washList
if i < washCount then
set washFirstPos to matchPos of item 2 of item (i + 1) of washList
else
exit repeat
end if
end repeat
end repeat
exit repeat -- done WASHING
else
set washFirstPos to matchPos of item 2 of item washItemNo of washList
set washLastPos to matchPos of item (item 1 of item washItemNo of washList) of origScrObjList
end if
end repeat
set parentList to parentList's records
-- else -just return the parentList
end if
return parentList
end washParentListForEmptyObjects
on washMergeListForEmptyScriptObjects(mergeList, washList)
-- cleans a list for empty script objects,when there were no new children.
local mergeItemNo, washItemNo, washCount, washFirstPos
set {mergeItemNo, mergeCount, washItemNo, washCount} to {1, (count mergeList), 1, (count washList)}
if washList ≠{} then
repeat with i from washItemNo to washCount
set washFirstPos to matchPos of item 2 of item i of washList
repeat with j from mergeItemNo to mergeCount
if matchPos of item j of mergeList = washFirstPos then
set item j of mergeList to missing value
set mergeItemNo to j + 1
exit repeat
end if
end repeat
end repeat
-- else -- nothing to merge nor wash
set mergeList to mergeList's records
else
return mergeList
end if
end washMergeListForEmptyScriptObjects
on washOutBlockCommentedEntities(entityList, blockCommentList)
-- removes any entity that is within a block comment.
script o
property l : entityList
end script
if not entityList is {} then
repeat with i from 1 to (count entityList)
if withinABlockComment(matchPos of (contents of item i of o's l), blockCommentList) then
set item i of o's l to missing value
end if
end repeat
set entityList to o's l's records
end if
return entityList
end washOutBlockCommentedEntities
on washOutOnErrorStatements(HandlerList)
-- removes the "on error" statements.
script o
property l : HandlerList
end script
if not HandlerList is {} then
repeat with i from 1 to (count HandlerList)
if matchResult of (contents of item i of o's l) is "error" or matchResult of (contents of item i of o's l) starts with "error " then
set item i of o's l to missing value
end if
end repeat
set HandlerList to o's l's records
end if
return HandlerList
end washOutOnErrorStatements
on makeEntityList(seachString, searchItemNr, theTextToPeruse)
-- Harvests out a list of entities of type specified by parameters.
script o
property l : theTextToPeruse
end script
local theRes
set theRes to find text seachString in o's l using searchItemNr starting at 0 with regexp and all occurrences
return theRes
end makeEntityList
on prependIndent(tabIndent, HandlerList)
-- idents the element after we have washed out error statments for handlers
-- anytime for script objects
script o
property l : HandlerList
end script
repeat with i from 1 to (count HandlerList)
set matchResult of (item i of o's l) to tabIndent & (matchResult of (contents of item i of o's l))
end repeat
return o's l
end prependIndent
-- tells us if a pos of a particular handler is witihin a block comment.
on withinABlockComment(aPos, listOfBlockPos)
script o
property l : listOfBlockPos
end script
repeat with i from 1 to (count listOfBlockPos)
if item 1 of item i of o's l < aPos and item 2 of item i of o's l > aPos then return true
end repeat
return false
end withinABlockComment
on getBlockCommentPositions(theTextToPeruse, listOfBlockPos)
-- creates a list with start and end positons of block comments
script o
property l : theTextToPeruse
property m : listOfBlockPos
end script
local startPattern, SearchPattern, frompos, theRes, PrevPos
set startPattern to true
set SearchPattern to "^([(][*])"
set frompos to 0
repeat
try
set theRes to find text SearchPattern in o's l using "\\0" starting at frompos with regexp
set startPattern to not startPattern
if startPattern then
-- it was the end pattern we just found
set SearchPattern to "^([(][*])"
-- saving our results
try
copy {PrevPos, matchPos of theRes} to end of o's m
on error e number n
display alert e & " : " & n
end try
else
copy matchPos of theRes to PrevPos
-- it was the start pattern we just found
set SearchPattern to "^([*][)])"
end if
set frompos to ((matchPos of theRes) + (matchLen of theRes) + 1)
on error
-- we better having erred on the first RegExp.
if not startPattern then
error "malformed comments - can't happen"
end if
exit repeat
end try
end repeat
return listOfBlockPos
end getBlockCommentPositions
on indexByNextAList(listToIndex)
-- returns a list of lists, with item in list preceeded by its index+1.
script o
property l : listToIndex
end script
local newList
set newList to {}
repeat with i from 1 to (count listToIndex)
set end of newList to {(i + 1), item i of o's l}
end repeat
return newList
end indexByNextAList
Hello.
Removed an undeleted line, which shouldn’t be there in order to create correct indenting.
It now indents correctly
I have updated the doc, to a state that I will leave it int. Until I actually uses it in some script, which may be sooner than you think!