How to create a variable for "Any digit"?

Get help using and writing Nisus Writer Pro macros.
Post Reply
Þorvarður
Posts: 331
Joined: 2012-12-19 05:02:52

How to create a variable for "Any digit"?

Post by Þorvarður »

I need to find out whether the character that precedes the insertion point is a number or not. In case it's a number, the macro will do some stuff. In case it's not, the macro will do some other stuff. How can I do that?

I tried
$theFoundCharacter = Find @Text<\X>, 'rE'
#$theFoundCharacter = Find "(AnyCharacter)", 're'
Select End

if $theFoundCharacter = ?

else

end

How can I replace the question mark with something that indicates "Any digit" or that a digit was found?

And another question. Could I also use the instruction Read Selection to build the variable?


Find @Text<\X>, 'rE'
#Find "(AnyCharacter)", 're'
$theFoundCharacter = Read Selection
Select End

if $theFoundCharacter = ?

else

end


Will the variable $theFoundCharacter have the same value in both cases?

User avatar
phspaelti
Posts: 1180
Joined: 2007-02-07 00:58:12
Location: Japan

Re: How to create a variable for "Any digit"?

Post by phspaelti »

Þorvarður wrote:
2021-03-29 05:50:26
I need to find out whether the character that precedes the insertion point is a number or not. In case it's a number, the macro will do some stuff. In case it's not, the macro will do some other stuff. How can I do that?

I tried
$theFoundCharacter = Find @Text<\X>, 'rE'
This will not give you what you want. The Find command will return the number of find hits, which in this case should always be 1.
Þorvarður wrote:
2021-03-29 05:50:26
And another question. Could I also use the instruction Read Selection to build the variable?

Find @Text<\X>, 'rE'
$theFoundCharacter = Read Selection
This will work, as you could easily check with the following command:

Code: Select all

prompt $theFoundCharacter
philip

User avatar
phspaelti
Posts: 1180
Joined: 2007-02-07 00:58:12
Location: Japan

Re: How to create a variable for "Any digit"?

Post by phspaelti »

Þorvarður wrote:
2021-03-29 05:50:26
if $theFoundCharacter = ?

else

end

How can I replace the question mark with something that indicates "Any digit" or that a digit was found?
Here the first thing to note is that if you want to test a variable you will need to use the double equal sign "==", not just a single one. So you could test whether the found character is the digit '1' (one) like this:

Code: Select all

if $theFoundCharacter == '1'
    prompt "You found a one!"
end
And you could of course use logical OR (written with a double pipe '||') to test all 10 digits, but I wouldn't recommend it :lol:
Also possible would be to use the the character code value and test whether it is between 30 and 39. But again this is rather ugly.

Since we are in Nisus the better method is to use Find, or actually (macro language) .find, like this:

Code: Select all

if $theFoundCharacter.find('\d','E')
…
else
…
end
At this point though I'd also like to point out that a more direct way to get the character you want to test is to use the macro language:

Code: Select all

$sel = TextSelection.active
$theFoundCharacterRange = Range.new $sel.location - 1, 1
$theFoundCharacter = $sel.text.substringInRange $theFoundCharacterRange
philip

User avatar
phspaelti
Posts: 1180
Joined: 2007-02-07 00:58:12
Location: Japan

Re: How to create a variable for "Any digit"?

Post by phspaelti »

If you understood all that you can now squeeze the whole logic into one statement like this:

Code: Select all

$sel = TextSelection.active
if $sel.text.substringInRange(Range.new($sel.location - 1, 1)).find('\d','E')
   prompt 'digit'
else
   prompt 'not a digit'
end
But whether that's recommended is another question :lol:
philip

User avatar
martin
Official Nisus Person
Posts: 4935
Joined: 2002-07-11 17:14:10
Location: San Diego, CA
Contact:

Re: How to create a variable for "Any digit"?

Post by martin »

There are many ways to skin this cat 🙀 I'd probably want to ask myself: how can I leverage the power of regular expressions (regex). Philip already provided a nice solution that lets you use \d to match any digit.

However, I'd prefer to generalize this so your code doesn't have to care about the length of text you want to match, eg: one or more characters. You can instead just search backwards from the current selection location for any regex:

Code: Select all

$selection = TextSelection.active
$searchRange = Range.new(0, $selection.location)
$foundSelection = $selection.text.find('\d+', 'Er', $searchRange)
If $foundSelection && ($foundSelection.bound == $selection.location)
	Prompt "Found the digit."
Else
	Prompt "Did not find a digit."
End
If your macro does this often enough, it's probably worth turning the code chunk into a handy function:

Code: Select all

Define Command IsSelectionPrecededBy($pattern)
	$selection = TextSelection.active
	$searchRange = Range.new(0, $selection.location)
	$foundSelection = $selection.text.find($pattern, 'Er', $searchRange)
	Return $foundSelection && ($foundSelection.bound == $selection.location)
