increment a string that has leading zeros

You’re right, that’s because va_list() is not supported in ASOC, it’s part of C’s stdarg.h.

Edit: To explain myself further: va_list() works only with C types (Object = Struct type) and ASOC doesn’t. %f is an floating point (double) but the given argument, created by AppleScriptObjC, is an object (read: C struct).

As what Nigel described match what I get with an other script ( and of course an other library ) I tried to run a slightly modified version of the script to see which is the striking error.

use theLib : script "NSNumbers"
set myVar to "0999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
repeat with i from 1 to 100000
	try
		set myVar to (theLib's incrementString:myVar)
	on error errMsg number errNbr
		return "" & i & " -| " & errMsg & " | " & errNbr
	end try
end repeat

After compiling, I ran it and got :

“29767 -| Une erreur de type -4960 est survenue. | -4960”

I typed some returns at the end of the script, compiled and ran :

“29532 -| Une erreur de type -4960 est survenue. | -4960”
Each time I run the script with no change I get :

Résultat :
“1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099999”

After every change I get the error for a different value of i

I will try to finish my own script then I will try to strip most of the code until I get a sufficiently simple code with the infamous error -4960.

After that I will file a report with the two sample scripts.

Yvan KOENIG (VALLAURIS, France) mercredi 12 mars 2014 16:39:59

I am not even dangerous with ASOC, but I wonder if you could use NSValue to “unbox” ints, floats and the like with?

By unboxing, I mean, as an opposite as boxing, where you promote a scalar value to an object, to demote a value contained in an object into a value with scalar type.

And I am not saying that this necessarily produces any better solution than Shane already has come up with, just that I wonder if that can’t be used, to “bridge” between ASOC, and the more c- value typed methods and functions of Cocoa.

Edit

This is an introductory article to NSValue, and boxing

You’ll find the different codes for the types to use as an argument to the valueWithBytes method of NSValue, (so that you can bypass the @encode compiler directive in the Objective-C runtime reference in the section that deals with @encode (just google: objc @encode).

My excuse for not doing this testing myself is that this is not in my alley.

Edit+

Maybe this can’t be done after all, since you have to pass along a pointer with NSValue’s getValue method.

Maybe this can be done with: “a reference to fakedInt” or not, but when someone actually has, or if somebody already tested that, then we know for sure. :slight_smile:

Edit++
IMHO: Apple should include a class named NSScalar for those occations, slightly misleading in its name as it should also be able to return rects, points, and sizes structs. (There is of course nothing that hinders anybody else in providing that class, that should aggregate upon NSValue, and return a pointer to memory, that is purged in the dealloc.) Such a class with methods for each datatype to return, would be really nifty to use from ASOC, because it would serve as a general interface to the c-part, and let you do other stuff than writing “wrapper-classes” the various places you need access to the c-parts. Shame such a scheme won’t work with function pointers. :slight_smile:

Edit+++
You can of course use NSNumber’s intValue, unsignedLongLongValue and so on for the regular cases, but that was the problem in the first place, the unability of the ScriptingBridge to use anything less than an NSNumber. So, maybe wrapper classes, is the only way to interface with the c typed part of the interfaces after all. :confused:

Here is the stripped script allowing to test the behavior of a call to a library.
It’s extracted from a script driving Pages 5.1 which doesn’t allow us to extract easily the document’s contents so, GUIScripting is required.

(*
Error -4960

Yvan KOENIG (VALLAURIS, France)
le 12 mars 2014
*)
#=====
(*
Install the library whose code is given below as ~/Library/Script Libraries/extractor Lib.scptd
*)
use theLib : script "extractor Lib"
use scripting additions

#=====

on run
	(*
create the text file from which we will extract datas
*)
	set bof to "<[nom]>

<[prénom]>

Il pleuvait fort sur la grand-route
Elle cheminait sans parapluie.


<[prénom]> <[nom]>
<[adresse]>"
	set chemin to (path to desktop as text) & "bof.txt"
	my writeto(chemin, bof, text, false)
	delay 0.5
	tell application "TextEdit" to open file chemin
	my Germaine()
end run

#=====

on Germaine()
	local file_fichier_Pages, theTags_lesBalises
	tell application "TextEdit"
		activate
		set file_fichier_Pages to name of document 1
	end tell
	set theTags_lesBalises to my extractTags("TextEdit")
end Germaine

#=====

on extractTags(theApp)
	local tt, beg_debut, end_fin, leTexte, theResult, lesbalises
	(*
IUse GUIScripting because it's required with the true script
*)
	activate application theApp
	tell application "System Events" to tell (first process whose frontmost is true)
		(*
Select All
Tout Sélectionner
*)
		keystroke "a" using {command down}
		(*
Fill the clipboard with a fake string
*)
		set tt to "All The Things You Could Be By Now If Sigmund Freud's Wife Was Your Mother, © Charles Mingus"
		tell me to set the clipboard to tt
		(*
Copy
*)
		keystroke "c" using {command down}
	end tell
	(*
Wait for the achievement of the Copy task and extract the text from the clipboard
*)
	tell me
		repeat 10 times
			delay 0.1
			try
				set leTexte to (the clipboard as text)
				if leTexte is not tt then exit repeat
			end try
		end repeat
	end tell
	(*
Must extract Tags resembling to "<[wxyz]>"

Thanks to Shane STANLEY use a library introduced with Mavericks
*)
	set theResult to theLib's findPattern:"<\\[[^>]+>" inString:leTexte
	# AT FIRST RUN issue an error but behaves well after
	--> error "Une erreur de type -4960 est survenue." number -4960 from "extractor Lib"
	set theResult to theLib's makeUnique:theResult
	
	return theResult
end extractTags

#=====
(*
Handler borrowed to Regulus6633 - http://macscripter.net/viewtopic.php?id=36861
*)
on writeto(targetFile, theData, dataType, apendData)
	-- targetFile is the path to the file you want to write
	-- theData is the data you want in the file.
	-- dataType is the data type of theData and it can be text, list, record etc.
	-- apendData is true to append theData to the end of the current contents of the file or false to overwrite it
	try
		set targetFile to targetFile as text
		set openFile to open for access file targetFile with write permission
		if not apendData then set eof of openFile to 0
		write theData to openFile starting at eof as dataType
		close access openFile
		return true
	on error
		try
			close access file targetFile
		end try
		return false
	end try
end writeto

#=====
*)

Would be fine if you may test it from Apple’s Script Editor.

Don’t forget to save and install the library “Extractor Lib.scptd”


#=========
#  extractor Lib
#=========
# ASObjC_Runner@yahoogroups.com  2014/03/06
# Shane STANLEY

use AppleScript version "2.3.1"
use scripting additions
use framework "Foundation"

on findPattern:thePattern inString:theString
	set theRegEx to current application's NSRegularExpression's ¬
		regularExpressionWithPattern:thePattern ¬
			options:0 |error|:(missing value)
	set theFinds to theRegEx's matchesInString:theString options:0 range:{location:0, |length|:length of theString}
	set theFinds to theFinds as list -- so we can loop through
	set theResult to {} -- we will add to this
	set theNSString to current application's NSString's stringWithString:theString
	repeat with i from 1 to count of items of theFinds
		set theRange to (item i of theFinds)'s range()
		set end of theResult to (theNSString's substringWithRange:theRange) as string
	end repeat
	return theResult
end findPattern:inString:

on makeUnique:theList
	set theSet to current application's NSOrderedSet's orderedSetWithArray:theList
	return theSet's allObjects() as list
end makeUnique:

#=====

As written before, AT FIRST RUN issue an error but behaves well after
→ error “Une erreur de type -4960 est survenue.” number -4960 from “extractor Lib”

Thanks in advance for your feedback.

Yvan KOENIG (VALLAURIS, France) mercredi 12 mars 2014 21:34:55

I suspect Stefan meant it in more in terms of being more typical, or encouraged, because you don’t have to stop and think about localisation issues.

Anyway, is doubleValue going to maintain 37 digits, and will they be converted to an AS real?

I just felt that, as we wanted a string, and NSDecimalNumber could do the conversion, it made sense to do it that way.

But that’s only going to affect what’s returned to your AppleScript. The problem here is what AS sends to Cocoa.

I think that the doubleValue keeps up with 37 decimal digits since the size of its storage is 8 bytes on a 64-bit processor, and it should hold for values less than 1.797693e+308, so that is really no problem to implement with NSDecimalNumber.

Of course this will not work when converting back to an AppleScript real, because of the loss of significant digits as statet above. I think the most you can convert is 12 decimal digits.

I realized that at the end of some post above, (the long one with many edits), that unless NSValue is somewhat excempted from the way the ObjC bridge deals with NSNumbers, Only uses an NSNumber internally, then it fails.

So I believe as you wrote in some posts above that you can only values back via an NSValue wrapper, not inject scalar values into an Objective-C method.

You can pass scalar values to a method as an argument – substringToIndex: is a perfect example. But it relies on the scripting bridge knowing about the requirements of the particular method, via a .scriptingbridge file in the relevant framework. In other words, it has to be defined in the method signature, and therefore set at framework compile time. That rules out format string tokens.

Hello.

Well, it is a pity then, that you can’t use it, because especially the methods that takes format strings, can save some programming. I think, that adding an NSScalar class for ASOC’s explicit use, would be a much better design-wise approach, then to add the missing parts for explicit methods in the .scriptingbridge file!

I’ll finish off this by saying, that there isn’t much localization to be had with a digitstring, and it can’t indeed be localized anyway, as long as there are zeroes involved.

Of course the correct way to deal with currencies,dates and what-not that is localizable, is to use some subclass of NSFormatter.

This was really interesting by the way. :slight_smile:

This whole thread is proof that no-one around here really wants to save some programming :wink:

Each to his own. :smiley:

For padding a number with leading zeros NSNumberFormatter is more appropriate than stringWithFormat, because.

¢ it’s the designated (and optimized) class to deal with number objects
¢ it can be easily used also in AppleScriptObjC
¢ precision is adjustable
¢ number format is adjustable
¢ localization is adjustable

I agree to the AppleScriptObjC, there isn’t much adjusting needed for numberformat, or localization, in this particular case with a string consisting of pure digits. What would have been a really good argument, would have been localization, but a string consisting of only digits are not localizable in the first place, the second you have to pad the string with zeroes.

As for stringWithFormat: I can adjust the length and precision (number of digits), and also choose among padding or no padding, (although I can’t pass in the padding as a parameter runtime, but I can pass a format string), and I don’t have to pad manually, which I guess makes it faster. At least it is fewer lines to write! :slight_smile:

Only if the definition of saving programming is creating a script library or a framework to save up a few lines of code in AppleScript and the programming of the library or framework itself is not taken into consideration. :smiley:

But you’re still missing the main point:

    NSString *nmbrStr = @"0033620340000003362034000000336203400";
    NSDecimalNumber *theNum = [[NSDecimalNumber decimalNumberWithString:nmbrStr]decimalNumberByAdding: [NSDecimalNumber one]] ;
    NSString *fmtStr = [NSString stringWithFormat:@"%038.0F",[theNum doubleValue]] ;
    NSNumberFormatter *fm = [[NSNumberFormatter alloc] init];
    [fm setFormat:@"00000000000000000000000000000000000000"];
    NSString *fmtStr2 = [fm stringFromNumber:theNum];
    NSLog(@"%@, %@",fmtStr, fmtStr2);
   --> 00033620340000003362709699719814184960, 00033620340000003362034000000336203401

What’s the point in being quicker and saving a few lines if it gives the wrong answer?

May I quote you? :slight_smile:

It’ll be an honor.

Hello.

Ok, it seems to me like stringWithFormat, doesn’t manage as long strings of digits as 38 digits, so in those cases you are better off with using a numberFormatter. (I consider that a bug in stringWithFormat.)

The ease of use of a Framework, is of course important, but then again that ease of use lies in careful planning of the interfaces, and wasn’t really an issue at least I had in mind when we started this thread.

My goal was to save speed, say if you created a sequence of 1000+ numbers in one go. Now I have really learned that some place along the road to 38 digits, the stringwith format will lack precision. They probably refused to spend energy into upgrading this for 64 bits. :slight_smile:

Hello.

I still find that may way would have been the best practice, hadn’t I been hampered by the bug.

It is not written anywhere in the docs, that there are any kinds of limitation to printing out a 64-bit double as a decimal.
:slight_smile: