This shows you the differences between two versions of the page.
math_operators [2007/09/01 05:53] |
math_operators [2007/09/01 05:53] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ======Math operators====== | ||
+ | The client's expression mode supports a large number of operators. This | ||
+ | document will explain all of them and the most important concepts. | ||
+ | |||
+ | =====Tokens===== | ||
+ | All expressions are broken into tokens. A token is either an operand, or | ||
+ | an operator. An operand is a "thing" that is operated upon, and an operator | ||
+ | is the action that you apply to operands. In the expression 3 + 4, 3 and 4 | ||
+ | are the operands, and + is the operator. | ||
+ | |||
+ | There are three types of operands: rvalues, lvalues, and numbers. An rvalue | ||
+ | is an operand that provides a value that you can use. An lvalue is an | ||
+ | operand that can be used as the target of an assignment operator. A number | ||
+ | is, well, a number. | ||
+ | |||
+ | As an example, an lvalue is a variable name. In the expression foo = 5, foo | ||
+ | is the lvalue. An rvalue is an operand that is used for its value, rather | ||
+ | than to assign to it. In the expression var1 = var2, var2 is the rvalue. | ||
+ | Rvalues also include string literals. | ||
+ | |||
+ | Numbers are different, because numbers look like lvalues, but behave like | ||
+ | rvalues. Some of the operators behave differently with numbers than with | ||
+ | rvalues. | ||
+ | |||
+ | ^PRECEDENCE ^OPERATOR ^USAGE ^REDUCES TO ^ASSOCIATIVITY ^ | ||
+ | | 1 | Sub-expression | ( **op** ) | **op** | L->R | | ||
+ | | 2 | Logical NOT | ! **bool** | **bool** | R->L | | ||
+ | | 2 | Bitwise NOT | ~ **int** | **int** | R->L | | ||
+ | | 2 | Prefix Decrement | %%--%% **[[lval]]** | **int** | R->L | | ||
+ | | 2 | Prefix Increment | %%++%% **[[lval]]** | **int** | R->L | | ||
+ | | 2 | Suffix Decrement | **[[lval]]** %%--%% | **int** | R->L | | ||
+ | | 2 | Suffix Increment | **[[lval]]** %%++%% | **int** | R->L | | ||
+ | | 2 | Unary Plus | + **float** | **float** | R->L | | ||
+ | | 2 | Unary Minus | - **float** | **float** | R->L | | ||
+ | | 2 | String length | @ [**op**] | **int** | R->L | | ||
+ | | 2 | Word Count | # [**op**] | **int** | R->L | | ||
+ | | 2 | Variable Dereference | * [**rval**] | **lval** | R->L | | ||
+ | | 2 | Variable Dereference | * [**number**] | **rval** | R->L | | ||
+ | | 2 | Double Expansion | ** [**op**] | **op** | R->L | | ||
+ | | 3 | Exponent | **float** %%**%% **float** | **float** | R->L | | ||
+ | | 4 | Multiplication | **float** %%*%% **float** | **float** | L->R | | ||
+ | | 4 | Division | **float** %%/%% **float** | **float** | L->R | | ||
+ | | 4 | Modulus | **float** % **int** | **float** | L->R | | ||
+ | | 5 | Addition | **float** %%+%% **float** | **float** | L->R | | ||
+ | | 5 | Subtraction | **float** - **float** | **float** | L->R | | ||
+ | | 5 | String Catenation | **op** %%##%% **op** | **op** | L->R | | ||
+ | | 6 | Bitwise shift left | **int** %%<<%% **int** | **int** | L->R | | ||
+ | | 6 | Bitwise shift right | **int** %%>>%% **int** | **int** | L->R | | ||
+ | | 7 | Less Than | **op** %%<%% **op** | **int** | L->R | | ||
+ | | 7 | Less than or equal to | **op** %%<=%% **op** | **int** | L->R | | ||
+ | | 7 | Greater than | **op** %%>%% **op** | **int** | L->R | | ||
+ | | 7 | Greater than or equal to | **op** %%>=%% **op** | **int** | L->R | | ||
+ | | 8 | Pattern match | **op** =~ **op** | **bool** | L->R | | ||
+ | | 8 | Pattern doesn't match | **op** !~ **op** | **bool** | L->R | | ||
+ | | 9 | Equal, ignore case | **op** %%==%% **op** | **bool** | L->R | | ||
+ | | 9 | Not equal, ignore case | **op** %%!=%% **op** | **bool** | L->R | | ||
+ | | 9 | Equal | **op** %%===%% **op** | **bool** | L->R | | ||
+ | | 9 | Not equal | **op** %%!==%% **op** | **bool** | L->R | | ||
+ | |10 | Bitwise AND | **int** & **int** | **int** | L->R | | ||
+ | |11 | Exclusive OR | **int** %%^%% **int** | **int** | L->R | | ||
+ | |12 | Bitwise OR | **int** %%|%% **int** | **int** | L->R | | ||
+ | |13 | Logical AND | **bool** %%&&%% **bool** | **bool** | L->R | | ||
+ | |14 | Logical XOR | **bool** %%^^%% **bool** | **bool** | L->R | | ||
+ | |15 | Logical OR | **bool** %%||%% **bool** | **bool** | L->R | | ||
+ | |16 | If-then-else | **bool** ? **op** : **op** | **op** | R->L | | ||
+ | |17 | Assignment | **[[lval]]** = **op** | **op** | R->L | | ||
+ | |17 | Addition-assign | **[[lval]]** += **float** | **float** | R->L | | ||
+ | |17 | Subtraction-assign | **[[lval]]** -= **float** | **float** | R->L | | ||
+ | |17 | Multiplication-assign | **[[lval]]** *= **float** | **float** | R->L | | ||
+ | |17 | Division-assign | **[[lval]]** /= **float** | **float** | R->L | | ||
+ | |17 | Modulus-assign | **[[lval]]** %= **float** | **float** | R->L | | ||
+ | |17 | Bitwise AND-assign | **[[lval]]** &= **int** | **int** | R->L | | ||
+ | |17 | Exclusive OR-assign | **[[lval]]** %%^=%% **int**| **int** | R->L | | ||
+ | |17 | Bitwise OR-assign | **[[lval]]** %%|=%% **int**| **int** | R->L | | ||
+ | |17 | Bitshift left-assign | **[[lval]]** %%<<=%% **int**| **int** | R->L | | ||
+ | |17 | Bitshift right-assign | **[[lval]]** %%>>=%% **int**| **int** | R->L | | ||
+ | |17 | Logical AND-assig | **[[lval]]** %%&&=%% **bool**| **bool**| R->L | | ||
+ | |17 | Logical OR-assign | **[[lval]]** %%||=%% **bool**| **bool**| R->L | | ||
+ | |17 | Logical XOR-assign | **[[lval]]** %%^^=%% **bool**| **bool**| R->L | | ||
+ | |17 | Exponent-assign | **[[lval]]** %%**=%% **float**|**float**| R->L | | ||
+ | |17 | strcat-assign | **[[lval]]** %%#=%% **op** | **op** | R->L | | ||
+ | |17 | String prefix-assign | **[[lval]]** %%#~%% **op** | **op** | R->L | | ||
+ | |17 | Swap values | **[[lval]]** %%<=>%% **[[lval]]** | **[[lval]]** | R->L | | ||
+ | |17 | Last Value | **op** %%,%% **op** | **op** | L->R | | ||
+ | |||
+ | [1] The operand must be an [[lval]]\\ | ||
+ | [3] Short circuit operator.\\ | ||
+ | [4] You do not have to give an explicit operand. If you omit the operand, then [$*] is used as the operand. | ||
+ | |||
+ | =====How operands are handled===== | ||
+ | There are four different kinds of operands | ||
+ | |||
+ | =====Escapes to text mode===== | ||
+ | [...] | ||
+ | {...} | ||
+ | '...' | ||
+ | "..." | ||
+ | |||
+ | =====Epic-only operators===== | ||
+ | |||
+ | |||
+ | =====Operators that behave different in epic===== | ||
+ | |||
+ | =====How errors in expressions are handled===== | ||
+ | |||
+ | The string concatenation operators, ##, #=, and #~, are a special case, as they | ||
+ | are not present in C or C++. As their name indicates, they are used to join | ||
+ | two or more strings together, end to end. For example: | ||
+ | |||
+ | @ foo = [foo] ## [bar] /* sets $foo to "foobar" */ | ||
+ | @ foo #= [blah] /* sets $foo to "foobarblah" */ | ||
+ | @ foo #~ [hmm] /* sets $foo to "hmmfoobarblah" */ | ||
+ | |||
+ | Also like C/C++, parentheses may be used to force certain parts of the | ||
+ | expression to be evaluated first (mainly in the event that the user wishes | ||
+ | for it to evaluate in an order other than that of operator precedence). | ||
+ | Parentheses may be nested. For example, if some variable $foo is set to 3: | ||
+ | |||
+ | foo * 4 + 5 /* returns 17 */ | ||
+ | foo * (4 + 5) /* returns 27 */ | ||
+ | 4 + ((foo + 9) / 3) /* returns 8 */ | ||
+ | |||
+ | All assignment operators always return the value assigned, which allows for | ||
+ | the assignment of multiple variables at once. Keep in mind that expressions | ||
+ | are evaluated right to left. For example, if $foo is 12 and $bar is 11: | ||
+ | |||
+ | @ foo += bar *= 2 /* $bar is 22, $foo is 34 */ | ||
+ | |||
+ | Since the release of the EPIC4 pre-betas, the client has been growing ever | ||
+ | more perlish. Like perl, the =~ and !~ operators match with wildcards. =~ is | ||
+ | a direct opposite of !~, where it returns true if the patterns patch, while | ||
+ | !~ returns false. In this example, $bar is "epic": | ||
+ | |||
+ | @ foo = bar =~ [*pi*] /* returns 1 */ | ||
+ | @ foo = bar !~ [*z*] /* returns 1 */ | ||
+ | |||
+ | The various bitwise operators are of special interest also. Assuming $foo is 12 | ||
+ | and $bar is 11: | ||
+ | |||
+ | foo & bar /* returns 8 */ | ||
+ | foo | bar /* returns 15 */ | ||
+ | foo ^ bar /* returns 7 */ | ||
+ | |||
+ | The exponential operator takes numbers to various powers. It is especially | ||
+ | useful, since many script writers create a $power() function for this purpose. | ||
+ | It supports negative and fractional exponents as long as the system's math | ||
+ | library (libm) does. Assuming $foo is 9: | ||
+ | |||
+ | foo ** 2 /* returns 81 */ | ||
+ | foo ** 0.5 /* returns 3 */ | ||
+ | |||
+ | The {pre,post}fix {in,de}crement operators are big timesavers that C and C++ | ||
+ | users everywhere swear by. They have also been known to swear at them, for | ||
+ | reasons you will soon see. Assume $foo is 5, each column shows 3 ways of | ||
+ | doing the same thing, from least efficient to most efficient: | ||
+ | |||
+ | @ foo = foo + 1 @ foo = foo - 1 | ||
+ | @ foo += 1 @ foo -= 1 | ||
+ | @ foo++ @ foo-- | ||
+ | |||
+ | However, these operators have pitfalls, which are mainly discovered by those | ||
+ | who do not understand how they work. Both may either prefix or postfix a | ||
+ | variable; prefix causes it to evaluate before the operation, postfix causes | ||
+ | it to evaluate aster. For the examples shown above, it makes no difference. | ||
+ | However, it does make a difference in this example: | ||
+ | |||
+ | while ( foo++ < 10 ) { ... } | ||
+ | |||
+ | The expression is evaluated for whether $foo is less than 10, and then $foo | ||
+ | is incremented. If the autoincrement operator was instead used in prefix | ||
+ | form, $foo would be incremented **before** the expression was evaluated, which | ||
+ | would cause the loop to have one less iteration. | ||
+ | |||
+ | Another pitfall of the autoincrement and decrement operators is the | ||
+ | ambiguity introduced by insufficient whitespace when used in conjunction | ||
+ | with addition and subtraction operators. Consider the following: | ||
+ | |||
+ | @ foo = 4 | ||
+ | @ bar = 8 | ||
+ | @ foobar = foo+++bar | ||
+ | |||
+ | How should one interpret the last assignment? Should it really look like | ||
+ | ${foo++ + bar} or ${foo + ++bar}? It's hard to tell. The best solution is | ||
+ | to not write code that looks so silly and unreadable. Add a couple spaces, | ||
+ | and there is no ambiguity. (The answer is, the first one.) | ||
+ | |||
+ | Another popular operator familiar to most C/C++ programmers is the tertiary | ||
+ | operator (sometimes referred to as the alternation operator). It performs | ||
+ | a function similar to IF, except is much more compact and efficient. We'll | ||
+ | let $foo be 5 again: | ||
+ | |||
+ | @ bar = foo > 3 ? 1 : 0 /* sets $bar to 1 */ | ||
+ | @ bar = foo > 8 ? 1 : 0 /* sets $bar to 0 */ | ||
+ | |||
+ | Functions (built-in and scripted) can also be used within expressions. The | ||
+ | function will be evaluated, and its return value is used in the expression: | ||
+ | |||
+ | @ foo = pattern(b* foo bar blah) /* sets $foo to "bar blah" */ | ||
+ | |||
+ | All functions implicitly use a special operator, (). That is, the pair of | ||
+ | parentheses themselves compose an operator, though of course it is somewhat | ||
+ | different in nature from more traditional operators like '+' or '<' or '&'. | ||
+ | Functions (aliases with return values) require the () to function properly. | ||
+ | |||
+ | A similar operator is [], which is used for alias and variable structures. | ||
+ | We've already seen that it can be used to explicitly switch the evaluation | ||
+ | context to text. This can be extended to structure elements, such that | ||
+ | they can be expanded on the fly: | ||
+ | |||
+ | @ foo.1.1 = [foo] | ||
+ | @ foo.1.2 = [bar] | ||
+ | alias blah echo $foo[1][$0] | ||
+ | /blah 2 /* expands to $foo.1.2 -> "bar" */ | ||
+ | |||
+ | The same can be applied to aliases and functions as well. Because of the | ||
+ | nature of the [] operator, anything may be expanded inside it, variables and | ||
+ | functions alike. | ||