Advanced scripting tips

Contributed by Sigh_
Advanced scripting related tips The following are some tips/tricks you can use either to shorten your code, optimize it or make it look more complicated. Some of these tips may appear very purpose specific at first glance but there's a good chance you may come across a code in which it would be useful. Then there are some that will never be useful to anyone

$*

$* is a useful identifier in a lot of cases. What it does is trigger an internal loop in mIRC. It will loop through $1, $2, $3 to the last $N substituting each token into the command in place of $*

 //tokenize 32 a b c | echo $* 

When used efficiently it is a quicker and shorter alternative to using a while loop from 1 to $0. It does have it's limitations however, it will not evaluate properly when used within other identifiers. i.e. echo $address($*,3) inside a popup for example will fail

The non-loop workaround involves using a command such as /scon, /scid or /timer which will re-evaluate the parameters passed

 //tokenize 32 a b c | scon -r echo Length of $* is $!len( $* ) 

Now $* is not inside any identifiers when it is evaluated. $!len( and ) are simply plaintext surrounding $*, spaced so that it may evaluate properly. The r switch of /scon is used so as not to affect the selected connection, be aware of this when using it in your code since it will of course reset the connection to the original one. Therefore, when handing out code it may be a good idea to use /scid $cid in place of /scon -r since you'll never know where the user may place your code. Here's a similar idea with /timer that may help you to see what exactly is going on:

 //tokenize 32 a b c | .timer 1 1 echo Length of $* is $!len( $* ) | timers 

Hopefully you can see what happens immediately after the timer initiates, how using $!len( ) sets you up for the echo. Always be careful when using or giving out code with /timer, /scon or /scid in it. Everything passed to them will be evaluated an extra time, so you need to consider whether this will lead to unwanted evaluations. This point has been stressed by a few others in the past so I won't go into any more detail.

Note the difference between using /scon|/scid and using /timer. The latter will not execute commands immediately, even if the timer delay is 0. That's why you'd probably end up using /scon or /scid in situations involving $*

$()

As you may or may not know, $() is the identifier used to evaluate matchtext in events. In other cases it can be used exactly as $eval(). It's useful when you want to escape certain characters while maintaining a small as possible codesize.

Character $chr method $() method
# $chr(35) $(#,)
{ $chr(123) $({,)
| $chr(124) $(|)
} $chr(125) $(},)

$(string,) is equivalent to $(string,0) i.e. it does not evaluate the string. For # it may be easy to see why we need this comma. $(#) will attempt to evaluate # once, which is the same as $chan. Similarly, evaluating { and } leads to strange results:

 //echo -a $({) -- $(}) 

