Evaluation brackets

Authorjaytea, 2011/10/17 23:51

Intro

A lot of people use evaluation brackets in day to day scripting, often to good effect, but I'm sure there are still a large majority of people out there who see a large expression containing a confusing arrangement of brackets and nested brackets, and are left wondering whether $eval() would be a more readable and more easily understood alternative. In a lot of cases, yes, it probably would. But wouldn't you like to know that for certain? To know precisely what evaluation brackets can and can't do for you is what I hope to explain in the following tutorial.

Your first encounter might well be with dynamic variables, creating nickname specific vars to hold information about passwords for example. A format you might recognize is %var [ $+ [ $nick ] ] to retrieve the value of these types of variables. I'll get into how that works a bit later, for now I'd like to start at what I consider to be the beginning.

Order of Evaluation

Every line you see in the following kind of gray box, starting with a '/' command slash, is a command that you should copy and paste into the editbox of any window of your choice in mIRC, so that you may observe, first hand, the effect of the given examples.

/alias ev inc -u %ev | echo -ag $ord(%ev) $+ : $1 | return $1

Let's use that alias as a visual aid. If you have a global variable named %ev you'll want to rename or remove it for the time being. Now we can experiment, using the following line as a template:

//echo -ag $ev(a) $ev(b) $ev(c) $ev(d)

As each of those $ev()s evaluate, we can of course determine the order of evaluation based on the information in the echos. The above command yields the following list:

1st. a
2nd. b
3rd. c
4th. d

As you can see, and what you probably already know, is that code evaluates from left to right. What if you want $ev(d) to evaluate first, without disrupting their order in the line? This is where evaluation brackets come into play:

//echo -ag $ev(a) $ev(b) $ev(c) [ $ev(d) ]

Now lists:

1st. d
2nd. a
3rd. b
4th. c

One thing to note, since it may not be intuitive to some people. Although we're changing the order of evaluation here, we're not changing the position of any pieces of the line. This is why, even though $ev(d) evaluated first, it retains its position at the end of the line and a b c d is echoed in these examples.

You can think of it in the following way, mIRC starts scanning the line starting from the very left. If it finds an open evaluation bracket, it enters that group and keeps looking for another, deeper set of nested brackets. As soon as it finds a closing bracket, it considers it to correspond to the last found open bracket so it stops searching for the moment, it has found the first thing it needs to evaluate. Note, I don't pretend to know detailed information about mIRC's parser, I'm just explaining what I understand about how the brackets manipulate the order in which pieces of code are evaluated. Going back to our testing, hopefully it's clear that if we now wanted the order to be "c → b → a → d", we could use the following:

//echo -ag $ev(a) [ $ev(b) [ $ev(c) ] ] $ev(d)

Resulting in the desired list:

1st. c
2nd. b
3rd. a
4th. d

Like I mentioned, we start from the left until an opening evaluation bracket is met, just before the $ev(b). But it doesn't stop there, another opening evaluation bracket is encountered just before $ev(c). When the closing bracket is found, we know to evaluate inside the group defined by the last opened evaluation bracket that was found and this closing evaluation bracket. So $ev(c) is evaluated. Now we can imagine that the [ $ev(c) ] chunk is out of the picture, so to speak. This leaves us with $ev(b) being the next item to evaluate, since it's now in the first complete group of evaluation brackets. Once that's finished with, we're left with $ev(a) $ev(d). No brackets, so it simply evaluates from left to right as normal.

Alright, so how do we completely reverse the order of evaluation? Pretty easy, it's just:

//echo -ag $ev(a) [ $ev(b) [ $ev(c) [ $ev(d) ] ] ]

Nothing different there, just another group of brackets operating in exactly the same way as before. You can play around with that some more, trying to come up with specific arrangements. Some are impossible, such as "d → b → a → c" simply due to the nature of the way evaluation brackets work. You can now remove the alias ev, it's no longer needed in this tutorial:

/alias ev

Here's a more practical example:

//invite $input(And who would you like to invite to $! $+ ?,eo) [ #$$input(Which channel would you like to invite to?,eo) ]

The syntax for /invite is /invite nick channel, so what if you want to prompt for the channel name first, without affecting the order in which the parameters are sent to the server? Instead of using any extra commands, you can very simply accomplish this by forcing the second $input() to evaluate first with evaluation brackets. The first $input() is then able to use in its prompt the result of the channel prompt which occured first, with $!

