Operator Precedence and Associativity in Formula Fields

Weekly updated column for helping skill development. Users can't directly post requests but a PM or mail can be sent to Skill-Clinician.

Moderator: Skill Clinician

Post Reply
User avatar
mmpx222
Junior Member
Paladin
Posts: 154
Joined: Sat Apr 26, 2014 9:19 am
Korea South

Operator Precedence and Associativity in Formula Fields

Post by mmpx222 » Mon Jul 13, 2020 5:24 pm

I've been trying to write a parser and interpreter for the formula syntax used in text files like SkillDesc.txt. In doing so, I discovered that there has been no definitive information regarding the operator precedence and associativity rules used by Diablo 2. I therefore prepared a series of experiments to verify the operator precedence rules.

The full details of the experiment is available on a branch of my GitHub repository.

Summary:
  • All binary operators and the conditional expression operator are left-associative.
  • Conditional expressions (cond ? true_expr : false_expr) are more tightly binding than any other operator (parentheses notwithstanding). This is different from how most C/C++-based programming languages, which given them much lower priority.
    • The true_expr does not accept "naked" binary operators. For example, 5 ? 2 + 3 : 7 is invalid syntax. To use binary operators in true_expr, you must surround them with parentheses like this: (5 ? (2 + 3) : 7).
    • The true_expr and false_expr do not accept "naked" unary operators. For example, 2 ? -5 : -3 is invalid syntax. To use unary operators in true_expr or false_expr, you must surround them with parentheses like this: (2 ? (-5) : (-3)).
  • There is no unary plus (+) operator. Diablo 2 simply does not support it.
  • The unary minus (-) operator cannot be chained. For example, --3 and - -5 are both invalid. However, -(-7) works.
Here is a table of operator groups by precedence, from highest (top) to lowest (bottom). Operators in the same group are left-associative; that is, they are parsed from left to right.

Group Operator
Parenthesis ( expr )
Conditional Expression cond ? true_expr : false_expr
Unary Operator -
Multiplication and Division *, /
Addition and Subtraction +, -
Comparisons ==, !=, >, <, >=, <=
D2TXT / D2INI - Python scripts for editing TXT files, or converting between TXT ↔ INI files

User avatar
mmpx222
Junior Member
Paladin
Posts: 154
Joined: Sat Apr 26, 2014 9:19 am
Korea South

Re: Operator Precedence and Associativity in Formula Fields

Post by mmpx222 » Tue Jul 14, 2020 10:23 am

I did a bit more research, this time regarding functions like min() and skill(). The results are available in the same repository linked in the first post.

Numeric Functions - min, max, rand

These functions accept two expressions as arguments and return an integer. Arguments are separated by a comma (,).
  • min(a, b): Returns the smaller of the two.
    • If either a or b is less than 0, returns 0 instead.
  • max(a, b): Returns the larger of the two.
    • If either a or b is less than 0, returns 0 instead.
  • rand(a, b): Returns a random number between the two, inclusive.
    • If a > b, returns a instead.
    • If either a or b is less than 0, returns 0 instead.
Reference Functions - skill, miss, stat, sklvl

These functions accept a reference string or a primary expression as the first argument, and an identifier as the second argument. Notably, sklvl() accepts two identifiers as the second and third arguments. Arguments are separated by dots (.).

A reference string can be the name of a skill, missile, or stat, depending on the function used. They must be surrounded by single quotes ('').

A primary expression is one of:
  • Integers (without the unary minus (-) operator)
  • Identifier codes (e.g. ln34, edmx).
  • Function calls
  • Any expression surrounded by parentheses (()).
Dynamically Selecting References

Using primary expressions, you can control which skill, missile, or stat is selected. For example, the following formula will retrieve the minimum elemental damage of Fire Arrow (id: 7) or Cold Arrow (id: 11) , depending on the character level:

Code: Select all

skill( ((ulvl < 20) ? 7 : 11).edmn )
Note that the dot (.) must be immediately followed by the identifier (edmx); no whitespace is allowed between the two. However, using whitespace anywhere else is OK:

Code: Select all

skill('Fire Arrow'  .edmn)
skill(  'Cold Arrow'  .usmc  )
skill  (  'Chilling Armor'  .len  )
D2TXT / D2INI - Python scripts for editing TXT files, or converting between TXT ↔ INI files

Post Reply

Return to “Skill Clinic”