End

If IsSelectionPrecededBy('\d+')
	Prompt "Found the digit."
Else
	Prompt "Did not find a digit."
End

User avatar
phspaelti
Posts: 1180
Joined: 2007-02-07 00:58:12
Location: Japan

Re: How to create a variable for "Any digit"?

Post by phspaelti »

martin wrote:
2021-03-29 11:24:02
$searchRange = Range.new(0, $selection.location)
$foundSelection = $selection.text.find('\d+', 'Er', $searchRange)
That's been one of those features that I have been looking for for some time now. Seems it was introduced in NW 3.0 but it slipped by me for some reason. :drunk:
philip

Þorvarður
Posts: 331
Joined: 2012-12-19 05:02:52

Re: How to create a variable for "Any digit"?

Post by Þorvarður »

Hello Philip and Martin,

Thank you both for valuable help. Before I jump in at the deep end, I would first like to address some of Philips corrections and suggestions.
phspaelti wrote:
2021-03-29 07:02:43
The Find command will return the number of find hits, which in this case should always be 1.
It seems I had forgotten about that. Thanks for reminding me.
if you want to test a variable you will need to use the double equal sign "==", not just a single one.
A cardinal sin. I will remember that.
And you could of course use logical OR (written with a double pipe '||') to test all 10 digits
I tried this, and although a number was found, it doesn't work. What am I doing wrong this time?

$number = Find @Text<\X>, 'E'
if $number == "1||2||3||4||5||6||7||8||9||0"
prompt "It's a number!!"
end

I found this double pipe only twice in the Macro Language Reference, and there was no mention of OR in connection with the double pipe, or I didn't understand it. I know a single pipe in regex means OR. Where can I learn more about a double pipe and how to use it?
I'd also like to point out that a more direct way to get the character you want to test is to use the macro language:

$sel = TextSelection.active
$theFoundCharacterRange = Range.new $sel.location - 1, 1
$theFoundCharacter = $sel.text.substringInRange $theFoundCharacterRange

If you understood all that you can now squeeze the whole logic into one statement like this:

$sel = TextSelection.active
if $sel.text.substringInRange(Range.new($sel.location - 1, 1)).find('\d','E')
prompt 'digit'
else
prompt 'not a digit'
end

But whether that's recommended is another question

This truly reminds me of Mark Twain's essay The Awful German Language (1880):

"There are ten parts of speech, and they are all troublesome. An average sentence, in a German newspaper, is a sublime and impressive curiosity; it occupies a quarter of a column; it contains all the ten parts of speech — not in regular order, but mixed; it is built mainly of compound words constructed by the writer on the spot, and not to be found in any dictionary — six or seven words compacted into one, without joint or seam — that is, without hyphens; it treats of fourteen or fifteen different subjects, each inclosed in a parenthesis of its own, with here and there extra parentheses which reinclose three or four of the minor parentheses, making pens within pens: finally, all the parentheses and reparentheses are massed together between a couple of king-parentheses, one of which is placed in the first line of the majestic sentence and the other in the middle of the last line of it — after which comes the VERB, and you find out for the first time what the man has been talking about; and after the verb — merely by way of ornament, as far as I can make out — the writer shovels in "haben sind gewesen gehabt haben geworden sein," or words to that effect, and the monument is finished."

User avatar
martin
Official Nisus Person
Posts: 4935
Joined: 2002-07-11 17:14:10
Location: San Diego, CA
Contact:

Re: How to create a variable for "Any digit"?

Post by martin »

phspaelti wrote:
2021-03-29 18:03:27
martin wrote:
2021-03-29 11:24:02
$searchRange = Range.new(0, $selection.location)
$foundSelection = $selection.text.find('\d+', 'Er', $searchRange)
That's been one of those features that I have been looking for for some time now. Seems it was introduced in NW 3.0 but it slipped by me for some reason. :drunk:
I'm glad you'll get some mileage from this new(ish) feature! Thanks for all you do to share your expertise with everyone here :)

User avatar
martin
Official Nisus Person
Posts: 4935
Joined: 2002-07-11 17:14:10
Location: San Diego, CA
Contact:

Re: How to create a variable for "Any digit"?

Post by martin »

Þorvarður wrote:
2021-03-30 07:53:39
I tried this, and although a number was found, it doesn't work. What am I doing wrong this time?

$number = Find @Text<\X>, 'E'
if $number == "1||2||3||4||5||6||7||8||9||0"
prompt "It's a number!!"
end
The double pipe "or" operator is covered only briefly in the Nisus Writer macro language reference. The guide doesn't do a great job explaining how to use the operator. You can use it to combine multiple boolean ("truth") tests. You don't use it inside a string. You use it to join together bits of code like so:

Code: Select all

$number = Prompt Input "Enter a number between 1 and 10"
If ($number < 0) || ($number > 10)
	Prompt "Your number ($number) is out of bounds."
End
So comparing all the different digits you would join together macro code like ($number == 1) || ($number == 2) || ($number == 3) || ...
This truly reminds me of Mark Twain's essay The Awful German Language (1880):
It's always fun to read something sharp from Twain (I like Ambrose Bierce too). I have German some heritage myself, but I certainly don't take any offense! I do find German compound words to be very practical (especially coming from a programming background which leverages the same technique) but I know they can be unwieldy.

User avatar
martin
Official Nisus Person
Posts: 4935
Joined: 2002-07-11 17:14:10
Location: San Diego, CA
Contact:

Re: How to create a variable for "Any digit"?

Post by martin »

Þorvarður wrote:
2021-03-29 05:50:26
I need to find out whether the character that precedes the insertion point is a number or not. In case it's a number, the macro will do some stuff. In case it's not, the macro will do some other stuff. How can I do that?

I tried
$theFoundCharacter = Find @Text<\X>, 'rE'
I think we're a bit beyond this original question now, but I just remembered something related that is very useful. You can use the $ or ¢ find option in Nisus Writer to automatically turn find/regex captures into macro variables. Your code to find, select, and extract the prior character could be:

Code: Select all

If Find @Text{(\X)}, 'rE$'
	$theFoundCharacter = $1
	Prompt $theFoundCharacter
End
The $ option instructs Nisus Writer to place the first capture inside a variable called $1. The second capture is in $2, and so on. If you're comfortable with regex named captures, you can bypass the cryptic variable names like so:

Code: Select all

If Find @Text{(?<theFoundCharacter>\X)}, 'rE$'
	Prompt $theFoundCharacter
End

User avatar
phspaelti
Posts: 1180
Joined: 2007-02-07 00:58:12
Location: Japan

Re: How to create a variable for "Any digit"?

Post by phspaelti »

martin wrote:
2021-03-30 08:54:18
So comparing all the different digits you would join together macro code like ($number == 1) || ($number == 2) || ($number == 3) || ...
… and with the added wrinkle that you will need to further combine the expressions pairwise with further parentheses.
Þorvarður wrote:
2021-03-30 07:53:39
This truly reminds me of Mark Twain's essay The Awful German Language (1880):
:lol:

That's one of my favorites too!
philip

Þorvarður
Posts: 331
Joined: 2012-12-19 05:02:52

Re: How to create a variable for "Any digit"?

Post by Þorvarður »

Thanks for explaining the double pipe "or" operator, Martin. Very well explained.
It's always fun to read something sharp from Twain […] I have German some heritage myself, but I certainly don't take any offense! I do find German compound words to be very practical
The Twain text was actually meant for Philip, because this satire is only funny for those who know German very well, and since Philip is a linguist I assumed he knows German. But I may be wrong.

In the text I quoted we find keen linguistic observation and hyperbolic nonsense side by side. ""haben sind gewesen gehabt haben geworden sein," or words to that effect" is an echo of true common closing phrases, but in this form here the phrase is grammatically completely unacceptable, and that's what is funny for those who know German well. It also shows that the writer (Twain) doesn't understand the construction, and falsely interprets it as an ornament. Here I saw a close resemblance between Twain and myself (regarding the macro language. :–)
Twain also makes fun of the German verb syntax; the distance between the auxiliary verb and the main verb can indeed be considerable. Examples can be found where, let's say, a sentence begins on page 5 and you have to turn over to page 6 to know what the author actually intends to say because the verb is at the end–it's the grand finale, so to say. This is the exception though, and such monster sentences can only be found in the written language. But there is a reason that people still keep telling jokes like this one:

A foreign ambassador to Germany visits the German Chancellor Bismarck. Bismarck gives a speech. He speaks loud and is obviously full of emotion. The interpreter remains silent. After five minutes the ambassador whispers to the interpreter: "What's he saying?" The interpreter leans towards the ambassador and whispers back: "I don't know. I'm waiting for the verb."
Last edited by Þorvarður on 2021-03-31 08:42:14, edited 1 time in total.

User avatar
martin
Official Nisus Person
Posts: 4935
Joined: 2002-07-11 17:14:10
Location: San Diego, CA
Contact:

Re: How to create a variable for "Any digit"?

Post by martin »

Þorvarður wrote:
2021-03-30 13:04:47
A foreign ambassador to Germany visits the German Chancellor Bismarck. Bismarck gives a speech. He speaks loud and is obviously full of emotion. The interpreter remains silent. After five minutes the ambassador whispers to the interpreter: "What's he saying?" The interpreter leans towards to the ambassador and whispers back: "I don't know. I'm waiting for the verb."
It's a good joke! :lol: I'm reminded of a passage from Sherlock Holmes:
And the man who wrote the note is a German. Do you note the peculiar construction of the sentence—‘This account of you we have from all quarters received.’ A Frenchman or Russian could not have written that. It is the German who is so uncourteous to his verbs.

Post Reply