The next topic of discussion is a brief one, it's merely an extension of the aforementioned behavior. Evaluation brackets can be used inside identifiers to pre-evaluate certain pieces of code that may contain either more code you want re-evaluated, or such things as commas and parentheses you want evaluated as part of identifier syntax rather than simply plaintext. In case that sounded a bit unclear, here's an example of what I mean:

//var %a = 2,32 | echo -ag $gettok(lol rofl lmao, [ %a ] )

Notice the evaluation brackets require spaces on both sides. To operate correctly, you should always space them out as above.

What happened when the brackets were used shouldn't be too difficult to understand. Before the whole $gettok() was evaluated, mIRC scanned the line for evaluation brackets as it does. [ %a ] was found and consequently evaluated resulting in $gettok(lol rofl lmao, 2,32 ). Then it goes through and evaluates the identifier as it normally would to give you the expected value. Now try removing the brackets, you should get a * Too few parameters: $gettok error.

If you consider $gettok as a custom identifier; in the case where evaluation brackets weren't used, from inside a gettok alias the number of parameters, $0, would be 2. First parameter would be lol rofl lmao, second parameter would be 2,32. mIRC's $gettok() then throws an insufficient parameters error since it requires an additional parameter. In the case where the brackets were used, this would be different. First parameter is the same, but this time the second parameter is 2, and third is 32.

So when pieces of code contained in brackets are used inside identifiers, they're pre-evaluated, then evaluated again as potential code when the identifier itself evaluates. To further explain this, consider the following:

//var -s %z = x,$me,x | echo -ag $+( [ %z ] )

Just as before, %z evaluates early and the result is $+( x,$me,x ). When the identifier evaluates, it treats the commas as parameter separators, and of course $me is evaluated since it is standard behavior for an identifier to evaluate each of its parameters once. Evaluation brackets can not only help bring commas 'to life' in identifiers, but other parts of syntax such as closing parentheses are honored:

//var %x = x,$me,x) | echo -ag $+( [ %x ] * xyz

The above is valid, since the closing ) in %x's value is taken to be the closing parenthesis that corresponds to the opening one in $+( after %x pre-evaluates. The parentheses have been colored differently to help you identify this correspondance. Although it's true that $eval() can be used, with some manipulation, to achieve the examples I outlined, understanding this technique of evaluation brackets will hopefully clear up some perplexities you may have later.

Now that you've seen and, with any luck, understood how evaluation brackets play a unique role in controlling the order of evaluation, let's move on to something equally simple and a feature you could relate more to $eval()

Number of Evaluations

This part is simple to understand if you've encountered the concept of evaluating a piece of code numerous times before. If you're new to this, read the following explanation. When mIRC interprets a line of code, such as in remotes or aliases, it evaluates every part of the line once. The value of each variable or identifier is retrieved, and shoved into the result as appropriate. If %x has the plaintext value $me and you echo it's value with //echo -ag %x, plaintext $me is printed to the screen, not its value, which means the line was evaluated once. If it had been evaluated any more times, we would have seen the value of $me

Now when I said mIRC evaluates each part of the line once, that was in the trivial case where such things as $eval() aren't used, nor are particular arrangements of evaluation brackets. Here's a small example:

//echo -ag [ $!me ]

One single pair of evaluation brackets means only one evaluation. The result of pre-evaluating $!me, which is plaintext $me, isn't evaluated an extra time as it would have been inside of an identifier like in our previous examples. Practically, when you're outside identifiers, single evaluation brackets around a particular identifier or variable, as in my example, are redundant since mIRC normally would evaluate it once. Now, they're redundant unless you have a specific reason for changing the order of evaluation, but this is not the case with my example. It was the case in the example I stated a while ago concerning $input(), for the reasons mentioned.

You can force a specific sequence of code to evaluate more times than once simply by enclosing it in more brackets:

//echo -ag [ [ $!me ] ]