Considering this behavior, if you ever need: $calc($a -1) $calc($a -1) where $a is any identifier that is sure to return an integer greater than 0, you can replace that code with $({,$a). Hint: this is one of those purpose specific examples I warned you about. However when $a gets very large more odd behavior is encountered that I can't be bothered to investigate. And finally, with $(|) no comma is necessary since the evaluation of '|' simply does nothing.

Another use for $() is when you wish to evaluate an identifier but don't want its result, for example when using $regsub and not needing the number of substitutions made, $findfile or $finddir with a command, $regex when you only want $regml, $input for an ok button, etc. When you use identifiers incorrectly, they usually return $null, e.g. when missing out the first parameter in $and() with $and(,code). The 'code' will still be evaluated, but the whole $and() identifier will return $null.

But why use $and when you can use something shorter, like $()? $(,code) would be possible except for a bug in $() that causes $(,code) to be treated as $(code,2) or $evalnext(code). The solution is to add another null parameter, $(,,code)

 //var %i | echo The size of my program files dir is $(,,$findfile(C:\program files,*,0,inc %i $file($1-))) $bytes(%i).suf 

Apologies if that hung your client a bit. Important thing to consider is whether this use of $() will remain consistent over time, such as the use of $lof() in place of $file() which is why I didn't use it in that last example. Some people argue that this method shouldn't be used as it may depreciate in future versions. They're right, it may.

Loops

Loops are one of the most frequently used mIRC concepts, so it's always best to ensure you are using them the most efficiently. Sometimes when you're looping with a variable %i or %x etc., it may well be possible to eliminate it. For example, echoing a list of people in the active channel separated by a space:

 
//var %i = 1,%o | while ($nick(#,%i)) { %o = %o $v1 | inc %i } | echo %o
//while ($nick(#,$0)) tokenize 32 $1- $v1 | echo $2-

Both methods yield the same output. In the second, the use of a looping variable %i is eliminated because as the loop iterates, $0 increases by one naturally. However, during the first iteration $0 was 0, so $nick(#,0) was added to the front of the variable, we need to echo $2- as opposed to $1-.

This again may appear to be too specific, but you can apply similar logic to other loops to eliminate the looping variable and shorten/quicken code.

Evaluation brackets and more $()

You might want to store data, rgb values for example, in their comma-inclusive format such as 0,0,127 (blue) in a var named %color. Then you may want to convert it with $rgb for use in /draw commands. $rgb(%color) would take the literal string '0,0,127' as its first parameter, the commas would not be evaluated as anything special. If you want these commas evaluated you need something extra, here are 2 methods:

 //var %color = 0,0,127 | echo -a $rgb( [ %color ] ) -- $($!rgb( %color ),2) 

Notice the evaluation brackets must be spaced from the parentheses. The $() method first evaluates $!rgb( ) and %color in between to give $rgb( 0,0,127 ) then evaluates the result again to return the rgb value.

Appending to $N

It's possible to attach characters directly to the end of $N identifiers excluding $0:

 //tokenize 32 a b c d | echo -a $1.. $2.. $1-3.. $3-... 

The only things that cannot be appended are numbers, and a hyphen -. $1a4 will not act as $1 $+ a4 and will instead return only $1. Also note that $1.. will eat those .s if $1 is $null. This again is different to $1 $+ .. in that way.

$mid and $len to eliminate $calc

Suppose $1 returns a string that isn't $null

 $calc($len($1)+1) = $len($1.)
 $calc($len($1)+2) = $len($1..) 
So to add N you append N .s to the back of $1. Obviously as you start adding more this method gets longer. Similarly:

 $calc($len($1)-1) = $mid($1,2,0)
 $calc($len($1)-2) = $mid($1,3,0)
 $calc($len($1)-N) = $mid($1,N+1,0)

Of course to use this method N would have to be <= $len($1)

$asc

$asc will only consider the first character of the given input.

 $asc($left(string,1)) = $asc(string) 

If statements

When using an equals to comparison in an if condition or $iif, beware that it looks at both parameters as numbers too.

 //echo $iif(0.1 == .1,yes,no) -- $iif(00000000000000000.1 == 0.1,yes,no) 

This can be avoided by adding an extra couple of characters to each side of the == so that they're no longer treated as numbers.

 //var %a = 0.1,%b = 0.1000 | echo $iif(%a == %b,yes,no) -- $iif(%a . == %b .,yes,no) 

After adding a space and . to each side, they're no longer seen as being equal to each other.

$istok

Assume %a and %b are 2 strings which don't contain chr 01:

 $iif(%a . == %b .,$true,$false) = $istok(%a,%b,1) 

The reason for the .s is explained in the previous topic. If they do happen to contain chr 01 then you can change the last parameter of $istok accordingly.

$comchan

Some people loop through $chan() expecting each name returned to be a channel that you're in. What they overlook is that $chan() may return a channel whose window is open but that you might not be in, you may have the 'keep channels open' option enabled. In cases where you need to loop through channels that you're actually on it's best to loop through $comchan($me,N) instead of $chan(N) if you expect you'll fall into the $chan() trap.

$str

When you're dealing with custom identifiers that return different output for the same input each time you call them (or $r aka $rand) and want to get a string of results from that identifier, you may notice putting it in $str directly doesn't work.

 //echo $str($r(a,z),9) 

This gives 9 of the same character instead of re-evaluating $r(a,z) each time. You shouldn't expect it to behave any other way. But if you actually want a string of 9 random letters an alternative to looping can be seen below:

 //echo $(,$str($!r(a,z) $!+ $chr(32),9)) 

What this does is first create the string with $str: "$r(a,z) $+ $r(a,z) $+ $r(a,z) .." then it evaluates the result since $(,string) was used, equivalent to $eval(string,2). Unfortunately this method does have a drawback, it is subject to mIRC's string length limitation of around 950 characters. The longest random string you can generate with my above example is 79 letters.

/filter

If you want to count the number of matching lines in a file without wanting to transfer data in any way, filtering to a filename such as NUL will speed up processing and also will not leave you with a file containing irrelevant data.

 //filter -ff mirc.ini NUL *text* | echo -a $filtered -- $file(NUL) 

This is because NUL represents something within the system and mIRC will not attempt to write to it. This technique can also be used when debugging to an alias (/debug -i). It requires that you specify a filename, you can just enter NUL and all raw msgs will only be passed to the alias.

/set and /var

Both /set and /var have the ability to perform a single mathematical operation involving +-*/^%:

 //var -s %a = 1 + 2 , %b = %a * 3 , %c = %b % 5 

This can be useful when you are aware it's happening. A lot of people use local variables in text events and input events etc. as /var %text = $1- not realizing that if $1- ever happened to be a mathematical operation, that it would be evaluated by /var when you don't want it to. To workaround this, you could declare the variable locally first, then use /set -n:

 var %text | set -n %text $1- 

/set will first look for a local variable, if it finds one then it will deal with that. If not then it moves onto global variables. Having said that, you could shorten the above line by using the undocumented l switch of /set which declares variables locally:

 set -ln %text $1- 

/set -l is useful in cases where you have complex variable name (such as /var %a $+ %b):

 //var -s %b = 1 , %a $+ %b = 2 

The = is included in the value of %a1. This buggy behavior can be further seen when trying to assign it a value of an identifier including a comma then variable name:

 //var -s %b = 1 , %a $+ %b = $gettok(x y,%b,32) 

This indicates that the = is not being evaluated as part of /var's syntax throwing an 'invalid format' error as if the = wasn't there. One way around this is to use /set -l to declare %a1:

 //var -s %b = 1 | set -ls %a $+ %b $gettok(x y,%b,32) 

$or and $int

In cases where N is a positive number:

 $int(N) = $or(N) 

As you can see the codesize is greatly decreased. Not too important, but useful in codesize challenges

$replace and $remove

These 2 can often be combined when used together:

 $replace($remove(string,%x,%y,%z),%a,%b,%c,%d) = $replace(string,%x,,%y,,%z,,%a,%b,%c,%d)
 $remove($replace(string,%a,%b,%c,%d),%x,%y,%z) = $replace(string,%a,%b,%c,%d,%x,,%y,,%z,) 

Replacing with $null is the same as removing

$findfile and $finddir

If you want to perform more than one command on each file/dir in these identifiers, you can use /scon or /scid (for immediate performance) or /timer:

 //.echo -q $findfile($mircdir,*.exe,0,scon -r echo Command1 on $1- $(|) echo Command2 on $1-) 

You'll see that | needs to be escaped with $(|) for obvious reasons

Parentheses in identifiers

When an opening parenthesis is encountered in an identifier, commas are no longer treated as parameter separators. This can be seen here:

 //echo $mid(xyz(,2,3),2) 

No error is thrown since the ending ) was within the first parameter, it works as expected. A good use for this behavior is in regex when you want to specify a quantifier such as {3,4}. The comma cannot be escaped with any regex escapes and still match correctly. Instead of a long bit of business involving $chr(44), or a local variable, you can surround the comma with ():

 //echo -a $regex(aaaa,/(?:a{3,4})/) 

There's many more little tips that I may not have mentioned here simply because to me they aren't as important, and the same could be said for what I've discussed in this document to you. But if at least one person found a part of this interesting or useful then my job is done

All content is copyright by mircscripts.org and cannot be used without permission. For more details, click here.