This time the value of $me is displayed, as two pairs of evaluation brackets means two evaluations. The first evaluation turned $!me into plaintext $me. The second evaluation gave us the value of $me. What I meant by specific sequence of code, was that whatever you're sticking inside the brackets must adhere to certain guidelines for all sets of brackets to take effect. The piece of code inside the innermost brackets, the one that you are attempting to evaluate multiple times, may not contain a space. If it does contain a space, then it must be handled with $+ (and we'll discuss $+'s uncharacteristic behavior within evaluation brackets next section) or $++, an outdated identifier which still exists and appears to operate like $+ with the difference being evaluation brackets don't change its behavior. Note $++() doesn't exist, it's used without parameters. I don't recommend anyone starts going around using it everywhere, just be aware of it and what it can do.

So, to summarize:

//echo -ag [ [ [ $!me $!calc(1+1) ] ] ]

Although three sets of evaluation brackets enclose the code, it is still evaluated only once, as if we were simply doing //echo -ag $!me $!calc(1+1). The reason for this, as I said, is that if the inner code contains a space with no $+ or $++ to 'correct' it, the extra evaluation brackets become useless. Different situations lend themself to different workarounds of course. The obvious one here is simply do away with the evaluation brackets and the ! that escapes each identifier. Or, use $eval() which doesn't have this restriction on spaces. But for sake of completing the example, here's one general purpose workaround:

//echo -ag [ [ [ $+($!me,$chr(32),$!calc(1+1)) ] ] ]

There we go, no more spaces and so it works as we need it to. Let us move on to the last aspect of evaluation brackets which is, to me, the most confusing part and hardest to get your head around. This is the way $+ behaves when it finds itself between a pair of brackets. Note we'll be dealing with $+ with no parameters, $+() is not under inspection as its behavior is no different to how it normally works.

The [ A $+ B ] case

Consider the following case:

//echo -ag [ $!!a $+ $b ]

That results in $a$b. What occured was, $!!a was evaluated once (to give $!a) and $b was evaluated no times. The strings $!a and $b were then combined to produce $!a$b and the result evaluated to leave you with $a$b being echoed. Are you wondering if instead $!!a evaluated twice and attached itself to the plaintext $b? That example may have been a bit ambiguous, but I merely wanted to introduce this concept. Here's a better, but more involved example:

//var -s %x$me = judge judy,%y = % $+ x | echo -ag [ %y $+ $me ]

This is no more different than what we saw with $!!a and $b. This time, with %y $+ $me, first %y is evaluated (to produce plaintext %x), then the value of %y is combined with plaintext $me to give you the string %x$me. That string is then evaluated, and judge judy is echoed. Let us summarize this as our first rule of $+:

[ A $+ B ] <=> $eval($eval(A,1) $+ $eval(B,0),2)

Where A and B represent single elements i.e. pieces of code that do not include spaces. Think of it as analagous to the guidelines I provided for using code in between multiple pairs of evaluation brackets, and the cases for which the brackets are successful. If you use spaces inside A or B that should not be there, you will run into behavior which may go beyond the rule mentioned here. By the way, A or B can even be a pair of evaluation brackets including an element of code, as the following trivial example shows:

//echo -ag [ [ $!!!a $+ $b ] $+ $c ]

[ $!!!a $+ $b ] can be thought of as A, and $b as B where the second $+ is the main connective. Inside that first complete group of brackets, we have yet another case of [ A $+ B ] where this time A = $!!!a and B = $b. This complicates it, but doesn't make it any less easy. [ $!!!a $+ $b ] evaluates as described in our rule to result in $!a$b. Once that has evaluated, in our larger group of evaluation brackets, element A has just evaluated once. Now our other $c is combined with it to give $!a$b$c, and the result evaluated to leave $a$b$c.

Remember, although I stated the rule as an equivalency, you shouldn't blindly consider $eval() to work exactly the same as evaluation brackets for reasons that I've selectively mentioned earlier. To those of you who are more comfortable with $eval(), that equivalence statement should sum up the rule&nbsp;in an understandable fashion. For those who aren't, I'll outline the steps that are taken when something of the form [ A $+ B ] is used:

  • A evaluates once
  • The value of A is combined with plaintext B
  • The result is evaluated once

Ok, hopefully you understand how $+ works when it's in the middle of what I referred to as two elements of code. But what if we encounter something of the form [ A $+ B $+ C ]? This is pretty much the same as what we saw earlier, and can be represented as follows:

[ A $+ B $+ C ] <=> [ [ A $+ B ] $+ C ] <=> $eval($eval($eval($eval(A,1) $+ $eval(B,0),2),1) $+ $eval(C,0),2)

You might notice a pattern emerging, one that allows us even to predict how something as large as [ A $+ B $+ C $+ D $+ …. $+ Z ] will evaluate (where …. is used to represent the missing string of E to Y). The special behavior of $+ is applied, starting at A, evaluating from left to right. Each time this process is completed for one $+, it moves on to the next one in turn. In fact, the whole thing is just a repeat of the simple case [ A $+ B ] many times. So if you understand the first rule, you should be making some sense out of this one, as it is essentially the same principle. We can sum it up in another line:

[ A $+ B $+ C $+ ... $+ Z ] <=> [ .... [ [ [ A $+ B ] $+ C ] $+ D ] $+ .... $+ Z ] &lt;=&gt; $eval(.... $eval($eval($eval($eval($eval(A,1) $+ $eval(B,0),2),1) $+ $eval(C,0),2),1) $+ $eval(D,0),2),1) $+ .... $+ $eval(Z,0),2)

I wouldn't consider this to be a rule, since it is explained in terms of the simple [ A $+ B ] case (notice the re-occuring pattern in the line), which is the only rule you should take away from this section. ….. represents where code has been left out to conserve space. Enough about the [ A $+ B ] situation though, let us move on to the last feature of evaluation brackets I will be talking about.

The A [ $+ B ] case

This topic relates to the %var [ $+ [ $nick ] ] you might already be familiar with. I have tried to consider a summary for this rule in terms of the earlier one, but couldn't come up with any logical explanation for its behavior. When $+ is at the start of an evaluation bracket, it works as described by the following rule:

A [ $+ B ] <=> $eval($eval(A,0) $+ $eval(B,0),2)

This might look different to %var [ $+ [ $nick ] ], but it's really the same. A is %var, and B is [ $nick ] (remember I mentioned that our code elements could themselves be an evaluation bracket group). $nick evaluates first, as we know, since it is within the first complete group of brackets, leaving us with what could be thought of as %var [ $+ <value of $nick> ]. Now the $+ is interpreted, so it takes the plaintext %var and combines it with our B which is <value of $nick>. Since $nick evaluated earlier than the rest of the line, we actually have its value now. The result of this concatenation is then evaluated, to give us our final value.

For those of you who still don't fully grasp this concept, I'll explain A [ $+ B ] in the following way:

  • Plaintext A is combined with plaintext B (if B was pre-evaluated i.e. is of the form [ C ], then plaintext B of course refers to the resulting value)
  • The result of this combination is evaluated.

What $+ at the beginning of the group also does, is it appears to change the behavior of all $+s that lie inside the portion of the group in which the original $+ exists. What I mean by this is, in the case A [ $+ B $+ C $+ [ D $+ E ] $+ F ], the behavior of all $+s except the one inside the nested [ D $+ E ] group are changed. [ D $+ E ] is still treated exactly as we know it (the [ A $+ B ] rule), since it evaluates before the rest of the line. All other occurences of $+ however are affected by that $+ at the beginning. No longer do you get any iterative behavior, this case is actually more simple. Let me explain it with our final rule:

A [ $+ B $+ C $+ D $+ .... $+ Z ] <=> $eval($eval(A,0) $+ $eval (B,0) $+ $eval(C,0) .... $+ $eval(Z,0),2)

Nothing fancy is happening this time, all elements are simply lined up together then evaluated at the end. That covers all I have to say about this topic, and if you've read and understood all of this material, that's excellent. You will no longer be intimidated by a great big mess of brackets, and by considering all the aspects of their usage, should be able to break these huge expressions down piece by piece until they become easily understandable.

I will mention a couple more things though. If you encounter $+ at the end of the contents of a pair of evaluation brackets, it's almost certainly being used incorrectly. [ A $+ ] B seems to work the same as [ A $+ B ] and the same can be said for [ A $+ ] B $+ C $+ … $+ Z being similar to [ A $+ B $+ C $+ … $+ Z ]. The behavior is carried over to outside the evaluation brackets, which I consider to be a bit intrusive. And buggy, for example:

//echo -ag [ $!+(x $+ ] ,y)

Reports a * Break: evaluation error whereas:

//echo -ag [ $!+(x $+ ,y) ]

Works as it should. It's for this reason, and the previously mentioned one, that I don't cite this as a formal rule. It's something that should be recorded in your brain in case you ever come across it and wonder what it does.

One final thing to note is how to escape evaluation brackets. [[ evaluates to give [, and ]] evaluates to give ]. You can use this to escape evaluation brackets in the same way that you would use $!me to escape $me:

//echo -ag [[ [[ $!me ]] ]]

Note that inside identifiers, [[ is not the same as $chr(91); the evaluation of [[ and ]] into [ and ] respectively seems to happen at the same "stage" as the processing of single [ and ]. This is perhaps clearer with an example:

//var %a = hello | echo -ag $upper( [[ %a ]] )

If [[ was treated as $chr(91), you would expect to see "[ HELLO ]". Instead, you see "HELLO", which means that [[ and ]] were "pre-evaluated" in the same sense as [ ]. This becomes slightly more serious in cases like:

//tokenize 32 $!findfile(C:,*,1) | echo -ag $lower( [[ [[ $1 ]] ]] )

which echoes c:\autoexec.bat here (this isn't a terribly realistic example, as quite possibly one would use [[ and ]] outside $lower, but you get the point).

Conclusion

I'll finish up by summarazing the rules that we have learnt over the course of this tutorial:

  • mIRC pre-evaluates groups of evaluation brackets by starting from the left and looking for the next complete group of brackets. It repeats this until no groups of evaluation brackets are left.
  • When evaluation brackets are used inside an identifier, the group is pre-evaluated such that when the identifier itself evaluates, it interprets all commas as parameter separators (including other syntax such as closing parentheses) as well as evaluates any code in its parameters once as normal.
  • The definition I use for an element of code is one that includes no spaces other than when $+ (not $+()), $++,surrounding evaluation brackets, even nested ones (providing their contents obeyes this rule, and so on) are used. $++ is an outdated identifier that appears to function exactly as $+ except in between evaluation brackets where $++ neutralizes the special behavior that $+ has. Again, I don't promote its use, just making you aware of it. In most cases, you could in fact simply enclose the items you wish to combine in $+()
  • Using the definition of an element &nbsp;of code, we can now say that extra groups of evaluation brackets around an element of code will evaluate it the corresponding number of times. So [ [ [ [ A ] ] ] ] means A will be evaluated four&nbsp;times.
  • [ A $+ B ] <=> $eval($eval(A,1) $+ $eval(B,0),2) and [ A $+ B $+ C ] <=> $eval($eval($eval($eval(A,1) $+ $eval(B,0),2),1) $+ $eval(C,0),2), where A, B and C are elements of code. The second is merely a repeated case of the first, and the case repeats further as you add more and more $+s and code elements in that fashion.
  • A [ $+ B ] <=> $eval($eval(A,0) $+ $eval(B,0),2) and A [ $+ B $+ C $+ …. $+ Z ] <=> $eval($eval(A,0) $+ $eval(B,0) $+ $eval(C,0) $+ …. $+ $eval(Z,0),2), where A-Z are elements of code. The $+ at the start changes the meaning of each $+ in the middle unless that $+ is in a nested group of evaluation brackets. This means that the elements in the group don't follow under the [ A $+ B ] rule as you'd expect, since this behavior is superceded by the fact that $+ is at the start of the group.
  • Expect that whenever a new group of evaluation brackets containing elements of code is opened, that the behavior of everything inside it (the way its contents evaluate) is defined only by the contents themselves. [ A $+ B [ $+ C ] ] is therefore equivalent to $eval($eval(A,1) $+ $eval($eval(B,0) $+ $eval(C,0),1),2). The first rule is of the type A [ $+ B ] which applies to B [ $+ C ], then the [ A $+ B ] rule takes effect.
  • [[ and ]] escape [ and ] respectively in a line of code.

Most if not all valid uses of evaluation brackets will entail a mixture of the above rules. Work through them in the order that I defined at the very beginning, and with any luck you will master them.

Thanks to qwerty for a post on mirc.com's message board about a couple of years ago which initially gave me a deeper insight as to how evaluation brackets operate. Hope you all enjoyed reading this tutorial :D

eval.txt · Last modified: 2021/05/14 18:47 by saturn
 
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki